[Interpreter] Support compiling for baseline on return from interpreted function.
We cannot tier up from interpreted to baseline code when there is an activation of the function on the stack. This significantly regresses the performance of recursive functions since they are unlikely to get tiered up. This CL adds the ability for a function to be marked for baseline compilation when it returns. To do this we patch the InterpreterEntryTrampoline return address to point to InterpreterMarkBaselineOnReturn, which leaves the interpreted frame and recompile the function for baseline. This improves the score of EarlyBoyer by ~8x for Ignition. BUG=v8:4280 LOG=N Review-Url: https://codereview.chromium.org/1965343002 Cr-Commit-Position: refs/heads/master@{#36360}
This commit is contained in:
parent
c04d547298
commit
f241a61a34
@ -959,6 +959,22 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
Generate_JSEntryTrampolineHelper(masm, true);
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) {
|
||||
Register args_count = scratch;
|
||||
|
||||
// Get the arguments + receiver count.
|
||||
__ ldr(args_count,
|
||||
MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ ldr(args_count,
|
||||
FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ add(sp, sp, args_count, LeaveCC);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -1062,15 +1078,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in r0.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ ldr(r2, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ ldr(r2, FieldMemOperand(r2, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
__ add(sp, sp, r2, LeaveCC);
|
||||
LeaveInterpreterFrame(masm, r2);
|
||||
__ Jump(lr);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
@ -1086,6 +1094,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Jump(r4);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ ldr(r1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
|
||||
__ ldr(kContextRegister,
|
||||
MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, r2);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ push(r0);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ push(r1);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ pop(r0);
|
||||
}
|
||||
__ Jump(lr);
|
||||
}
|
||||
|
||||
static void Generate_InterpreterPushArgs(MacroAssembler* masm, Register index,
|
||||
Register limit, Register scratch) {
|
||||
Label loop_header, loop_check;
|
||||
|
@ -963,6 +963,22 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
Generate_JSEntryTrampolineHelper(masm, true);
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) {
|
||||
Register args_count = scratch;
|
||||
|
||||
// Get the arguments + receiver count.
|
||||
__ ldr(args_count,
|
||||
MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ Ldr(args_count.W(),
|
||||
FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ Drop(args_count, 1);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -1064,16 +1080,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in x0.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ ldr(x1, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ Ldr(w1, FieldMemOperand(x1, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments and return.
|
||||
__ Drop(x1, 1);
|
||||
LeaveInterpreterFrame(masm, x2);
|
||||
__ Ret();
|
||||
|
||||
// Load debug copy of the bytecode array.
|
||||
@ -1095,6 +1102,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Jump(x7);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ ldr(x1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
|
||||
__ ldr(kContextRegister,
|
||||
MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, x2);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ push(x0);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ push(x1);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ pop(x0);
|
||||
}
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
|
||||
MacroAssembler* masm, TailCallMode tail_call_mode) {
|
||||
|
@ -237,6 +237,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
|
||||
V(NotifyStubFailureSaveDoubles, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterMarkBaselineOnReturn, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndTailCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(InterpreterPushArgsAndConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
@ -657,6 +658,7 @@ class Builtins {
|
||||
|
||||
static void Generate_InterpreterEntryTrampoline(MacroAssembler* masm);
|
||||
static void Generate_InterpreterEnterBytecodeDispatch(MacroAssembler* masm);
|
||||
static void Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm);
|
||||
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
|
||||
return Generate_InterpreterPushArgsAndCallImpl(masm,
|
||||
TailCallMode::kDisallow);
|
||||
|
@ -789,18 +789,23 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
|
||||
class InterpreterActivationsFinder : public ThreadVisitor,
|
||||
public OptimizedFunctionVisitor {
|
||||
public:
|
||||
SharedFunctionInfo* shared_;
|
||||
bool has_activations_;
|
||||
|
||||
explicit InterpreterActivationsFinder(SharedFunctionInfo* shared)
|
||||
: shared_(shared), has_activations_(false) {}
|
||||
|
||||
void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
|
||||
Address* activation_pc_address = nullptr;
|
||||
JavaScriptFrameIterator it(isolate, top);
|
||||
for (; !it.done() && !has_activations_; it.Advance()) {
|
||||
for (; !it.done(); it.Advance()) {
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
if (!frame->is_interpreted()) continue;
|
||||
if (frame->function()->shared() == shared_) has_activations_ = true;
|
||||
if (frame->function()->shared() == shared_) {
|
||||
has_activations_ = true;
|
||||
activation_pc_address = frame->pc_address();
|
||||
}
|
||||
}
|
||||
|
||||
if (activation_pc_address) {
|
||||
activation_pc_addresses_.push_back(activation_pc_address);
|
||||
}
|
||||
}
|
||||
|
||||
@ -810,19 +815,39 @@ class InterpreterActivationsFinder : public ThreadVisitor,
|
||||
|
||||
void EnterContext(Context* context) {}
|
||||
void LeaveContext(Context* context) {}
|
||||
|
||||
bool MarkActivationsForBaselineOnReturn(Isolate* isolate) {
|
||||
if (activation_pc_addresses_.empty()) return false;
|
||||
|
||||
for (Address* activation_pc_address : activation_pc_addresses_) {
|
||||
DCHECK(isolate->inner_pointer_to_code_cache()
|
||||
->GetCacheEntry(*activation_pc_address)
|
||||
->code->is_interpreter_trampoline_builtin());
|
||||
*activation_pc_address =
|
||||
isolate->builtins()->InterpreterMarkBaselineOnReturn()->entry();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_activations() { return has_activations_; }
|
||||
|
||||
private:
|
||||
SharedFunctionInfo* shared_;
|
||||
bool has_activations_;
|
||||
std::vector<Address*> activation_pc_addresses_;
|
||||
};
|
||||
|
||||
bool HasInterpreterActivations(Isolate* isolate, SharedFunctionInfo* shared) {
|
||||
InterpreterActivationsFinder activations_finder(shared);
|
||||
activations_finder.VisitThread(isolate, isolate->thread_local_top());
|
||||
isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
|
||||
bool HasInterpreterActivations(
|
||||
Isolate* isolate, InterpreterActivationsFinder* activations_finder) {
|
||||
activations_finder->VisitThread(isolate, isolate->thread_local_top());
|
||||
isolate->thread_manager()->IterateArchivedThreads(activations_finder);
|
||||
if (FLAG_turbo_from_bytecode) {
|
||||
// If we are able to optimize functions directly from bytecode, then there
|
||||
// might be optimized functions that rely on bytecode being around. We need
|
||||
// to prevent switching the given function to baseline code in those cases.
|
||||
Deoptimizer::VisitAllOptimizedFunctions(isolate, &activations_finder);
|
||||
Deoptimizer::VisitAllOptimizedFunctions(isolate, activations_finder);
|
||||
}
|
||||
return activations_finder.has_activations_;
|
||||
return activations_finder->has_activations();
|
||||
}
|
||||
|
||||
MaybeHandle<Code> GetBaselineCode(Handle<JSFunction> function) {
|
||||
@ -860,12 +885,22 @@ MaybeHandle<Code> GetBaselineCode(Handle<JSFunction> function) {
|
||||
// of interpreter activations of the given function. The reasons are:
|
||||
// 1) The debugger assumes each function is either full-code or bytecode.
|
||||
// 2) The underlying bytecode is cleared below, breaking stack unwinding.
|
||||
if (HasInterpreterActivations(isolate, function->shared())) {
|
||||
InterpreterActivationsFinder activations_finder(function->shared());
|
||||
if (HasInterpreterActivations(isolate, &activations_finder)) {
|
||||
if (FLAG_trace_opt) {
|
||||
OFStream os(stdout);
|
||||
os << "[unable to switch " << Brief(*function) << " due to activations]"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (activations_finder.MarkActivationsForBaselineOnReturn(isolate)) {
|
||||
if (FLAG_trace_opt) {
|
||||
OFStream os(stdout);
|
||||
os << "[marking " << Brief(function->shared())
|
||||
<< " for baseline recompilation on return]" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return MaybeHandle<Code>();
|
||||
}
|
||||
|
||||
@ -1282,9 +1317,11 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
|
||||
// 2) The underlying bytecode is cleared below, breaking stack unwinding.
|
||||
// The expensive check for activations only needs to be done when the given
|
||||
// function has bytecode, otherwise we can be sure there are no activations.
|
||||
if (shared->HasBytecodeArray() &&
|
||||
HasInterpreterActivations(info->isolate(), *shared)) {
|
||||
return false;
|
||||
if (shared->HasBytecodeArray()) {
|
||||
InterpreterActivationsFinder activations_finder(*shared);
|
||||
if (HasInterpreterActivations(info->isolate(), &activations_finder)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current code has reloc info for serialization, also include
|
||||
|
@ -399,11 +399,15 @@ static bool IsInterpreterFramePc(Isolate* isolate, Address pc) {
|
||||
isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline);
|
||||
Code* interpreter_bytecode_dispatch =
|
||||
isolate->builtins()->builtin(Builtins::kInterpreterEnterBytecodeDispatch);
|
||||
Code* interpreter_baseline_on_return =
|
||||
isolate->builtins()->builtin(Builtins::kInterpreterMarkBaselineOnReturn);
|
||||
|
||||
return (pc >= interpreter_entry_trampoline->instruction_start() &&
|
||||
pc < interpreter_entry_trampoline->instruction_end()) ||
|
||||
(pc >= interpreter_bytecode_dispatch->instruction_start() &&
|
||||
pc < interpreter_bytecode_dispatch->instruction_end());
|
||||
pc < interpreter_bytecode_dispatch->instruction_end()) ||
|
||||
(pc >= interpreter_baseline_on_return->instruction_start() &&
|
||||
pc < interpreter_baseline_on_return->instruction_end());
|
||||
}
|
||||
|
||||
StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
|
||||
@ -444,13 +448,12 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
|
||||
Code* code_obj =
|
||||
GetContainingCode(iterator->isolate(), *(state->pc_address));
|
||||
if (code_obj != nullptr) {
|
||||
if (code_obj->is_interpreter_entry_trampoline() ||
|
||||
code_obj->is_interpreter_enter_bytecode_dispatch()) {
|
||||
return INTERPRETED;
|
||||
}
|
||||
switch (code_obj->kind()) {
|
||||
case Code::BUILTIN:
|
||||
if (marker->IsSmi()) break;
|
||||
if (code_obj->is_interpreter_trampoline_builtin()) {
|
||||
return INTERPRETED;
|
||||
}
|
||||
// We treat frames for BUILTIN Code objects as OptimizedFrame for now
|
||||
// (all the builtins with JavaScript linkage are actually generated
|
||||
// with TurboFan currently, so this is sound).
|
||||
|
@ -520,6 +520,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1,
|
||||
Register scratch2) {
|
||||
Register args_count = scratch1;
|
||||
Register return_pc = scratch2;
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ mov(args_count,
|
||||
Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ mov(args_count,
|
||||
FieldOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ leave();
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ pop(return_pc);
|
||||
__ add(esp, args_count);
|
||||
__ push(return_pc);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -623,18 +643,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in eax.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ mov(ebx, Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ mov(ebx, FieldOperand(ebx, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ leave();
|
||||
|
||||
// Drop receiver + arguments and return.
|
||||
__ pop(ecx);
|
||||
__ add(esp, ebx);
|
||||
__ push(ecx);
|
||||
LeaveInterpreterFrame(masm, ebx, ecx);
|
||||
__ ret(0);
|
||||
|
||||
// Load debug copy of the bytecode array.
|
||||
@ -661,6 +670,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ jmp(ecx);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ mov(edi, Operand(ebp, StandardFrameConstants::kFunctionOffset));
|
||||
__ mov(kContextRegister,
|
||||
Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, ebx, ecx);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ push(eax);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ push(edi);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ pop(eax);
|
||||
}
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
Register array_limit) {
|
||||
// ----------- S t a t e -------------
|
||||
|
@ -952,6 +952,22 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) {
|
||||
Register args_count = scratch;
|
||||
|
||||
// Get the arguments + receiver count.
|
||||
__ lw(args_count,
|
||||
MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ lw(args_count,
|
||||
FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ Addu(sp, sp, args_count);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -1054,16 +1070,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in v0.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ lw(t0, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ lw(t0, FieldMemOperand(t0, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments and return.
|
||||
__ Addu(sp, sp, t0);
|
||||
LeaveInterpreterFrame(masm, t0);
|
||||
__ Jump(ra);
|
||||
|
||||
// Load debug copy of the bytecode array.
|
||||
@ -1085,6 +1092,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Jump(t0);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ lw(a1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
|
||||
__ lw(kContextRegister,
|
||||
MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, t0);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ push(v0);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ push(a1);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ pop(v0);
|
||||
}
|
||||
__ Jump(ra);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
|
||||
MacroAssembler* masm, TailCallMode tail_call_mode) {
|
||||
|
@ -940,6 +940,21 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
Generate_JSEntryTrampolineHelper(masm, true);
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) {
|
||||
Register args_count = scratch;
|
||||
|
||||
// Get the arguments + receiver count.
|
||||
__ ld(args_count,
|
||||
MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ lw(t0, FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ Daddu(sp, sp, args_count);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -1042,16 +1057,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in v0.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ ld(t0, MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ lw(t0, FieldMemOperand(t0, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Drop receiver + arguments and return.
|
||||
__ Daddu(sp, sp, t0);
|
||||
LeaveInterpreterFrame(masm, t0);
|
||||
__ Jump(ra);
|
||||
|
||||
// Load debug copy of the bytecode array.
|
||||
@ -1073,6 +1079,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Jump(a4);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ ld(a1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
|
||||
__ ld(kContextRegister,
|
||||
MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, t0);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ push(v0);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ push(a1);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ pop(v0);
|
||||
}
|
||||
__ Jump(ra);
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_InterpreterPushArgsAndCallImpl(
|
||||
MacroAssembler* masm, TailCallMode tail_call_mode) {
|
||||
|
@ -4787,13 +4787,6 @@ bool Code::IsCodeStubOrIC() {
|
||||
kind() == COMPARE_IC || kind() == TO_BOOLEAN_IC;
|
||||
}
|
||||
|
||||
|
||||
bool Code::IsJavaScriptCode() {
|
||||
return kind() == FUNCTION || kind() == OPTIMIZED_FUNCTION ||
|
||||
is_interpreter_entry_trampoline();
|
||||
}
|
||||
|
||||
|
||||
InlineCacheState Code::ic_state() {
|
||||
InlineCacheState result = ExtractICStateFromFlags(flags());
|
||||
// Only allow uninitialized or debugger states for non-IC code
|
||||
@ -4833,18 +4826,11 @@ inline bool Code::is_hydrogen_stub() {
|
||||
return is_crankshafted() && kind() != OPTIMIZED_FUNCTION;
|
||||
}
|
||||
|
||||
|
||||
inline bool Code::is_interpreter_entry_trampoline() {
|
||||
Handle<Code> interpreter_entry =
|
||||
GetIsolate()->builtins()->InterpreterEntryTrampoline();
|
||||
return interpreter_entry.location() != nullptr && *interpreter_entry == this;
|
||||
}
|
||||
|
||||
inline bool Code::is_interpreter_enter_bytecode_dispatch() {
|
||||
Handle<Code> interpreter_handler =
|
||||
GetIsolate()->builtins()->InterpreterEnterBytecodeDispatch();
|
||||
return interpreter_handler.location() != nullptr &&
|
||||
*interpreter_handler == this;
|
||||
inline bool Code::is_interpreter_trampoline_builtin() {
|
||||
Builtins* builtins = GetIsolate()->builtins();
|
||||
return this == *builtins->InterpreterEntryTrampoline() ||
|
||||
this == *builtins->InterpreterEnterBytecodeDispatch() ||
|
||||
this == *builtins->InterpreterMarkBaselineOnReturn();
|
||||
}
|
||||
|
||||
inline void Code::set_is_crankshafted(bool value) {
|
||||
@ -6123,7 +6109,7 @@ void Map::InobjectSlackTrackingStep() {
|
||||
|
||||
AbstractCode* JSFunction::abstract_code() {
|
||||
Code* code = this->code();
|
||||
if (code->is_interpreter_entry_trampoline()) {
|
||||
if (code->is_interpreter_trampoline_builtin()) {
|
||||
return AbstractCode::cast(shared()->bytecode_array());
|
||||
} else {
|
||||
return AbstractCode::cast(code);
|
||||
|
@ -12932,7 +12932,7 @@ void SharedFunctionInfo::ResetForNewContext(int new_ic_age) {
|
||||
}
|
||||
set_opt_count(0);
|
||||
set_deopt_count(0);
|
||||
} else if (code()->is_interpreter_entry_trampoline()) {
|
||||
} else if (code()->is_interpreter_trampoline_builtin()) {
|
||||
set_profiler_ticks(0);
|
||||
if (optimization_disabled() && opt_count() >= FLAG_max_opt_count) {
|
||||
// Re-enable optimizations if they were disabled due to opt_count limit.
|
||||
|
@ -4970,14 +4970,12 @@ class Code: public HeapObject {
|
||||
inline bool embeds_maps_weakly();
|
||||
|
||||
inline bool IsCodeStubOrIC();
|
||||
inline bool IsJavaScriptCode();
|
||||
|
||||
inline void set_raw_kind_specific_flags1(int value);
|
||||
inline void set_raw_kind_specific_flags2(int value);
|
||||
|
||||
// Testers for interpreter builtins.
|
||||
inline bool is_interpreter_entry_trampoline();
|
||||
inline bool is_interpreter_enter_bytecode_dispatch();
|
||||
inline bool is_interpreter_trampoline_builtin();
|
||||
|
||||
// [is_crankshafted]: For kind STUB or ICs, tells whether or not a code
|
||||
// object was generated by either the hydrogen or the TurboFan optimizing
|
||||
|
@ -34,7 +34,8 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
|
||||
// replace it with lazy-compile builtin. Only exception is when we are
|
||||
// serializing the canonical interpreter-entry-trampoline builtin.
|
||||
if (code->kind() == Code::FUNCTION ||
|
||||
(!serializing_builtins_ && code->is_interpreter_entry_trampoline())) {
|
||||
(!serializing_builtins_ &&
|
||||
code->is_interpreter_trampoline_builtin())) {
|
||||
obj = isolate()->builtins()->builtin(Builtins::kCompileLazy);
|
||||
}
|
||||
} else if (obj->IsBytecodeArray()) {
|
||||
|
@ -598,6 +598,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1,
|
||||
Register scratch2) {
|
||||
Register args_count = scratch1;
|
||||
Register return_pc = scratch2;
|
||||
|
||||
// Get the arguments + receiver count.
|
||||
__ movp(args_count,
|
||||
Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ movl(args_count,
|
||||
FieldOperand(args_count, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ leave();
|
||||
|
||||
// Drop receiver + arguments.
|
||||
__ PopReturnAddressTo(return_pc);
|
||||
__ addp(rsp, args_count);
|
||||
__ PushReturnAddressFrom(return_pc);
|
||||
}
|
||||
|
||||
// Generate code for entering a JS function with the interpreter.
|
||||
// On entry to the function the receiver and arguments have been pushed on the
|
||||
// stack left to right. The actual argument count matches the formal parameter
|
||||
@ -702,18 +722,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset());
|
||||
|
||||
// The return value is in rax.
|
||||
|
||||
// Get the arguments + reciever count.
|
||||
__ movp(rbx, Operand(rbp, InterpreterFrameConstants::kBytecodeArrayFromFp));
|
||||
__ movl(rbx, FieldOperand(rbx, BytecodeArray::kParameterSizeOffset));
|
||||
|
||||
// Leave the frame (also dropping the register file).
|
||||
__ leave();
|
||||
|
||||
// Drop receiver + arguments and return.
|
||||
__ PopReturnAddressTo(rcx);
|
||||
__ addp(rsp, rbx);
|
||||
__ PushReturnAddressFrom(rcx);
|
||||
LeaveInterpreterFrame(masm, rbx, rcx);
|
||||
__ ret(0);
|
||||
|
||||
// Load debug copy of the bytecode array.
|
||||
@ -737,6 +746,31 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ jmp(rcx);
|
||||
}
|
||||
|
||||
void Builtins::Generate_InterpreterMarkBaselineOnReturn(MacroAssembler* masm) {
|
||||
// Save the function and context for call to CompileBaseline.
|
||||
__ movp(rdi, Operand(rbp, StandardFrameConstants::kFunctionOffset));
|
||||
__ movp(kContextRegister,
|
||||
Operand(rbp, StandardFrameConstants::kContextOffset));
|
||||
|
||||
// Leave the frame before recompiling for baseline so that we don't count as
|
||||
// an activation on the stack.
|
||||
LeaveInterpreterFrame(masm, rbx, rcx);
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Push return value.
|
||||
__ Push(rax);
|
||||
|
||||
// Push function as argument and compile for baseline.
|
||||
__ Push(rdi);
|
||||
__ CallRuntime(Runtime::kCompileBaseline);
|
||||
|
||||
// Restore return value.
|
||||
__ Pop(rax);
|
||||
}
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
static void Generate_InterpreterPushArgs(MacroAssembler* masm,
|
||||
bool push_receiver) {
|
||||
// ----------- S t a t e -------------
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include "src/compiler.h"
|
||||
#include "src/disasm.h"
|
||||
#include "src/interpreter/interpreter.h"
|
||||
#include "src/parsing/parser.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
|
||||
@ -757,3 +758,45 @@ TEST(SplitConstantsInFullCompiler) {
|
||||
CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void IsBaselineCompiled(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Handle<Object> object = v8::Utils::OpenHandle(*args[0]);
|
||||
Handle<JSFunction> function = Handle<JSFunction>::cast(object);
|
||||
bool is_baseline = function->shared()->code()->kind() == Code::FUNCTION;
|
||||
return args.GetReturnValue().Set(is_baseline);
|
||||
}
|
||||
|
||||
static void InstallIsBaselineCompiledHelper(v8::Isolate* isolate) {
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::Local<v8::FunctionTemplate> t =
|
||||
v8::FunctionTemplate::New(isolate, IsBaselineCompiled);
|
||||
CHECK(context->Global()
|
||||
->Set(context, v8_str("IsBaselineCompiled"),
|
||||
t->GetFunction(context).ToLocalChecked())
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
TEST(IgnitionBaselineOnReturn) {
|
||||
FLAG_allow_natives_syntax = true;
|
||||
FLAG_always_opt = false;
|
||||
CcTest::InitializeVM();
|
||||
FLAG_ignition = true;
|
||||
reinterpret_cast<i::Isolate*>(CcTest::isolate())->interpreter()->Initialize();
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
InstallIsBaselineCompiledHelper(CcTest::isolate());
|
||||
|
||||
CompileRun(
|
||||
"var is_baseline_in_function, is_baseline_after_return;\n"
|
||||
"var return_val;\n"
|
||||
"function f() {\n"
|
||||
" %CompileBaseline(f);\n"
|
||||
" is_baseline_in_function = IsBaselineCompiled(f);\n"
|
||||
" return 1234;\n"
|
||||
"};\n"
|
||||
"return_val = f();\n"
|
||||
"is_baseline_after_return = IsBaselineCompiled(f);\n");
|
||||
CHECK_EQ(false, GetGlobalProperty("is_baseline_in_function")->BooleanValue());
|
||||
CHECK_EQ(true, GetGlobalProperty("is_baseline_after_return")->BooleanValue());
|
||||
CHECK_EQ(1234.0, GetGlobalProperty("return_val")->Number());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user