// Copyright 2018 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. #include "src/builtins/builtins-lazy-gen.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/feedback-vector.h" #include "src/globals.h" #include "src/objects/shared-function-info.h" namespace v8 { namespace internal { void LazyBuiltinsAssembler::GenerateTailCallToJSCode( TNode code, TNode function) { TNode argc = UncheckedCast(Parameter(Descriptor::kActualArgumentsCount)); TNode context = CAST(Parameter(Descriptor::kContext)); TNode new_target = CAST(Parameter(Descriptor::kNewTarget)); TailCallJSCode(code, context, function, new_target, argc); } void LazyBuiltinsAssembler::GenerateTailCallToReturnedCode( Runtime::FunctionId function_id, TNode function) { TNode context = CAST(Parameter(Descriptor::kContext)); TNode code = CAST(CallRuntime(function_id, context, function)); GenerateTailCallToJSCode(code, function); } void LazyBuiltinsAssembler::TailCallRuntimeIfMarkerEquals( TNode marker, OptimizationMarker expected_marker, Runtime::FunctionId function_id, TNode function) { Label no_match(this); GotoIfNot(SmiEqual(marker, SmiConstant(expected_marker)), &no_match); GenerateTailCallToReturnedCode(function_id, function); BIND(&no_match); } void LazyBuiltinsAssembler::MaybeTailCallOptimizedCodeSlot( TNode function, TNode feedback_vector) { Label fallthrough(this); TNode maybe_optimized_code_entry = LoadMaybeWeakObjectField( feedback_vector, FeedbackVector::kOptimizedCodeOffset); // Check if the code entry is a Smi. If yes, we interpret it as an // optimisation marker. Otherwise, interpret it as a weak reference to a code // object. Label optimized_code_slot_is_smi(this), optimized_code_slot_is_weak_ref(this); Branch(TaggedIsSmi(maybe_optimized_code_entry), &optimized_code_slot_is_smi, &optimized_code_slot_is_weak_ref); BIND(&optimized_code_slot_is_smi); { // Optimized code slot is a Smi optimization marker. TNode marker = CAST(maybe_optimized_code_entry); // Fall through if no optimization trigger. GotoIf(SmiEqual(marker, SmiConstant(OptimizationMarker::kNone)), &fallthrough); // TODO(ishell): introduce Runtime::kHandleOptimizationMarker and check // all these marker values there. TailCallRuntimeIfMarkerEquals(marker, OptimizationMarker::kLogFirstExecution, Runtime::kFunctionFirstExecution, function); TailCallRuntimeIfMarkerEquals(marker, OptimizationMarker::kCompileOptimized, Runtime::kCompileOptimized_NotConcurrent, function); TailCallRuntimeIfMarkerEquals( marker, OptimizationMarker::kCompileOptimizedConcurrent, Runtime::kCompileOptimized_Concurrent, function); // Otherwise, the marker is InOptimizationQueue, so fall through hoping // that an interrupt will eventually update the slot with optimized code. CSA_ASSERT(this, SmiEqual(marker, SmiConstant(OptimizationMarker::kInOptimizationQueue))); Goto(&fallthrough); } BIND(&optimized_code_slot_is_weak_ref); { // Optimized code slot is a weak reference. TNode optimized_code = CAST(ToWeakHeapObject(maybe_optimized_code_entry, &fallthrough)); // Check if the optimized code is marked for deopt. If it is, call the // runtime to clear it. Label found_deoptimized_code(this); TNode code_data_container = CAST(LoadObjectField(optimized_code, Code::kCodeDataContainerOffset)); TNode code_kind_specific_flags = LoadObjectField( code_data_container, CodeDataContainer::kKindSpecificFlagsOffset); GotoIf(IsSetWord32( code_kind_specific_flags), &found_deoptimized_code); // Optimized code is good, get it into the closure and link the closure into // the optimized functions list, then tail call the optimized code. StoreObjectField(function, JSFunction::kCodeOffset, optimized_code); GenerateTailCallToJSCode(optimized_code, function); // Optimized code slot contains deoptimized code, evict it and re-enter the // closure's code. BIND(&found_deoptimized_code); GenerateTailCallToReturnedCode(Runtime::kEvictOptimizedCodeSlot, function); } // Fall-through if the optimized code cell is clear and there is no // optimization marker. BIND(&fallthrough); } void LazyBuiltinsAssembler::CompileLazy(TNode function) { // First lookup code, maybe we don't need to compile! Label compile_function(this, Label::kDeferred); // Compile function if we don't have a valid feedback vector. TNode feedback_vector = LoadFeedbackVector(function, &compile_function); // Is there an optimization marker or optimized code in the feedback vector? MaybeTailCallOptimizedCodeSlot(function, feedback_vector); // We found no optimized code. Infer the code object needed for the SFI. TNode shared = CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset)); // If code entry points to anything other than CompileLazy, install that, // otherwise call runtime to compile the function. TNode code = GetSharedFunctionInfoCode(shared, &compile_function); CSA_ASSERT( this, WordNotEqual(code, HeapConstant(BUILTIN_CODE(isolate(), CompileLazy)))); // Install the SFI's code entry. StoreObjectField(function, JSFunction::kCodeOffset, code); GenerateTailCallToJSCode(code, function); BIND(&compile_function); { GenerateTailCallToReturnedCode(Runtime::kCompileLazy, function); } } TF_BUILTIN(CompileLazy, LazyBuiltinsAssembler) { TNode function = CAST(Parameter(Descriptor::kTarget)); CompileLazy(function); } TF_BUILTIN(CompileLazyDeoptimizedCode, LazyBuiltinsAssembler) { TNode function = CAST(Parameter(Descriptor::kTarget)); // Set the code slot inside the JSFunction to CompileLazy. TNode code = HeapConstant(BUILTIN_CODE(isolate(), CompileLazy)); StoreObjectField(function, JSFunction::kCodeOffset, code); GenerateTailCallToJSCode(code, function); } // Lazy deserialization design doc: http://goo.gl/dxkYDZ. TF_BUILTIN(DeserializeLazy, LazyBuiltinsAssembler) { Label deserialize_in_runtime(this, Label::kDeferred); TNode function = CAST(Parameter(Descriptor::kTarget)); // Load the builtin id for lazy deserialization from SharedFunctionInfo. TNode shared = CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset)); TNode sfi_data = CAST(LoadObjectField(shared, SharedFunctionInfo::kFunctionDataOffset)); // The builtin may already have been deserialized. If that is the case, it is // stored in the builtins table, and we can copy to correct code object to // both the shared function info and function without calling into runtime. // // Otherwise, we need to call into runtime to deserialize. TNode code = LoadBuiltin(sfi_data); // Check if the loaded code object has already been deserialized. This is // the case iff it does not equal DeserializeLazy. GotoIf( WordEqual(code, HeapConstant(BUILTIN_CODE(isolate(), DeserializeLazy))), &deserialize_in_runtime); // If we've reached this spot, the target builtin has been deserialized and // we simply need to copy it over to the target function. StoreObjectField(function, JSFunction::kCodeOffset, code); // All copying is done. Jump to the deserialized code object. GenerateTailCallToJSCode(code, function); BIND(&deserialize_in_runtime); { GenerateTailCallToReturnedCode(Runtime::kDeserializeLazy, function); } } } // namespace internal } // namespace v8