[interpreter] Heal closures when bytecode array is gone.
This ensures the InterpreterEntryTrampoline heals code entry fields inside closures when being called without a valid bytecode array. This is preparatory work to allow removal of bytecode when switching some functions to other types of code. R=rmcilroy@chromium.org BUG=v8:4280 LOG=n Review URL: https://codereview.chromium.org/1904093002 Cr-Commit-Position: refs/heads/master@{#35724}
This commit is contained in:
parent
550c0f9f55
commit
5c8609de9d
@ -992,8 +992,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FrameScope frame_scope(masm, StackFrame::MANUAL);
|
||||
__ PushStandardFrame(r1);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ ldr(r0, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
Register debug_info = kInterpreterBytecodeArrayRegister;
|
||||
DCHECK(!debug_info.is(r0));
|
||||
@ -1005,8 +1005,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ ldr(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(debug_info, DebugInfo::kAbstractCodeIndex), ne);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ CompareRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex);
|
||||
__ b(eq, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
__ CompareObjectType(kInterpreterBytecodeArrayRegister, r0, no_reg,
|
||||
@ -1066,6 +1070,18 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
|
||||
// Even though the first bytecode handler was called, we will never return.
|
||||
__ Abort(kUnexpectedReturnFromBytecodeHandler);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ bind(&bytecode_array_not_present);
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
__ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r4, FieldMemOperand(r4, SharedFunctionInfo::kCodeOffset));
|
||||
__ add(r4, r4, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ str(r4, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
|
||||
__ RecordWriteCodeEntryField(r1, r4, r5);
|
||||
__ Jump(r4);
|
||||
}
|
||||
|
||||
|
||||
|
@ -996,8 +996,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Push(lr, fp, cp, x1);
|
||||
__ Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ Ldr(x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
|
||||
Register debug_info = kInterpreterBytecodeArrayRegister;
|
||||
Label load_debug_bytecode_array, bytecode_array_loaded;
|
||||
@ -1009,8 +1009,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FieldMemOperand(x0, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ Bind(&bytecode_array_loaded);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ CompareRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex);
|
||||
__ B(eq, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister,
|
||||
kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
|
||||
__ CompareObjectType(kInterpreterBytecodeArrayRegister, x0, x0,
|
||||
@ -1074,6 +1078,18 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Ldr(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(debug_info, DebugInfo::kAbstractCodeIndex));
|
||||
__ B(&bytecode_array_loaded);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ Bind(&bytecode_array_not_present);
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
__ Ldr(x7, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldr(x7, FieldMemOperand(x7, SharedFunctionInfo::kCodeOffset));
|
||||
__ Add(x7, x7, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Str(x7, FieldMemOperand(x1, JSFunction::kCodeEntryOffset));
|
||||
__ RecordWriteCodeEntryField(x1, x7, x5);
|
||||
__ Jump(x7);
|
||||
}
|
||||
|
||||
|
||||
|
@ -554,10 +554,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ push(edi); // Callee's JS function.
|
||||
__ push(edx); // Callee's new target.
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into edi (InterpreterBytecodeRegister).
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
||||
Label load_debug_bytecode_array, bytecode_array_loaded;
|
||||
__ cmp(FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset),
|
||||
Immediate(DebugInfo::uninitialized()));
|
||||
@ -566,8 +565,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FieldOperand(eax, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ bind(&bytecode_array_loaded);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ CompareRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex);
|
||||
__ j(equal, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister);
|
||||
__ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
|
||||
eax);
|
||||
@ -638,6 +641,21 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ mov(kInterpreterBytecodeArrayRegister,
|
||||
FieldOperand(debug_info, DebugInfo::kAbstractCodeIndex));
|
||||
__ jmp(&bytecode_array_loaded);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ bind(&bytecode_array_not_present);
|
||||
__ pop(edx); // Callee's new target.
|
||||
__ pop(edi); // Callee's JS function.
|
||||
__ pop(esi); // Callee's context.
|
||||
__ leave(); // Leave the frame so we can tail call.
|
||||
__ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kCodeOffset));
|
||||
__ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
|
||||
__ mov(FieldOperand(edi, JSFunction::kCodeEntryOffset), ecx);
|
||||
__ RecordWriteCodeEntryField(edi, ecx, ebx);
|
||||
__ jmp(ecx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -983,8 +983,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FrameScope frame_scope(masm, StackFrame::MANUAL);
|
||||
__ PushStandardFrame(a1);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ lw(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
Label load_debug_bytecode_array, bytecode_array_loaded;
|
||||
Register debug_info = kInterpreterBytecodeArrayRegister;
|
||||
@ -996,8 +996,11 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FieldMemOperand(a0, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ bind(&bytecode_array_loaded);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ JumpIfRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister, t0);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, t0,
|
||||
Operand(zero_reg));
|
||||
@ -1062,6 +1065,18 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ lw(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(debug_info, DebugInfo::kAbstractCodeIndex));
|
||||
__ Branch(&bytecode_array_loaded);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ bind(&bytecode_array_not_present);
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
__ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(t0, FieldMemOperand(t0, SharedFunctionInfo::kCodeOffset));
|
||||
__ Addu(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ sw(t0, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
|
||||
__ RecordWriteCodeEntryField(a1, t0, t1);
|
||||
__ Jump(t0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -972,8 +972,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FrameScope frame_scope(masm, StackFrame::MANUAL);
|
||||
__ PushStandardFrame(a1);
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into kInterpreterBytecodeRegister.
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ ld(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
Label load_debug_bytecode_array, bytecode_array_loaded;
|
||||
Register debug_info = kInterpreterBytecodeArrayRegister;
|
||||
@ -985,8 +985,11 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FieldMemOperand(a0, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ bind(&bytecode_array_loaded);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ JumpIfRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ SmiTst(kInterpreterBytecodeArrayRegister, a4);
|
||||
__ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, a4,
|
||||
Operand(zero_reg));
|
||||
@ -1051,6 +1054,18 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ ld(kInterpreterBytecodeArrayRegister,
|
||||
FieldMemOperand(debug_info, DebugInfo::kAbstractCodeIndex));
|
||||
__ Branch(&bytecode_array_loaded);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ bind(&bytecode_array_not_present);
|
||||
__ LeaveFrame(StackFrame::JAVA_SCRIPT);
|
||||
__ ld(a4, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ld(a4, FieldMemOperand(a4, SharedFunctionInfo::kCodeOffset));
|
||||
__ Daddu(a4, a4, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ sd(a4, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
|
||||
__ RecordWriteCodeEntryField(a1, a4, a5);
|
||||
__ Jump(a4);
|
||||
}
|
||||
|
||||
|
||||
|
@ -632,10 +632,9 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ Push(rdi); // Callee's JS function.
|
||||
__ Push(rdx); // Callee's new target.
|
||||
|
||||
// Get the bytecode array from the function object and load the pointer to the
|
||||
// first entry into edi (InterpreterBytecodeRegister).
|
||||
// Get the bytecode array from the function object (or from the DebugInfo if
|
||||
// it is present) and load it into kInterpreterBytecodeArrayRegister.
|
||||
__ movp(rax, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
||||
Label load_debug_bytecode_array, bytecode_array_loaded;
|
||||
DCHECK_EQ(Smi::FromInt(0), DebugInfo::uninitialized());
|
||||
__ cmpp(FieldOperand(rax, SharedFunctionInfo::kDebugInfoOffset),
|
||||
@ -645,8 +644,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
FieldOperand(rax, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ bind(&bytecode_array_loaded);
|
||||
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
Label bytecode_array_not_present;
|
||||
__ CompareRoot(kInterpreterBytecodeArrayRegister,
|
||||
Heap::kUndefinedValueRootIndex);
|
||||
__ j(equal, &bytecode_array_not_present);
|
||||
if (FLAG_debug_code) {
|
||||
// Check function data field is actually a BytecodeArray object.
|
||||
__ AssertNotSmi(kInterpreterBytecodeArrayRegister);
|
||||
__ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE,
|
||||
rax);
|
||||
@ -715,6 +718,18 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
|
||||
__ movp(kInterpreterBytecodeArrayRegister,
|
||||
FieldOperand(debug_info, DebugInfo::kAbstractCodeIndex));
|
||||
__ jmp(&bytecode_array_loaded);
|
||||
|
||||
// If the bytecode array is no longer present, then the underlying function
|
||||
// has been switched to a different kind of code and we heal the closure by
|
||||
// switching the code entry field over to the new code object as well.
|
||||
__ bind(&bytecode_array_not_present);
|
||||
__ leave(); // Leave the frame so we can tail call.
|
||||
__ movp(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(rcx, FieldOperand(rcx, SharedFunctionInfo::kCodeOffset));
|
||||
__ leap(rcx, FieldOperand(rcx, Code::kHeaderSize));
|
||||
__ movp(FieldOperand(rdi, JSFunction::kCodeEntryOffset), rcx);
|
||||
__ RecordWriteCodeEntryField(rdi, rcx, r15);
|
||||
__ jmp(rcx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6275,6 +6275,28 @@ TEST(CanonicalSharedFunctionInfo) {
|
||||
"check(g1, g2);");
|
||||
}
|
||||
|
||||
TEST(RemoveCodeFromSharedFunctionInfoButNotFromClosure) {
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
|
||||
global->Set(isolate, "check", v8::FunctionTemplate::New(
|
||||
isolate, CheckEqualSharedFunctionInfos));
|
||||
global->Set(isolate, "remove",
|
||||
v8::FunctionTemplate::New(isolate, RemoveCodeAndGC));
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
|
||||
v8::Context::Scope cscope(context);
|
||||
CompileRun(
|
||||
"function f() { return function g() {}; }"
|
||||
"var g1 = f();"
|
||||
"var g2 = f();"
|
||||
"check(g1, g2);"
|
||||
"g1();"
|
||||
"g2();"
|
||||
"remove(g1);"
|
||||
"g2();"
|
||||
"check(g1, g2);");
|
||||
}
|
||||
|
||||
TEST(OldGenerationAllocationThroughput) {
|
||||
CcTest::InitializeVM();
|
||||
|
Loading…
Reference in New Issue
Block a user