blob: d99e412a60b92f0cbea6049db0a5ed90ab1683ca [file] [log] [blame] [edit]
// 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_