|  | // 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 <functional> | 
|  |  | 
|  | #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 ZoneAllocated { | 
|  | 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<UnresolvedPcRelativeCall*>; | 
|  |  | 
|  | 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::*<stub-name>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<void(Assembler*, int)>; | 
|  | #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, | 
|  | // InitSharedLateFinalStaticField, | 
|  | 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[TA][Generic] stubs. | 
|  | void GenerateAllocateClosureStub(bool has_instantiator_type_args, | 
|  | bool is_generic); | 
|  |  | 
|  | // Common function for generating Allocate<TypedData>Array 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<void()> 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 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 GenerateLoadBSSEntry(BSS::Relocation relocation, | 
|  | Register dst, | 
|  | Register tmp); | 
|  | void InsertBSSRelocation(BSS::Relocation reloc); | 
|  |  | 
|  | 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_ |