[TypeFeedbackVector] Root literal arrays in function literals slots
Literal arrays and feedback vectors for a function can be garbage
collected if we don't have a rooted closure for the function, which
happens often. It's expensive to come back from this (recreating
boilerplates and gathering feedback again), and the cost is
disproportionate if the function was inlined into optimized code.
To guard against losing these arrays when we need them, we'll now
create literal arrays when creating the feedback vector for the outer
closure, and root them strongly in that vector.
BUG=v8:5456
Review-Url: https://codereview.chromium.org/2620753003
Cr-Original-Commit-Position: refs/heads/master@{#42258}
Committed: 3188780410
Review-Url: https://codereview.chromium.org/2620753003
Cr-Commit-Position: refs/heads/master@{#42264}
This commit is contained in:
parent
2bca05685f
commit
b8294aaa97
@ -1337,12 +1337,19 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register argument_count = r0;
|
||||
Register closure = r1;
|
||||
Register new_target = r3;
|
||||
Register map = argument_count;
|
||||
Register index = r2;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ ldr(index, FieldMemOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ ldr(index, FieldMemOperand(index, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ JumpIfRoot(index, Heap::kUndefinedValueRootIndex,
|
||||
&gotta_call_runtime_no_stack);
|
||||
|
||||
__ push(argument_count);
|
||||
__ push(new_target);
|
||||
__ push(closure);
|
||||
|
||||
Register map = argument_count;
|
||||
Register index = r2;
|
||||
__ ldr(map, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(map,
|
||||
FieldMemOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset));
|
||||
@ -1350,7 +1357,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ cmp(index, Operand(Smi::FromInt(2)));
|
||||
__ b(lt, &gotta_call_runtime);
|
||||
|
||||
// Find literals.
|
||||
// r3 : native context
|
||||
// r2 : length / index
|
||||
// r0 : optimized code map
|
||||
@ -1370,20 +1376,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ ldr(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ cmp(temp, native_context);
|
||||
__ b(ne, &loop_bottom);
|
||||
// Literals available?
|
||||
__ ldr(temp, FieldMemOperand(array_pointer,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ ldr(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ ldr(r4, MemOperand(sp, 0));
|
||||
__ str(temp, FieldMemOperand(r4, JSFunction::kLiteralsOffset));
|
||||
__ push(index);
|
||||
__ RecordWriteField(r4, JSFunction::kLiteralsOffset, temp, index,
|
||||
kLRHasNotBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
__ pop(index);
|
||||
|
||||
// Code available?
|
||||
Register entry = r4;
|
||||
@ -1393,7 +1385,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ ldr(entry, FieldMemOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ pop(closure);
|
||||
// Store code entry in the closure.
|
||||
__ add(entry, entry, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
@ -1428,7 +1420,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ cmp(index, Operand(Smi::FromInt(1)));
|
||||
__ b(gt, &loop_top);
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ jmp(&gotta_call_runtime);
|
||||
|
||||
__ bind(&try_shared);
|
||||
|
@ -1347,6 +1347,12 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register closure = x1;
|
||||
Register map = x13;
|
||||
Register index = x2;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ Ldr(index, FieldMemOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ Ldr(index, FieldMemOperand(index, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ JumpIfRoot(index, Heap::kUndefinedValueRootIndex, &gotta_call_runtime);
|
||||
|
||||
__ Ldr(map, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldr(map,
|
||||
FieldMemOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset));
|
||||
@ -1354,7 +1360,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ Cmp(index, Operand(2));
|
||||
__ B(lt, &gotta_call_runtime);
|
||||
|
||||
// Find literals.
|
||||
// x3 : native context
|
||||
// x2 : length / index
|
||||
// x13 : optimized code map
|
||||
@ -1374,17 +1379,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ Ldr(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ Cmp(temp, native_context);
|
||||
__ B(ne, &loop_bottom);
|
||||
// Literals available?
|
||||
__ Ldr(temp, FieldMemOperand(array_pointer,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ Ldr(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ Str(temp, FieldMemOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ RecordWriteField(closure, JSFunction::kLiteralsOffset, temp, x7,
|
||||
kLRHasNotBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
|
||||
// Code available?
|
||||
Register entry = x7;
|
||||
@ -1394,7 +1388,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ Ldr(entry, FieldMemOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ Add(entry, entry, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ Str(entry, FieldMemOperand(closure, JSFunction::kCodeEntryOffset));
|
||||
__ RecordWriteCodeEntryField(closure, entry, x5);
|
||||
@ -1423,7 +1417,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ Cmp(index, Operand(1));
|
||||
__ B(gt, &loop_top);
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ B(&gotta_call_runtime);
|
||||
|
||||
__ Bind(&try_shared);
|
||||
|
@ -120,13 +120,14 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
|
||||
|
||||
// Initialize the rest of the function.
|
||||
Node* empty_fixed_array = HeapConstant(factory->empty_fixed_array());
|
||||
Node* empty_literals_array = HeapConstant(factory->empty_literals_array());
|
||||
StoreObjectFieldNoWriteBarrier(result, JSObject::kPropertiesOffset,
|
||||
empty_fixed_array);
|
||||
StoreObjectFieldNoWriteBarrier(result, JSObject::kElementsOffset,
|
||||
empty_fixed_array);
|
||||
Node* literals_array = LoadFixedArrayElement(
|
||||
feedback_vector, slot, 0, CodeStubAssembler::SMI_PARAMETERS);
|
||||
StoreObjectFieldNoWriteBarrier(result, JSFunction::kLiteralsOffset,
|
||||
empty_literals_array);
|
||||
literals_array);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
result, JSFunction::kPrototypeOrInitialMapOffset, TheHoleConstant());
|
||||
StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset,
|
||||
|
@ -1025,6 +1025,12 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register new_target = edx;
|
||||
Register argument_count = eax;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ mov(ebx, FieldOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ mov(ebx, FieldOperand(ebx, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ cmp(ebx, masm->isolate()->factory()->undefined_value());
|
||||
__ j(equal, &gotta_call_runtime_no_stack);
|
||||
|
||||
__ push(argument_count);
|
||||
__ push(new_target);
|
||||
__ push(closure);
|
||||
@ -1037,7 +1043,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ cmp(index, Immediate(Smi::FromInt(2)));
|
||||
__ j(less, &gotta_call_runtime);
|
||||
|
||||
// Find literals.
|
||||
// edx : native context
|
||||
// ebx : length / index
|
||||
// eax : optimized code map
|
||||
@ -1055,20 +1060,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ mov(temp, FieldOperand(temp, WeakCell::kValueOffset));
|
||||
__ cmp(temp, native_context);
|
||||
__ j(not_equal, &loop_bottom);
|
||||
// Literals available?
|
||||
__ mov(temp, FieldOperand(map, index, times_half_pointer_size,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ mov(temp, FieldOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ mov(ecx, Operand(esp, 0));
|
||||
__ mov(FieldOperand(ecx, JSFunction::kLiteralsOffset), temp);
|
||||
__ push(index);
|
||||
__ RecordWriteField(ecx, JSFunction::kLiteralsOffset, temp, index,
|
||||
kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
|
||||
__ pop(index);
|
||||
|
||||
// Code available?
|
||||
Register entry = ecx;
|
||||
__ mov(entry, FieldOperand(map, index, times_half_pointer_size,
|
||||
@ -1076,7 +1067,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ mov(entry, FieldOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ pop(closure);
|
||||
// Store code entry in the closure.
|
||||
__ lea(entry, FieldOperand(entry, Code::kHeaderSize));
|
||||
@ -1110,7 +1101,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ cmp(index, Immediate(Smi::FromInt(1)));
|
||||
__ j(greater, &loop_top);
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ jmp(&gotta_call_runtime);
|
||||
|
||||
__ bind(&try_shared);
|
||||
|
@ -1349,18 +1349,24 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register argument_count = a0;
|
||||
Register closure = a1;
|
||||
Register new_target = a3;
|
||||
Register map = a0;
|
||||
Register index = a2;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ lw(index, FieldMemOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ lw(index, FieldMemOperand(index, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ JumpIfRoot(index, Heap::kUndefinedValueRootIndex,
|
||||
&gotta_call_runtime_no_stack);
|
||||
|
||||
__ push(argument_count);
|
||||
__ push(new_target);
|
||||
__ push(closure);
|
||||
|
||||
Register map = a0;
|
||||
Register index = a2;
|
||||
__ lw(map, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(map, FieldMemOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset));
|
||||
__ lw(index, FieldMemOperand(map, FixedArray::kLengthOffset));
|
||||
__ Branch(&gotta_call_runtime, lt, index, Operand(Smi::FromInt(2)));
|
||||
|
||||
// Find literals.
|
||||
// a3 : native context
|
||||
// a2 : length / index
|
||||
// a0 : optimized code map
|
||||
@ -1380,20 +1386,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
SharedFunctionInfo::kOffsetToPreviousContext));
|
||||
__ lw(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ Branch(&loop_bottom, ne, temp, Operand(native_context));
|
||||
// Literals available?
|
||||
__ lw(temp, FieldMemOperand(array_pointer,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ lw(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ lw(t0, MemOperand(sp, 0));
|
||||
__ sw(temp, FieldMemOperand(t0, JSFunction::kLiteralsOffset));
|
||||
__ push(index);
|
||||
__ RecordWriteField(t0, JSFunction::kLiteralsOffset, temp, index,
|
||||
kRAHasNotBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
__ pop(index);
|
||||
|
||||
// Code available?
|
||||
Register entry = t0;
|
||||
@ -1403,7 +1395,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ lw(entry, FieldMemOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ pop(closure);
|
||||
// Store code entry in the closure.
|
||||
__ Addu(entry, entry, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
@ -1438,7 +1430,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
|
||||
__ Branch(&loop_top, gt, index, Operand(Smi::FromInt(1)));
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ jmp(&gotta_call_runtime);
|
||||
|
||||
__ bind(&try_shared);
|
||||
|
@ -1340,18 +1340,24 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register argument_count = a0;
|
||||
Register closure = a1;
|
||||
Register new_target = a3;
|
||||
Register map = a0;
|
||||
Register index = a2;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ ld(index, FieldMemOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ ld(index, FieldMemOperand(index, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ JumpIfRoot(index, Heap::kUndefinedValueRootIndex,
|
||||
&gotta_call_runtime_no_stack);
|
||||
|
||||
__ push(argument_count);
|
||||
__ push(new_target);
|
||||
__ push(closure);
|
||||
|
||||
Register map = a0;
|
||||
Register index = a2;
|
||||
__ ld(map, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ld(map, FieldMemOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset));
|
||||
__ ld(index, FieldMemOperand(map, FixedArray::kLengthOffset));
|
||||
__ Branch(&gotta_call_runtime, lt, index, Operand(Smi::FromInt(2)));
|
||||
|
||||
// Find literals.
|
||||
// a3 : native context
|
||||
// a2 : length / index
|
||||
// a0 : optimized code map
|
||||
@ -1371,20 +1377,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
SharedFunctionInfo::kOffsetToPreviousContext));
|
||||
__ ld(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ Branch(&loop_bottom, ne, temp, Operand(native_context));
|
||||
// Literals available?
|
||||
__ ld(temp, FieldMemOperand(array_pointer,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ ld(temp, FieldMemOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ ld(a4, MemOperand(sp, 0));
|
||||
__ sd(temp, FieldMemOperand(a4, JSFunction::kLiteralsOffset));
|
||||
__ push(index);
|
||||
__ RecordWriteField(a4, JSFunction::kLiteralsOffset, temp, index,
|
||||
kRAHasNotBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
__ pop(index);
|
||||
|
||||
// Code available?
|
||||
Register entry = a4;
|
||||
@ -1394,7 +1386,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ ld(entry, FieldMemOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ pop(closure);
|
||||
// Store code entry in the closure.
|
||||
__ Daddu(entry, entry, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
@ -1429,7 +1421,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
|
||||
__ Branch(&loop_top, gt, index, Operand(Smi::FromInt(1)));
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ jmp(&gotta_call_runtime);
|
||||
|
||||
__ bind(&try_shared);
|
||||
|
@ -994,13 +994,18 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
Register closure = rdi;
|
||||
Register map = r8;
|
||||
Register index = r9;
|
||||
|
||||
// Do we have a valid feedback vector?
|
||||
__ movp(rbx, FieldOperand(closure, JSFunction::kLiteralsOffset));
|
||||
__ movp(rbx, FieldOperand(rbx, LiteralsArray::kFeedbackVectorOffset));
|
||||
__ JumpIfRoot(rbx, Heap::kUndefinedValueRootIndex, &gotta_call_runtime);
|
||||
|
||||
__ movp(map, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(map, FieldOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset));
|
||||
__ SmiToInteger32(index, FieldOperand(map, FixedArray::kLengthOffset));
|
||||
__ cmpl(index, Immediate(2));
|
||||
__ j(less, &gotta_call_runtime);
|
||||
|
||||
// Find literals.
|
||||
// r14 : native context
|
||||
// r9 : length / index
|
||||
// r8 : optimized code map
|
||||
@ -1017,17 +1022,6 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ movp(temp, FieldOperand(temp, WeakCell::kValueOffset));
|
||||
__ cmpp(temp, native_context);
|
||||
__ j(not_equal, &loop_bottom);
|
||||
// Literals available?
|
||||
__ movp(temp, FieldOperand(map, index, times_pointer_size,
|
||||
SharedFunctionInfo::kOffsetToPreviousLiterals));
|
||||
__ movp(temp, FieldOperand(temp, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(temp, &gotta_call_runtime);
|
||||
|
||||
// Save the literals in the closure.
|
||||
__ movp(FieldOperand(closure, JSFunction::kLiteralsOffset), temp);
|
||||
__ movp(r15, index);
|
||||
__ RecordWriteField(closure, JSFunction::kLiteralsOffset, temp, r15,
|
||||
kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
|
||||
|
||||
// Code available?
|
||||
Register entry = rcx;
|
||||
@ -1036,7 +1030,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ movp(entry, FieldOperand(entry, WeakCell::kValueOffset));
|
||||
__ JumpIfSmi(entry, &try_shared);
|
||||
|
||||
// Found literals and code. Get them into the closure and return.
|
||||
// Found code. Get it into the closure and return.
|
||||
__ leap(entry, FieldOperand(entry, Code::kHeaderSize));
|
||||
__ movp(FieldOperand(closure, JSFunction::kCodeEntryOffset), entry);
|
||||
__ RecordWriteCodeEntryField(closure, entry, r15);
|
||||
@ -1067,7 +1061,7 @@ void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
|
||||
__ cmpl(index, Immediate(1));
|
||||
__ j(greater, &loop_top);
|
||||
|
||||
// We found neither literals nor code.
|
||||
// We found no code.
|
||||
__ jmp(&gotta_call_runtime);
|
||||
|
||||
__ bind(&try_shared);
|
||||
|
@ -597,14 +597,13 @@ MUST_USE_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeMap(
|
||||
&RuntimeCallStats::CompileGetFromOptimizedCodeMap);
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
DisallowHeapAllocation no_gc;
|
||||
CodeAndLiterals cached = shared->SearchOptimizedCodeMap(
|
||||
Code* code = shared->SearchOptimizedCodeMap(
|
||||
function->context()->native_context(), osr_ast_id);
|
||||
if (cached.code != nullptr) {
|
||||
if (code != nullptr) {
|
||||
// Caching of optimized code enabled and optimized code found.
|
||||
if (cached.literals != nullptr) function->set_literals(cached.literals);
|
||||
DCHECK(!cached.code->marked_for_deoptimization());
|
||||
DCHECK(!code->marked_for_deoptimization());
|
||||
DCHECK(function->shared()->is_compiled());
|
||||
return Handle<Code>(cached.code);
|
||||
return Handle<Code>(code);
|
||||
}
|
||||
return MaybeHandle<Code>();
|
||||
}
|
||||
@ -626,10 +625,9 @@ void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
|
||||
// Cache optimized context-specific code.
|
||||
Handle<JSFunction> function = info->closure();
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
Handle<LiteralsArray> literals(function->literals());
|
||||
Handle<Context> native_context(function->context()->native_context());
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, native_context, code,
|
||||
literals, info->osr_ast_id());
|
||||
info->osr_ast_id());
|
||||
}
|
||||
|
||||
bool GetOptimizedCodeNow(CompilationJob* job) {
|
||||
@ -865,10 +863,8 @@ CompilationJob::Status FinalizeOptimizedCompilationJob(CompilationJob* job) {
|
||||
} else if (job->FinalizeJob() == CompilationJob::SUCCEEDED) {
|
||||
job->RecordOptimizedCompilationStats();
|
||||
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
|
||||
if (shared
|
||||
->SearchOptimizedCodeMap(info->context()->native_context(),
|
||||
info->osr_ast_id())
|
||||
.code == nullptr) {
|
||||
if (shared->SearchOptimizedCodeMap(info->context()->native_context(),
|
||||
info->osr_ast_id()) == nullptr) {
|
||||
InsertCodeIntoOptimizedCodeMap(info);
|
||||
}
|
||||
if (FLAG_trace_opt) {
|
||||
@ -1756,19 +1752,16 @@ void Compiler::PostInstantiation(Handle<JSFunction> function,
|
||||
function->MarkForOptimization();
|
||||
}
|
||||
|
||||
CodeAndLiterals cached = shared->SearchOptimizedCodeMap(
|
||||
Code* code = shared->SearchOptimizedCodeMap(
|
||||
function->context()->native_context(), BailoutId::None());
|
||||
if (cached.code != nullptr) {
|
||||
if (code != nullptr) {
|
||||
// Caching of optimized code enabled and optimized code found.
|
||||
DCHECK(!cached.code->marked_for_deoptimization());
|
||||
DCHECK(!code->marked_for_deoptimization());
|
||||
DCHECK(function->shared()->is_compiled());
|
||||
function->ReplaceCode(cached.code);
|
||||
function->ReplaceCode(code);
|
||||
}
|
||||
|
||||
if (cached.literals != nullptr) {
|
||||
DCHECK(shared->is_compiled());
|
||||
function->set_literals(cached.literals);
|
||||
} else if (shared->is_compiled()) {
|
||||
if (shared->is_compiled()) {
|
||||
// TODO(mvstanton): pass pretenure flag to EnsureLiterals.
|
||||
JSFunction::EnsureLiterals(function);
|
||||
}
|
||||
|
@ -762,12 +762,15 @@ Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
|
||||
Node* function_map = jsgraph()->HeapConstant(
|
||||
handle(Map::cast(native_context()->get(function_map_index)), isolate()));
|
||||
|
||||
FeedbackVectorSlot slot = p.feedback().slot();
|
||||
Node* literals = jsgraph()->HeapConstant(
|
||||
handle(LiteralsArray::cast(p.feedback().vector()->Get(slot)), isolate()));
|
||||
|
||||
// Note that it is only safe to embed the raw entry point of the compile
|
||||
// lazy stub into the code, because that stub is immortal and immovable.
|
||||
Node* compile_entry = jsgraph()->PointerConstant(
|
||||
jsgraph()->isolate()->builtins()->CompileLazy()->entry());
|
||||
Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
|
||||
Node* empty_literals_array = jsgraph()->EmptyLiteralsArrayConstant();
|
||||
Node* the_hole = jsgraph()->TheHoleConstant();
|
||||
Node* undefined = jsgraph()->UndefinedConstant();
|
||||
AllocationBuilder a(jsgraph(), effect, control);
|
||||
@ -776,7 +779,7 @@ Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
|
||||
a.Store(AccessBuilder::ForMap(), function_map);
|
||||
a.Store(AccessBuilder::ForJSObjectProperties(), empty_fixed_array);
|
||||
a.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
|
||||
a.Store(AccessBuilder::ForJSFunctionLiterals(), empty_literals_array);
|
||||
a.Store(AccessBuilder::ForJSFunctionLiterals(), literals);
|
||||
a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(), the_hole);
|
||||
a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
|
||||
a.Store(AccessBuilder::ForJSFunctionContext(), context);
|
||||
|
@ -66,11 +66,6 @@ Node* JSGraph::EmptyFixedArrayConstant() {
|
||||
HeapConstant(factory()->empty_fixed_array()));
|
||||
}
|
||||
|
||||
Node* JSGraph::EmptyLiteralsArrayConstant() {
|
||||
return CACHED(kEmptyLiteralsArrayConstant,
|
||||
HeapConstant(factory()->empty_literals_array()));
|
||||
}
|
||||
|
||||
Node* JSGraph::EmptyStringConstant() {
|
||||
return CACHED(kEmptyStringConstant, HeapConstant(factory()->empty_string()));
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
|
||||
ArgvMode argv_mode = kArgvOnStack,
|
||||
bool builtin_exit_frame = false);
|
||||
Node* EmptyFixedArrayConstant();
|
||||
Node* EmptyLiteralsArrayConstant();
|
||||
Node* EmptyStringConstant();
|
||||
Node* FixedArrayMapConstant();
|
||||
Node* FixedDoubleArrayMapConstant();
|
||||
@ -167,7 +166,6 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
|
||||
kCEntryStub3Constant,
|
||||
kCEntryStub1WithBuiltinExitFrameConstant,
|
||||
kEmptyFixedArrayConstant,
|
||||
kEmptyLiteralsArrayConstant,
|
||||
kEmptyStringConstant,
|
||||
kFixedArrayMapConstant,
|
||||
kFixedDoubleArrayMapConstant,
|
||||
|
@ -1281,7 +1281,13 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
|
||||
|
||||
DCHECK_EQ(MONOMORPHIC, nexus.ic_state());
|
||||
|
||||
Handle<Map> receiver_map(nexus.FindFirstMap(), isolate());
|
||||
Map* map = nexus.FindFirstMap();
|
||||
if (map == nullptr) {
|
||||
// Maps are weakly held in the type feedback vector, we may not have one.
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Handle<Map> receiver_map(map, isolate());
|
||||
Handle<Name> cached_name =
|
||||
handle(Name::cast(nexus.GetFeedbackExtra()), isolate());
|
||||
|
||||
|
@ -412,9 +412,8 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
|
||||
|
||||
static const int kSharedOffset = 0;
|
||||
static const int kCachedCodeOffset = 1;
|
||||
static const int kLiteralsOffset = 2;
|
||||
static const int kOsrAstIdOffset = 3;
|
||||
static const int kEntryLength = 4;
|
||||
static const int kOsrAstIdOffset = 2;
|
||||
static const int kEntryLength = 3;
|
||||
static const int kInitialLength = kEntryLength;
|
||||
|
||||
int Context::SearchOptimizedCodeMapEntry(SharedFunctionInfo* shared,
|
||||
@ -436,38 +435,29 @@ int Context::SearchOptimizedCodeMapEntry(SharedFunctionInfo* shared,
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Context::SearchOptimizedCodeMap(SharedFunctionInfo* shared,
|
||||
BailoutId osr_ast_id, Code** pcode,
|
||||
LiteralsArray** pliterals) {
|
||||
Code* Context::SearchOptimizedCodeMap(SharedFunctionInfo* shared,
|
||||
BailoutId osr_ast_id) {
|
||||
DCHECK(this->IsNativeContext());
|
||||
int entry = SearchOptimizedCodeMapEntry(shared, osr_ast_id);
|
||||
if (entry != -1) {
|
||||
FixedArray* code_map = osr_code_table();
|
||||
DCHECK_LE(entry + kEntryLength, code_map->length());
|
||||
WeakCell* cell = WeakCell::cast(code_map->get(entry + kCachedCodeOffset));
|
||||
WeakCell* literals_cell =
|
||||
WeakCell::cast(code_map->get(entry + kLiteralsOffset));
|
||||
|
||||
*pcode = cell->cleared() ? nullptr : Code::cast(cell->value());
|
||||
*pliterals = literals_cell->cleared()
|
||||
? nullptr
|
||||
: LiteralsArray::cast(literals_cell->value());
|
||||
} else {
|
||||
*pcode = nullptr;
|
||||
*pliterals = nullptr;
|
||||
return cell->cleared() ? nullptr : Code::cast(cell->value());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Context::AddToOptimizedCodeMap(Handle<Context> native_context,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<Code> code,
|
||||
Handle<LiteralsArray> literals,
|
||||
BailoutId osr_ast_id) {
|
||||
DCHECK(native_context->IsNativeContext());
|
||||
Isolate* isolate = native_context->GetIsolate();
|
||||
if (isolate->serializer_enabled()) return;
|
||||
|
||||
STATIC_ASSERT(kEntryLength == 4);
|
||||
STATIC_ASSERT(kEntryLength == 3);
|
||||
Handle<FixedArray> new_code_map;
|
||||
int entry;
|
||||
|
||||
@ -478,12 +468,9 @@ void Context::AddToOptimizedCodeMap(Handle<Context> native_context,
|
||||
Handle<FixedArray> old_code_map(native_context->osr_code_table(), isolate);
|
||||
entry = native_context->SearchOptimizedCodeMapEntry(*shared, osr_ast_id);
|
||||
if (entry >= 0) {
|
||||
// Just set the code and literals of the entry.
|
||||
// Just set the code of the entry.
|
||||
Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code);
|
||||
old_code_map->set(entry + kCachedCodeOffset, *code_cell);
|
||||
Handle<WeakCell> literals_cell =
|
||||
isolate->factory()->NewWeakCell(literals);
|
||||
old_code_map->set(entry + kLiteralsOffset, *literals_cell);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -507,12 +494,10 @@ void Context::AddToOptimizedCodeMap(Handle<Context> native_context,
|
||||
}
|
||||
|
||||
Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code);
|
||||
Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals);
|
||||
Handle<WeakCell> shared_cell = isolate->factory()->NewWeakCell(shared);
|
||||
|
||||
new_code_map->set(entry + kSharedOffset, *shared_cell);
|
||||
new_code_map->set(entry + kCachedCodeOffset, *code_cell);
|
||||
new_code_map->set(entry + kLiteralsOffset, *literals_cell);
|
||||
new_code_map->set(entry + kOsrAstIdOffset, Smi::FromInt(osr_ast_id.ToInt()));
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -523,8 +508,6 @@ void Context::AddToOptimizedCodeMap(Handle<Context> native_context,
|
||||
DCHECK(cell->cleared() ||
|
||||
(cell->value()->IsCode() &&
|
||||
Code::cast(cell->value())->kind() == Code::OPTIMIZED_FUNCTION));
|
||||
cell = WeakCell::cast(new_code_map->get(i + kLiteralsOffset));
|
||||
DCHECK(cell->cleared() || cell->value()->IsFixedArray());
|
||||
DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi());
|
||||
}
|
||||
#endif
|
||||
@ -565,8 +548,6 @@ void Context::EvictFromOptimizedCodeMap(Code* optimized_code,
|
||||
code_map->set(dst + kSharedOffset, code_map->get(src + kSharedOffset));
|
||||
code_map->set(dst + kCachedCodeOffset,
|
||||
code_map->get(src + kCachedCodeOffset));
|
||||
code_map->set(dst + kLiteralsOffset,
|
||||
code_map->get(src + kLiteralsOffset));
|
||||
code_map->set(dst + kOsrAstIdOffset,
|
||||
code_map->get(src + kOsrAstIdOffset));
|
||||
}
|
||||
|
@ -567,15 +567,14 @@ class Context: public FixedArray {
|
||||
|
||||
// A native context keeps track of all osrd optimized functions.
|
||||
inline bool OptimizedCodeMapIsCleared();
|
||||
void SearchOptimizedCodeMap(SharedFunctionInfo* shared, BailoutId osr_ast_id,
|
||||
Code** pcode, LiteralsArray** pliterals);
|
||||
Code* SearchOptimizedCodeMap(SharedFunctionInfo* shared,
|
||||
BailoutId osr_ast_id);
|
||||
int SearchOptimizedCodeMapEntry(SharedFunctionInfo* shared,
|
||||
BailoutId osr_ast_id);
|
||||
|
||||
static void AddToOptimizedCodeMap(Handle<Context> native_context,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<Code> code,
|
||||
Handle<LiteralsArray> literals,
|
||||
BailoutId osr_ast_id);
|
||||
|
||||
// A native context holds a list of all functions with optimized code.
|
||||
|
@ -823,35 +823,33 @@ class LiteralFixer {
|
||||
public:
|
||||
static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
|
||||
Handle<SharedFunctionInfo> shared_info,
|
||||
bool feedback_metadata_changed, Isolate* isolate) {
|
||||
Isolate* isolate) {
|
||||
int new_literal_count = compile_info_wrapper->GetLiteralCount();
|
||||
int old_literal_count = shared_info->num_literals();
|
||||
|
||||
if (old_literal_count == new_literal_count && !feedback_metadata_changed) {
|
||||
// If literal count didn't change, simply go over all functions
|
||||
// and clear literal arrays.
|
||||
ClearValuesVisitor visitor;
|
||||
IterateJSFunctions(shared_info, &visitor);
|
||||
} else {
|
||||
// When literal count changes, we have to create new array instances.
|
||||
// Since we cannot create instances when iterating heap, we should first
|
||||
// collect all functions and fix their literal arrays.
|
||||
Handle<FixedArray> function_instances =
|
||||
CollectJSFunctions(shared_info, isolate);
|
||||
Handle<TypeFeedbackMetadata> feedback_metadata(
|
||||
shared_info->feedback_metadata());
|
||||
// Recreate the literal array and type feedback vector.
|
||||
// Since the feedback vector roots literal arrays for nested functions,
|
||||
// we can't simply leave it in place because those nested literal
|
||||
// array and feedback vectors may have changed structure.
|
||||
Handle<FixedArray> function_instances =
|
||||
CollectJSFunctions(shared_info, isolate);
|
||||
Handle<TypeFeedbackMetadata> feedback_metadata(
|
||||
shared_info->feedback_metadata());
|
||||
|
||||
for (int i = 0; i < function_instances->length(); i++) {
|
||||
Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
|
||||
Handle<TypeFeedbackVector> vector =
|
||||
TypeFeedbackVector::New(isolate, feedback_metadata);
|
||||
Handle<LiteralsArray> new_literals =
|
||||
LiteralsArray::New(isolate, vector, new_literal_count);
|
||||
fun->set_literals(*new_literals);
|
||||
}
|
||||
for (int i = 0; i < function_instances->length(); i++) {
|
||||
Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
|
||||
Handle<TypeFeedbackVector> vector =
|
||||
TypeFeedbackVector::New(isolate, feedback_metadata);
|
||||
Handle<LiteralsArray> new_literals =
|
||||
LiteralsArray::New(isolate, vector, new_literal_count);
|
||||
Handle<LiteralsArray> old_literals(fun->literals(), isolate);
|
||||
fun->set_literals(*new_literals);
|
||||
|
||||
shared_info->set_num_literals(new_literal_count);
|
||||
// The literals are rooted in a containing feedback vector.
|
||||
// Replace them there, so new closures have the correct literals.
|
||||
ReplaceRoots(old_literals, new_literals);
|
||||
}
|
||||
|
||||
shared_info->set_num_literals(new_literal_count);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -872,6 +870,56 @@ class LiteralFixer {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
static void IterateAllJSFunctions(Heap* heap, Visitor* visitor) {
|
||||
HeapIterator iterator(heap);
|
||||
for (HeapObject* obj = iterator.next(); obj != NULL;
|
||||
obj = iterator.next()) {
|
||||
if (obj->IsJSFunction()) {
|
||||
JSFunction* function = JSFunction::cast(obj);
|
||||
visitor->visit(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReplaceRootsVisitor {
|
||||
public:
|
||||
ReplaceRootsVisitor(Handle<LiteralsArray> old_literals,
|
||||
Handle<LiteralsArray> new_literals)
|
||||
: old_literals_(old_literals), new_literals_(new_literals) {}
|
||||
|
||||
void visit(JSFunction* fun) {
|
||||
if (!fun->shared()->is_compiled()) return;
|
||||
|
||||
// Look in the type feedback vector for a copy of literals.
|
||||
TypeFeedbackVector* vector = fun->feedback_vector();
|
||||
// Note: it's important to get the feedback metadata from the
|
||||
// type feedback vector, because there may be a new metadata
|
||||
// object in the SharedFunctionInfo (with a different slot
|
||||
// configuration).
|
||||
TypeFeedbackMetadataIterator iter(vector->metadata());
|
||||
while (iter.HasNext()) {
|
||||
FeedbackVectorSlot slot = iter.Next();
|
||||
FeedbackVectorSlotKind kind = iter.kind();
|
||||
if (kind == FeedbackVectorSlotKind::CREATE_CLOSURE) {
|
||||
Object* obj = vector->Get(slot);
|
||||
if (obj == *old_literals_) {
|
||||
vector->Set(slot, *new_literals_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Handle<LiteralsArray> old_literals_;
|
||||
Handle<LiteralsArray> new_literals_;
|
||||
};
|
||||
|
||||
static void ReplaceRoots(Handle<LiteralsArray> old_literals,
|
||||
Handle<LiteralsArray> new_literals) {
|
||||
ReplaceRootsVisitor replace_visitor(old_literals, new_literals);
|
||||
IterateAllJSFunctions(old_literals->GetHeap(), &replace_visitor);
|
||||
}
|
||||
|
||||
// Finds all instances of JSFunction that refers to the provided shared_info
|
||||
// and returns array with them.
|
||||
static Handle<FixedArray> CollectJSFunctions(
|
||||
@ -972,7 +1020,6 @@ void LiveEdit::ReplaceFunctionCode(
|
||||
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
||||
Handle<SharedFunctionInfo> new_shared_info =
|
||||
compile_info_wrapper.GetSharedFunctionInfo();
|
||||
bool feedback_metadata_changed = false;
|
||||
|
||||
if (shared_info->is_compiled()) {
|
||||
// Take whatever code we can get from the new shared function info. We
|
||||
@ -1019,8 +1066,6 @@ void LiveEdit::ReplaceFunctionCode(
|
||||
// Update the type feedback vector, if needed.
|
||||
Handle<TypeFeedbackMetadata> new_feedback_metadata(
|
||||
new_shared_info->feedback_metadata());
|
||||
feedback_metadata_changed =
|
||||
new_feedback_metadata->DiffersFrom(shared_info->feedback_metadata());
|
||||
shared_info->set_feedback_metadata(*new_feedback_metadata);
|
||||
}
|
||||
|
||||
@ -1029,8 +1074,7 @@ void LiveEdit::ReplaceFunctionCode(
|
||||
shared_info->set_start_position(start_position);
|
||||
shared_info->set_end_position(end_position);
|
||||
|
||||
LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info,
|
||||
feedback_metadata_changed, isolate);
|
||||
LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
|
||||
|
||||
DeoptimizeDependentFunctions(*shared_info);
|
||||
isolate->compilation_cache()->Remove(shared_info);
|
||||
|
@ -551,26 +551,6 @@ void ObjectStatsCollector::RecordSharedFunctionInfoDetails(
|
||||
RecordFixedArrayHelper(sfi, optimized_code_map, OPTIMIZED_CODE_MAP_SUB_TYPE,
|
||||
0);
|
||||
// Optimized code map should be small, so skip accounting.
|
||||
int len = optimized_code_map->length();
|
||||
for (int i = SharedFunctionInfo::kEntriesStart; i < len;
|
||||
i += SharedFunctionInfo::kEntryLength) {
|
||||
Object* slot =
|
||||
optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset);
|
||||
LiteralsArray* literals = nullptr;
|
||||
if (slot->IsWeakCell()) {
|
||||
WeakCell* cell = WeakCell::cast(slot);
|
||||
if (!cell->cleared()) {
|
||||
literals = LiteralsArray::cast(cell->value());
|
||||
}
|
||||
} else {
|
||||
literals = LiteralsArray::cast(slot);
|
||||
}
|
||||
if (literals != nullptr) {
|
||||
RecordFixedArrayHelper(sfi, literals, LITERALS_ARRAY_SUB_TYPE, 0);
|
||||
RecordFixedArrayHelper(sfi, literals->feedback_vector(),
|
||||
TYPE_FEEDBACK_VECTOR_SUB_TYPE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3505,12 +3505,16 @@ LiteralsArray* LiteralsArray::cast(Object* object) {
|
||||
return reinterpret_cast<LiteralsArray*>(object);
|
||||
}
|
||||
|
||||
bool LiteralsArray::has_feedback_vector() const {
|
||||
return !get(kVectorIndex)->IsUndefined(this->GetIsolate());
|
||||
}
|
||||
|
||||
TypeFeedbackVector* LiteralsArray::feedback_vector() const {
|
||||
if (length() == 0) {
|
||||
if (length() == 0 || !has_feedback_vector()) {
|
||||
return TypeFeedbackVector::cast(
|
||||
const_cast<FixedArray*>(FixedArray::cast(this)));
|
||||
this->GetIsolate()->heap()->empty_type_feedback_vector());
|
||||
}
|
||||
|
||||
return TypeFeedbackVector::cast(get(kVectorIndex));
|
||||
}
|
||||
|
||||
@ -6657,6 +6661,13 @@ void JSFunction::ReplaceCode(Code* code) {
|
||||
}
|
||||
}
|
||||
|
||||
bool JSFunction::has_literals_array() const {
|
||||
SharedFunctionInfo* shared = this->shared();
|
||||
|
||||
return (literals() != shared->GetIsolate()->heap()->empty_literals_array() ||
|
||||
(shared->feedback_metadata()->slot_count() == 0 &&
|
||||
shared->num_literals() == 0));
|
||||
}
|
||||
|
||||
Context* JSFunction::context() {
|
||||
return Context::cast(READ_FIELD(this, kContextOffset));
|
||||
|
119
src/objects.cc
119
src/objects.cc
@ -11947,48 +11947,20 @@ void JSFunction::AttemptConcurrentOptimization() {
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<LiteralsArray> SharedFunctionInfo::FindOrCreateLiterals(
|
||||
Handle<SharedFunctionInfo> shared, Handle<Context> native_context) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
CodeAndLiterals result =
|
||||
shared->SearchOptimizedCodeMap(*native_context, BailoutId::None());
|
||||
if (result.literals != nullptr) {
|
||||
DCHECK(shared->feedback_metadata()->is_empty() ||
|
||||
!result.literals->feedback_vector()->is_empty());
|
||||
return handle(result.literals, isolate);
|
||||
}
|
||||
|
||||
Handle<TypeFeedbackVector> feedback_vector =
|
||||
TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
|
||||
Handle<LiteralsArray> literals =
|
||||
LiteralsArray::New(isolate, feedback_vector, shared->num_literals());
|
||||
Handle<Code> code;
|
||||
if (result.code != nullptr) {
|
||||
code = Handle<Code>(result.code, isolate);
|
||||
}
|
||||
AddToOptimizedCodeMap(shared, native_context, code, literals,
|
||||
BailoutId::None());
|
||||
return literals;
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedFunctionInfo::AddToOptimizedCodeMap(
|
||||
Handle<SharedFunctionInfo> shared, Handle<Context> native_context,
|
||||
MaybeHandle<Code> code, Handle<LiteralsArray> literals,
|
||||
BailoutId osr_ast_id) {
|
||||
Handle<Code> code, BailoutId osr_ast_id) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
if (isolate->serializer_enabled()) return;
|
||||
DCHECK(code.is_null() ||
|
||||
code.ToHandleChecked()->kind() == Code::OPTIMIZED_FUNCTION);
|
||||
DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
|
||||
DCHECK(native_context->IsNativeContext());
|
||||
STATIC_ASSERT(kEntryLength == 3);
|
||||
STATIC_ASSERT(kEntryLength == 2);
|
||||
Handle<FixedArray> new_code_map;
|
||||
int entry;
|
||||
|
||||
if (!osr_ast_id.IsNone()) {
|
||||
Context::AddToOptimizedCodeMap(
|
||||
native_context, shared, code.ToHandleChecked(), literals, osr_ast_id);
|
||||
Context::AddToOptimizedCodeMap(native_context, shared, code, osr_ast_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -12000,15 +11972,9 @@ void SharedFunctionInfo::AddToOptimizedCodeMap(
|
||||
Handle<FixedArray> old_code_map(shared->optimized_code_map(), isolate);
|
||||
entry = shared->SearchOptimizedCodeMapEntry(*native_context);
|
||||
if (entry >= kEntriesStart) {
|
||||
// Just set the code and literals of the entry.
|
||||
if (!code.is_null()) {
|
||||
Handle<WeakCell> code_cell =
|
||||
isolate->factory()->NewWeakCell(code.ToHandleChecked());
|
||||
old_code_map->set(entry + kCachedCodeOffset, *code_cell);
|
||||
}
|
||||
Handle<WeakCell> literals_cell =
|
||||
isolate->factory()->NewWeakCell(literals);
|
||||
old_code_map->set(entry + kLiteralsOffset, *literals_cell);
|
||||
// Just set the code of the entry.
|
||||
Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code);
|
||||
old_code_map->set(entry + kCachedCodeOffset, *code_cell);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -12036,15 +12002,11 @@ void SharedFunctionInfo::AddToOptimizedCodeMap(
|
||||
}
|
||||
}
|
||||
|
||||
Handle<WeakCell> code_cell =
|
||||
code.is_null() ? isolate->factory()->empty_weak_cell()
|
||||
: isolate->factory()->NewWeakCell(code.ToHandleChecked());
|
||||
Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals);
|
||||
Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code);
|
||||
WeakCell* context_cell = native_context->self_weak_cell();
|
||||
|
||||
new_code_map->set(entry + kContextOffset, context_cell);
|
||||
new_code_map->set(entry + kCachedCodeOffset, *code_cell);
|
||||
new_code_map->set(entry + kLiteralsOffset, *literals_cell);
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) {
|
||||
@ -12054,8 +12016,6 @@ void SharedFunctionInfo::AddToOptimizedCodeMap(
|
||||
DCHECK(cell->cleared() ||
|
||||
(cell->value()->IsCode() &&
|
||||
Code::cast(cell->value())->kind() == Code::OPTIMIZED_FUNCTION));
|
||||
cell = WeakCell::cast(new_code_map->get(i + kLiteralsOffset));
|
||||
DCHECK(cell->cleared() || cell->value()->IsFixedArray());
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -12093,7 +12053,7 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code,
|
||||
ShortPrint();
|
||||
PrintF("]\n");
|
||||
}
|
||||
// Just clear the code in order to continue sharing literals.
|
||||
// Just clear the code.
|
||||
code_map->set(src + kCachedCodeOffset, heap->empty_weak_cell(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
}
|
||||
@ -12110,12 +12070,45 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code,
|
||||
void JSFunction::EnsureLiterals(Handle<JSFunction> function) {
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
Handle<Context> native_context(function->context()->native_context());
|
||||
if (function->literals() ==
|
||||
function->GetIsolate()->heap()->empty_literals_array()) {
|
||||
Handle<LiteralsArray> literals =
|
||||
SharedFunctionInfo::FindOrCreateLiterals(shared, native_context);
|
||||
function->set_literals(*literals);
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
|
||||
if (!function->has_literals_array()) {
|
||||
if (FLAG_trace_strong_rooted_literals) {
|
||||
PrintF("EnsureLiterals: Installing literals array in %s %p\n",
|
||||
shared->DebugName()->ToCString().get(),
|
||||
reinterpret_cast<void*>(*function));
|
||||
}
|
||||
// Top level code didn't get it's literals installed.
|
||||
Handle<TypeFeedbackVector> feedback_vector =
|
||||
TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
|
||||
Handle<LiteralsArray> new_literals =
|
||||
LiteralsArray::New(isolate, feedback_vector, shared->num_literals());
|
||||
function->set_literals(*new_literals);
|
||||
} else if (!function->literals()->has_feedback_vector()) {
|
||||
if (FLAG_trace_strong_rooted_literals) {
|
||||
PrintF("EnsureLiterals: Installing feedback vector in %s %p\n",
|
||||
shared->DebugName()->ToCString().get(),
|
||||
reinterpret_cast<void*>(*function));
|
||||
}
|
||||
// If the feedback vector hasn't been installed, do that.
|
||||
Handle<TypeFeedbackVector> feedback_vector = TypeFeedbackVector::New(
|
||||
shared->GetIsolate(), handle(shared->feedback_metadata()));
|
||||
function->literals()->set_feedback_vector(*feedback_vector);
|
||||
} else {
|
||||
if (FLAG_trace_strong_rooted_literals) {
|
||||
PrintF("EnsureLiterals: did nothing for %s %p\n",
|
||||
shared->DebugName()->ToCString().get(),
|
||||
reinterpret_cast<void*>(*function));
|
||||
}
|
||||
}
|
||||
|
||||
// No matter what, ensure some post-conditions.
|
||||
DCHECK(shared->feedback_metadata()->slot_count() != 0 ||
|
||||
function->feedback_vector() ==
|
||||
shared->GetIsolate()->heap()->empty_type_feedback_vector());
|
||||
DCHECK(shared->num_literals() == 0 ||
|
||||
function->literals() !=
|
||||
shared->GetIsolate()->heap()->empty_literals_array());
|
||||
}
|
||||
|
||||
static void GetMinInobjectSlack(Map* map, void* data) {
|
||||
@ -13685,15 +13678,11 @@ void SharedFunctionInfo::ClearCodeFromOptimizedCodeMap() {
|
||||
}
|
||||
}
|
||||
|
||||
CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap(
|
||||
Context* native_context, BailoutId osr_ast_id) {
|
||||
CodeAndLiterals result = {nullptr, nullptr};
|
||||
Code* SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context,
|
||||
BailoutId osr_ast_id) {
|
||||
Code* result = nullptr;
|
||||
if (!osr_ast_id.IsNone()) {
|
||||
Code* code;
|
||||
LiteralsArray* literals;
|
||||
native_context->SearchOptimizedCodeMap(this, osr_ast_id, &code, &literals);
|
||||
result = {code, literals};
|
||||
return result;
|
||||
return native_context->SearchOptimizedCodeMap(this, osr_ast_id);
|
||||
}
|
||||
|
||||
DCHECK(osr_ast_id.IsNone());
|
||||
@ -13702,12 +13691,8 @@ CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap(
|
||||
FixedArray* code_map = optimized_code_map();
|
||||
DCHECK_LE(entry + kEntryLength, code_map->length());
|
||||
WeakCell* cell = WeakCell::cast(code_map->get(entry + kCachedCodeOffset));
|
||||
WeakCell* literals_cell =
|
||||
WeakCell::cast(code_map->get(entry + kLiteralsOffset));
|
||||
|
||||
result = {cell->cleared() ? nullptr : Code::cast(cell->value()),
|
||||
literals_cell->cleared() ? nullptr : LiteralsArray::cast(
|
||||
literals_cell->value())};
|
||||
result = cell->cleared() ? nullptr : Code::cast(cell->value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -5016,8 +5016,10 @@ class LiteralsArray : public FixedArray {
|
||||
return OffsetOfElementAt(index + kFirstLiteralIndex);
|
||||
}
|
||||
|
||||
inline bool has_feedback_vector() const;
|
||||
inline TypeFeedbackVector* feedback_vector() const;
|
||||
inline void set_feedback_vector(TypeFeedbackVector* vector);
|
||||
|
||||
inline Object* literal(int literal_index) const;
|
||||
inline void set_literal(int literal_index, Object* literal);
|
||||
inline void set_literal_undefined(int literal_index);
|
||||
@ -7157,14 +7159,6 @@ enum BuiltinFunctionId {
|
||||
};
|
||||
|
||||
|
||||
// Result of searching in an optimized code map of a SharedFunctionInfo. Note
|
||||
// that both {code} and {literals} can be NULL to pass search result status.
|
||||
struct CodeAndLiterals {
|
||||
Code* code; // Cached optimized code.
|
||||
LiteralsArray* literals; // Cached literals array.
|
||||
};
|
||||
|
||||
|
||||
// SharedFunctionInfo describes the JSFunction information that can be
|
||||
// shared by multiple instances of the function.
|
||||
class SharedFunctionInfo: public HeapObject {
|
||||
@ -7195,11 +7189,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
DECL_ACCESSORS(optimized_code_map, FixedArray)
|
||||
|
||||
// Returns entry from optimized code map for specified context and OSR entry.
|
||||
// Note that {code == nullptr, literals == nullptr} indicates no matching
|
||||
// entry has been found, whereas {code, literals == nullptr} indicates that
|
||||
// code is context-independent.
|
||||
CodeAndLiterals SearchOptimizedCodeMap(Context* native_context,
|
||||
BailoutId osr_ast_id);
|
||||
Code* SearchOptimizedCodeMap(Context* native_context, BailoutId osr_ast_id);
|
||||
|
||||
// Clear optimized code map.
|
||||
void ClearOptimizedCodeMap();
|
||||
@ -7221,12 +7211,9 @@ class SharedFunctionInfo: public HeapObject {
|
||||
Handle<SharedFunctionInfo> shared, Handle<Context> native_context);
|
||||
|
||||
// Add or update entry in the optimized code map for context-dependent code.
|
||||
// If {code} is not given, then an existing entry's code won't be overwritten.
|
||||
static void AddToOptimizedCodeMap(Handle<SharedFunctionInfo> shared,
|
||||
Handle<Context> native_context,
|
||||
MaybeHandle<Code> code,
|
||||
Handle<LiteralsArray> literals,
|
||||
BailoutId osr_ast_id);
|
||||
Handle<Code> code, BailoutId osr_ast_id);
|
||||
|
||||
// Set up the link between shared function info and the script. The shared
|
||||
// function info is added to the list on the script.
|
||||
@ -7237,8 +7224,7 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kEntriesStart = 0;
|
||||
static const int kContextOffset = 0;
|
||||
static const int kCachedCodeOffset = 1;
|
||||
static const int kLiteralsOffset = 2;
|
||||
static const int kEntryLength = 3;
|
||||
static const int kEntryLength = 2;
|
||||
static const int kInitialLength = kEntriesStart + kEntryLength;
|
||||
|
||||
static const int kNotFound = -1;
|
||||
@ -7250,8 +7236,6 @@ class SharedFunctionInfo: public HeapObject {
|
||||
static const int kOffsetToPreviousCachedCode =
|
||||
FixedArray::kHeaderSize +
|
||||
kPointerSize * (kCachedCodeOffset - kEntryLength);
|
||||
static const int kOffsetToPreviousLiterals =
|
||||
FixedArray::kHeaderSize + kPointerSize * (kLiteralsOffset - kEntryLength);
|
||||
|
||||
// [scope_info]: Scope info.
|
||||
DECL_ACCESSORS(scope_info, ScopeInfo)
|
||||
@ -8220,6 +8204,7 @@ class JSFunction: public JSObject {
|
||||
// access to. For API objects we store the boilerplate in the literal array.
|
||||
DECL_ACCESSORS(literals, LiteralsArray)
|
||||
|
||||
inline bool has_literals_array() const;
|
||||
static void EnsureLiterals(Handle<JSFunction> function);
|
||||
inline TypeFeedbackVector* feedback_vector();
|
||||
|
||||
|
@ -23,10 +23,15 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(4, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(TypeFeedbackVector, vector, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(index, 2);
|
||||
CONVERT_SMI_ARG_CHECKED(pretenured_flag, 3);
|
||||
Handle<Context> context(isolate->context(), isolate);
|
||||
FeedbackVectorSlot slot = TypeFeedbackVector::ToSlot(index);
|
||||
Handle<LiteralsArray> literals(LiteralsArray::cast(vector->Get(slot)),
|
||||
isolate);
|
||||
return *isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
shared, context, static_cast<PretenureFlag>(pretenured_flag));
|
||||
shared, context, literals, static_cast<PretenureFlag>(pretenured_flag));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -613,10 +613,15 @@ RUNTIME_FUNCTION(Runtime_NewClosure) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(TypeFeedbackVector, vector, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(index, 2);
|
||||
Handle<Context> context(isolate->context(), isolate);
|
||||
FeedbackVectorSlot slot = TypeFeedbackVector::ToSlot(index);
|
||||
Handle<LiteralsArray> literals(LiteralsArray::cast(vector->Get(slot)),
|
||||
isolate);
|
||||
Handle<JSFunction> function =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
||||
NOT_TENURED);
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
shared, context, literals, NOT_TENURED);
|
||||
return *function;
|
||||
}
|
||||
|
||||
@ -625,12 +630,17 @@ RUNTIME_FUNCTION(Runtime_NewClosure_Tenured) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(TypeFeedbackVector, vector, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(index, 2);
|
||||
Handle<Context> context(isolate->context(), isolate);
|
||||
FeedbackVectorSlot slot = TypeFeedbackVector::ToSlot(index);
|
||||
Handle<LiteralsArray> literals(LiteralsArray::cast(vector->Get(slot)),
|
||||
isolate);
|
||||
// The caller ensures that we pretenure closures that are assigned
|
||||
// directly to properties.
|
||||
Handle<JSFunction> function =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
||||
TENURED);
|
||||
literals, TENURED);
|
||||
return *function;
|
||||
}
|
||||
|
||||
|
@ -252,10 +252,8 @@ Handle<TypeFeedbackVector> TypeFeedbackVector::New(
|
||||
// the empty literals array here.
|
||||
array->set(index, *factory->empty_literals_array(), SKIP_WRITE_BARRIER);
|
||||
} else {
|
||||
// TODO(mvstanton): Create the array.
|
||||
// Handle<FixedArray> value = factory->NewFixedArray(length);
|
||||
// array->set(index, *value);
|
||||
array->set(index, *factory->empty_literals_array(), SKIP_WRITE_BARRIER);
|
||||
Handle<FixedArray> value = factory->NewFixedArray(length);
|
||||
array->set(index, *value);
|
||||
}
|
||||
}
|
||||
i += entry_size;
|
||||
@ -376,10 +374,10 @@ void TypeFeedbackVector::ClearSlotsImpl(SharedFunctionInfo* shared,
|
||||
break;
|
||||
}
|
||||
case FeedbackVectorSlotKind::CREATE_CLOSURE: {
|
||||
// Fill the array with undefined.
|
||||
FixedArray* array = FixedArray::cast(Get(slot));
|
||||
for (int i = 1; i < array->length(); i++) {
|
||||
array->set_undefined(i);
|
||||
// Clear the literals in the embedded LiteralsArray.
|
||||
LiteralsArray* literals = LiteralsArray::cast(Get(slot));
|
||||
for (int i = 0; i < literals->literals_count(); i++) {
|
||||
literals->set_literal_undefined(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -4296,10 +4296,6 @@ TEST(Regress513507) {
|
||||
if (!code->is_optimized_code()) return;
|
||||
}
|
||||
|
||||
Handle<TypeFeedbackVector> vector =
|
||||
TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
|
||||
Handle<LiteralsArray> lit =
|
||||
LiteralsArray::New(isolate, vector, shared->num_literals());
|
||||
Handle<Context> context(isolate->context());
|
||||
|
||||
// Add the new code several times to the optimized code map and also set an
|
||||
@ -4308,212 +4304,11 @@ TEST(Regress513507) {
|
||||
FLAG_gc_interval = 1000;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
BailoutId id = BailoutId(i);
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, id);
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
TEST(Regress514122) {
|
||||
if (!i::FLAG_incremental_marking) return;
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
LocalContext env;
|
||||
Heap* heap = isolate->heap();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
// Perfrom one initial GC to enable code flushing.
|
||||
CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask);
|
||||
|
||||
// Prepare function whose optimized code map we can use.
|
||||
Handle<SharedFunctionInfo> shared;
|
||||
{
|
||||
HandleScope inner_scope(isolate);
|
||||
CompileRun("function f() { return 1 }"
|
||||
"f(); %OptimizeFunctionOnNextCall(f); f();");
|
||||
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(
|
||||
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
|
||||
CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
|
||||
shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
|
||||
CompileRun("f = null");
|
||||
}
|
||||
|
||||
// Prepare optimized code that we can use.
|
||||
Handle<Code> code;
|
||||
{
|
||||
HandleScope inner_scope(isolate);
|
||||
CompileRun("function g() { return 2 }"
|
||||
"g(); %OptimizeFunctionOnNextCall(g); g();");
|
||||
|
||||
Handle<JSFunction> g = Handle<JSFunction>::cast(
|
||||
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
|
||||
CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked())));
|
||||
code = inner_scope.CloseAndEscape(handle(g->code(), isolate));
|
||||
if (!code->is_optimized_code()) return;
|
||||
}
|
||||
|
||||
Handle<TypeFeedbackVector> vector =
|
||||
TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
|
||||
Handle<LiteralsArray> lit =
|
||||
LiteralsArray::New(isolate, vector, shared->num_literals(), TENURED);
|
||||
Handle<Context> context(isolate->context());
|
||||
|
||||
// Add the code several times to the optimized code map.
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
HandleScope inner_scope(isolate);
|
||||
BailoutId id = BailoutId(i);
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
|
||||
}
|
||||
shared->optimized_code_map()->Print();
|
||||
|
||||
// Add the code with a literals array to be evacuated.
|
||||
Page* evac_page;
|
||||
{
|
||||
HandleScope inner_scope(isolate);
|
||||
AlwaysAllocateScope always_allocate(isolate);
|
||||
// Make sure literal is placed on an old-space evacuation candidate.
|
||||
heap::SimulateFullSpace(heap->old_space());
|
||||
|
||||
// Make sure there the number of literals is > 0.
|
||||
Handle<LiteralsArray> lit = LiteralsArray::New(isolate, vector, 23);
|
||||
|
||||
evac_page = Page::FromAddress(lit->address());
|
||||
BailoutId id = BailoutId(100);
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
|
||||
}
|
||||
|
||||
// Heap is ready, force {lit_page} to become an evacuation candidate and
|
||||
// simulate incremental marking to enqueue optimized code map.
|
||||
FLAG_manual_evacuation_candidates_selection = true;
|
||||
heap::ForceEvacuationCandidate(evac_page);
|
||||
heap::SimulateIncrementalMarking(heap);
|
||||
|
||||
// No matter whether reachable or not, {boomer} is doomed.
|
||||
Handle<Object> boomer(shared->optimized_code_map(), isolate);
|
||||
|
||||
// Add the code several times to the optimized code map. This will leave old
|
||||
// copies of the optimized code map unreachable but still marked.
|
||||
for (int i = 3; i < 6; ++i) {
|
||||
HandleScope inner_scope(isolate);
|
||||
BailoutId id = BailoutId(i);
|
||||
SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
|
||||
}
|
||||
|
||||
// Trigger a GC to flush out the bug.
|
||||
CcTest::CollectGarbage(i::OLD_SPACE);
|
||||
boomer->Print();
|
||||
}
|
||||
|
||||
|
||||
TEST(OptimizedCodeMapReuseEntries) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
// BUG(v8:4598): Since TurboFan doesn't treat maps in code weakly, we can't
|
||||
// run this test.
|
||||
if (i::FLAG_turbo) return;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* v8_isolate = CcTest::isolate();
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
// Create 3 contexts, allow the 2nd one to be disposed, and verify that
|
||||
// a 4th context will re-use the weak slots in the optimized code map
|
||||
// to hold data, rather than expanding the map.
|
||||
v8::Local<v8::Context> c1 = v8::Context::New(v8_isolate);
|
||||
const char* source = "function foo(x) { var l = [1]; return x+l[0]; }";
|
||||
v8::ScriptCompiler::Source script_source(
|
||||
v8::String::NewFromUtf8(v8_isolate, source, v8::NewStringType::kNormal)
|
||||
.ToLocalChecked());
|
||||
v8::Local<v8::UnboundScript> indep =
|
||||
v8::ScriptCompiler::CompileUnboundScript(v8_isolate, &script_source)
|
||||
.ToLocalChecked();
|
||||
const char* toplevel = "foo(3); %OptimizeFunctionOnNextCall(foo); foo(3);";
|
||||
// Perfrom one initial GC to enable code flushing.
|
||||
CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask);
|
||||
|
||||
c1->Enter();
|
||||
indep->BindToCurrentContext()->Run(c1).ToLocalChecked();
|
||||
CompileRun(toplevel);
|
||||
|
||||
Handle<SharedFunctionInfo> shared;
|
||||
Handle<JSFunction> foo = Handle<JSFunction>::cast(
|
||||
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
|
||||
CcTest::global()->Get(c1, v8_str("foo")).ToLocalChecked())));
|
||||
CHECK(foo->shared()->is_compiled());
|
||||
shared = handle(foo->shared());
|
||||
c1->Exit();
|
||||
|
||||
{
|
||||
HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> c2 = v8::Context::New(v8_isolate);
|
||||
c2->Enter();
|
||||
indep->BindToCurrentContext()->Run(c2).ToLocalChecked();
|
||||
CompileRun(toplevel);
|
||||
c2->Exit();
|
||||
}
|
||||
|
||||
{
|
||||
HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> c3 = v8::Context::New(v8_isolate);
|
||||
c3->Enter();
|
||||
indep->BindToCurrentContext()->Run(c3).ToLocalChecked();
|
||||
CompileRun(toplevel);
|
||||
c3->Exit();
|
||||
|
||||
// Now, collect garbage. Context c2 should have no roots to it, and it's
|
||||
// entry in the optimized code map should be free for a new context.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask);
|
||||
}
|
||||
|
||||
Handle<FixedArray> optimized_code_map =
|
||||
handle(shared->optimized_code_map());
|
||||
// There should be 3 entries in the map.
|
||||
CHECK_EQ(
|
||||
3, ((optimized_code_map->length() - SharedFunctionInfo::kEntriesStart) /
|
||||
SharedFunctionInfo::kEntryLength));
|
||||
// But one of them (formerly for c2) should be cleared.
|
||||
int cleared_count = 0;
|
||||
for (int i = SharedFunctionInfo::kEntriesStart;
|
||||
i < optimized_code_map->length();
|
||||
i += SharedFunctionInfo::kEntryLength) {
|
||||
cleared_count +=
|
||||
WeakCell::cast(
|
||||
optimized_code_map->get(i + SharedFunctionInfo::kContextOffset))
|
||||
->cleared()
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
CHECK_EQ(1, cleared_count);
|
||||
|
||||
// Verify that a new context uses the cleared entry rather than creating a
|
||||
// new
|
||||
// optimized code map array.
|
||||
v8::Local<v8::Context> c4 = v8::Context::New(v8_isolate);
|
||||
c4->Enter();
|
||||
indep->BindToCurrentContext()->Run(c4).ToLocalChecked();
|
||||
CompileRun(toplevel);
|
||||
c4->Exit();
|
||||
CHECK_EQ(*optimized_code_map, shared->optimized_code_map());
|
||||
|
||||
// Now each entry is in use.
|
||||
cleared_count = 0;
|
||||
for (int i = SharedFunctionInfo::kEntriesStart;
|
||||
i < optimized_code_map->length();
|
||||
i += SharedFunctionInfo::kEntryLength) {
|
||||
cleared_count +=
|
||||
WeakCell::cast(
|
||||
optimized_code_map->get(i + SharedFunctionInfo::kContextOffset))
|
||||
->cleared()
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
CHECK_EQ(0, cleared_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Regress513496) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
CcTest::InitializeVM();
|
||||
@ -4559,9 +4354,9 @@ TEST(Regress513496) {
|
||||
}
|
||||
|
||||
// Lookup the optimized code and keep it alive.
|
||||
CodeAndLiterals result = shared->SearchOptimizedCodeMap(
|
||||
Code* result = shared->SearchOptimizedCodeMap(
|
||||
isolate->context()->native_context(), BailoutId::None());
|
||||
Handle<Code> optimized_code(result.code, isolate);
|
||||
Handle<Code> optimized_code(result, isolate);
|
||||
|
||||
// Finish a full GC cycle so that the unoptimized code of 'g' is flushed even
|
||||
// though the optimized code for 'f' is reachable via the optimized code map.
|
||||
|
@ -107,7 +107,9 @@ TEST(VectorStructure) {
|
||||
FeedbackVectorSlotKind::CREATE_CLOSURE));
|
||||
FeedbackVectorSlot slot = helper.slot(1);
|
||||
FixedArray* array = FixedArray::cast(vector->Get(slot));
|
||||
CHECK_EQ(array, *factory->empty_literals_array());
|
||||
CHECK_EQ(5, array->length());
|
||||
CHECK_EQ(5, vector->GetParameter(slot));
|
||||
CHECK_EQ(array->get(0), *factory->undefined_value());
|
||||
}
|
||||
}
|
||||
|
||||
|
56
test/mjsunit/strong-rooted-literals.js
Normal file
56
test/mjsunit/strong-rooted-literals.js
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-gc
|
||||
|
||||
// Make sure literals are strongly rooted and safe from weak-code deopts.
|
||||
|
||||
(function() {
|
||||
function foo() {
|
||||
var a = { y: 0 };
|
||||
a.y = 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
foo();
|
||||
foo();
|
||||
%OptimizeFunctionOnNextCall(foo);
|
||||
foo();
|
||||
gc();
|
||||
assertOptimized(foo);
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
function hot(o) {
|
||||
return o.x + o.y;
|
||||
}
|
||||
function mapPlus(a, y) {
|
||||
return a.map(x => hot({x, y}));
|
||||
}
|
||||
|
||||
var a = [1, 2, 3];
|
||||
print(mapPlus(a, 1));
|
||||
print(mapPlus(a, 2));
|
||||
%OptimizeFunctionOnNextCall(hot);
|
||||
print(mapPlus(a, 3));
|
||||
gc(); // BOOOM!
|
||||
assertOptimized(hot);
|
||||
print(mapPlus(a, 4));
|
||||
})();
|
||||
|
||||
// Verify that we can handle the creation of a new script, where the
|
||||
// code is cached and the feedback vector has to be re-created.
|
||||
(function() {
|
||||
var sopen = "function wrapper() { ";
|
||||
var s1 = "function foo() { return bar(5); } ";
|
||||
var s2 = "foo(); foo(); %OptimizeFunctionOnNextCall(foo); foo(); ";
|
||||
var sclose = "} wrapper(); ";
|
||||
var s = sopen + s1 + s2 + sclose;
|
||||
function bar(i) { return i + 3 };
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
eval(s);
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue
Block a user