|  | // Copyright (c) 2014, 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. | 
|  | // Classes that describe assembly patterns as used by inline caches. | 
|  |  | 
|  | #ifndef RUNTIME_VM_INSTRUCTIONS_ARM64_H_ | 
|  | #define RUNTIME_VM_INSTRUCTIONS_ARM64_H_ | 
|  |  | 
|  | #ifndef RUNTIME_VM_INSTRUCTIONS_H_ | 
|  | #error Do not include instructions_arm64.h directly; use instructions.h instead. | 
|  | #endif | 
|  |  | 
|  | #include "vm/allocation.h" | 
|  | #include "vm/constants.h" | 
|  | #include "vm/native_function.h" | 
|  | #include "vm/tagged_pointer.h" | 
|  |  | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  | #include "vm/compiler/assembler/assembler.h" | 
|  | #endif  // !defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | class Code; | 
|  | class ICData; | 
|  | class Object; | 
|  | class ObjectPool; | 
|  |  | 
|  | class InstructionPattern : public AllStatic { | 
|  | public: | 
|  | // Decodes a load sequence ending at 'end' (the last instruction of the | 
|  | // load sequence is the instruction before the one at end).  Returns the | 
|  | // address of the first instruction in the sequence.  Returns the register | 
|  | // being loaded and the loaded immediate value in the output parameters | 
|  | // 'reg' and 'value' respectively. | 
|  | static uword DecodeLoadWordImmediate(uword end, | 
|  | Register* reg, | 
|  | intptr_t* value); | 
|  |  | 
|  | // Decodes a load sequence ending at 'end' (the last instruction of the | 
|  | // load sequence is the instruction before the one at end).  Returns the | 
|  | // address of the first instruction in the sequence.  Returns the register | 
|  | // being loaded and the index in the pool being read from in the output | 
|  | // parameters 'reg' and 'index' respectively. | 
|  | // IMPORTANT: When generating code loading values from pool on ARM64 use | 
|  | // LoadWordFromPool macro instruction instead of emitting direct load. | 
|  | // The macro instruction takes care of pool offsets that can't be | 
|  | // encoded as immediates. | 
|  | static uword DecodeLoadWordFromPool(uword end, | 
|  | Register* reg, | 
|  | intptr_t* index); | 
|  |  | 
|  | // Decodes a load sequence ending at 'end' (the last instruction of the | 
|  | // load sequence is the instruction before the one at end).  Returns the | 
|  | // address of the first instruction in the sequence.  Returns the registers | 
|  | // being loaded and the index in the pool being read from in the output | 
|  | // parameters 'reg1', 'reg2' and 'index' respectively. | 
|  | // IMPORTANT: When generating code loading values from pool on ARM64 use | 
|  | // LoadDoubleWordFromPool macro instruction instead of emitting direct load. | 
|  | // The macro instruction takes care of pool offsets that can't be | 
|  | // encoded as immediates. | 
|  | static uword DecodeLoadDoubleWordFromPool(uword end, | 
|  | Register* reg1, | 
|  | Register* reg2, | 
|  | intptr_t* index); | 
|  |  | 
|  | // Encodes a load sequence ending at 'end'. Encodes a fixed length two | 
|  | // instruction load from the pool pointer in PP using the destination | 
|  | // register reg as a temporary for the base address. | 
|  | static void EncodeLoadWordFromPoolFixed(uword end, int32_t offset); | 
|  | }; | 
|  |  | 
|  | class CallPattern : public ValueObject { | 
|  | public: | 
|  | CallPattern(uword pc, const Code& code); | 
|  |  | 
|  | CodePtr TargetCode() const; | 
|  | void SetTargetCode(const Code& target) const; | 
|  |  | 
|  | private: | 
|  | const ObjectPool& object_pool_; | 
|  |  | 
|  | intptr_t target_code_pool_index_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CallPattern); | 
|  | }; | 
|  |  | 
|  | class ICCallPattern : public ValueObject { | 
|  | public: | 
|  | ICCallPattern(uword pc, const Code& caller_code); | 
|  |  | 
|  | ObjectPtr Data() const; | 
|  | void SetData(const Object& data) const; | 
|  |  | 
|  | CodePtr TargetCode() const; | 
|  | void SetTargetCode(const Code& target) const; | 
|  |  | 
|  | private: | 
|  | const ObjectPool& object_pool_; | 
|  |  | 
|  | intptr_t target_pool_index_; | 
|  | intptr_t data_pool_index_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ICCallPattern); | 
|  | }; | 
|  |  | 
|  | class NativeCallPattern : public ValueObject { | 
|  | public: | 
|  | NativeCallPattern(uword pc, const Code& code); | 
|  |  | 
|  | CodePtr target() const; | 
|  | void set_target(const Code& target) const; | 
|  |  | 
|  | NativeFunction native_function() const; | 
|  | void set_native_function(NativeFunction target) const; | 
|  |  | 
|  | private: | 
|  | const ObjectPool& object_pool_; | 
|  |  | 
|  | uword end_; | 
|  | intptr_t native_function_pool_index_; | 
|  | intptr_t target_code_pool_index_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(NativeCallPattern); | 
|  | }; | 
|  |  | 
|  | // Instance call that can switch between a direct monomorphic call, an IC call, | 
|  | // and a megamorphic call. | 
|  | //   load guarded cid            load ICData             load MegamorphicCache | 
|  | //   load monomorphic target <-> load ICLookup stub  ->  load MMLookup stub | 
|  | //   call target.entry           call stub.entry         call stub.entry | 
|  | class SwitchableCallPatternBase : public ValueObject { | 
|  | public: | 
|  | explicit SwitchableCallPatternBase(const ObjectPool& object_pool); | 
|  |  | 
|  | ObjectPtr data() const; | 
|  | void SetData(const Object& data) const; | 
|  |  | 
|  | protected: | 
|  | const ObjectPool& object_pool_; | 
|  | intptr_t data_pool_index_; | 
|  | intptr_t target_pool_index_; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(SwitchableCallPatternBase); | 
|  | }; | 
|  |  | 
|  | // See [SwitchableCallBase] for a switchable calls in general. | 
|  | // | 
|  | // The target slot is always a [Code] object: Either the code of the | 
|  | // monomorphic function or a stub code. | 
|  | class SwitchableCallPattern : public SwitchableCallPatternBase { | 
|  | public: | 
|  | SwitchableCallPattern(uword pc, const Code& code); | 
|  |  | 
|  | uword target_entry() const; | 
|  | void SetTarget(const Code& target) const; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(SwitchableCallPattern); | 
|  | }; | 
|  |  | 
|  | // See [SwitchableCallBase] for a switchable calls in general. | 
|  | // | 
|  | // The target slot is always a direct entrypoint address: Either the entry point | 
|  | // of the monomorphic function or a stub entry point. | 
|  | class BareSwitchableCallPattern : public SwitchableCallPatternBase { | 
|  | public: | 
|  | explicit BareSwitchableCallPattern(uword pc); | 
|  |  | 
|  | uword target_entry() const; | 
|  | void SetTarget(const Code& target) const; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(BareSwitchableCallPattern); | 
|  | }; | 
|  |  | 
|  | class ReturnPattern : public ValueObject { | 
|  | public: | 
|  | explicit ReturnPattern(uword pc); | 
|  |  | 
|  | // bx_lr = 1. | 
|  | static constexpr int kLengthInBytes = 1 * Instr::kInstrSize; | 
|  |  | 
|  | int pattern_length_in_bytes() const { return kLengthInBytes; } | 
|  |  | 
|  | bool IsValid() const; | 
|  |  | 
|  | private: | 
|  | const uword pc_; | 
|  | }; | 
|  |  | 
|  | class PcRelativePatternBase : public ValueObject { | 
|  | public: | 
|  | // 26 bit signed integer which will get multiplied by 4. | 
|  | static constexpr intptr_t kLowerCallingRange = -(1 << 27); | 
|  | static constexpr intptr_t kUpperCallingRange = (1 << 27) - Instr::kInstrSize; | 
|  |  | 
|  | explicit PcRelativePatternBase(uword pc) : pc_(pc) {} | 
|  |  | 
|  | static constexpr int kLengthInBytes = 1 * Instr::kInstrSize; | 
|  |  | 
|  | int32_t distance() { | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  | return compiler::Assembler::DecodeImm26BranchOffset( | 
|  | *reinterpret_cast<int32_t*>(pc_)); | 
|  | #else | 
|  | UNREACHABLE(); | 
|  | return 0; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void set_distance(int32_t distance) { | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  | int32_t* word = reinterpret_cast<int32_t*>(pc_); | 
|  | *word = compiler::Assembler::EncodeImm26BranchOffset(distance, *word); | 
|  | #else | 
|  | UNREACHABLE(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool IsValid() const; | 
|  |  | 
|  | protected: | 
|  | uword pc_; | 
|  | }; | 
|  |  | 
|  | class PcRelativeCallPattern : public PcRelativePatternBase { | 
|  | public: | 
|  | explicit PcRelativeCallPattern(uword pc) : PcRelativePatternBase(pc) {} | 
|  |  | 
|  | bool IsValid() const; | 
|  | }; | 
|  |  | 
|  | class PcRelativeTailCallPattern : public PcRelativePatternBase { | 
|  | public: | 
|  | explicit PcRelativeTailCallPattern(uword pc) : PcRelativePatternBase(pc) {} | 
|  |  | 
|  | bool IsValid() const; | 
|  | }; | 
|  |  | 
|  | // Instruction pattern for a tail call to a signed 32-bit PC-relative offset | 
|  | // | 
|  | // The AOT compiler can emit PC-relative calls. If the destination of such a | 
|  | // call is not in range for the "bl <offset>" instruction, the AOT compiler will | 
|  | // emit a trampoline which is in range. That trampoline will then tail-call to | 
|  | // the final destination (also via PC-relative offset, but it supports a full | 
|  | // signed 32-bit offset). | 
|  | // | 
|  | // The pattern of the trampoline looks like: | 
|  | // | 
|  | //     adr TMP, #lower16  (same as TMP = PC + #lower16) | 
|  | //     movz TMP2, #higher16 lsl 16 | 
|  | //     add TMP, TMP, TMP2, SXTW | 
|  | //     br TMP | 
|  | // | 
|  | class PcRelativeTrampolineJumpPattern : public ValueObject { | 
|  | public: | 
|  | explicit PcRelativeTrampolineJumpPattern(uword pattern_start) | 
|  | : pattern_start_(pattern_start) { | 
|  | USE(pattern_start_); | 
|  | } | 
|  |  | 
|  | static constexpr int kLengthInBytes = 4 * Instr::kInstrSize; | 
|  |  | 
|  | void Initialize(); | 
|  |  | 
|  | int32_t distance(); | 
|  | void set_distance(int32_t distance); | 
|  | bool IsValid() const; | 
|  |  | 
|  | private: | 
|  | // This offset must be applied to account for the fact that | 
|  | //   a) the actual "branch" is only in the 3rd instruction | 
|  | //   b) when reading the PC it reports current instruction + 8 | 
|  | static constexpr intptr_t kDistanceOffset = -5 * Instr::kInstrSize; | 
|  |  | 
|  | // adr TMP, #lower16  (same as TMP = PC + #lower16) | 
|  | static constexpr uint32_t kAdrEncoding = (1 << 28) | (TMP << kRdShift); | 
|  |  | 
|  | // movz TMP2, #higher16 lsl 16 | 
|  | static constexpr uint32_t kMovzEncoding = MOVZ | (1 << kHWShift) | TMP2; | 
|  |  | 
|  | // add TMP, TMP, TMP2, SXTW | 
|  | static constexpr uint32_t kAddTmpTmp2 = 0x8b31c210; | 
|  |  | 
|  | // br TMP | 
|  | static constexpr uint32_t kJumpEncoding = BR | (TMP << kRnShift); | 
|  |  | 
|  | uword pattern_start_; | 
|  | }; | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // RUNTIME_VM_INSTRUCTIONS_ARM64_H_ |