| // Copyright (c) 2013, 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_ARM_H_ | |
| #define RUNTIME_VM_INSTRUCTIONS_ARM_H_ | |
| #ifndef RUNTIME_VM_INSTRUCTIONS_H_ | |
| #error Do not include instructions_arm.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 ICData; | |
| class Code; | |
| class Object; | |
| class ObjectPool; | |
| class UntaggedCode; | |
| 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); | |
| // Encodes a load immediate sequence ending at 'end' (the last instruction of | |
| // the load sequence is the instruction before the one at end). | |
| // | |
| // Supports only a subset of [DecodeLoadWordImmediate], namely: | |
| // movw r, #lower16 | |
| // movt r, #upper16 | |
| static void EncodeLoadWordImmediate(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 ARM 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); | |
| }; | |
| class CallPattern : public ValueObject { | |
| public: | |
| CallPattern(uword pc, const Code& code); | |
| CodePtr TargetCode() const; | |
| void SetTargetCode(const Code& code) 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& code); | |
| ObjectPtr Data() const; | |
| void SetData(const Object& data) const; | |
| CodePtr TargetCode() const; | |
| void SetTargetCode(const Code& code) 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 SetDataRelease(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); | |
| ObjectPtr target() const; | |
| void SetTargetRelease(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 SetTargetRelease(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 PcRelativeCallPatternBase : public ValueObject { | |
| public: | |
| // 24 bit signed integer which will get multiplied by 4. | |
| static constexpr intptr_t kLowerCallingRange = | |
| -(1 << 25) + Instr::kPCReadOffset; | |
| static constexpr intptr_t kUpperCallingRange = | |
| (1 << 25) - Instr::kInstrSize + Instr::kPCReadOffset; | |
| explicit PcRelativeCallPatternBase(uword pc) : pc_(pc) {} | |
| static constexpr int kLengthInBytes = 1 * Instr::kInstrSize; | |
| int32_t distance() { | |
| #if !defined(DART_PRECOMPILED_RUNTIME) | |
| return compiler::Assembler::DecodeBranchOffset( | |
| *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::EncodeBranchOffset(distance, *word); | |
| #else | |
| UNREACHABLE(); | |
| #endif | |
| } | |
| protected: | |
| uword pc_; | |
| }; | |
| class PcRelativeCallPattern : public PcRelativeCallPatternBase { | |
| public: | |
| explicit PcRelativeCallPattern(uword pc) : PcRelativeCallPatternBase(pc) {} | |
| bool IsValid() const; | |
| }; | |
| class PcRelativeTailCallPattern : public PcRelativeCallPatternBase { | |
| public: | |
| explicit PcRelativeTailCallPattern(uword pc) | |
| : PcRelativeCallPatternBase(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.<cond> <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: | |
| // | |
| // movw TMP, #lower16 | |
| // movt TMP, #upper16 | |
| // add PC, PC, TMP lsl #0 | |
| // | |
| class PcRelativeTrampolineJumpPattern : public ValueObject { | |
| public: | |
| explicit PcRelativeTrampolineJumpPattern(uword pattern_start) | |
| : pattern_start_(pattern_start) { | |
| USE(pattern_start_); | |
| } | |
| static constexpr int kLengthInBytes = 3 * 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 = -4 * Instr::kInstrSize; | |
| // add PC, PC, TMP lsl #0 | |
| static constexpr uint32_t kAddPcEncoding = | |
| (ADD << kOpcodeShift) | (AL << kConditionShift) | (PC << kRnShift) | | |
| (PC << kRdShift) | (TMP << kRmShift); | |
| uword pattern_start_; | |
| }; | |
| } // namespace dart | |
| #endif // RUNTIME_VM_INSTRUCTIONS_ARM_H_ |