[Interpreter] Add basic deoptimization support from TurboFan to Ignition.
Adds support for generating deoptimization translations for interpreter stack frames, and building interpreter frames for these translations when a function deopts. Also adds builtins for InterpreterNotifyDeoptimized which resume the function's continuation at the correct point in the interpreter after deopt. MIPS patch contributed by balazs.kilvady@igmtec.com BUG=v8:4280 LOG=N TEST=test-deoptimization.cc with --ignition and --turbo Review URL: https://codereview.chromium.org/1528913003 Cr-Commit-Position: refs/heads/master@{#32971}
This commit is contained in:
parent
a4e3a3b6a8
commit
b10d24ff2c
@ -946,6 +946,93 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ mov(r1, Operand(Smi::FromInt(static_cast<int>(type))));
|
||||
__ push(r1);
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Drop state (we don't use these for interpreter deopts) and push PC at top
|
||||
// of stack (to simulate initial call to bytecode handler in interpreter entry
|
||||
// trampoline).
|
||||
__ pop(r1);
|
||||
__ Drop(1);
|
||||
__ push(r1);
|
||||
|
||||
// Initialize register file register and dispatch table register.
|
||||
__ add(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
Heap::kInterpreterTableRootIndex);
|
||||
__ add(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
|
||||
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ ldr(kContextRegister,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ ldr(r1,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(r1, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
__ CompareObjectType(kInterpreterBytecodeArrayRegister, r1, no_reg,
|
||||
BYTECODE_ARRAY_TYPE);
|
||||
__ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ ldr(kInterpreterBytecodeOffsetRegister,
|
||||
MemOperand(
|
||||
kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiUntag(kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ ldrb(r1, MemOperand(kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister));
|
||||
__ ldr(ip, MemOperand(kInterpreterDispatchTableRegister, r1, LSL,
|
||||
kPointerSizeLog2));
|
||||
__ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ mov(pc, ip);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
@ -905,6 +905,94 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ Push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ Mov(x1, Operand(Smi::FromInt(static_cast<int>(type))));
|
||||
__ Push(x1);
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ Pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Drop state (we don't use these for interpreter deopts) and push PC at top
|
||||
// of stack (to simulate initial call to bytecode handler in interpreter entry
|
||||
// trampoline).
|
||||
__ Pop(x1);
|
||||
__ Drop(1);
|
||||
__ Push(x1);
|
||||
|
||||
// Initialize register file register and dispatch table register.
|
||||
__ Add(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
Heap::kInterpreterTableRootIndex);
|
||||
__ Add(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
|
||||
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ Ldr(kContextRegister,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ Ldr(x1,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ Ldr(x1, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldr(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(x1, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister,
|
||||
kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
__ CompareObjectType(kInterpreterBytecodeArrayRegister, x1, x1,
|
||||
BYTECODE_ARRAY_TYPE);
|
||||
__ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ Ldr(kInterpreterBytecodeOffsetRegister,
|
||||
MemOperand(
|
||||
kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiUntag(kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ Ldrb(x1, MemOperand(kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister));
|
||||
__ Mov(x1, Operand(x1, LSL, kPointerSizeLog2));
|
||||
__ Ldr(ip0, MemOperand(kInterpreterDispatchTableRegister, x1));
|
||||
__ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(ip0);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
180
src/builtins.h
180
src/builtins.h
@ -97,93 +97,96 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
|
||||
V(RestrictedStrictArgumentsPropertiesThrower, kNone)
|
||||
|
||||
// Define list of builtins implemented in assembly.
|
||||
#define BUILTIN_LIST_A(V) \
|
||||
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(ConstructedNonConstructable, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(CallFunction_ReceiverIsNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(CallFunction_ReceiverIsNotNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(CallFunction_ReceiverIsAny, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Call_ReceiverIsNullOrUndefined, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Call_ReceiverIsNotNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(Call_ReceiverIsAny, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(ConstructFunction, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ConstructProxy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Construct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(Apply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(HandleFastApiCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSBuiltinsConstructStub, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileLazy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileOptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileOptimizedConcurrent, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifySoftDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyStubFailure, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyStubFailureSaveDoubles, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(LoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StoreIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(LoadIC_Getter_ForDeopt, LOAD_IC, MONOMORPHIC, kNoExtraICState) \
|
||||
V(KeyedLoadIC_Megamorphic, KEYED_LOAD_IC, MEGAMORPHIC, kNoExtraICState) \
|
||||
\
|
||||
V(KeyedLoadIC_Megamorphic_Strong, KEYED_LOAD_IC, MEGAMORPHIC, \
|
||||
LoadICState::kStrongModeState) \
|
||||
\
|
||||
V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
\
|
||||
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedStoreIC_PreMonomorphic, KEYED_STORE_IC, PREMONOMORPHIC, \
|
||||
kNoExtraICState) \
|
||||
V(KeyedStoreIC_Megamorphic, KEYED_STORE_IC, MEGAMORPHIC, kNoExtraICState) \
|
||||
\
|
||||
V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
|
||||
StoreICState::kStrictModeState) \
|
||||
V(KeyedStoreIC_PreMonomorphic_Strict, KEYED_STORE_IC, PREMONOMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
V(KeyedStoreIC_Megamorphic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
\
|
||||
V(FunctionCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(FunctionApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InternalArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(StringConstructor, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StringConstructor_ConstructStub, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(OnStackReplacement, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterruptCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(OsrAfterStackCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StackCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(MarkCodeAsToBeExecutedOnce, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(MarkCodeAsExecutedOnce, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(MarkCodeAsExecutedTwice, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
#define BUILTIN_LIST_A(V) \
|
||||
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(ConstructedNonConstructable, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(CallFunction_ReceiverIsNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(CallFunction_ReceiverIsNotNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(CallFunction_ReceiverIsAny, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Call_ReceiverIsNullOrUndefined, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Call_ReceiverIsNotNullOrUndefined, BUILTIN, UNINITIALIZED, \
|
||||
kNoExtraICState) \
|
||||
V(Call_ReceiverIsAny, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(ConstructFunction, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ConstructProxy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(Construct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(Apply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(HandleFastApiCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSBuiltinsConstructStub, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileLazy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileOptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(CompileOptimizedConcurrent, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifySoftDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyStubFailure, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(NotifyStubFailureSaveDoubles, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterNotifyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterNotifySoftDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterNotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(LoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StoreIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(LoadIC_Getter_ForDeopt, LOAD_IC, MONOMORPHIC, kNoExtraICState) \
|
||||
V(KeyedLoadIC_Megamorphic, KEYED_LOAD_IC, MEGAMORPHIC, kNoExtraICState) \
|
||||
\
|
||||
V(KeyedLoadIC_Megamorphic_Strong, KEYED_LOAD_IC, MEGAMORPHIC, \
|
||||
LoadICState::kStrongModeState) \
|
||||
\
|
||||
V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
\
|
||||
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, kNoExtraICState) \
|
||||
V(KeyedStoreIC_PreMonomorphic, KEYED_STORE_IC, PREMONOMORPHIC, \
|
||||
kNoExtraICState) \
|
||||
V(KeyedStoreIC_Megamorphic, KEYED_STORE_IC, MEGAMORPHIC, kNoExtraICState) \
|
||||
\
|
||||
V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
|
||||
StoreICState::kStrictModeState) \
|
||||
V(KeyedStoreIC_PreMonomorphic_Strict, KEYED_STORE_IC, PREMONOMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
V(KeyedStoreIC_Megamorphic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
\
|
||||
V(FunctionCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(FunctionApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InternalArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(StringConstructor, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StringConstructor_ConstructStub, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(OnStackReplacement, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterruptCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(OsrAfterStackCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(StackCheck, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(MarkCodeAsToBeExecutedOnce, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(MarkCodeAsExecutedOnce, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(MarkCodeAsExecutedTwice, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
CODE_AGE_LIST_WITH_ARG(DECLARE_CODE_AGE_BUILTIN, V)
|
||||
|
||||
// Define list of builtin handlers implemented in assembly.
|
||||
@ -373,6 +376,9 @@ class Builtins {
|
||||
static void Generate_InterpreterExitTrampoline(MacroAssembler* masm);
|
||||
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm);
|
||||
static void Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm);
|
||||
static void Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm);
|
||||
static void Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm);
|
||||
static void Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm);
|
||||
|
||||
#define DECLARE_CODE_AGE_BUILTIN_GENERATOR(C) \
|
||||
static void Generate_Make##C##CodeYoungAgainEvenMarking( \
|
||||
|
@ -559,7 +559,9 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
|
||||
(1 + descriptor->parameters_count())));
|
||||
break;
|
||||
case FrameStateType::kInterpretedFunction:
|
||||
// TODO(rmcilroy): Implement interpreted function translation.
|
||||
translation->BeginInterpretedFrame(
|
||||
descriptor->bailout_id(), shared_info_id,
|
||||
static_cast<unsigned int>(descriptor->locals_count()));
|
||||
break;
|
||||
case FrameStateType::kArgumentsAdaptor:
|
||||
translation->BeginArgumentsAdaptorFrame(
|
||||
|
@ -763,6 +763,10 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
DoComputeJSFrame(frame_index);
|
||||
jsframe_count_++;
|
||||
break;
|
||||
case TranslatedFrame::kInterpretedFunction:
|
||||
DoComputeInterpretedFrame(frame_index);
|
||||
jsframe_count_++;
|
||||
break;
|
||||
case TranslatedFrame::kArgumentsAdaptor:
|
||||
DoComputeArgumentsAdaptorFrame(frame_index);
|
||||
break;
|
||||
@ -828,7 +832,7 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
||||
|
||||
// The 'fixed' part of the frame consists of the incoming parameters and
|
||||
// the part described by JavaScriptFrameConstants.
|
||||
unsigned fixed_frame_size = ComputeFixedSize(function);
|
||||
unsigned fixed_frame_size = ComputeJavascriptFixedSize(function);
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
@ -1022,6 +1026,222 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
||||
TranslatedFrame* translated_frame =
|
||||
&(translated_state_.frames()[frame_index]);
|
||||
TranslatedFrame::iterator value_iterator = translated_frame->begin();
|
||||
int input_index = 0;
|
||||
|
||||
BailoutId bytecode_offset = translated_frame->node_id();
|
||||
unsigned height = translated_frame->height();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
JSFunction* function = JSFunction::cast(value_iterator->GetRawValue());
|
||||
value_iterator++;
|
||||
input_index++;
|
||||
if (trace_scope_ != NULL) {
|
||||
PrintF(trace_scope_->file(), " translating interpreted frame ");
|
||||
function->PrintName(trace_scope_->file());
|
||||
PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d\n",
|
||||
bytecode_offset.ToInt(), height_in_bytes);
|
||||
}
|
||||
|
||||
// The 'fixed' part of the frame consists of the incoming parameters and
|
||||
// the part described by InterpreterFrameConstants.
|
||||
unsigned fixed_frame_size = ComputeInterpretedFixedSize(function);
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new (output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::INTERPRETED);
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
CHECK(frame_index >= 0 && frame_index < output_count_);
|
||||
CHECK_NULL(output_[frame_index]);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address for the bottommost output frame can be computed from
|
||||
// the input frame pointer and the output frame's height. For all
|
||||
// subsequent output frames, it can be computed from the previous one's
|
||||
// top address and the current frame's size.
|
||||
Register fp_reg = InterpretedFrame::fp_register();
|
||||
intptr_t top_address;
|
||||
if (is_bottommost) {
|
||||
// Subtract interpreter fixed frame size for the context function slots,
|
||||
// new,target and bytecode offset.
|
||||
top_address = input_->GetRegister(fp_reg.code()) -
|
||||
InterpreterFrameConstants::kFixedFrameSizeFromFp -
|
||||
height_in_bytes;
|
||||
} else {
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
}
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count =
|
||||
function->shared()->internal_formal_parameter_count() + 1;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index,
|
||||
output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// There are no translation commands for the caller's pc and fp, the
|
||||
// context, the function, new.target and the bytecode offset. Synthesize
|
||||
// their values and set them up
|
||||
// explicitly.
|
||||
//
|
||||
// The caller's pc for the bottommost output frame is the same as in the
|
||||
// input frame. For all subsequent output frames, it can be read from the
|
||||
// previous one. This frame's pc can be computed from the non-optimized
|
||||
// function code and AST id of the bailout.
|
||||
output_offset -= kPCOnStackSize;
|
||||
input_offset -= kPCOnStackSize;
|
||||
intptr_t value;
|
||||
if (is_bottommost) {
|
||||
value = input_->GetFrameSlot(input_offset);
|
||||
} else {
|
||||
value = output_[frame_index - 1]->GetPc();
|
||||
}
|
||||
output_frame->SetCallerPc(output_offset, value);
|
||||
DebugPrintOutputSlot(value, frame_index, output_offset, "caller's pc\n");
|
||||
|
||||
// The caller's frame pointer for the bottommost output frame is the same
|
||||
// as in the input frame. For all subsequent output frames, it can be
|
||||
// read from the previous one. Also compute and set this frame's frame
|
||||
// pointer.
|
||||
output_offset -= kFPOnStackSize;
|
||||
input_offset -= kFPOnStackSize;
|
||||
if (is_bottommost) {
|
||||
value = input_->GetFrameSlot(input_offset);
|
||||
} else {
|
||||
value = output_[frame_index - 1]->GetFp();
|
||||
}
|
||||
output_frame->SetCallerFp(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
DCHECK(!is_bottommost ||
|
||||
(input_->GetRegister(fp_reg.code()) +
|
||||
has_alignment_padding_ * kPointerSize) == fp_value);
|
||||
output_frame->SetFp(fp_value);
|
||||
if (is_topmost) output_frame->SetRegister(fp_reg.code(), fp_value);
|
||||
DebugPrintOutputSlot(value, frame_index, output_offset, "caller's fp\n");
|
||||
DCHECK(!is_bottommost || !has_alignment_padding_ ||
|
||||
(fp_value & kPointerSize) != 0);
|
||||
|
||||
if (FLAG_enable_embedded_constant_pool) {
|
||||
// For the bottommost output frame the constant pool pointer can be gotten
|
||||
// from the input frame. For subsequent output frames, it can be read from
|
||||
// the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
if (is_bottommost) {
|
||||
value = input_->GetFrameSlot(input_offset);
|
||||
} else {
|
||||
value = output_[frame_index - 1]->GetConstantPool();
|
||||
}
|
||||
output_frame->SetCallerConstantPool(output_offset, value);
|
||||
DebugPrintOutputSlot(value, frame_index, output_offset,
|
||||
"caller's constant_pool\n");
|
||||
}
|
||||
|
||||
// For the bottommost output frame the context can be gotten from the input
|
||||
// frame. For all subsequent output frames it can be gotten from the function
|
||||
// so long as we don't inline functions that need local contexts.
|
||||
Register context_reg = InterpretedFrame::context_register();
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
// Read the context from the translations.
|
||||
Object* context = value_iterator->GetRawValue();
|
||||
// The context should not be a placeholder for a materialized object.
|
||||
CHECK(context != isolate_->heap()->arguments_marker());
|
||||
value = reinterpret_cast<intptr_t>(context);
|
||||
output_frame->SetContext(value);
|
||||
if (is_topmost) output_frame->SetRegister(context_reg.code(), value);
|
||||
WriteValueToOutput(context, input_index, frame_index, output_offset,
|
||||
"context ");
|
||||
value_iterator++;
|
||||
input_index++;
|
||||
|
||||
// The function was mentioned explicitly in the BEGIN_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
// The function for the bottommost output frame should also agree with the
|
||||
// input frame.
|
||||
DCHECK(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
|
||||
WriteValueToOutput(function, 0, frame_index, output_offset, "function ");
|
||||
|
||||
// TODO(rmcilroy): Deal with new.target correctly - currently just set it to
|
||||
// undefined.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
Object* new_target = isolate_->heap()->undefined_value();
|
||||
WriteValueToOutput(new_target, 0, frame_index, output_offset, "new_target ");
|
||||
|
||||
// The bytecode offset was mentioned explicitly in the BEGIN_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
int raw_bytecode_offset =
|
||||
BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset.ToInt();
|
||||
Smi* smi_bytecode_offset = Smi::FromInt(raw_bytecode_offset);
|
||||
WriteValueToOutput(smi_bytecode_offset, 0, frame_index, output_offset,
|
||||
"bytecode offset ");
|
||||
|
||||
// Translate the rest of the interpreter registers in the frame.
|
||||
for (unsigned i = 0; i < height; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index,
|
||||
output_offset);
|
||||
}
|
||||
CHECK_EQ(0u, output_offset);
|
||||
|
||||
// Set the accumulator register.
|
||||
output_frame->SetRegister(
|
||||
kInterpreterAccumulatorRegister.code(),
|
||||
reinterpret_cast<intptr_t>(value_iterator->GetRawValue()));
|
||||
value_iterator++;
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* trampoline = builtins->builtin(Builtins::kInterpreterEntryTrampoline);
|
||||
output_frame->SetPc(reinterpret_cast<intptr_t>(trampoline->entry()));
|
||||
output_frame->SetState(0);
|
||||
|
||||
// Update constant pool.
|
||||
if (FLAG_enable_embedded_constant_pool) {
|
||||
intptr_t constant_pool_value =
|
||||
reinterpret_cast<intptr_t>(trampoline->constant_pool());
|
||||
output_frame->SetConstantPool(constant_pool_value);
|
||||
if (is_topmost) {
|
||||
Register constant_pool_reg =
|
||||
InterpretedFrame::constant_pool_pointer_register();
|
||||
output_frame->SetRegister(constant_pool_reg.code(), constant_pool_value);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the continuation for the topmost frame.
|
||||
if (is_topmost && bailout_type_ != DEBUGGER) {
|
||||
Code* continuation =
|
||||
builtins->builtin(Builtins::kInterpreterNotifyDeoptimized);
|
||||
if (bailout_type_ == LAZY) {
|
||||
continuation =
|
||||
builtins->builtin(Builtins::kInterpreterNotifyLazyDeoptimized);
|
||||
} else if (bailout_type_ == SOFT) {
|
||||
continuation =
|
||||
builtins->builtin(Builtins::kInterpreterNotifySoftDeoptimized);
|
||||
} else {
|
||||
CHECK_EQ(bailout_type_, EAGER);
|
||||
}
|
||||
output_frame->SetContinuation(
|
||||
reinterpret_cast<intptr_t>(continuation->entry()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeArgumentsAdaptorFrame(int frame_index) {
|
||||
TranslatedFrame* translated_frame =
|
||||
&(translated_state_.frames()[frame_index]);
|
||||
@ -1756,7 +1976,7 @@ void Deoptimizer::DebugPrintOutputSlot(intptr_t value, int frame_index,
|
||||
|
||||
|
||||
unsigned Deoptimizer::ComputeInputFrameSize() const {
|
||||
unsigned fixed_size = ComputeFixedSize(function_);
|
||||
unsigned fixed_size = ComputeJavascriptFixedSize(function_);
|
||||
// The fp-to-sp delta already takes the context, constant pool pointer and the
|
||||
// function into account so we have to avoid double counting them.
|
||||
unsigned result = fixed_size + fp_to_sp_delta_ -
|
||||
@ -1771,7 +1991,7 @@ unsigned Deoptimizer::ComputeInputFrameSize() const {
|
||||
}
|
||||
|
||||
|
||||
unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const {
|
||||
unsigned Deoptimizer::ComputeJavascriptFixedSize(JSFunction* function) const {
|
||||
// The fixed part of the frame consists of the return address, frame
|
||||
// pointer, function, context, and all the incoming arguments.
|
||||
return ComputeIncomingArgumentSize(function) +
|
||||
@ -1779,6 +1999,15 @@ unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const {
|
||||
}
|
||||
|
||||
|
||||
unsigned Deoptimizer::ComputeInterpretedFixedSize(JSFunction* function) const {
|
||||
// The fixed part of the frame consists of the return address, frame
|
||||
// pointer, function, context, new.target, bytecode offset and all the
|
||||
// incoming arguments.
|
||||
return ComputeIncomingArgumentSize(function) +
|
||||
InterpreterFrameConstants::kFixedFrameSize;
|
||||
}
|
||||
|
||||
|
||||
unsigned Deoptimizer::ComputeIncomingArgumentSize(JSFunction* function) const {
|
||||
// The incoming arguments is the values for formal parameters and
|
||||
// the receiver. Every slot contains a pointer.
|
||||
@ -1872,8 +2101,13 @@ FrameDescription::FrameDescription(uint32_t frame_size,
|
||||
|
||||
|
||||
int FrameDescription::ComputeFixedSize() {
|
||||
return StandardFrameConstants::kFixedFrameSize +
|
||||
(ComputeParametersCount() + 1) * kPointerSize;
|
||||
if (type_ == StackFrame::INTERPRETED) {
|
||||
return InterpreterFrameConstants::kFixedFrameSize +
|
||||
(ComputeParametersCount() + 1) * kPointerSize;
|
||||
} else {
|
||||
return StandardFrameConstants::kFixedFrameSize +
|
||||
(ComputeParametersCount() + 1) * kPointerSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2011,6 +2245,15 @@ void Translation::BeginJSFrame(BailoutId node_id,
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginInterpretedFrame(BailoutId bytecode_offset,
|
||||
int literal_id, unsigned height) {
|
||||
buffer_->Add(INTERPRETED_FRAME, zone());
|
||||
buffer_->Add(bytecode_offset.ToInt(), zone());
|
||||
buffer_->Add(literal_id, zone());
|
||||
buffer_->Add(height, zone());
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginCompiledStubFrame(int height) {
|
||||
buffer_->Add(COMPILED_STUB_FRAME, zone());
|
||||
buffer_->Add(height, zone());
|
||||
@ -2143,6 +2386,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
|
||||
case CONSTRUCT_STUB_FRAME:
|
||||
return 2;
|
||||
case JS_FRAME:
|
||||
case INTERPRETED_FRAME:
|
||||
return 3;
|
||||
}
|
||||
FATAL("Unexpected translation type");
|
||||
@ -2616,6 +2860,15 @@ TranslatedFrame TranslatedFrame::JSFrame(BailoutId node_id,
|
||||
}
|
||||
|
||||
|
||||
TranslatedFrame TranslatedFrame::InterpretedFrame(
|
||||
BailoutId bytecode_offset, SharedFunctionInfo* shared_info, int height) {
|
||||
TranslatedFrame frame(kInterpretedFunction, shared_info->GetIsolate(),
|
||||
shared_info, height);
|
||||
frame.node_id_ = bytecode_offset;
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
TranslatedFrame TranslatedFrame::AccessorFrame(
|
||||
Kind kind, SharedFunctionInfo* shared_info) {
|
||||
DCHECK(kind == kSetter || kind == kGetter);
|
||||
@ -2642,9 +2895,17 @@ int TranslatedFrame::GetValueCount() {
|
||||
case kFunction: {
|
||||
int parameter_count =
|
||||
raw_shared_info_->internal_formal_parameter_count() + 1;
|
||||
// + 1 for function.
|
||||
return height_ + parameter_count + 1;
|
||||
}
|
||||
|
||||
case kInterpretedFunction: {
|
||||
int parameter_count =
|
||||
raw_shared_info_->internal_formal_parameter_count() + 1;
|
||||
// + 3 for function, context and accumulator.
|
||||
return height_ + parameter_count + 3;
|
||||
}
|
||||
|
||||
case kGetter:
|
||||
return 2; // Function and receiver.
|
||||
|
||||
@ -2700,6 +2961,24 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
|
||||
return TranslatedFrame::JSFrame(node_id, shared_info, height);
|
||||
}
|
||||
|
||||
case Translation::INTERPRETED_FRAME: {
|
||||
BailoutId bytecode_offset = BailoutId(iterator->Next());
|
||||
SharedFunctionInfo* shared_info =
|
||||
SharedFunctionInfo::cast(literal_array->get(iterator->Next()));
|
||||
int height = iterator->Next();
|
||||
if (trace_file != nullptr) {
|
||||
base::SmartArrayPointer<char> name =
|
||||
shared_info->DebugName()->ToCString();
|
||||
PrintF(trace_file, " reading input frame %s", name.get());
|
||||
int arg_count = shared_info->internal_formal_parameter_count() + 1;
|
||||
PrintF(trace_file,
|
||||
" => bytecode_offset=%d, args=%d, height=%d; inputs:\n",
|
||||
bytecode_offset.ToInt(), arg_count, height);
|
||||
}
|
||||
return TranslatedFrame::InterpretedFrame(bytecode_offset, shared_info,
|
||||
height);
|
||||
}
|
||||
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME: {
|
||||
SharedFunctionInfo* shared_info =
|
||||
SharedFunctionInfo::cast(literal_array->get(iterator->Next()));
|
||||
@ -2812,6 +3091,7 @@ TranslatedValue TranslatedState::CreateNextTranslatedValue(
|
||||
switch (opcode) {
|
||||
case Translation::BEGIN:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::INTERPRETED_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::CONSTRUCT_STUB_FRAME:
|
||||
case Translation::GETTER_STUB_FRAME:
|
||||
|
@ -112,6 +112,7 @@ class TranslatedFrame {
|
||||
public:
|
||||
enum Kind {
|
||||
kFunction,
|
||||
kInterpretedFunction,
|
||||
kGetter,
|
||||
kSetter,
|
||||
kArgumentsAdaptor,
|
||||
@ -172,6 +173,9 @@ class TranslatedFrame {
|
||||
// Constructor static methods.
|
||||
static TranslatedFrame JSFrame(BailoutId node_id,
|
||||
SharedFunctionInfo* shared_info, int height);
|
||||
static TranslatedFrame InterpretedFrame(BailoutId bytecode_offset,
|
||||
SharedFunctionInfo* shared_info,
|
||||
int height);
|
||||
static TranslatedFrame AccessorFrame(Kind kind,
|
||||
SharedFunctionInfo* shared_info);
|
||||
static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo* shared_info,
|
||||
@ -589,6 +593,7 @@ class Deoptimizer : public Malloced {
|
||||
|
||||
void DoComputeOutputFrames();
|
||||
void DoComputeJSFrame(int frame_index);
|
||||
void DoComputeInterpretedFrame(int frame_index);
|
||||
void DoComputeArgumentsAdaptorFrame(int frame_index);
|
||||
void DoComputeConstructStubFrame(int frame_index);
|
||||
void DoComputeAccessorStubFrame(int frame_index, bool is_setter_stub_frame);
|
||||
@ -606,7 +611,8 @@ class Deoptimizer : public Malloced {
|
||||
const char* debug_hint_string);
|
||||
|
||||
unsigned ComputeInputFrameSize() const;
|
||||
unsigned ComputeFixedSize(JSFunction* function) const;
|
||||
unsigned ComputeJavascriptFixedSize(JSFunction* function) const;
|
||||
unsigned ComputeInterpretedFixedSize(JSFunction* function) const;
|
||||
|
||||
unsigned ComputeIncomingArgumentSize(JSFunction* function) const;
|
||||
static unsigned ComputeOutgoingArgumentSize(Code* code, unsigned bailout_id);
|
||||
@ -951,6 +957,7 @@ class TranslationIterator BASE_EMBEDDED {
|
||||
#define TRANSLATION_OPCODE_LIST(V) \
|
||||
V(BEGIN) \
|
||||
V(JS_FRAME) \
|
||||
V(INTERPRETED_FRAME) \
|
||||
V(CONSTRUCT_STUB_FRAME) \
|
||||
V(GETTER_STUB_FRAME) \
|
||||
V(SETTER_STUB_FRAME) \
|
||||
@ -996,6 +1003,8 @@ class Translation BASE_EMBEDDED {
|
||||
|
||||
// Commands.
|
||||
void BeginJSFrame(BailoutId node_id, int literal_id, unsigned height);
|
||||
void BeginInterpretedFrame(BailoutId bytecode_offset, int literal_id,
|
||||
unsigned height);
|
||||
void BeginCompiledStubFrame(int height);
|
||||
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
|
||||
void BeginConstructStubFrame(int literal_id, unsigned height);
|
||||
|
@ -937,8 +937,9 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
|
||||
TranslationIterator it(data->TranslationByteArray(),
|
||||
data->TranslationIndex(deopt_index)->value());
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
DCHECK_EQ(Translation::BEGIN, opcode);
|
||||
Translation::Opcode frame_opcode =
|
||||
static_cast<Translation::Opcode>(it.Next());
|
||||
DCHECK_EQ(Translation::BEGIN, frame_opcode);
|
||||
it.Next(); // Drop frame count.
|
||||
int jsframe_count = it.Next();
|
||||
|
||||
@ -946,8 +947,9 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
// in the deoptimization translation are ordered bottom-to-top.
|
||||
bool is_constructor = IsConstructor();
|
||||
while (jsframe_count != 0) {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (opcode == Translation::JS_FRAME) {
|
||||
frame_opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (frame_opcode == Translation::JS_FRAME ||
|
||||
frame_opcode == Translation::INTERPRETED_FRAME) {
|
||||
jsframe_count--;
|
||||
BailoutId const ast_id = BailoutId(it.Next());
|
||||
SharedFunctionInfo* const shared_info =
|
||||
@ -956,7 +958,7 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
|
||||
// The translation commands are ordered and the function is always
|
||||
// at the first position, and the receiver is next.
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
|
||||
// Get the correct function in the optimized frame.
|
||||
JSFunction* function;
|
||||
@ -993,25 +995,33 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
}
|
||||
|
||||
Code* const code = shared_info->code();
|
||||
DeoptimizationOutputData* const output_data =
|
||||
DeoptimizationOutputData::cast(code->deoptimization_data());
|
||||
unsigned const entry =
|
||||
Deoptimizer::GetOutputInfo(output_data, ast_id, shared_info);
|
||||
unsigned const pc_offset =
|
||||
FullCodeGenerator::PcField::decode(entry) + Code::kHeaderSize;
|
||||
DCHECK_NE(0U, pc_offset);
|
||||
|
||||
unsigned pc_offset;
|
||||
if (frame_opcode == Translation::JS_FRAME) {
|
||||
DeoptimizationOutputData* const output_data =
|
||||
DeoptimizationOutputData::cast(code->deoptimization_data());
|
||||
unsigned const entry =
|
||||
Deoptimizer::GetOutputInfo(output_data, ast_id, shared_info);
|
||||
pc_offset =
|
||||
FullCodeGenerator::PcField::decode(entry) + Code::kHeaderSize;
|
||||
DCHECK_NE(0U, pc_offset);
|
||||
} else {
|
||||
// TODO(rmcilroy): Modify FrameSummary to enable us to summarize
|
||||
// based on the BytecodeArray and bytecode offset.
|
||||
DCHECK_EQ(frame_opcode, Translation::INTERPRETED_FRAME);
|
||||
pc_offset = 0;
|
||||
}
|
||||
FrameSummary summary(receiver, function, code, pc_offset, is_constructor);
|
||||
frames->Add(summary);
|
||||
is_constructor = false;
|
||||
} else if (opcode == Translation::CONSTRUCT_STUB_FRAME) {
|
||||
} else if (frame_opcode == Translation::CONSTRUCT_STUB_FRAME) {
|
||||
// The next encountered JS_FRAME will be marked as a constructor call.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
it.Skip(Translation::NumberOfOperandsFor(frame_opcode));
|
||||
DCHECK(!is_constructor);
|
||||
is_constructor = true;
|
||||
} else {
|
||||
// Skip over operands to advance to the next opcode.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
it.Skip(Translation::NumberOfOperandsFor(frame_opcode));
|
||||
}
|
||||
}
|
||||
DCHECK(!is_constructor);
|
||||
@ -1083,7 +1093,8 @@ void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) const {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
// Skip over operands to advance to the next opcode.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
if (opcode == Translation::JS_FRAME) {
|
||||
if (opcode == Translation::JS_FRAME ||
|
||||
opcode == Translation::INTERPRETED_FRAME) {
|
||||
jsframe_count--;
|
||||
|
||||
// The translation commands are ordered and the function is always at the
|
||||
|
@ -176,6 +176,12 @@ class ConstructFrameConstants : public AllStatic {
|
||||
|
||||
class InterpreterFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Fixed frame includes new.target and bytecode offset.
|
||||
static const int kFixedFrameSize =
|
||||
StandardFrameConstants::kFixedFrameSize + 2 * kPointerSize;
|
||||
static const int kFixedFrameSizeFromFp =
|
||||
StandardFrameConstants::kFixedFrameSizeFromFp + 2 * kPointerSize;
|
||||
|
||||
// FP-relative.
|
||||
static const int kRegisterFilePointerFromFp =
|
||||
-StandardFrameConstants::kFixedFrameSizeFromFp - 3 * kPointerSize;
|
||||
|
@ -603,7 +603,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ LoadRoot(ebx, Heap::kInterpreterTableRootIndex);
|
||||
__ add(ebx, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Push context as a stack located parameter to the bytecode handler.
|
||||
// Push dispatch table as a stack located parameter to the bytecode handler.
|
||||
DCHECK_EQ(-1, kInterpreterDispatchTableSpillSlot);
|
||||
__ push(ebx);
|
||||
|
||||
@ -733,6 +733,90 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ Push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ Push(Smi::FromInt(static_cast<int>(type)));
|
||||
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ Pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Initialize register file register.
|
||||
__ mov(kInterpreterRegisterFileRegister, ebp);
|
||||
__ add(kInterpreterRegisterFileRegister,
|
||||
Immediate(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ mov(ebx, Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ mov(ebx, FieldOperand(ebx, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(kInterpreterBytecodeArrayRegister,
|
||||
FieldOperand(ebx, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister);
|
||||
__ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
|
||||
ebx);
|
||||
__ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ mov(
|
||||
kInterpreterBytecodeOffsetRegister,
|
||||
Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiUntag(kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Push dispatch table as a stack located parameter to the bytecode handler -
|
||||
// overwrite the state slot (we don't use these for interpreter deopts).
|
||||
__ LoadRoot(ebx, Heap::kInterpreterTableRootIndex);
|
||||
__ add(ebx, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
DCHECK_EQ(-1, kInterpreterDispatchTableSpillSlot);
|
||||
__ mov(ebx, Operand(esp, -2 * kPointerSize));
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ movzx_b(esi, Operand(kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister, times_1, 0));
|
||||
__ mov(ebx, Operand(ebx, esi, times_pointer_size, 0));
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ mov(kContextRegister,
|
||||
Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
|
||||
// and header removal.
|
||||
__ add(ebx, Immediate(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ jmp(ebx);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
@ -942,6 +942,96 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ li(a1, Operand(Smi::FromInt(static_cast<int>(type))));
|
||||
__ push(a1);
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Drop state (we don't use these for interpreter deopts) and push PC at top
|
||||
// of stack (to simulate initial call to bytecode handler in interpreter entry
|
||||
// trampoline).
|
||||
__ lw(a1, MemOperand(sp));
|
||||
__ Drop(1);
|
||||
__ sw(a1, MemOperand(sp));
|
||||
|
||||
// Initialize register file register and dispatch table register.
|
||||
__ Addu(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
Heap::kInterpreterTableRootIndex);
|
||||
__ Addu(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
|
||||
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ lw(kContextRegister,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ lw(a1,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ lw(a1, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(a1, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister, at);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, at,
|
||||
Operand(zero_reg));
|
||||
__ GetObjectType(kInterpreterBytecodeArrayRegister, a1, a1);
|
||||
__ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, a1,
|
||||
Operand(BYTECODE_ARRAY_TYPE));
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ lw(kInterpreterBytecodeOffsetRegister,
|
||||
MemOperand(
|
||||
kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiUntag(kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ Addu(a1, kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister);
|
||||
__ lbu(a1, MemOperand(a1));
|
||||
__ sll(a1, a1, kPointerSizeLog2);
|
||||
__ Addu(a1, kInterpreterDispatchTableRegister, a1);
|
||||
__ lw(a1, MemOperand(a1));
|
||||
__ Addu(a1, a1, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(a1);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
@ -933,6 +933,96 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ li(a1, Operand(Smi::FromInt(static_cast<int>(type))));
|
||||
__ push(a1);
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Drop state (we don't use these for interpreter deopts) and push PC at top
|
||||
// of stack (to simulate initial call to bytecode handler in interpreter entry
|
||||
// trampoline).
|
||||
__ ld(a1, MemOperand(sp));
|
||||
__ Drop(1);
|
||||
__ sd(a1, MemOperand(sp));
|
||||
|
||||
// Initialize register file register and dispatch table register.
|
||||
__ Daddu(kInterpreterRegisterFileRegister, fp,
|
||||
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
Heap::kInterpreterTableRootIndex);
|
||||
__ Daddu(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
|
||||
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ ld(kContextRegister,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ ld(a1,
|
||||
MemOperand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ ld(a1, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ld(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(a1, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister, at);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, at,
|
||||
Operand(zero_reg));
|
||||
__ GetObjectType(kInterpreterBytecodeArrayRegister, a1, a1);
|
||||
__ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, a1,
|
||||
Operand(BYTECODE_ARRAY_TYPE));
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ ld(kInterpreterBytecodeOffsetRegister,
|
||||
MemOperand(
|
||||
kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiUntag(kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ Daddu(a1, kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister);
|
||||
__ lbu(a1, MemOperand(a1));
|
||||
__ dsll(a1, a1, kPointerSizeLog2);
|
||||
__ Daddu(a1, kInterpreterDispatchTableRegister, a1);
|
||||
__ ld(a1, MemOperand(a1));
|
||||
__ Daddu(a1, a1, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Jump(a1);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
@ -14350,6 +14350,17 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
|
||||
break;
|
||||
}
|
||||
|
||||
case Translation::INTERPRETED_FRAME: {
|
||||
int bytecode_offset = iterator.Next();
|
||||
int shared_info_id = iterator.Next();
|
||||
unsigned height = iterator.Next();
|
||||
Object* shared_info = LiteralArray()->get(shared_info_id);
|
||||
os << "{bytecode_offset=" << bytecode_offset << ", function="
|
||||
<< Brief(SharedFunctionInfo::cast(shared_info)->DebugName())
|
||||
<< ", height=" << height << "}";
|
||||
break;
|
||||
}
|
||||
|
||||
case Translation::JS_FRAME_FUNCTION: {
|
||||
os << "{function}";
|
||||
break;
|
||||
|
@ -784,6 +784,94 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_InterpreterNotifyDeoptimizedHelper(
|
||||
MacroAssembler* masm, Deoptimizer::BailoutType type) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
__ Push(kInterpreterAccumulatorRegister); // Save accumulator register.
|
||||
|
||||
// Pass the deoptimization type to the runtime system.
|
||||
__ Push(Smi::FromInt(static_cast<int>(type)));
|
||||
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
__ Pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
|
||||
// Tear down internal frame.
|
||||
}
|
||||
|
||||
// Drop state (we don't use these for interpreter deopts) and push PC at top
|
||||
// of stack (to simulate initial call to bytecode handler in interpreter entry
|
||||
// trampoline).
|
||||
__ Pop(rbx);
|
||||
__ Drop(1);
|
||||
__ Push(rbx);
|
||||
|
||||
// Initialize register file register and dispatch table register.
|
||||
__ movp(kInterpreterRegisterFileRegister, rbp);
|
||||
__ addp(kInterpreterRegisterFileRegister,
|
||||
Immediate(InterpreterFrameConstants::kRegisterFilePointerFromFp));
|
||||
__ LoadRoot(kInterpreterDispatchTableRegister,
|
||||
Heap::kInterpreterTableRootIndex);
|
||||
__ addp(kInterpreterDispatchTableRegister,
|
||||
Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Get the context from the frame.
|
||||
// TODO(rmcilroy): Update interpreter frame to expect current context at the
|
||||
// context slot instead of the function context.
|
||||
__ movp(kContextRegister,
|
||||
Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kContextFromRegisterPointer));
|
||||
|
||||
// Get the bytecode array pointer from the frame.
|
||||
__ movp(rbx,
|
||||
Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kFunctionFromRegisterPointer));
|
||||
__ movp(rbx, FieldOperand(rbx, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(kInterpreterBytecodeArrayRegister,
|
||||
FieldOperand(rbx, SharedFunctionInfo::kFunctionDataOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister);
|
||||
__ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
|
||||
rbx);
|
||||
__ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
}
|
||||
|
||||
// Get the target bytecode offset from the frame.
|
||||
__ movp(
|
||||
kInterpreterBytecodeOffsetRegister,
|
||||
Operand(kInterpreterRegisterFileRegister,
|
||||
InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer));
|
||||
__ SmiToInteger32(kInterpreterBytecodeOffsetRegister,
|
||||
kInterpreterBytecodeOffsetRegister);
|
||||
|
||||
// Dispatch to the target bytecode.
|
||||
__ movzxbp(rbx, Operand(kInterpreterBytecodeArrayRegister,
|
||||
kInterpreterBytecodeOffsetRegister, times_1, 0));
|
||||
__ movp(rbx, Operand(kInterpreterDispatchTableRegister, rbx,
|
||||
times_pointer_size, 0));
|
||||
__ addp(rbx, Immediate(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ jmp(rbx);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) {
|
||||
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
|
||||
GenerateTailCallToReturnedCode(masm);
|
||||
|
Loading…
Reference in New Issue
Block a user