Commit 5c8609de authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[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}
parent 550c0f9f
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment