From e3ccb5387770a0dcef6a45d0ddb35314ba2094cd Mon Sep 17 00:00:00 2001 From: Patrick Thier Date: Wed, 24 Mar 2021 15:24:39 +0000 Subject: [PATCH] Reland "[sparkplug][deoptimizer] Deoptimize to baseline." This is a reland of bdcd7d79d337182eff98d9c3d63b53da7e56ae2f Handle lazy deopts when the current bytecode is JumpLoop. Instead of advancing to the next bytecode, re-execute the JumpLoop. TBR=jgruber@chromium.org, neis@chromium.org Original change's description: > [sparkplug][deoptimizer] Deoptimize to baseline. > > If we have baseline code, deoptimize to baseline instead of the > interpreter. The process is similar to deopting to the interpreter. > We just use different builtins > (BaselineEnterAtBytecode/BaselineEnterAtNextBytecode) instead of > InterpreterEnterBytecodeDispatch/InterpreterEnterBytecodeAdvance, that > patch an interpreter frame to a baseline frame and continue execution in > baseline code (based on the deopt type, at the current or next > bytecode). > > Bug: v8:11420 > Change-Id: Iabaefb36c05155a435c7b380906a86d9b9d549fa > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2695591 > Commit-Queue: Patrick Thier > Reviewed-by: Leszek Swirski > Reviewed-by: Georg Neis > Reviewed-by: Jakob Gruber > Cr-Commit-Position: refs/heads/master@{#73609} Bug: v8:11420 Change-Id: Ib8cac028121188ddc23ff29377760ed684eb7392 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2783035 Reviewed-by: Patrick Thier Reviewed-by: Leszek Swirski Commit-Queue: Patrick Thier Cr-Commit-Position: refs/heads/master@{#73636} --- src/baseline/arm/baseline-assembler-arm-inl.h | 4 + .../arm64/baseline-assembler-arm64-inl.h | 2 + src/baseline/baseline-assembler.h | 3 + src/baseline/baseline-compiler.cc | 9 ++ .../ia32/baseline-assembler-ia32-inl.h | 4 + src/baseline/x64/baseline-assembler-x64-inl.h | 4 + src/builtins/arm/builtins-arm.cc | 119 +++++++++++++++++ src/builtins/arm64/builtins-arm64.cc | 121 +++++++++++++++++ src/builtins/builtins-definitions.h | 2 + src/builtins/ia32/builtins-ia32.cc | 122 ++++++++++++++++++ src/builtins/x64/builtins-x64.cc | 117 +++++++++++++++++ src/codegen/external-reference.cc | 29 +++++ src/codegen/external-reference.h | 2 + src/deoptimizer/deoptimizer-cfi-builtins.cc | 4 + src/deoptimizer/deoptimizer.cc | 78 +++++++---- src/execution/frames.cc | 28 ++-- src/objects/code-inl.h | 36 +++++- src/objects/code.h | 21 ++- test/mjsunit/regress/regress-crbug-1191886.js | 9 ++ 19 files changed, 664 insertions(+), 50 deletions(-) create mode 100644 test/mjsunit/regress/regress-crbug-1191886.js diff --git a/src/baseline/arm/baseline-assembler-arm-inl.h b/src/baseline/arm/baseline-assembler-arm-inl.h index d9174e2408..37bfb41c5f 100644 --- a/src/baseline/arm/baseline-assembler-arm-inl.h +++ b/src/baseline/arm/baseline-assembler-arm-inl.h @@ -92,6 +92,10 @@ void BaselineAssembler::Bind(Label* label) { __ bind(label); } +void BaselineAssembler::JumpTarget() { + // NOP on arm. +} + void BaselineAssembler::Jump(Label* target, Label::Distance distance) { __ b(target); } diff --git a/src/baseline/arm64/baseline-assembler-arm64-inl.h b/src/baseline/arm64/baseline-assembler-arm64-inl.h index b2da900f75..292e94b355 100644 --- a/src/baseline/arm64/baseline-assembler-arm64-inl.h +++ b/src/baseline/arm64/baseline-assembler-arm64-inl.h @@ -87,6 +87,8 @@ void BaselineAssembler::Bind(Label* label) { __ BindJumpTarget(label); } +void BaselineAssembler::JumpTarget() { __ JumpTarget(); } + void BaselineAssembler::Jump(Label* target, Label::Distance distance) { __ B(target); } diff --git a/src/baseline/baseline-assembler.h b/src/baseline/baseline-assembler.h index b9dae4724b..2c77a7199f 100644 --- a/src/baseline/baseline-assembler.h +++ b/src/baseline/baseline-assembler.h @@ -40,6 +40,9 @@ class BaselineAssembler { inline void DebugBreak(); inline void Bind(Label* label); + // Marks the current position as a valid jump target on CFI enabled + // architectures. + inline void JumpTarget(); inline void JumpIf(Condition cc, Label* target, Label::Distance distance = Label::kFar); inline void Jump(Label* target, Label::Distance distance = Label::kFar); diff --git a/src/baseline/baseline-compiler.cc b/src/baseline/baseline-compiler.cc index 85b87b93c5..30cccc92e1 100644 --- a/src/baseline/baseline-compiler.cc +++ b/src/baseline/baseline-compiler.cc @@ -426,6 +426,7 @@ void BaselineCompiler::PreVisitSingleBytecode() { void BaselineCompiler::VisitSingleBytecode() { int offset = iterator().current_offset(); + bool is_marked_as_jump_target = false; if (labels_[offset]) { // Bind labels for this offset that have already been linked to a // jump (i.e. forward jumps, excluding jump tables). @@ -436,15 +437,23 @@ void BaselineCompiler::VisitSingleBytecode() { labels_[offset]->linked.Clear(); #endif __ Bind(&labels_[offset]->unlinked); + is_marked_as_jump_target = true; } // Record positions of exception handlers. if (iterator().current_offset() == *next_handler_offset_) { __ ExceptionHandler(); next_handler_offset_++; + is_marked_as_jump_target = true; } DCHECK_LT(iterator().current_offset(), *next_handler_offset_); + // Mark position as valid jump target, if it isn't one already. + // This is required for the deoptimizer, when CFI is enabled. + if (!is_marked_as_jump_target) { + __ JumpTarget(); + } + if (FLAG_code_comments) { std::ostringstream str; str << "[ "; diff --git a/src/baseline/ia32/baseline-assembler-ia32-inl.h b/src/baseline/ia32/baseline-assembler-ia32-inl.h index cbd830172d..f86666e043 100644 --- a/src/baseline/ia32/baseline-assembler-ia32-inl.h +++ b/src/baseline/ia32/baseline-assembler-ia32-inl.h @@ -90,6 +90,10 @@ MemOperand BaselineAssembler::FeedbackVectorOperand() { void BaselineAssembler::Bind(Label* label) { __ bind(label); } +void BaselineAssembler::JumpTarget() { + // NOP on ia32. +} + void BaselineAssembler::Jump(Label* target, Label::Distance distance) { __ jmp(target, distance); } diff --git a/src/baseline/x64/baseline-assembler-x64-inl.h b/src/baseline/x64/baseline-assembler-x64-inl.h index 03b4903b87..81f624b886 100644 --- a/src/baseline/x64/baseline-assembler-x64-inl.h +++ b/src/baseline/x64/baseline-assembler-x64-inl.h @@ -93,6 +93,10 @@ MemOperand BaselineAssembler::FeedbackVectorOperand() { void BaselineAssembler::Bind(Label* label) { __ bind(label); } +void BaselineAssembler::JumpTarget() { + // NOP on x64. +} + void BaselineAssembler::Jump(Label* target, Label::Distance distance) { __ jmp(target, distance); } diff --git a/src/builtins/arm/builtins-arm.cc b/src/builtins/arm/builtins-arm.cc index d32ae88dbe..8c2243b25f 100644 --- a/src/builtins/arm/builtins-arm.cc +++ b/src/builtins/arm/builtins-arm.cc @@ -3462,6 +3462,125 @@ void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); } +namespace { + +// Converts an interpreter frame into a baseline frame and continues execution +// in baseline code (baseline code has to exist on the shared function info), +// either at the start or the end of the current bytecode. +void Generate_BaselineEntry(MacroAssembler* masm, bool next_bytecode) { + // Get bytecode array and bytecode offset from the stack frame. + __ ldr(kInterpreterBytecodeArrayRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ ldr(kInterpreterBytecodeOffsetRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister); + + // Get function from the frame. + Register closure = r1; + __ ldr(closure, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + + // Replace BytecodeOffset with the feedback vector. + Register feedback_vector = r2; + __ ldr(feedback_vector, + FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); + __ ldr(feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); + if (__ emit_debug_code()) { + Register scratch = r3; + __ CompareObjectType(feedback_vector, scratch, scratch, + FEEDBACK_VECTOR_TYPE); + __ Assert(eq, AbortReason::kExpectedFeedbackVector); + } + __ str(feedback_vector, + MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + feedback_vector = no_reg; + + // Get the Code object from the shared function info. + Register code_obj = r4; + __ ldr(code_obj, + FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(code_obj, + FieldMemOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); + __ ldr(code_obj, + FieldMemOperand(code_obj, BaselineData::kBaselineCodeOffset)); + closure = no_reg; + + // Compute baseline pc for bytecode offset. + __ Push(kInterpreterAccumulatorRegister); + ExternalReference get_baseline_pc_extref = + next_bytecode + ? ExternalReference::baseline_end_pc_for_bytecode_offset() + : ExternalReference::baseline_start_pc_for_bytecode_offset(); + Register get_baseline_pc = r3; + __ Move(get_baseline_pc, get_baseline_pc_extref); + + // If the code deoptimizes during the implicit function entry stack interrupt + // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is + // not a valid bytecode offset. + // TODO(pthier): Investigate if it is feasible to handle this special case + // in TurboFan instead of here. + Label valid_bytecode_offset, function_entry_bytecode; + __ cmp(kInterpreterBytecodeOffsetRegister, + Operand(BytecodeArray::kHeaderSize - kHeapObjectTag + + kFunctionEntryBytecodeOffset)); + __ b(eq, &function_entry_bytecode); + __ bind(&valid_bytecode_offset); + + // In the case we advance the BC, check if the current bytecode is JumpLoop. + // If it is, re-execute it instead of continuing at the next bytecode. + if (next_bytecode) { + Label not_jump_loop; + Register bytecode = r1; + __ ldrb(bytecode, MemOperand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister)); + __ cmp(bytecode, + Operand(static_cast(interpreter::Bytecode::kJumpLoop))); + __ b(ne, ¬_jump_loop); + __ Move(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ bind(¬_jump_loop); + } + + __ sub(kInterpreterBytecodeOffsetRegister, kInterpreterBytecodeOffsetRegister, + Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ PrepareCallCFunction(3, 0, r0); + Register arg_reg_1 = r0; + Register arg_reg_2 = r1; + Register arg_reg_3 = r2; + __ mov(arg_reg_1, code_obj); + __ mov(arg_reg_2, kInterpreterBytecodeOffsetRegister); + __ mov(arg_reg_3, kInterpreterBytecodeArrayRegister); + __ CallCFunction(get_baseline_pc, 3, 0); + } + __ add(code_obj, code_obj, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ add(code_obj, code_obj, kReturnRegister0); + __ Pop(kInterpreterAccumulatorRegister); + + __ Jump(code_obj); + __ Trap(); // Unreachable. + + __ bind(&function_entry_bytecode); + // If the bytecode offset is kFunctionEntryOffset, get the start address of + // the first bytecode. + __ mov(kInterpreterBytecodeOffsetRegister, + Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); + __ Move(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ b(&valid_bytecode_offset); +} + +} // namespace + +void Builtins::Generate_BaselineEnterAtBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, false); +} + +void Builtins::Generate_BaselineEnterAtNextBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, true); +} + void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::MANUAL); __ EnterFrame(StackFrame::INTERNAL); diff --git a/src/builtins/arm64/builtins-arm64.cc b/src/builtins/arm64/builtins-arm64.cc index de7b82e25b..26c628bb9c 100644 --- a/src/builtins/arm64/builtins-arm64.cc +++ b/src/builtins/arm64/builtins-arm64.cc @@ -3969,6 +3969,127 @@ void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); } +namespace { + +// Converts an interpreter frame into a baseline frame and continues execution +// in baseline code (baseline code has to exist on the shared function info), +// either at the start or the end of the current bytecode. +void Generate_BaselineEntry(MacroAssembler* masm, bool next_bytecode) { + // Get bytecode array and bytecode offset from the stack frame. + __ Ldr(kInterpreterBytecodeArrayRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + + // Get function from the frame. + Register closure = x1; + __ Ldr(closure, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + + // Replace BytecodeOffset with the feedback vector. + Register feedback_vector = x2; + __ LoadTaggedPointerField( + feedback_vector, + FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); + __ LoadTaggedPointerField( + feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); + if (__ emit_debug_code()) { + Register scratch = x3; + __ CompareObjectType(feedback_vector, scratch, scratch, + FEEDBACK_VECTOR_TYPE); + __ Assert(eq, AbortReason::kExpectedFeedbackVector); + } + __ Str(feedback_vector, + MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + feedback_vector = no_reg; + + // Get the Code object from the shared function info. + Register code_obj = x22; + __ LoadTaggedPointerField( + code_obj, + FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + code_obj, + FieldMemOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); + __ LoadTaggedPointerField( + code_obj, FieldMemOperand(code_obj, BaselineData::kBaselineCodeOffset)); + closure = no_reg; + + // Compute baseline pc for bytecode offset. + __ Push(padreg, kInterpreterAccumulatorRegister); + ExternalReference get_baseline_pc_extref = + next_bytecode + ? ExternalReference::baseline_end_pc_for_bytecode_offset() + : ExternalReference::baseline_start_pc_for_bytecode_offset(); + Register get_baseline_pc = x3; + __ Mov(get_baseline_pc, get_baseline_pc_extref); + + // If the code deoptimizes during the implicit function entry stack interrupt + // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is + // not a valid bytecode offset. + // TODO(pthier): Investigate if it is feasible to handle this special case + // in TurboFan instead of here. + Label valid_bytecode_offset, function_entry_bytecode; + __ cmp(kInterpreterBytecodeOffsetRegister, + Operand(BytecodeArray::kHeaderSize - kHeapObjectTag + + kFunctionEntryBytecodeOffset)); + __ B(eq, &function_entry_bytecode); + __ bind(&valid_bytecode_offset); + + // In the case we advance the BC, check if the current bytecode is JumpLoop. + // If it is, re-execute it instead of continuing at the next bytecode. + if (next_bytecode) { + Label not_jump_loop; + Register bytecode = x1; + __ Ldrb(bytecode, MemOperand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister)); + __ Cmp(bytecode, + Operand(static_cast(interpreter::Bytecode::kJumpLoop))); + __ B(ne, ¬_jump_loop); + __ Mov(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ bind(¬_jump_loop); + } + + __ Sub(kInterpreterBytecodeOffsetRegister, kInterpreterBytecodeOffsetRegister, + (BytecodeArray::kHeaderSize - kHeapObjectTag)); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + Register arg_reg_1 = x0; + Register arg_reg_2 = x1; + Register arg_reg_3 = x2; + __ Mov(arg_reg_1, code_obj); + __ Mov(arg_reg_2, kInterpreterBytecodeOffsetRegister); + __ Mov(arg_reg_3, kInterpreterBytecodeArrayRegister); + __ CallCFunction(get_baseline_pc, 3, 0); + } + __ Add(code_obj, code_obj, Code::kHeaderSize - kHeapObjectTag); + __ Add(code_obj, code_obj, kReturnRegister0); + __ Pop(kInterpreterAccumulatorRegister, padreg); + + __ Jump(code_obj); + __ Trap(); // Unreachable. + + __ bind(&function_entry_bytecode); + // If the bytecode offset is kFunctionEntryOffset, get the start address of + // the first bytecode. + __ Mov(kInterpreterBytecodeOffsetRegister, + BytecodeArray::kHeaderSize - kHeapObjectTag); + __ Mov(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ B(&valid_bytecode_offset); +} + +} // namespace + +void Builtins::Generate_BaselineEnterAtBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, false); +} + +void Builtins::Generate_BaselineEnterAtNextBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, true); +} + void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::MANUAL); __ EnterFrame(StackFrame::INTERNAL); diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index aeeae714df..b865201b5f 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -141,6 +141,8 @@ namespace internal { ASM(BaselineOutOfLinePrologue, BaselineOutOfLinePrologue) \ ASM(BaselineOnStackReplacement, ContextOnly) \ ASM(BaselineLeaveFrame, BaselineLeaveFrame) \ + ASM(BaselineEnterAtBytecode, Void) \ + ASM(BaselineEnterAtNextBytecode, Void) \ \ /* Code life-cycle */ \ TFC(CompileLazy, JSTrampoline) \ diff --git a/src/builtins/ia32/builtins-ia32.cc b/src/builtins/ia32/builtins-ia32.cc index 7b5802a571..eef926d345 100644 --- a/src/builtins/ia32/builtins-ia32.cc +++ b/src/builtins/ia32/builtins-ia32.cc @@ -4073,6 +4073,128 @@ void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); } +namespace { + +// Converts an interpreter frame into a baseline frame and continues execution +// in baseline code (baseline code has to exist on the shared function info), +// either at the start or the end of the current bytecode. +void Generate_BaselineEntry(MacroAssembler* masm, bool next_bytecode) { + // Get bytecode array and bytecode offset from the stack frame. + __ mov(kInterpreterBytecodeArrayRegister, + MemOperand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ mov(kInterpreterBytecodeOffsetRegister, + MemOperand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister); + + // Get function from the frame. + Register closure = esi; + __ mov(closure, MemOperand(ebp, StandardFrameConstants::kFunctionOffset)); + + // Replace BytecodeOffset with the feedback vector. + Register feedback_vector = ecx; + __ mov(feedback_vector, + FieldOperand(closure, JSFunction::kFeedbackCellOffset)); + __ mov(feedback_vector, FieldOperand(feedback_vector, Cell::kValueOffset)); + if (__ emit_debug_code()) { + __ push(kInterpreterAccumulatorRegister); + Register scratch = kInterpreterAccumulatorRegister; + __ CmpObjectType(feedback_vector, FEEDBACK_VECTOR_TYPE, scratch); + __ Assert(equal, AbortReason::kExpectedFeedbackVector); + scratch = no_reg; + __ pop(kInterpreterAccumulatorRegister); + } + __ mov(MemOperand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp), + feedback_vector); + feedback_vector = no_reg; + + // Get the Code object from the shared function info. + Register code_obj = closure; + __ mov(code_obj, + FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + closure = no_reg; + __ mov(code_obj, + FieldOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); + __ mov(code_obj, FieldOperand(code_obj, BaselineData::kBaselineCodeOffset)); + + // Compute baseline pc for bytecode offset. + __ push(kInterpreterAccumulatorRegister); + ExternalReference get_baseline_pc_extref = + next_bytecode + ? ExternalReference::baseline_end_pc_for_bytecode_offset() + : ExternalReference::baseline_start_pc_for_bytecode_offset(); + Register get_baseline_pc = ecx; + __ LoadAddress(get_baseline_pc, get_baseline_pc_extref); + + // If the code deoptimizes during the implicit function entry stack interrupt + // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is + // not a valid bytecode offset. + // TODO(pthier): Investigate if it is feasible to handle this special case + // in TurboFan instead of here. + Label valid_bytecode_offset, function_entry_bytecode; + __ cmp(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag + + kFunctionEntryBytecodeOffset)); + __ j(equal, &function_entry_bytecode); + __ bind(&valid_bytecode_offset); + + // In the case we advance the BC, check if the current bytecode is JumpLoop. + // If it is, re-execute it instead of continuing at the next bytecode. + if (next_bytecode) { + Label not_jump_loop; + __ push(kInterpreterBytecodeOffsetRegister); + Register bytecode = kInterpreterBytecodeOffsetRegister; + __ movzx_b(bytecode, + Operand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister, times_1, 0)); + __ cmpb(bytecode, + Immediate(static_cast(interpreter::Bytecode::kJumpLoop))); + __ j(not_equal, ¬_jump_loop, Label::kNear); + __ LoadAddress(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ bind(¬_jump_loop); + __ pop(kInterpreterBytecodeOffsetRegister); + } + + __ sub(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ PrepareCallCFunction(3, eax); + __ mov(Operand(esp, 0 * kSystemPointerSize), code_obj); + __ mov(Operand(esp, 1 * kSystemPointerSize), + kInterpreterBytecodeOffsetRegister); + __ mov(Operand(esp, 2 * kSystemPointerSize), + kInterpreterBytecodeArrayRegister); + __ CallCFunction(get_baseline_pc, 3); + } + __ lea(code_obj, + FieldOperand(code_obj, kReturnRegister0, times_1, Code::kHeaderSize)); + __ pop(kInterpreterAccumulatorRegister); + + __ jmp(code_obj); + __ Trap(); // Unreachable. + + __ bind(&function_entry_bytecode); + // If the bytecode offset is kFunctionEntryOffset, get the start address of + // the first bytecode. + __ mov(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); + __ LoadAddress(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ jmp(&valid_bytecode_offset); +} + +} // namespace + +void Builtins::Generate_BaselineEnterAtBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, false); +} + +void Builtins::Generate_BaselineEnterAtNextBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, true); +} + void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::MANUAL); __ EnterFrame(StackFrame::INTERNAL); diff --git a/src/builtins/x64/builtins-x64.cc b/src/builtins/x64/builtins-x64.cc index b6a6d3cb49..494769b941 100644 --- a/src/builtins/x64/builtins-x64.cc +++ b/src/builtins/x64/builtins-x64.cc @@ -4343,6 +4343,123 @@ void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); } +namespace { + +// Converts an interpreter frame into a baseline frame and continues execution +// in baseline code (baseline code has to exist on the shared function info), +// either at the start or the end of the current bytecode. +void Generate_BaselineEntry(MacroAssembler* masm, bool next_bytecode) { + // Get bytecode array and bytecode offset from the stack frame. + __ movq(kInterpreterBytecodeArrayRegister, + MemOperand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ SmiUntag( + kInterpreterBytecodeOffsetRegister, + MemOperand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + + // Get function from the frame. + Register closure = rdi; + __ movq(closure, MemOperand(rbp, StandardFrameConstants::kFunctionOffset)); + + // Replace BytecodeOffset with the feedback vector. + Register feedback_vector = rbx; + __ LoadTaggedPointerField( + feedback_vector, FieldOperand(closure, JSFunction::kFeedbackCellOffset)); + __ LoadTaggedPointerField(feedback_vector, + FieldOperand(feedback_vector, Cell::kValueOffset)); + if (__ emit_debug_code()) { + __ CmpObjectType(feedback_vector, FEEDBACK_VECTOR_TYPE, kScratchRegister); + __ Assert(equal, AbortReason::kExpectedFeedbackVector); + } + __ movq(MemOperand(rbp, InterpreterFrameConstants::kBytecodeOffsetFromFp), + feedback_vector); + feedback_vector = no_reg; + + // Get the Code object from the shared function info. + Register code_obj = rbx; + __ LoadTaggedPointerField( + code_obj, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + code_obj, + FieldOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); + __ LoadTaggedPointerField( + code_obj, FieldOperand(code_obj, BaselineData::kBaselineCodeOffset)); + closure = no_reg; + + // Compute baseline pc for bytecode offset. + __ pushq(kInterpreterAccumulatorRegister); + ExternalReference get_baseline_pc_extref = + next_bytecode + ? ExternalReference::baseline_end_pc_for_bytecode_offset() + : ExternalReference::baseline_start_pc_for_bytecode_offset(); + Register get_baseline_pc = rax; + __ LoadAddress(get_baseline_pc, get_baseline_pc_extref); + + // If the code deoptimizes during the implicit function entry stack interrupt + // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is + // not a valid bytecode offset. + // TODO(pthier): Investigate if it is feasible to handle this special case + // in TurboFan instead of here. + Label valid_bytecode_offset, function_entry_bytecode; + __ cmpq(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag + + kFunctionEntryBytecodeOffset)); + __ j(equal, &function_entry_bytecode); + __ bind(&valid_bytecode_offset); + + // In the case we advance the BC, check if the current bytecode is JumpLoop. + // If it is, re-execute it instead of continuing at the next bytecode. + if (next_bytecode) { + Label not_jump_loop; + Register bytecode = rdi; + __ movzxbq(bytecode, + Operand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister, times_1, 0)); + __ cmpb(bytecode, + Immediate(static_cast(interpreter::Bytecode::kJumpLoop))); + __ j(not_equal, ¬_jump_loop, Label::kNear); + __ LoadAddress(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ bind(¬_jump_loop); + } + + __ subq(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ PrepareCallCFunction(3); + __ movq(arg_reg_1, code_obj); + __ movq(arg_reg_2, kInterpreterBytecodeOffsetRegister); + __ movq(arg_reg_3, kInterpreterBytecodeArrayRegister); + __ CallCFunction(get_baseline_pc, 3); + } + __ leaq(code_obj, + FieldOperand(code_obj, kReturnRegister0, times_1, Code::kHeaderSize)); + __ popq(kInterpreterAccumulatorRegister); + + __ jmp(code_obj); + __ Trap(); // Unreachable. + + __ bind(&function_entry_bytecode); + // If the bytecode offset is kFunctionEntryOffset, get the start address of + // the first bytecode. + __ movq(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); + __ LoadAddress(get_baseline_pc, + ExternalReference::baseline_start_pc_for_bytecode_offset()); + __ jmp(&valid_bytecode_offset); +} + +} // namespace + +void Builtins::Generate_BaselineEnterAtBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, false); +} + +void Builtins::Generate_BaselineEnterAtNextBytecode(MacroAssembler* masm) { + Generate_BaselineEntry(masm, true); +} + void Builtins::Generate_DynamicCheckMapsTrampoline(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::MANUAL); __ EnterFrame(StackFrame::INTERNAL); diff --git a/src/codegen/external-reference.cc b/src/codegen/external-reference.cc index befddc02d4..0b1aa1e280 100644 --- a/src/codegen/external-reference.cc +++ b/src/codegen/external-reference.cc @@ -621,6 +621,35 @@ ExternalReference::address_of_enable_experimental_regexp_engine() { return ExternalReference(&FLAG_enable_experimental_regexp_engine); } +namespace { + +static uintptr_t BaselineStartPCForBytecodeOffset(Address raw_code_obj, + int bytecode_offset, + Address raw_bytecode_array) { + Code code_obj = Code::cast(Object(raw_code_obj)); + BytecodeArray bytecode_array = + BytecodeArray::cast(Object(raw_bytecode_array)); + return code_obj.GetBaselineStartPCForBytecodeOffset(bytecode_offset, + bytecode_array); +} + +static uintptr_t BaselineEndPCForBytecodeOffset(Address raw_code_obj, + int bytecode_offset, + Address raw_bytecode_array) { + Code code_obj = Code::cast(Object(raw_code_obj)); + BytecodeArray bytecode_array = + BytecodeArray::cast(Object(raw_bytecode_array)); + return code_obj.GetBaselineEndPCForBytecodeOffset(bytecode_offset, + bytecode_array); +} + +} // namespace + +FUNCTION_REFERENCE(baseline_end_pc_for_bytecode_offset, + BaselineEndPCForBytecodeOffset) +FUNCTION_REFERENCE(baseline_start_pc_for_bytecode_offset, + BaselineStartPCForBytecodeOffset) + ExternalReference ExternalReference::thread_in_wasm_flag_address_address( Isolate* isolate) { return ExternalReference(isolate->thread_in_wasm_flag_address_address()); diff --git a/src/codegen/external-reference.h b/src/codegen/external-reference.h index bbc3c9236b..959a3d13c4 100644 --- a/src/codegen/external-reference.h +++ b/src/codegen/external-reference.h @@ -124,6 +124,8 @@ class StatsCounter; V(address_of_wasm_i8x16_splat_0x33, "wasm_i8x16_splat_0x33") \ V(address_of_wasm_i8x16_splat_0x55, "wasm_i8x16_splat_0x55") \ V(address_of_wasm_i16x8_splat_0x0001, "wasm_16x8_splat_0x0001") \ + V(baseline_start_pc_for_bytecode_offset, "BaselineStartPCForBytecodeOffset") \ + V(baseline_end_pc_for_bytecode_offset, "BaselineEndPCForBytecodeOffset") \ V(bytecode_size_table_address, "Bytecodes::bytecode_size_table_address") \ V(check_object_type, "check_object_type") \ V(compute_integer_hash, "ComputeSeededHash") \ diff --git a/src/deoptimizer/deoptimizer-cfi-builtins.cc b/src/deoptimizer/deoptimizer-cfi-builtins.cc index e1c08e4a99..0b7472dbea 100644 --- a/src/deoptimizer/deoptimizer-cfi-builtins.cc +++ b/src/deoptimizer/deoptimizer-cfi-builtins.cc @@ -14,6 +14,8 @@ void Builtins_ContinueToJavaScriptBuiltinWithResult(); void Builtins_ContinueToJavaScriptBuiltin(); void construct_stub_create_deopt_addr(); void construct_stub_invoke_deopt_addr(); +void Builtins_BaselineEnterAtBytecode(); +void Builtins_BaselineEnterAtNextBytecode(); typedef void (*function_ptr)(); } @@ -30,6 +32,8 @@ constexpr function_ptr builtins[] = { &Builtins_ContinueToJavaScriptBuiltin, &construct_stub_create_deopt_addr, &construct_stub_invoke_deopt_addr, + &Builtins_BaselineEnterAtBytecode, + &Builtins_BaselineEnterAtNextBytecode, }; bool Deoptimizer::IsValidReturnAddress(Address address) { diff --git a/src/deoptimizer/deoptimizer.cc b/src/deoptimizer/deoptimizer.cc index a1dff40331..7c4562dbd5 100644 --- a/src/deoptimizer/deoptimizer.cc +++ b/src/deoptimizer/deoptimizer.cc @@ -991,11 +991,25 @@ void Deoptimizer::DoComputeOutputFrames() { stack_guard->real_jslimit() - kStackLimitSlackForDeoptimizationInBytes); } +namespace { + +// Get the dispatch builtin for unoptimized frames. +Builtins::Name DispatchBuiltinFor(bool is_baseline, bool advance_bc) { + if (is_baseline) { + return advance_bc ? Builtins::kBaselineEnterAtNextBytecode + : Builtins::kBaselineEnterAtBytecode; + } else { + return advance_bc ? Builtins::kInterpreterEnterBytecodeAdvance + : Builtins::kInterpreterEnterBytecodeDispatch; + } +} + +} // namespace + void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, int frame_index, bool goto_catch_handler) { SharedFunctionInfo shared = translated_frame->raw_shared_info(); - TranslatedFrame::iterator value_iterator = translated_frame->begin(); const bool is_bottommost = (0 == frame_index); const bool is_topmost = (output_count_ - 1 == frame_index); @@ -1020,15 +1034,10 @@ void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, const uint32_t output_frame_size = frame_info.frame_size_in_bytes(); TranslatedFrame::iterator function_iterator = value_iterator++; - if (verbose_tracing_enabled()) { - PrintF(trace_scope()->file(), " translating unoptimized frame "); - std::unique_ptr name = shared.DebugNameCStr(); - PrintF(trace_scope()->file(), "%s", name.get()); - PrintF(trace_scope()->file(), - " => bytecode_offset=%d, variable_frame_size=%d, frame_size=%d%s\n", - real_bytecode_offset, frame_info.frame_size_in_bytes_without_fixed(), - output_frame_size, goto_catch_handler ? " (throw)" : ""); - } + + BytecodeArray bytecode_array = + shared.HasBreakInfo() ? shared.GetDebugInfo().DebugBytecodeArray() + : shared.GetBytecodeArray(isolate()); // Allocate and store the output frame description. FrameDescription* output_frame = new (output_frame_size) @@ -1039,6 +1048,34 @@ void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, CHECK_NULL(output_[frame_index]); output_[frame_index] = output_frame; + // Compute this frame's PC and state. + // For interpreted frames, the PC will be a special builtin that + // continues the bytecode dispatch. Note that non-topmost and lazy-style + // bailout handlers also advance the bytecode offset before dispatch, hence + // simulating what normal handlers do upon completion of the operation. + // For baseline frames, the PC will be a builtin to convert the interpreter + // frame to a baseline frame before continuing execution of baseline code. + // We can't directly continue into baseline code, because of CFI. + Builtins* builtins = isolate_->builtins(); + const bool advance_bc = + (!is_topmost || (deopt_kind_ == DeoptimizeKind::kLazy)) && + !goto_catch_handler; + const bool is_baseline = shared.HasBaselineData(); + Code dispatch_builtin = + builtins->builtin(DispatchBuiltinFor(is_baseline, advance_bc)); + + if (verbose_tracing_enabled()) { + PrintF(trace_scope()->file(), " translating %s frame ", + is_baseline ? "baseline" : "interpreted"); + std::unique_ptr name = shared.DebugNameCStr(); + PrintF(trace_scope()->file(), "%s", name.get()); + PrintF(trace_scope()->file(), " => bytecode_offset=%d, ", + real_bytecode_offset); + PrintF(trace_scope()->file(), "variable_frame_size=%d, frame_size=%d%s\n", + frame_info.frame_size_in_bytes_without_fixed(), output_frame_size, + goto_catch_handler ? " (throw)" : ""); + } + // The top address of the frame is computed from the previous frame's top and // this frame's size. const intptr_t top_address = @@ -1145,9 +1182,6 @@ void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, frame_writer.PushRawValue(argc, "actual argument count\n"); // Set the bytecode array pointer. - Object bytecode_array = shared.HasBreakInfo() - ? shared.GetDebugInfo().DebugBytecodeArray() - : shared.GetBytecodeArray(isolate()); frame_writer.PushRawObject(bytecode_array, "bytecode array\n"); // The bytecode offset was mentioned explicitly in the BEGIN_FRAME. @@ -1237,26 +1271,16 @@ void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, CHECK_EQ(translated_frame->end(), value_iterator); CHECK_EQ(0u, frame_writer.top_offset()); - // Compute this frame's PC and state. The PC will be a special builtin that - // continues the bytecode dispatch. Note that non-topmost and lazy-style - // bailout handlers also advance the bytecode offset before dispatch, hence - // simulating what normal handlers do upon completion of the operation. - Builtins* builtins = isolate_->builtins(); - Code dispatch_builtin = - (!is_topmost || (deopt_kind_ == DeoptimizeKind::kLazy)) && - !goto_catch_handler - ? builtins->builtin(Builtins::kInterpreterEnterBytecodeAdvance) - : builtins->builtin(Builtins::kInterpreterEnterBytecodeDispatch); + const intptr_t pc = + static_cast(dispatch_builtin.InstructionStart()); if (is_topmost) { // Only the pc of the topmost frame needs to be signed since it is // authenticated at the end of the DeoptimizationEntry builtin. const intptr_t top_most_pc = PointerAuthentication::SignAndCheckPC( - static_cast(dispatch_builtin.InstructionStart()), - frame_writer.frame()->GetTop()); + pc, frame_writer.frame()->GetTop()); output_frame->SetPc(top_most_pc); } else { - output_frame->SetPc( - static_cast(dispatch_builtin.InstructionStart())); + output_frame->SetPc(pc); } // Update constant pool. diff --git a/src/execution/frames.cc b/src/execution/frames.cc index 905aa4ee2d..3d125a4bb2 100644 --- a/src/execution/frames.cc +++ b/src/execution/frames.cc @@ -225,16 +225,13 @@ namespace { bool IsInterpreterFramePc(Isolate* isolate, Address pc, StackFrame::State* state) { - Code interpreter_entry_trampoline = - isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline); - Code interpreter_bytecode_advance = - isolate->builtins()->builtin(Builtins::kInterpreterEnterBytecodeAdvance); - Code interpreter_bytecode_dispatch = - isolate->builtins()->builtin(Builtins::kInterpreterEnterBytecodeDispatch); - - if (interpreter_entry_trampoline.contains(isolate, pc) || - interpreter_bytecode_advance.contains(isolate, pc) || - interpreter_bytecode_dispatch.contains(isolate, pc)) { + Builtins::Name builtin_index = InstructionStream::TryLookupCode(isolate, pc); + if (builtin_index != Builtins::kNoBuiltinId && + (builtin_index == Builtins::kInterpreterEntryTrampoline || + builtin_index == Builtins::kInterpreterEnterBytecodeAdvance || + builtin_index == Builtins::kInterpreterEnterBytecodeDispatch || + builtin_index == Builtins::kBaselineEnterAtBytecode || + builtin_index == Builtins::kBaselineEnterAtNextBytecode)) { return true; } else if (FLAG_interpreted_frames_native_stack) { intptr_t marker = Memory( @@ -251,7 +248,7 @@ bool IsInterpreterFramePc(Isolate* isolate, Address pc, } else if (!isolate->heap()->InSpaceSlow(pc, CODE_SPACE)) { return false; } - interpreter_entry_trampoline = + Code interpreter_entry_trampoline = isolate->heap()->GcSafeFindCodeForInnerPointer(pc); return interpreter_entry_trampoline.is_interpreter_trampoline_builtin(); } else { @@ -595,7 +592,10 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator, switch (code_obj.kind()) { case CodeKind::BUILTIN: if (StackFrame::IsTypeMarker(marker)) break; - if (code_obj.is_interpreter_trampoline_builtin()) { + if (code_obj.is_interpreter_trampoline_builtin() || + // Frames for baseline entry trampolines on the stack are still + // interpreted frames. + code_obj.is_baseline_trampoline_builtin()) { return INTERPRETED; } if (code_obj.is_baseline_leave_frame_builtin()) { @@ -1838,8 +1838,8 @@ int BaselineFrame::GetBytecodeOffset() const { } intptr_t BaselineFrame::GetPCForBytecodeOffset(int bytecode_offset) const { - return LookupCode().GetBaselinePCForBytecodeOffset(bytecode_offset, - GetBytecodeArray()); + return LookupCode().GetBaselineStartPCForBytecodeOffset(bytecode_offset, + GetBytecodeArray()); } void BaselineFrame::PatchContext(Context value) { diff --git a/src/objects/code-inl.h b/src/objects/code-inl.h index 7c3787517a..49eb3480fb 100644 --- a/src/objects/code-inl.h +++ b/src/objects/code-inl.h @@ -371,7 +371,7 @@ CodeKind Code::kind() const { int Code::GetBytecodeOffsetForBaselinePC(Address baseline_pc, BytecodeArray bytecodes) { DisallowGarbageCollection no_gc; - CHECK(!is_baseline_prologue_builtin()); + CHECK(!is_baseline_trampoline_builtin()); if (is_baseline_leave_frame_builtin()) return kFunctionExitBytecodeOffset; CHECK_EQ(kind(), CodeKind::BASELINE); baseline::BytecodeOffsetIterator offset_iterator( @@ -382,13 +382,33 @@ int Code::GetBytecodeOffsetForBaselinePC(Address baseline_pc, } uintptr_t Code::GetBaselinePCForBytecodeOffset(int bytecode_offset, + BytecodeToPCPosition position, BytecodeArray bytecodes) { DisallowGarbageCollection no_gc; CHECK_EQ(kind(), CodeKind::BASELINE); baseline::BytecodeOffsetIterator offset_iterator( ByteArray::cast(bytecode_offset_table()), bytecodes); offset_iterator.AdvanceToBytecodeOffset(bytecode_offset); - return offset_iterator.current_pc_start_offset(); + uintptr_t pc = 0; + if (position == kPcAtStartOfBytecode) { + pc = offset_iterator.current_pc_start_offset(); + } else { + DCHECK_EQ(position, kPcAtEndOfBytecode); + pc = offset_iterator.current_pc_end_offset(); + } + return pc; +} + +uintptr_t Code::GetBaselineStartPCForBytecodeOffset(int bytecode_offset, + BytecodeArray bytecodes) { + return GetBaselinePCForBytecodeOffset(bytecode_offset, kPcAtStartOfBytecode, + bytecodes); +} + +uintptr_t Code::GetBaselineEndPCForBytecodeOffset(int bytecode_offset, + BytecodeArray bytecodes) { + return GetBaselinePCForBytecodeOffset(bytecode_offset, kPcAtEndOfBytecode, + bytecodes); } void Code::initialize_flags(CodeKind kind, bool is_turbofanned, int stack_slots, @@ -414,12 +434,16 @@ inline bool Code::is_interpreter_trampoline_builtin() const { index == Builtins::kInterpreterEnterBytecodeDispatch); } -inline bool Code::is_baseline_leave_frame_builtin() const { - return builtin_index() == Builtins::kBaselineLeaveFrame; +inline bool Code::is_baseline_trampoline_builtin() const { + const int index = builtin_index(); + return index != Builtins::kNoBuiltinId && + (index == Builtins::kBaselineOutOfLinePrologue || + index == Builtins::kBaselineEnterAtBytecode || + index == Builtins::kBaselineEnterAtNextBytecode); } -inline bool Code::is_baseline_prologue_builtin() const { - return builtin_index() == Builtins::kBaselineOutOfLinePrologue; +inline bool Code::is_baseline_leave_frame_builtin() const { + return builtin_index() == Builtins::kBaselineLeaveFrame; } inline bool Code::checks_optimization_marker() const { diff --git a/src/objects/code.h b/src/objects/code.h index c71c6c995f..b5034f49c0 100644 --- a/src/objects/code.h +++ b/src/objects/code.h @@ -277,7 +277,7 @@ class Code : public HeapObject { inline bool is_interpreter_trampoline_builtin() const; // Testers for baseline builtins. - inline bool is_baseline_prologue_builtin() const; + inline bool is_baseline_trampoline_builtin() const; inline bool is_baseline_leave_frame_builtin() const; // Tells whether the code checks the optimization marker in the function's @@ -406,8 +406,12 @@ class Code : public HeapObject { static inline void CopyRelocInfoToByteArray(ByteArray dest, const CodeDesc& desc); - inline uintptr_t GetBaselinePCForBytecodeOffset(int bytecode_offset, - BytecodeArray bytecodes); + inline uintptr_t GetBaselineStartPCForBytecodeOffset(int bytecode_offset, + BytecodeArray bytecodes); + + inline uintptr_t GetBaselineEndPCForBytecodeOffset(int bytecode_offset, + BytecodeArray bytecodes); + inline int GetBytecodeOffsetForBaselinePC(Address baseline_pc, BytecodeArray bytecodes); @@ -554,6 +558,17 @@ class Code : public HeapObject { bool is_promise_rejection() const; bool is_exception_caught() const; + enum BytecodeToPCPosition { + kPcAtStartOfBytecode, + // End of bytecode equals the start of the next bytecode. + // We need it when we deoptimize to the next bytecode (lazy deopt or deopt + // of non-topmost frame). + kPcAtEndOfBytecode + }; + inline uintptr_t GetBaselinePCForBytecodeOffset(int bytecode_offset, + BytecodeToPCPosition position, + BytecodeArray bytecodes); + OBJECT_CONSTRUCTORS(Code, HeapObject); }; diff --git a/test/mjsunit/regress/regress-crbug-1191886.js b/test/mjsunit/regress/regress-crbug-1191886.js new file mode 100644 index 0000000000..87df25605f --- /dev/null +++ b/test/mjsunit/regress/regress-crbug-1191886.js @@ -0,0 +1,9 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +let arr = []; +for (var i = 0; i < 1000000; i++) { + arr[i] = []; +} +assertEquals(1000000, i);