// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_ #define RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_ #if defined(DART_PRECOMPILED_RUNTIME) #error "AOT runtime should not use compiler sources (including header files)" #endif // defined(DART_PRECOMPILED_RUNTIME) #include #include "vm/allocation.h" #include "vm/compiler/runtime_api.h" #include "vm/constants.h" #include "vm/growable_array.h" #include "vm/stub_code_list.h" #include "vm/tagged_pointer.h" namespace dart { // Forward declarations. class Code; class DescriptorList; class Label; namespace compiler { // Forward declarations. class Assembler; // Represents an unresolved PC-relative Call/TailCall. class UnresolvedPcRelativeCall : public ZoneObject { public: UnresolvedPcRelativeCall(intptr_t offset, const dart::Code& target, bool is_tail_call) : offset_(offset), target_(target), is_tail_call_(is_tail_call) {} intptr_t offset() const { return offset_; } const dart::Code& target() const { return target_; } bool is_tail_call() const { return is_tail_call_; } private: const intptr_t offset_; const dart::Code& target_; const bool is_tail_call_; }; using UnresolvedPcRelativeCalls = GrowableArray; class StubCodeCompiler { public: StubCodeCompiler(Assembler* assembler_, DescriptorList* pc_descriptors_list) : assembler(assembler_), pc_descriptors_list_(pc_descriptors_list) {} Assembler* assembler; void EnsureIsNewOrRemembered(); static ArrayPtr BuildStaticCallsTable( Zone* zone, compiler::UnresolvedPcRelativeCalls* unresolved_calls); #define STUB_CODE_GENERATE(name) void Generate##name##Stub(); VM_STUB_CODE_LIST(STUB_CODE_GENERATE) #undef STUB_CODE_GENERATE void GenerateAllocationStubForClass( UnresolvedPcRelativeCalls* unresolved_calls, const Class& cls, const dart::Code& allocate_object, const dart::Code& allocat_object_parametrized); enum Optimized { kUnoptimized, kOptimized, }; enum CallType { kInstanceCall, kStaticCall, }; enum Exactness { kCheckExactness, kIgnoreExactness, }; void GenerateNArgsCheckInlineCacheStub(intptr_t num_args, const RuntimeEntry& handle_ic_miss, Token::Kind kind, Optimized optimized, CallType type, Exactness exactness); void GenerateNArgsCheckInlineCacheStubForEntryKind( intptr_t num_args, const RuntimeEntry& handle_ic_miss, Token::Kind kind, Optimized optimized, CallType type, Exactness exactness, CodeEntryKind entry_kind); void GenerateUsageCounterIncrement(Register temp_reg); void GenerateOptimizedUsageCounterIncrement(); // Calculates the offset (in words) from FP to the provided [cpu_register]. // // Assumes // * all [kDartAvailableCpuRegs] followed by saved-PC, saved-FP were // pushed on the stack // * [cpu_register] is in [kDartAvailableCpuRegs] // // The intended use of this function is to find registers on the stack which // were spilled in the // `StubCode::*Shared{With,Without}FpuRegsStub()` static intptr_t WordOffsetFromFpToCpuRegister(Register cpu_register); #if !defined(TARGET_ARCH_IA32) // Used for passing functions that generate exit branches for a // SubtypeTestCache search stub. Must generate a return instruction. using STCSearchExitGenerator = std::function; #endif private: #if !defined(TARGET_ARCH_IA32) // Generates the code for searching a subtype test cache for an entry that // matches the contents of the TypeTestABI registers. If no matching // entry is found, then the code generated by [not_found] is executed. // Otherwise, the code generated by [found] is executed, which can assume // that [cache_entry_reg] points to the start of the matching cache entry. // Both generators should return from the stub and not fall through. // // Inputs in addition to those in TypeTestABI: // - null_reg: a register containing the address Object::null(). // // The following registers from TypeTestABI are inputs under the following // conditions: // - kScratchReg: always // - kInstanceReg: always // - kDstTypeReg: [n] >= 3 // - kInstantiatorTypeArgumentsReg: [n] >= 4 // - kFunctionTypeArgumentsReg: [n] >= 5 // // Has the following input registers in addition to those in TypeTestABI: // - null_reg: contains the ObjectPtr for Object::null() // - cache_entry_reg: contains the ArrayPtr for the backing array of the STC // // The following registers must be provided for the given conditions and // are clobbered, and can be kNoRegister otherwise: // - instance_cid_or_sig_reg: always // - instance_type_args_reg: [n] >= 2 // - parent_fun_type_args_reg: [n] >= 6 // - delayed_type_args_reg: [n] >= 7 // // The following registers must be distinct from other inputs when provided, // but can be kNoRegister: // - cache_entry_end_reg: used in the hash-based cache iteration loop // on each iteration // - cache_entry_start_reg, used in the hash-based cache iteration loop // to reset if iteration hits the end of the entries // - cache_entry_count_reg, used to calculate the starting index to probe // Note that if any of these are kNoRegister, then a stack slot is used to // store and retrieve the corresponding value. // // Note that all input registers must be distinct, except for the case // of kInstanceReg, which can be used for one of [delayed_type_args_reg], // [cache_entry_end_reg], [cache_entry_start_reg], or [cache_entry_count_reg], // which are all set after the last use of kInstanceReg. // // Also note that if any non-TypeTestABI registers overlap with any // non-scratch TypeTestABI registers, the original value of the TypeTestABI // register must be stored before the generated code and restored afterwards. static void GenerateSubtypeTestCacheSearch( Assembler* assembler, int n, Register null_reg, Register cache_entry_reg, Register instance_cid_or_sig_reg, Register instance_type_args_reg, Register parent_fun_type_args_reg, Register delayed_type_args_reg, Register cache_entry_end_reg, Register cache_entry_start_reg, Register cache_entry_count_reg, const STCSearchExitGenerator& gen_found, const STCSearchExitGenerator& gen_not_found); #endif // Common function for generating the different SubtypeTestCache search // stubs. Check architecture-specific version for inputs/outputs. static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n); // Common function for generating InitLateStaticField, // InitLateFinalStaticField, InitSharedLateStaticField, void GenerateInitLateStaticFieldStub(bool is_final, bool is_shared); // Common function for generating InitLateInstanceField and // InitLateFinalInstanceField stubs. void GenerateInitLateInstanceFieldStub(bool is_final); // Common function for generating AllocateClosure stubs. void GenerateAllocateClosureStub(intptr_t num_elements); // Common function for generating AllocateArray stubs. void GenerateAllocateTypedDataArrayStub(intptr_t cid); void GenerateAllocateSmallRecordStub(intptr_t num_fields, bool has_named_fields); void GenerateSharedStubGeneric(bool save_fpu_registers, intptr_t self_code_stub_offset_from_thread, bool allow_return, std::function perform_runtime_call); // Generates shared slow path stub which saves registers and calls // [target] runtime entry. // If [store_runtime_result_in_result_register], then stub puts result into // SharedSlowPathStubABI::kResultReg. void GenerateSharedStub(bool save_fpu_registers, const RuntimeEntry* target, intptr_t self_code_stub_offset_from_thread, bool allow_return, bool store_runtime_result_in_result_register = false); void GenerateLateInitializationError(bool with_fpu_regs); void GenerateRangeError(bool with_fpu_regs); void GenerateWriteError(bool with_fpu_regs); void GenerateFieldAccessError(bool with_fpu_regs); void GenerateSuspendStub(bool call_suspend_function, bool pass_type_arguments, intptr_t suspend_entry_point_offset_in_thread, intptr_t suspend_function_offset_in_object_store); void GenerateInitSuspendableFunctionStub( intptr_t init_entry_point_offset_in_thread, intptr_t init_function_offset_in_object_store); void GenerateReturnStub(intptr_t return_entry_point_offset_in_thread, intptr_t return_function_offset_in_object_store, intptr_t return_stub_offset_in_thread); void GenerateLoadFfiCallbackMetadataRuntimeFunction(uword function_index, Register dst); DescriptorList* pc_descriptors_list_ = nullptr; DISALLOW_COPY_AND_ASSIGN(StubCodeCompiler); }; } // namespace compiler enum DeoptStubKind { kLazyDeoptFromReturn, kLazyDeoptFromThrow, kEagerDeopt }; // Zap value used to indicate unused CODE_REG in deopt. static constexpr uword kZapCodeReg = 0xf1f1f1f1; // Zap value used to indicate unused return address in deopt. static constexpr uword kZapReturnAddress = 0xe1e1e1e1; } // namespace dart #endif // RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_