Commit 9cb14242 authored by Zhao Jiazhong's avatar Zhao Jiazhong Committed by Commit Bot

[mips][deoptimizer] Change deopt entries into builtins

Port c7cb9bec
https://chromium-review.googlesource.com/c/v8/v8/+/2488682

Change-Id: I782663f35d0beeea83a95a266cb787ef4c192ea8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2496406Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Zhao Jiazhong <zhaojiazhong-hf@loongson.cn>
Cr-Commit-Position: refs/heads/master@{#70748}
parent a4449f26
...@@ -3624,6 +3624,219 @@ void Builtins::Generate_MemCopyUint8Uint8(MacroAssembler* masm) { ...@@ -3624,6 +3624,219 @@ void Builtins::Generate_MemCopyUint8Uint8(MacroAssembler* masm) {
} }
} }
namespace {
// This code tries to be close to ia32 code so that any changes can be
// easily ported.
void Generate_DeoptimizationEntry(MacroAssembler* masm,
DeoptimizeKind deopt_kind) {
Isolate* isolate = masm->isolate();
// Unlike on ARM we don't save all the registers, just the useful ones.
// For the rest, there are gaps on the stack, so the offsets remain the same.
static constexpr int kNumberOfRegisters = Register::kNumRegisters;
RegList restored_regs = kJSCallerSaved | kCalleeSaved;
RegList saved_regs = restored_regs | sp.bit() | ra.bit();
static constexpr int kDoubleRegsSize =
kDoubleSize * DoubleRegister::kNumRegisters;
// Save all FPU registers before messing with them.
__ Subu(sp, sp, Operand(kDoubleRegsSize));
const RegisterConfiguration* config = RegisterConfiguration::Default();
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int offset = code * kDoubleSize;
__ Sdc1(fpu_reg, MemOperand(sp, offset));
}
// Push saved_regs (needed to populate FrameDescription::registers_).
// Leave gaps for other registers.
__ Subu(sp, sp, kNumberOfRegisters * kPointerSize);
for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
if ((saved_regs & (1 << i)) != 0) {
__ sw(ToRegister(i), MemOperand(sp, kPointerSize * i));
}
}
__ li(a2,
ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate));
__ sw(fp, MemOperand(a2));
static constexpr int kSavedRegistersAreaSize =
(kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
__ li(a2, Operand(Deoptimizer::kFixedExitSizeMarker));
// Get the address of the location in the code object (a3) (return
// address for lazy deoptimization) and compute the fp-to-sp delta in
// register t0.
__ mov(a3, ra);
__ Addu(t0, sp, Operand(kSavedRegistersAreaSize));
__ Subu(t0, fp, t0);
// Allocate a new deoptimizer object.
__ PrepareCallCFunction(6, t1);
// Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
__ mov(a0, zero_reg);
Label context_check;
__ lw(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
__ JumpIfSmi(a1, &context_check);
__ lw(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
__ bind(&context_check);
__ li(a1, Operand(static_cast<int>(deopt_kind)));
// a2: bailout id already loaded.
// a3: code address or 0 already loaded.
__ sw(t0, CFunctionArgumentOperand(5)); // Fp-to-sp delta.
__ li(t1, ExternalReference::isolate_address(isolate));
__ sw(t1, CFunctionArgumentOperand(6)); // Isolate.
// Call Deoptimizer::New().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::new_deoptimizer_function(), 6);
}
// Preserve "deoptimizer" object in register v0 and get the input
// frame descriptor pointer to a1 (deoptimizer->input_);
// Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
__ mov(a0, v0);
__ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
// Copy core registers into FrameDescription::registers_[kNumRegisters].
DCHECK_EQ(Register::kNumRegisters, kNumberOfRegisters);
for (int i = 0; i < kNumberOfRegisters; i++) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((saved_regs & (1 << i)) != 0) {
__ lw(a2, MemOperand(sp, i * kPointerSize));
__ sw(a2, MemOperand(a1, offset));
} else if (FLAG_debug_code) {
__ li(a2, kDebugZapValue);
__ sw(a2, MemOperand(a1, offset));
}
}
int double_regs_offset = FrameDescription::double_registers_offset();
// Copy FPU registers to
// double_registers_[DoubleRegister::kNumAllocatableRegisters]
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
int dst_offset = code * kDoubleSize + double_regs_offset;
int src_offset = code * kDoubleSize + kNumberOfRegisters * kPointerSize;
__ Ldc1(f0, MemOperand(sp, src_offset));
__ Sdc1(f0, MemOperand(a1, dst_offset));
}
// Remove the saved registers from the stack.
__ Addu(sp, sp, Operand(kSavedRegistersAreaSize));
// Compute a pointer to the unwinding limit in register a2; that is
// the first stack slot not part of the input frame.
__ lw(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
__ Addu(a2, a2, sp);
// Unwind the stack down to - but not including - the unwinding
// limit and copy the contents of the activation frame to the input
// frame description.
__ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
Label pop_loop;
Label pop_loop_header;
__ BranchShort(&pop_loop_header);
__ bind(&pop_loop);
__ pop(t0);
__ sw(t0, MemOperand(a3, 0));
__ addiu(a3, a3, sizeof(uint32_t));
__ bind(&pop_loop_header);
__ BranchShort(&pop_loop, ne, a2, Operand(sp));
// Compute the output frame in the deoptimizer.
__ push(a0); // Preserve deoptimizer object across call.
// a0: deoptimizer object; a1: scratch.
__ PrepareCallCFunction(1, a1);
// Call Deoptimizer::ComputeOutputFrames().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
}
__ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ lw(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header;
// Outer loop state: t0 = current "FrameDescription** output_",
// a1 = one past the last FrameDescription**.
__ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
__ lw(t0, MemOperand(a0, Deoptimizer::output_offset())); // t0 is output_.
__ Lsa(a1, t0, a1, kPointerSizeLog2);
__ BranchShort(&outer_loop_header);
__ bind(&outer_push_loop);
// Inner loop state: a2 = current FrameDescription*, a3 = loop index.
__ lw(a2, MemOperand(t0, 0)); // output_[ix]
__ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
__ BranchShort(&inner_loop_header);
__ bind(&inner_push_loop);
__ Subu(a3, a3, Operand(sizeof(uint32_t)));
__ Addu(t2, a2, Operand(a3));
__ lw(t3, MemOperand(t2, FrameDescription::frame_content_offset()));
__ push(t3);
__ bind(&inner_loop_header);
__ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
__ Addu(t0, t0, Operand(kPointerSize));
__ bind(&outer_loop_header);
__ BranchShort(&outer_push_loop, lt, t0, Operand(a1));
__ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int src_offset = code * kDoubleSize + double_regs_offset;
__ Ldc1(fpu_reg, MemOperand(a1, src_offset));
}
// Push pc and continuation from the last output frame.
__ lw(t2, MemOperand(a2, FrameDescription::pc_offset()));
__ push(t2);
__ lw(t2, MemOperand(a2, FrameDescription::continuation_offset()));
__ push(t2);
// Technically restoring 'at' should work unless zero_reg is also restored
// but it's safer to check for this.
DCHECK(!(at.bit() & restored_regs));
// Restore the registers from the last output frame.
__ mov(at, a2);
for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((restored_regs & (1 << i)) != 0) {
__ lw(ToRegister(i), MemOperand(at, offset));
}
}
__ pop(at); // Get continuation, leave pc on stack.
__ pop(ra);
__ Jump(at);
__ stop();
}
} // namespace
void Builtins::Generate_DeoptimizationEntry_Eager(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kEager);
}
void Builtins::Generate_DeoptimizationEntry_Soft(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kSoft);
}
void Builtins::Generate_DeoptimizationEntry_Bailout(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kBailout);
}
void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy);
}
#undef __ #undef __
} // namespace internal } // namespace internal
......
...@@ -3190,6 +3190,218 @@ void Builtins::Generate_DirectCEntry(MacroAssembler* masm) { ...@@ -3190,6 +3190,218 @@ void Builtins::Generate_DirectCEntry(MacroAssembler* masm) {
__ Jump(t9); __ Jump(t9);
} }
namespace {
// This code tries to be close to ia32 code so that any changes can be
// easily ported.
void Generate_DeoptimizationEntry(MacroAssembler* masm,
DeoptimizeKind deopt_kind) {
Isolate* isolate = masm->isolate();
// Unlike on ARM we don't save all the registers, just the useful ones.
// For the rest, there are gaps on the stack, so the offsets remain the same.
const int kNumberOfRegisters = Register::kNumRegisters;
RegList restored_regs = kJSCallerSaved | kCalleeSaved;
RegList saved_regs = restored_regs | sp.bit() | ra.bit();
const int kDoubleRegsSize = kDoubleSize * DoubleRegister::kNumRegisters;
// Save all double FPU registers before messing with them.
__ Dsubu(sp, sp, Operand(kDoubleRegsSize));
const RegisterConfiguration* config = RegisterConfiguration::Default();
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int offset = code * kDoubleSize;
__ Sdc1(fpu_reg, MemOperand(sp, offset));
}
// Push saved_regs (needed to populate FrameDescription::registers_).
// Leave gaps for other registers.
__ Dsubu(sp, sp, kNumberOfRegisters * kPointerSize);
for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
if ((saved_regs & (1 << i)) != 0) {
__ Sd(ToRegister(i), MemOperand(sp, kPointerSize * i));
}
}
__ li(a2,
ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate));
__ Sd(fp, MemOperand(a2));
const int kSavedRegistersAreaSize =
(kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
__ li(a2, Operand(Deoptimizer::kFixedExitSizeMarker));
// Get the address of the location in the code object (a3) (return
// address for lazy deoptimization) and compute the fp-to-sp delta in
// register a4.
__ mov(a3, ra);
__ Daddu(a4, sp, Operand(kSavedRegistersAreaSize));
__ Dsubu(a4, fp, a4);
// Allocate a new deoptimizer object.
__ PrepareCallCFunction(6, a5);
// Pass six arguments, according to n64 ABI.
__ mov(a0, zero_reg);
Label context_check;
__ Ld(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
__ JumpIfSmi(a1, &context_check);
__ Ld(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
__ bind(&context_check);
__ li(a1, Operand(static_cast<int>(deopt_kind)));
// a2: bailout id already loaded.
// a3: code address or 0 already loaded.
// a4: already has fp-to-sp delta.
__ li(a5, ExternalReference::isolate_address(isolate));
// Call Deoptimizer::New().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::new_deoptimizer_function(), 6);
}
// Preserve "deoptimizer" object in register v0 and get the input
// frame descriptor pointer to a1 (deoptimizer->input_);
// Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
__ mov(a0, v0);
__ Ld(a1, MemOperand(v0, Deoptimizer::input_offset()));
// Copy core registers into FrameDescription::registers_[kNumRegisters].
DCHECK_EQ(Register::kNumRegisters, kNumberOfRegisters);
for (int i = 0; i < kNumberOfRegisters; i++) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((saved_regs & (1 << i)) != 0) {
__ Ld(a2, MemOperand(sp, i * kPointerSize));
__ Sd(a2, MemOperand(a1, offset));
} else if (FLAG_debug_code) {
__ li(a2, kDebugZapValue);
__ Sd(a2, MemOperand(a1, offset));
}
}
int double_regs_offset = FrameDescription::double_registers_offset();
// Copy FPU registers to
// double_registers_[DoubleRegister::kNumAllocatableRegisters]
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
int dst_offset = code * kDoubleSize + double_regs_offset;
int src_offset = code * kDoubleSize + kNumberOfRegisters * kPointerSize;
__ Ldc1(f0, MemOperand(sp, src_offset));
__ Sdc1(f0, MemOperand(a1, dst_offset));
}
// Remove the saved registers from the stack.
__ Daddu(sp, sp, Operand(kSavedRegistersAreaSize));
// Compute a pointer to the unwinding limit in register a2; that is
// the first stack slot not part of the input frame.
__ Ld(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
__ Daddu(a2, a2, sp);
// Unwind the stack down to - but not including - the unwinding
// limit and copy the contents of the activation frame to the input
// frame description.
__ Daddu(a3, a1, Operand(FrameDescription::frame_content_offset()));
Label pop_loop;
Label pop_loop_header;
__ BranchShort(&pop_loop_header);
__ bind(&pop_loop);
__ pop(a4);
__ Sd(a4, MemOperand(a3, 0));
__ daddiu(a3, a3, sizeof(uint64_t));
__ bind(&pop_loop_header);
__ BranchShort(&pop_loop, ne, a2, Operand(sp));
// Compute the output frame in the deoptimizer.
__ push(a0); // Preserve deoptimizer object across call.
// a0: deoptimizer object; a1: scratch.
__ PrepareCallCFunction(1, a1);
// Call Deoptimizer::ComputeOutputFrames().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
}
__ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ Ld(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header;
// Outer loop state: a4 = current "FrameDescription** output_",
// a1 = one past the last FrameDescription**.
__ Lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
__ Ld(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_.
__ Dlsa(a1, a4, a1, kPointerSizeLog2);
__ BranchShort(&outer_loop_header);
__ bind(&outer_push_loop);
// Inner loop state: a2 = current FrameDescription*, a3 = loop index.
__ Ld(a2, MemOperand(a4, 0)); // output_[ix]
__ Ld(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
__ BranchShort(&inner_loop_header);
__ bind(&inner_push_loop);
__ Dsubu(a3, a3, Operand(sizeof(uint64_t)));
__ Daddu(a6, a2, Operand(a3));
__ Ld(a7, MemOperand(a6, FrameDescription::frame_content_offset()));
__ push(a7);
__ bind(&inner_loop_header);
__ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
__ Daddu(a4, a4, Operand(kPointerSize));
__ bind(&outer_loop_header);
__ BranchShort(&outer_push_loop, lt, a4, Operand(a1));
__ Ld(a1, MemOperand(a0, Deoptimizer::input_offset()));
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int src_offset = code * kDoubleSize + double_regs_offset;
__ Ldc1(fpu_reg, MemOperand(a1, src_offset));
}
// Push pc and continuation from the last output frame.
__ Ld(a6, MemOperand(a2, FrameDescription::pc_offset()));
__ push(a6);
__ Ld(a6, MemOperand(a2, FrameDescription::continuation_offset()));
__ push(a6);
// Technically restoring 'at' should work unless zero_reg is also restored
// but it's safer to check for this.
DCHECK(!(at.bit() & restored_regs));
// Restore the registers from the last output frame.
__ mov(at, a2);
for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((restored_regs & (1 << i)) != 0) {
__ Ld(ToRegister(i), MemOperand(at, offset));
}
}
__ pop(at); // Get continuation, leave pc on stack.
__ pop(ra);
__ Jump(at);
__ stop();
}
} // namespace
void Builtins::Generate_DeoptimizationEntry_Eager(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kEager);
}
void Builtins::Generate_DeoptimizationEntry_Soft(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kSoft);
}
void Builtins::Generate_DeoptimizationEntry_Bailout(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kBailout);
}
void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) {
Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy);
}
#undef __ #undef __
} // namespace internal } // namespace internal
......
...@@ -5522,17 +5522,18 @@ void TurboAssembler::ResetSpeculationPoisonRegister() { ...@@ -5522,17 +5522,18 @@ void TurboAssembler::ResetSpeculationPoisonRegister() {
li(kSpeculationPoisonRegister, -1); li(kSpeculationPoisonRegister, -1);
} }
void TurboAssembler::CallForDeoptimization(Address target, int deopt_id, void TurboAssembler::CallForDeoptimization(Builtins::Name target, int,
Label* exit, DeoptimizeKind kind) { Label* exit, DeoptimizeKind kind,
Label*) {
BlockTrampolinePoolScope block_trampoline_pool(this);
Lw(t9,
MemOperand(kRootRegister, IsolateData::builtin_entry_slot_offset(target)));
Call(t9);
DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
(kind == DeoptimizeKind::kLazy)
? Deoptimizer::kLazyDeoptExitSize
: Deoptimizer::kNonLazyDeoptExitSize);
USE(exit, kind); USE(exit, kind);
NoRootArrayScope no_root_array(this);
// Save the deipt id in kRootRegister (we don't need the roots array from now
// on).
DCHECK_LE(deopt_id, 0xFFFF);
li(kRootRegister, deopt_id);
Call(target, RelocInfo::RUNTIME_ENTRY);
} }
} // namespace internal } // namespace internal
......
...@@ -237,8 +237,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -237,8 +237,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
// The return address on the stack is used by frame iteration. // The return address on the stack is used by frame iteration.
void StoreReturnAddressAndCall(Register target); void StoreReturnAddressAndCall(Register target);
void CallForDeoptimization(Address target, int deopt_id, Label* exit, void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
DeoptimizeKind kind); DeoptimizeKind kind,
Label* jump_deoptimization_entry_label);
void Ret(COND_ARGS); void Ret(COND_ARGS);
inline void Ret(BranchDelaySlot bd, Condition cond = al, inline void Ret(BranchDelaySlot bd, Condition cond = al,
......
...@@ -5882,16 +5882,17 @@ void TurboAssembler::ResetSpeculationPoisonRegister() { ...@@ -5882,16 +5882,17 @@ void TurboAssembler::ResetSpeculationPoisonRegister() {
li(kSpeculationPoisonRegister, -1); li(kSpeculationPoisonRegister, -1);
} }
void TurboAssembler::CallForDeoptimization(Address target, int deopt_id, void TurboAssembler::CallForDeoptimization(Builtins::Name target, int,
Label* exit, DeoptimizeKind kind) { Label* exit, DeoptimizeKind kind,
Label*) {
Ld(t9,
MemOperand(kRootRegister, IsolateData::builtin_entry_slot_offset(target)));
Call(t9);
DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
(kind == DeoptimizeKind::kLazy)
? Deoptimizer::kLazyDeoptExitSize
: Deoptimizer::kNonLazyDeoptExitSize);
USE(exit, kind); USE(exit, kind);
NoRootArrayScope no_root_array(this);
// Save the deopt id in kRootRegister (we don't need the roots array from now
// on).
DCHECK_LE(deopt_id, 0xFFFF);
li(kRootRegister, deopt_id);
Call(target, RelocInfo::RUNTIME_ENTRY);
} }
} // namespace internal } // namespace internal
......
...@@ -260,8 +260,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -260,8 +260,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
// The return address on the stack is used by frame iteration. // The return address on the stack is used by frame iteration.
void StoreReturnAddressAndCall(Register target); void StoreReturnAddressAndCall(Register target);
void CallForDeoptimization(Address target, int deopt_id, Label* exit, void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
DeoptimizeKind kind); DeoptimizeKind kind,
Label* jump_deoptimization_entry_label);
void Ret(COND_ARGS); void Ret(COND_ARGS);
inline void Ret(BranchDelaySlot bd, Condition cond = al, inline void Ret(BranchDelaySlot bd, Condition cond = al,
......
...@@ -868,8 +868,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -868,8 +868,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kArchDeoptimize: { case kArchDeoptimize: {
DeoptimizationExit* exit = DeoptimizationExit* exit =
BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
CodeGenResult result = AssembleDeoptimizerCall(exit); __ Branch(exit->label());
if (result != kSuccess) return result;
break; break;
} }
case kArchRet: case kArchRet:
...@@ -4088,7 +4087,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) { ...@@ -4088,7 +4087,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
void CodeGenerator::FinishCode() {} void CodeGenerator::FinishCode() {}
void CodeGenerator::PrepareForDeoptimizationExits(int deopt_count) {} void CodeGenerator::PrepareForDeoptimizationExits(
ZoneDeque<DeoptimizationExit*>* exits) {}
void CodeGenerator::AssembleMove(InstructionOperand* source, void CodeGenerator::AssembleMove(InstructionOperand* source,
InstructionOperand* destination) { InstructionOperand* destination) {
......
...@@ -847,8 +847,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -847,8 +847,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kArchDeoptimize: { case kArchDeoptimize: {
DeoptimizationExit* exit = DeoptimizationExit* exit =
BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
CodeGenResult result = AssembleDeoptimizerCall(exit); __ Branch(exit->label());
if (result != kSuccess) return result;
break; break;
} }
case kArchRet: case kArchRet:
...@@ -4354,7 +4353,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) { ...@@ -4354,7 +4353,8 @@ void CodeGenerator::AssembleReturn(InstructionOperand* pop) {
void CodeGenerator::FinishCode() {} void CodeGenerator::FinishCode() {}
void CodeGenerator::PrepareForDeoptimizationExits(int deopt_count) {} void CodeGenerator::PrepareForDeoptimizationExits(
ZoneDeque<DeoptimizationExit*>* exits) {}
void CodeGenerator::AssembleMove(InstructionOperand* source, void CodeGenerator::AssembleMove(InstructionOperand* source,
InstructionOperand* destination) { InstructionOperand* destination) {
......
...@@ -2,215 +2,14 @@ ...@@ -2,215 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/codegen/macro-assembler.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/safepoint-table.h"
#include "src/deoptimizer/deoptimizer.h" #include "src/deoptimizer/deoptimizer.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
const bool Deoptimizer::kSupportsFixedDeoptExitSizes = false; const bool Deoptimizer::kSupportsFixedDeoptExitSizes = true;
const int Deoptimizer::kNonLazyDeoptExitSize = 0; const int Deoptimizer::kNonLazyDeoptExitSize = 3 * kInstrSize;
const int Deoptimizer::kLazyDeoptExitSize = 0; const int Deoptimizer::kLazyDeoptExitSize = 3 * kInstrSize;
#define __ masm->
// This code tries to be close to ia32 code so that any changes can be
// easily ported.
void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
Isolate* isolate,
DeoptimizeKind deopt_kind) {
NoRootArrayScope no_root_array(masm);
// Unlike on ARM we don't save all the registers, just the useful ones.
// For the rest, there are gaps on the stack, so the offsets remain the same.
const int kNumberOfRegisters = Register::kNumRegisters;
RegList restored_regs = kJSCallerSaved | kCalleeSaved;
RegList saved_regs = restored_regs | sp.bit() | ra.bit();
const int kDoubleRegsSize = kDoubleSize * DoubleRegister::kNumRegisters;
// Save all FPU registers before messing with them.
__ Subu(sp, sp, Operand(kDoubleRegsSize));
const RegisterConfiguration* config = RegisterConfiguration::Default();
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int offset = code * kDoubleSize;
__ Sdc1(fpu_reg, MemOperand(sp, offset));
}
// Push saved_regs (needed to populate FrameDescription::registers_).
// Leave gaps for other registers.
__ Subu(sp, sp, kNumberOfRegisters * kPointerSize);
for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
if ((saved_regs & (1 << i)) != 0) {
__ sw(ToRegister(i), MemOperand(sp, kPointerSize * i));
}
}
__ li(a2, Operand(ExternalReference::Create(
IsolateAddressId::kCEntryFPAddress, isolate)));
__ sw(fp, MemOperand(a2));
const int kSavedRegistersAreaSize =
(kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
// Get the bailout id is passed as kRootRegister by the caller.
__ mov(a2, kRootRegister);
// Get the address of the location in the code object (a3) (return
// address for lazy deoptimization) and compute the fp-to-sp delta in
// register t0.
__ mov(a3, ra);
__ Addu(t0, sp, Operand(kSavedRegistersAreaSize));
__ Subu(t0, fp, t0);
// Allocate a new deoptimizer object.
__ PrepareCallCFunction(6, t1);
// Pass four arguments in a0 to a3 and fifth & sixth arguments on stack.
__ mov(a0, zero_reg);
Label context_check;
__ lw(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
__ JumpIfSmi(a1, &context_check);
__ lw(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
__ bind(&context_check);
__ li(a1, Operand(static_cast<int>(deopt_kind)));
// a2: bailout id already loaded.
// a3: code address or 0 already loaded.
__ sw(t0, CFunctionArgumentOperand(5)); // Fp-to-sp delta.
__ li(t1, Operand(ExternalReference::isolate_address(isolate)));
__ sw(t1, CFunctionArgumentOperand(6)); // Isolate.
// Call Deoptimizer::New().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::new_deoptimizer_function(), 6);
}
// Preserve "deoptimizer" object in register v0 and get the input
// frame descriptor pointer to a1 (deoptimizer->input_);
// Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
__ mov(a0, v0);
__ lw(a1, MemOperand(v0, Deoptimizer::input_offset()));
// Copy core registers into FrameDescription::registers_[kNumRegisters].
DCHECK_EQ(Register::kNumRegisters, kNumberOfRegisters);
for (int i = 0; i < kNumberOfRegisters; i++) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((saved_regs & (1 << i)) != 0) {
__ lw(a2, MemOperand(sp, i * kPointerSize));
__ sw(a2, MemOperand(a1, offset));
} else if (FLAG_debug_code) {
__ li(a2, kDebugZapValue);
__ sw(a2, MemOperand(a1, offset));
}
}
int double_regs_offset = FrameDescription::double_registers_offset();
// Copy FPU registers to
// double_registers_[DoubleRegister::kNumAllocatableRegisters]
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
int dst_offset = code * kDoubleSize + double_regs_offset;
int src_offset =
code * kDoubleSize + kNumberOfRegisters * kPointerSize;
__ Ldc1(f0, MemOperand(sp, src_offset));
__ Sdc1(f0, MemOperand(a1, dst_offset));
}
// Remove the saved registers from the stack.
__ Addu(sp, sp, Operand(kSavedRegistersAreaSize));
// Compute a pointer to the unwinding limit in register a2; that is
// the first stack slot not part of the input frame.
__ lw(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
__ Addu(a2, a2, sp);
// Unwind the stack down to - but not including - the unwinding
// limit and copy the contents of the activation frame to the input
// frame description.
__ Addu(a3, a1, Operand(FrameDescription::frame_content_offset()));
Label pop_loop;
Label pop_loop_header;
__ BranchShort(&pop_loop_header);
__ bind(&pop_loop);
__ pop(t0);
__ sw(t0, MemOperand(a3, 0));
__ addiu(a3, a3, sizeof(uint32_t));
__ bind(&pop_loop_header);
__ BranchShort(&pop_loop, ne, a2, Operand(sp));
// Compute the output frame in the deoptimizer.
__ push(a0); // Preserve deoptimizer object across call.
// a0: deoptimizer object; a1: scratch.
__ PrepareCallCFunction(1, a1);
// Call Deoptimizer::ComputeOutputFrames().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
}
__ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ lw(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header;
// Outer loop state: t0 = current "FrameDescription** output_",
// a1 = one past the last FrameDescription**.
__ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
__ lw(t0, MemOperand(a0, Deoptimizer::output_offset())); // t0 is output_.
__ Lsa(a1, t0, a1, kPointerSizeLog2);
__ BranchShort(&outer_loop_header);
__ bind(&outer_push_loop);
// Inner loop state: a2 = current FrameDescription*, a3 = loop index.
__ lw(a2, MemOperand(t0, 0)); // output_[ix]
__ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
__ BranchShort(&inner_loop_header);
__ bind(&inner_push_loop);
__ Subu(a3, a3, Operand(sizeof(uint32_t)));
__ Addu(t2, a2, Operand(a3));
__ lw(t3, MemOperand(t2, FrameDescription::frame_content_offset()));
__ push(t3);
__ bind(&inner_loop_header);
__ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
__ Addu(t0, t0, Operand(kPointerSize));
__ bind(&outer_loop_header);
__ BranchShort(&outer_push_loop, lt, t0, Operand(a1));
__ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int src_offset = code * kDoubleSize + double_regs_offset;
__ Ldc1(fpu_reg, MemOperand(a1, src_offset));
}
// Push pc and continuation from the last output frame.
__ lw(t2, MemOperand(a2, FrameDescription::pc_offset()));
__ push(t2);
__ lw(t2, MemOperand(a2, FrameDescription::continuation_offset()));
__ push(t2);
// Technically restoring 'at' should work unless zero_reg is also restored
// but it's safer to check for this.
DCHECK(!(at.bit() & restored_regs));
// Restore the registers from the last output frame.
__ mov(at, a2);
for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((restored_regs & (1 << i)) != 0) {
__ lw(ToRegister(i), MemOperand(at, offset));
}
}
__ pop(at); // Get continuation, leave pc on stack.
__ pop(ra);
__ Jump(at);
__ stop();
}
// Maximum size of a table entry generated below. // Maximum size of a table entry generated below.
#ifdef _MIPS_ARCH_MIPS32R6 #ifdef _MIPS_ARCH_MIPS32R6
...@@ -239,7 +38,5 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { ...@@ -239,7 +38,5 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
void FrameDescription::SetPc(intptr_t pc) { pc_ = pc; } void FrameDescription::SetPc(intptr_t pc) { pc_ = pc; }
#undef __
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -2,215 +2,14 @@ ...@@ -2,215 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/codegen/macro-assembler.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/safepoint-table.h"
#include "src/deoptimizer/deoptimizer.h" #include "src/deoptimizer/deoptimizer.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
const bool Deoptimizer::kSupportsFixedDeoptExitSizes = false; const bool Deoptimizer::kSupportsFixedDeoptExitSizes = true;
const int Deoptimizer::kNonLazyDeoptExitSize = 0; const int Deoptimizer::kNonLazyDeoptExitSize = 3 * kInstrSize;
const int Deoptimizer::kLazyDeoptExitSize = 0; const int Deoptimizer::kLazyDeoptExitSize = 3 * kInstrSize;
#define __ masm->
// This code tries to be close to ia32 code so that any changes can be
// easily ported.
void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
Isolate* isolate,
DeoptimizeKind deopt_kind) {
NoRootArrayScope no_root_array(masm);
// Unlike on ARM we don't save all the registers, just the useful ones.
// For the rest, there are gaps on the stack, so the offsets remain the same.
const int kNumberOfRegisters = Register::kNumRegisters;
RegList restored_regs = kJSCallerSaved | kCalleeSaved;
RegList saved_regs = restored_regs | sp.bit() | ra.bit();
const int kDoubleRegsSize = kDoubleSize * DoubleRegister::kNumRegisters;
// Save all double FPU registers before messing with them.
__ Dsubu(sp, sp, Operand(kDoubleRegsSize));
const RegisterConfiguration* config = RegisterConfiguration::Default();
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int offset = code * kDoubleSize;
__ Sdc1(fpu_reg, MemOperand(sp, offset));
}
// Push saved_regs (needed to populate FrameDescription::registers_).
// Leave gaps for other registers.
__ Dsubu(sp, sp, kNumberOfRegisters * kPointerSize);
for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
if ((saved_regs & (1 << i)) != 0) {
__ Sd(ToRegister(i), MemOperand(sp, kPointerSize * i));
}
}
__ li(a2, Operand(ExternalReference::Create(
IsolateAddressId::kCEntryFPAddress, isolate)));
__ Sd(fp, MemOperand(a2));
const int kSavedRegistersAreaSize =
(kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
// Get the bailout is passed as kRootRegister by the caller.
__ mov(a2, kRootRegister);
// Get the address of the location in the code object (a3) (return
// address for lazy deoptimization) and compute the fp-to-sp delta in
// register a4.
__ mov(a3, ra);
__ Daddu(a4, sp, Operand(kSavedRegistersAreaSize));
__ Dsubu(a4, fp, a4);
// Allocate a new deoptimizer object.
__ PrepareCallCFunction(6, a5);
// Pass six arguments, according to n64 ABI.
__ mov(a0, zero_reg);
Label context_check;
__ Ld(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset));
__ JumpIfSmi(a1, &context_check);
__ Ld(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
__ bind(&context_check);
__ li(a1, Operand(static_cast<int>(deopt_kind)));
// a2: bailout id already loaded.
// a3: code address or 0 already loaded.
// a4: already has fp-to-sp delta.
__ li(a5, Operand(ExternalReference::isolate_address(isolate)));
// Call Deoptimizer::New().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::new_deoptimizer_function(), 6);
}
// Preserve "deoptimizer" object in register v0 and get the input
// frame descriptor pointer to a1 (deoptimizer->input_);
// Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below.
__ mov(a0, v0);
__ Ld(a1, MemOperand(v0, Deoptimizer::input_offset()));
// Copy core registers into FrameDescription::registers_[kNumRegisters].
DCHECK_EQ(Register::kNumRegisters, kNumberOfRegisters);
for (int i = 0; i < kNumberOfRegisters; i++) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((saved_regs & (1 << i)) != 0) {
__ Ld(a2, MemOperand(sp, i * kPointerSize));
__ Sd(a2, MemOperand(a1, offset));
} else if (FLAG_debug_code) {
__ li(a2, kDebugZapValue);
__ Sd(a2, MemOperand(a1, offset));
}
}
int double_regs_offset = FrameDescription::double_registers_offset();
// Copy FPU registers to
// double_registers_[DoubleRegister::kNumAllocatableRegisters]
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
int dst_offset = code * kDoubleSize + double_regs_offset;
int src_offset =
code * kDoubleSize + kNumberOfRegisters * kPointerSize;
__ Ldc1(f0, MemOperand(sp, src_offset));
__ Sdc1(f0, MemOperand(a1, dst_offset));
}
// Remove the saved registers from the stack.
__ Daddu(sp, sp, Operand(kSavedRegistersAreaSize));
// Compute a pointer to the unwinding limit in register a2; that is
// the first stack slot not part of the input frame.
__ Ld(a2, MemOperand(a1, FrameDescription::frame_size_offset()));
__ Daddu(a2, a2, sp);
// Unwind the stack down to - but not including - the unwinding
// limit and copy the contents of the activation frame to the input
// frame description.
__ Daddu(a3, a1, Operand(FrameDescription::frame_content_offset()));
Label pop_loop;
Label pop_loop_header;
__ BranchShort(&pop_loop_header);
__ bind(&pop_loop);
__ pop(a4);
__ Sd(a4, MemOperand(a3, 0));
__ daddiu(a3, a3, sizeof(uint64_t));
__ bind(&pop_loop_header);
__ BranchShort(&pop_loop, ne, a2, Operand(sp));
// Compute the output frame in the deoptimizer.
__ push(a0); // Preserve deoptimizer object across call.
// a0: deoptimizer object; a1: scratch.
__ PrepareCallCFunction(1, a1);
// Call Deoptimizer::ComputeOutputFrames().
{
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(ExternalReference::compute_output_frames_function(), 1);
}
__ pop(a0); // Restore deoptimizer object (class Deoptimizer).
__ Ld(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset()));
// Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header;
// Outer loop state: a4 = current "FrameDescription** output_",
// a1 = one past the last FrameDescription**.
__ Lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
__ Ld(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_.
__ Dlsa(a1, a4, a1, kPointerSizeLog2);
__ BranchShort(&outer_loop_header);
__ bind(&outer_push_loop);
// Inner loop state: a2 = current FrameDescription*, a3 = loop index.
__ Ld(a2, MemOperand(a4, 0)); // output_[ix]
__ Ld(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
__ BranchShort(&inner_loop_header);
__ bind(&inner_push_loop);
__ Dsubu(a3, a3, Operand(sizeof(uint64_t)));
__ Daddu(a6, a2, Operand(a3));
__ Ld(a7, MemOperand(a6, FrameDescription::frame_content_offset()));
__ push(a7);
__ bind(&inner_loop_header);
__ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg));
__ Daddu(a4, a4, Operand(kPointerSize));
__ bind(&outer_loop_header);
__ BranchShort(&outer_push_loop, lt, a4, Operand(a1));
__ Ld(a1, MemOperand(a0, Deoptimizer::input_offset()));
for (int i = 0; i < config->num_allocatable_double_registers(); ++i) {
int code = config->GetAllocatableDoubleCode(i);
const DoubleRegister fpu_reg = DoubleRegister::from_code(code);
int src_offset = code * kDoubleSize + double_regs_offset;
__ Ldc1(fpu_reg, MemOperand(a1, src_offset));
}
// Push pc and continuation from the last output frame.
__ Ld(a6, MemOperand(a2, FrameDescription::pc_offset()));
__ push(a6);
__ Ld(a6, MemOperand(a2, FrameDescription::continuation_offset()));
__ push(a6);
// Technically restoring 'at' should work unless zero_reg is also restored
// but it's safer to check for this.
DCHECK(!(at.bit() & restored_regs));
// Restore the registers from the last output frame.
__ mov(at, a2);
for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
if ((restored_regs & (1 << i)) != 0) {
__ Ld(ToRegister(i), MemOperand(at, offset));
}
}
__ pop(at); // Get continuation, leave pc on stack.
__ pop(ra);
__ Jump(at);
__ stop();
}
// Maximum size of a table entry generated below. // Maximum size of a table entry generated below.
#ifdef _MIPS_ARCH_MIPS64R6 #ifdef _MIPS_ARCH_MIPS64R6
...@@ -239,7 +38,5 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { ...@@ -239,7 +38,5 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
void FrameDescription::SetPc(intptr_t pc) { pc_ = pc; } void FrameDescription::SetPc(intptr_t pc) { pc_ = pc; }
#undef __
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment