| // Copyright (c) 1994-2006 Sun Microsystems Inc. |
| // All Rights Reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // - Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // - Redistribution in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the |
| // distribution. |
| // |
| // - Neither the name of Sun Microsystems or the names of contributors may |
| // be used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| // OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // The original source code covered by the above license above has been |
| // modified significantly by Google Inc. |
| // Copyright 2012 the V8 project authors. All rights reserved. |
| |
| // A light-weight ARM Assembler |
| // Generates user mode instructions for the ARM architecture up to version 5 |
| |
| #ifndef V8_ARM_ASSEMBLER_ARM_H_ |
| #define V8_ARM_ASSEMBLER_ARM_H_ |
| |
| #include <stdio.h> |
| #include <vector> |
| |
| #include "src/arm/constants-arm.h" |
| #include "src/assembler.h" |
| #include "src/boxed-float.h" |
| #include "src/double.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // clang-format off |
| #define GENERAL_REGISTERS(V) \ |
| V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) \ |
| V(r8) V(r9) V(r10) V(fp) V(ip) V(sp) V(lr) V(pc) |
| |
| #define ALLOCATABLE_GENERAL_REGISTERS(V) \ |
| V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) V(r8) |
| |
| #define FLOAT_REGISTERS(V) \ |
| V(s0) V(s1) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7) \ |
| V(s8) V(s9) V(s10) V(s11) V(s12) V(s13) V(s14) V(s15) \ |
| V(s16) V(s17) V(s18) V(s19) V(s20) V(s21) V(s22) V(s23) \ |
| V(s24) V(s25) V(s26) V(s27) V(s28) V(s29) V(s30) V(s31) |
| |
| #define LOW_DOUBLE_REGISTERS(V) \ |
| V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ |
| V(d8) V(d9) V(d10) V(d11) V(d12) V(d13) V(d14) V(d15) |
| |
| #define NON_LOW_DOUBLE_REGISTERS(V) \ |
| V(d16) V(d17) V(d18) V(d19) V(d20) V(d21) V(d22) V(d23) \ |
| V(d24) V(d25) V(d26) V(d27) V(d28) V(d29) V(d30) V(d31) |
| |
| #define DOUBLE_REGISTERS(V) \ |
| LOW_DOUBLE_REGISTERS(V) NON_LOW_DOUBLE_REGISTERS(V) |
| |
| #define SIMD128_REGISTERS(V) \ |
| V(q0) V(q1) V(q2) V(q3) V(q4) V(q5) V(q6) V(q7) \ |
| V(q8) V(q9) V(q10) V(q11) V(q12) V(q13) V(q14) V(q15) |
| |
| #define ALLOCATABLE_DOUBLE_REGISTERS(V) \ |
| V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ |
| V(d8) V(d9) V(d10) V(d11) V(d12) \ |
| V(d16) V(d17) V(d18) V(d19) V(d20) V(d21) V(d22) V(d23) \ |
| V(d24) V(d25) V(d26) V(d27) V(d28) V(d29) V(d30) V(d31) |
| |
| #define ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(V) \ |
| V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ |
| V(d8) V(d9) V(d10) V(d11) V(d12) V(d15) |
| |
| #define C_REGISTERS(V) \ |
| V(cr0) V(cr1) V(cr2) V(cr3) V(cr4) V(cr5) V(cr6) V(cr7) \ |
| V(cr8) V(cr9) V(cr10) V(cr11) V(cr12) V(cr15) |
| // clang-format on |
| |
| // The ARM ABI does not specify the usage of register r9, which may be reserved |
| // as the static base or thread register on some platforms, in which case we |
| // leave it alone. Adjust the value of kR9Available accordingly: |
| const int kR9Available = 1; // 1 if available to us, 0 if reserved |
| |
| // Register list in load/store instructions |
| // Note that the bit values must match those used in actual instruction encoding |
| const int kNumRegs = 16; |
| |
| // Caller-saved/arguments registers |
| const RegList kJSCallerSaved = |
| 1 << 0 | // r0 a1 |
| 1 << 1 | // r1 a2 |
| 1 << 2 | // r2 a3 |
| 1 << 3; // r3 a4 |
| |
| const int kNumJSCallerSaved = 4; |
| |
| // Callee-saved registers preserved when switching from C to JavaScript |
| const RegList kCalleeSaved = |
| 1 << 4 | // r4 v1 |
| 1 << 5 | // r5 v2 |
| 1 << 6 | // r6 v3 |
| 1 << 7 | // r7 v4 (cp in JavaScript code) |
| 1 << 8 | // r8 v5 (pp in JavaScript code) |
| kR9Available << 9 | // r9 v6 |
| 1 << 10 | // r10 v7 |
| 1 << 11; // r11 v8 (fp in JavaScript code) |
| |
| // When calling into C++ (only for C++ calls that can't cause a GC). |
| // The call code will take care of lr, fp, etc. |
| const RegList kCallerSaved = |
| 1 << 0 | // r0 |
| 1 << 1 | // r1 |
| 1 << 2 | // r2 |
| 1 << 3 | // r3 |
| 1 << 9; // r9 |
| |
| const int kNumCalleeSaved = 7 + kR9Available; |
| |
| // Double registers d8 to d15 are callee-saved. |
| const int kNumDoubleCalleeSaved = 8; |
| |
| // Number of registers for which space is reserved in safepoints. Must be a |
| // multiple of 8. |
| // TODO(regis): Only 8 registers may actually be sufficient. Revisit. |
| const int kNumSafepointRegisters = 16; |
| |
| // Define the list of registers actually saved at safepoints. |
| // Note that the number of saved registers may be smaller than the reserved |
| // space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. |
| const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; |
| const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved; |
| |
| enum RegisterCode { |
| #define REGISTER_CODE(R) kRegCode_##R, |
| GENERAL_REGISTERS(REGISTER_CODE) |
| #undef REGISTER_CODE |
| kRegAfterLast |
| }; |
| |
| class Register : public RegisterBase<Register, kRegAfterLast> { |
| friend class RegisterBase; |
| explicit constexpr Register(int code) : RegisterBase(code) {} |
| }; |
| |
| static_assert(IS_TRIVIALLY_COPYABLE(Register) && |
| sizeof(Register) == sizeof(int), |
| "Register can efficiently be passed by value"); |
| |
| // r7: context register |
| // r9: lithium scratch |
| #define DECLARE_REGISTER(R) \ |
| constexpr Register R = Register::from_code<kRegCode_##R>(); |
| GENERAL_REGISTERS(DECLARE_REGISTER) |
| #undef DECLARE_REGISTER |
| constexpr Register no_reg = Register::no_reg(); |
| |
| constexpr bool kPadArguments = false; |
| constexpr bool kSimpleFPAliasing = false; |
| constexpr bool kSimdMaskRegisters = false; |
| |
| enum SwVfpRegisterCode { |
| #define REGISTER_CODE(R) kSwVfpCode_##R, |
| FLOAT_REGISTERS(REGISTER_CODE) |
| #undef REGISTER_CODE |
| kSwVfpAfterLast |
| }; |
| |
| // Single word VFP register. |
| class SwVfpRegister : public RegisterBase<SwVfpRegister, kSwVfpAfterLast> { |
| public: |
| static constexpr int kSizeInBytes = 4; |
| |
| static void split_code(int reg_code, int* vm, int* m) { |
| DCHECK(from_code(reg_code).is_valid()); |
| *m = reg_code & 0x1; |
| *vm = reg_code >> 1; |
| } |
| void split_code(int* vm, int* m) const { split_code(code(), vm, m); } |
| |
| private: |
| friend class RegisterBase; |
| explicit constexpr SwVfpRegister(int code) : RegisterBase(code) {} |
| }; |
| |
| static_assert(IS_TRIVIALLY_COPYABLE(SwVfpRegister) && |
| sizeof(SwVfpRegister) == sizeof(int), |
| "SwVfpRegister can efficiently be passed by value"); |
| |
| typedef SwVfpRegister FloatRegister; |
| |
| enum DoubleRegisterCode { |
| #define REGISTER_CODE(R) kDoubleCode_##R, |
| DOUBLE_REGISTERS(REGISTER_CODE) |
| #undef REGISTER_CODE |
| kDoubleAfterLast |
| }; |
| |
| // Double word VFP register. |
| class DwVfpRegister : public RegisterBase<DwVfpRegister, kDoubleAfterLast> { |
| public: |
| // A few double registers are reserved: one as a scratch register and one to |
| // hold 0.0, that does not fit in the immediate field of vmov instructions. |
| // d14: 0.0 |
| // d15: scratch register. |
| static constexpr int kSizeInBytes = 8; |
| |
| inline static int NumRegisters(); |
| |
| static void split_code(int reg_code, int* vm, int* m) { |
| DCHECK(from_code(reg_code).is_valid()); |
| *m = (reg_code & 0x10) >> 4; |
| *vm = reg_code & 0x0F; |
| } |
| void split_code(int* vm, int* m) const { split_code(code(), vm, m); } |
| |
| private: |
| friend class RegisterBase; |
| friend class LowDwVfpRegister; |
| explicit constexpr DwVfpRegister(int code) : RegisterBase(code) {} |
| }; |
| |
| static_assert(IS_TRIVIALLY_COPYABLE(DwVfpRegister) && |
| sizeof(DwVfpRegister) == sizeof(int), |
| "DwVfpRegister can efficiently be passed by value"); |
| |
| typedef DwVfpRegister DoubleRegister; |
| |
| |
| // Double word VFP register d0-15. |
| class LowDwVfpRegister |
| : public RegisterBase<LowDwVfpRegister, kDoubleCode_d16> { |
| public: |
| constexpr operator DwVfpRegister() const { return DwVfpRegister(reg_code_); } |
| |
| SwVfpRegister low() const { return SwVfpRegister::from_code(code() * 2); } |
| SwVfpRegister high() const { |
| return SwVfpRegister::from_code(code() * 2 + 1); |
| } |
| |
| private: |
| friend class RegisterBase; |
| explicit constexpr LowDwVfpRegister(int code) : RegisterBase(code) {} |
| }; |
| |
| enum Simd128RegisterCode { |
| #define REGISTER_CODE(R) kSimd128Code_##R, |
| SIMD128_REGISTERS(REGISTER_CODE) |
| #undef REGISTER_CODE |
| kSimd128AfterLast |
| }; |
| |
| // Quad word NEON register. |
| class QwNeonRegister : public RegisterBase<QwNeonRegister, kSimd128AfterLast> { |
| public: |
| static void split_code(int reg_code, int* vm, int* m) { |
| DCHECK(from_code(reg_code).is_valid()); |
| int encoded_code = reg_code << 1; |
| *m = (encoded_code & 0x10) >> 4; |
| *vm = encoded_code & 0x0F; |
| } |
| void split_code(int* vm, int* m) const { split_code(code(), vm, m); } |
| DwVfpRegister low() const { return DwVfpRegister::from_code(code() * 2); } |
| DwVfpRegister high() const { |
| return DwVfpRegister::from_code(code() * 2 + 1); |
| } |
| |
| private: |
| friend class RegisterBase; |
| explicit constexpr QwNeonRegister(int code) : RegisterBase(code) {} |
| }; |
| |
| |
| typedef QwNeonRegister QuadRegister; |
| |
| typedef QwNeonRegister Simd128Register; |
| |
| enum CRegisterCode { |
| #define REGISTER_CODE(R) kCCode_##R, |
| C_REGISTERS(REGISTER_CODE) |
| #undef REGISTER_CODE |
| kCAfterLast |
| }; |
| |
| // Coprocessor register |
| class CRegister : public RegisterBase<CRegister, kCAfterLast> { |
| friend class RegisterBase; |
| explicit constexpr CRegister(int code) : RegisterBase(code) {} |
| }; |
| |
| // Support for the VFP registers s0 to s31 (d0 to d15). |
| // Note that "s(N):s(N+1)" is the same as "d(N/2)". |
| #define DECLARE_FLOAT_REGISTER(R) \ |
| constexpr SwVfpRegister R = SwVfpRegister::from_code<kSwVfpCode_##R>(); |
| FLOAT_REGISTERS(DECLARE_FLOAT_REGISTER) |
| #undef DECLARE_FLOAT_REGISTER |
| |
| #define DECLARE_LOW_DOUBLE_REGISTER(R) \ |
| constexpr LowDwVfpRegister R = LowDwVfpRegister::from_code<kDoubleCode_##R>(); |
| LOW_DOUBLE_REGISTERS(DECLARE_LOW_DOUBLE_REGISTER) |
| #undef DECLARE_LOW_DOUBLE_REGISTER |
| |
| #define DECLARE_DOUBLE_REGISTER(R) \ |
| constexpr DwVfpRegister R = DwVfpRegister::from_code<kDoubleCode_##R>(); |
| NON_LOW_DOUBLE_REGISTERS(DECLARE_DOUBLE_REGISTER) |
| #undef DECLARE_DOUBLE_REGISTER |
| |
| constexpr DwVfpRegister no_dreg = DwVfpRegister::no_reg(); |
| |
| #define DECLARE_SIMD128_REGISTER(R) \ |
| constexpr Simd128Register R = Simd128Register::from_code<kSimd128Code_##R>(); |
| SIMD128_REGISTERS(DECLARE_SIMD128_REGISTER) |
| #undef DECLARE_SIMD128_REGISTER |
| |
| // Aliases for double registers. |
| constexpr LowDwVfpRegister kFirstCalleeSavedDoubleReg = d8; |
| constexpr LowDwVfpRegister kLastCalleeSavedDoubleReg = d15; |
| constexpr LowDwVfpRegister kDoubleRegZero = d13; |
| constexpr LowDwVfpRegister kScratchDoubleReg = d14; |
| // This scratch q-register aliases d14 (kScratchDoubleReg) and d15, but is only |
| // used if NEON is supported, which implies VFP32DREGS. When there are only 16 |
| // d-registers, d15 is still allocatable. |
| constexpr QwNeonRegister kScratchQuadReg = q7; |
| constexpr LowDwVfpRegister kScratchDoubleReg2 = d15; |
| |
| constexpr CRegister no_creg = CRegister::no_reg(); |
| |
| #define DECLARE_C_REGISTER(R) \ |
| constexpr CRegister R = CRegister::from_code<kCCode_##R>(); |
| C_REGISTERS(DECLARE_C_REGISTER) |
| #undef DECLARE_C_REGISTER |
| |
| // Coprocessor number |
| enum Coprocessor { |
| p0 = 0, |
| p1 = 1, |
| p2 = 2, |
| p3 = 3, |
| p4 = 4, |
| p5 = 5, |
| p6 = 6, |
| p7 = 7, |
| p8 = 8, |
| p9 = 9, |
| p10 = 10, |
| p11 = 11, |
| p12 = 12, |
| p13 = 13, |
| p14 = 14, |
| p15 = 15 |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Machine instruction Operands |
| |
| // Class Operand represents a shifter operand in data processing instructions |
| class Operand BASE_EMBEDDED { |
| public: |
| // immediate |
| INLINE(explicit Operand(int32_t immediate, |
| RelocInfo::Mode rmode = RelocInfo::NONE32)); |
| INLINE(static Operand Zero()); |
| INLINE(explicit Operand(const ExternalReference& f)); |
| explicit Operand(Handle<HeapObject> handle); |
| INLINE(explicit Operand(Smi* value)); |
| |
| // rm |
| INLINE(explicit Operand(Register rm)); |
| |
| // rm <shift_op> shift_imm |
| explicit Operand(Register rm, ShiftOp shift_op, int shift_imm); |
| INLINE(static Operand SmiUntag(Register rm)) { |
| return Operand(rm, ASR, kSmiTagSize); |
| } |
| INLINE(static Operand PointerOffsetFromSmiKey(Register key)) { |
| STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); |
| return Operand(key, LSL, kPointerSizeLog2 - kSmiTagSize); |
| } |
| INLINE(static Operand DoubleOffsetFromSmiKey(Register key)) { |
| STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kDoubleSizeLog2); |
| return Operand(key, LSL, kDoubleSizeLog2 - kSmiTagSize); |
| } |
| |
| // rm <shift_op> rs |
| explicit Operand(Register rm, ShiftOp shift_op, Register rs); |
| |
| static Operand EmbeddedNumber(double number); // Smi or HeapNumber. |
| static Operand EmbeddedCode(CodeStub* stub); |
| |
| // Return true if this is a register operand. |
| bool IsRegister() const { |
| return rm_.is_valid() && rs_ == no_reg && shift_op_ == LSL && |
| shift_imm_ == 0; |
| } |
| // Return true if this is a register operand shifted with an immediate. |
| bool IsImmediateShiftedRegister() const { |
| return rm_.is_valid() && !rs_.is_valid(); |
| } |
| // Return true if this is a register operand shifted with a register. |
| bool IsRegisterShiftedRegister() const { |
| return rm_.is_valid() && rs_.is_valid(); |
| } |
| |
| // Return the number of actual instructions required to implement the given |
| // instruction for this particular operand. This can be a single instruction, |
| // if no load into a scratch register is necessary, or anything between 2 and |
| // 4 instructions when we need to load from the constant pool (depending upon |
| // whether the constant pool entry is in the small or extended section). If |
| // the instruction this operand is used for is a MOV or MVN instruction the |
| // actual instruction to use is required for this calculation. For other |
| // instructions instr is ignored. |
| // |
| // The value returned is only valid as long as no entries are added to the |
| // constant pool between this call and the actual instruction being emitted. |
| int InstructionsRequired(const Assembler* assembler, Instr instr = 0) const; |
| bool MustOutputRelocInfo(const Assembler* assembler) const; |
| |
| inline int32_t immediate() const { |
| DCHECK(IsImmediate()); |
| DCHECK(!IsHeapObjectRequest()); |
| return value_.immediate; |
| } |
| bool IsImmediate() const { |
| return !rm_.is_valid(); |
| } |
| |
| HeapObjectRequest heap_object_request() const { |
| DCHECK(IsHeapObjectRequest()); |
| return value_.heap_object_request; |
| } |
| bool IsHeapObjectRequest() const { |
| DCHECK_IMPLIES(is_heap_object_request_, IsImmediate()); |
| DCHECK_IMPLIES(is_heap_object_request_, |
| rmode_ == RelocInfo::EMBEDDED_OBJECT || |
| rmode_ == RelocInfo::CODE_TARGET); |
| return is_heap_object_request_; |
| } |
| |
| Register rm() const { return rm_; } |
| Register rs() const { return rs_; } |
| ShiftOp shift_op() const { return shift_op_; } |
| |
| |
| private: |
| Register rm_ = no_reg; |
| Register rs_ = no_reg; |
| ShiftOp shift_op_; |
| int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg |
| union Value { |
| Value() {} |
| HeapObjectRequest heap_object_request; // if is_heap_object_request_ |
| int32_t immediate; // otherwise |
| } value_; // valid if rm_ == no_reg |
| bool is_heap_object_request_ = false; |
| RelocInfo::Mode rmode_; |
| |
| friend class Assembler; |
| }; |
| |
| |
| // Class MemOperand represents a memory operand in load and store instructions |
| class MemOperand BASE_EMBEDDED { |
| public: |
| // [rn +/- offset] Offset/NegOffset |
| // [rn +/- offset]! PreIndex/NegPreIndex |
| // [rn], +/- offset PostIndex/NegPostIndex |
| // offset is any signed 32-bit value; offset is first loaded to a scratch |
| // register if it does not fit the addressing mode (12-bit unsigned and sign |
| // bit) |
| explicit MemOperand(Register rn, int32_t offset = 0, AddrMode am = Offset); |
| |
| // [rn +/- rm] Offset/NegOffset |
| // [rn +/- rm]! PreIndex/NegPreIndex |
| // [rn], +/- rm PostIndex/NegPostIndex |
| explicit MemOperand(Register rn, Register rm, AddrMode am = Offset); |
| |
| // [rn +/- rm <shift_op> shift_imm] Offset/NegOffset |
| // [rn +/- rm <shift_op> shift_imm]! PreIndex/NegPreIndex |
| // [rn], +/- rm <shift_op> shift_imm PostIndex/NegPostIndex |
| explicit MemOperand(Register rn, Register rm, |
| ShiftOp shift_op, int shift_imm, AddrMode am = Offset); |
| INLINE(static MemOperand PointerAddressFromSmiKey(Register array, |
| Register key, |
| AddrMode am = Offset)) { |
| STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); |
| return MemOperand(array, key, LSL, kPointerSizeLog2 - kSmiTagSize, am); |
| } |
| |
| void set_offset(int32_t offset) { |
| DCHECK(rm_ == no_reg); |
| offset_ = offset; |
| } |
| |
| uint32_t offset() const { |
| DCHECK(rm_ == no_reg); |
| return offset_; |
| } |
| |
| Register rn() const { return rn_; } |
| Register rm() const { return rm_; } |
| AddrMode am() const { return am_; } |
| |
| bool OffsetIsUint12Encodable() const { |
| return offset_ >= 0 ? is_uint12(offset_) : is_uint12(-offset_); |
| } |
| |
| private: |
| Register rn_; // base |
| Register rm_; // register offset |
| int32_t offset_; // valid if rm_ == no_reg |
| ShiftOp shift_op_; |
| int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg |
| AddrMode am_; // bits P, U, and W |
| |
| friend class Assembler; |
| }; |
| |
| |
| // Class NeonMemOperand represents a memory operand in load and |
| // store NEON instructions |
| class NeonMemOperand BASE_EMBEDDED { |
| public: |
| // [rn {:align}] Offset |
| // [rn {:align}]! PostIndex |
| explicit NeonMemOperand(Register rn, AddrMode am = Offset, int align = 0); |
| |
| // [rn {:align}], rm PostIndex |
| explicit NeonMemOperand(Register rn, Register rm, int align = 0); |
| |
| Register rn() const { return rn_; } |
| Register rm() const { return rm_; } |
| int align() const { return align_; } |
| |
| private: |
| void SetAlignment(int align); |
| |
| Register rn_; // base |
| Register rm_; // register increment |
| int align_; |
| }; |
| |
| |
| // Class NeonListOperand represents a list of NEON registers |
| class NeonListOperand BASE_EMBEDDED { |
| public: |
| explicit NeonListOperand(DoubleRegister base, int register_count = 1) |
| : base_(base), register_count_(register_count) {} |
| explicit NeonListOperand(QwNeonRegister q_reg) |
| : base_(q_reg.low()), register_count_(2) {} |
| DoubleRegister base() const { return base_; } |
| int register_count() { return register_count_; } |
| int length() const { return register_count_ - 1; } |
| NeonListType type() const { |
| switch (register_count_) { |
| default: UNREACHABLE(); |
| // Fall through. |
| case 1: return nlt_1; |
| case 2: return nlt_2; |
| case 3: return nlt_3; |
| case 4: return nlt_4; |
| } |
| } |
| private: |
| DoubleRegister base_; |
| int register_count_; |
| }; |
| |
| |
| struct VmovIndex { |
| unsigned char index; |
| }; |
| constexpr VmovIndex VmovIndexLo = { 0 }; |
| constexpr VmovIndex VmovIndexHi = { 1 }; |
| |
| class Assembler : public AssemblerBase { |
| public: |
| // Create an assembler. Instructions and relocation information are emitted |
| // into a buffer, with the instructions starting from the beginning and the |
| // relocation information starting from the end of the buffer. See CodeDesc |
| // for a detailed comment on the layout (globals.h). |
| // |
| // If the provided buffer is nullptr, the assembler allocates and grows its |
| // own buffer, and buffer_size determines the initial buffer size. The buffer |
| // is owned by the assembler and deallocated upon destruction of the |
| // assembler. |
| // |
| // If the provided buffer is not nullptr, the assembler uses the provided |
| // buffer for code generation and assumes its size to be buffer_size. If the |
| // buffer is too small, a fatal error occurs. No deallocation of the buffer is |
| // done upon destruction of the assembler. |
| Assembler(Isolate* isolate, void* buffer, int buffer_size) |
| : Assembler(IsolateData(isolate), buffer, buffer_size) {} |
| Assembler(IsolateData isolate_data, void* buffer, int buffer_size); |
| virtual ~Assembler(); |
| |
| // GetCode emits any pending (non-emitted) code and fills the descriptor |
| // desc. GetCode() is idempotent; it returns the same result if no other |
| // Assembler functions are invoked in between GetCode() calls. |
| void GetCode(Isolate* isolate, CodeDesc* desc); |
| |
| // Label operations & relative jumps (PPUM Appendix D) |
| // |
| // Takes a branch opcode (cc) and a label (L) and generates |
| // either a backward branch or a forward branch and links it |
| // to the label fixup chain. Usage: |
| // |
| // Label L; // unbound label |
| // j(cc, &L); // forward branch to unbound label |
| // bind(&L); // bind label to the current pc |
| // j(cc, &L); // backward branch to bound label |
| // bind(&L); // illegal: a label may be bound only once |
| // |
| // Note: The same Label can be used for forward and backward branches |
| // but it may be bound only once. |
| |
| void bind(Label* L); // binds an unbound label L to the current code position |
| |
| // Returns the branch offset to the given label from the current code position |
| // Links the label to the current position if it is still unbound |
| // Manages the jump elimination optimization if the second parameter is true. |
| int branch_offset(Label* L); |
| |
| // Returns true if the given pc address is the start of a constant pool load |
| // instruction sequence. |
| INLINE(static bool is_constant_pool_load(Address pc)); |
| |
| // Return the address in the constant pool of the code target address used by |
| // the branch/call instruction at pc, or the object in a mov. |
| INLINE(static Address constant_pool_entry_address(Address pc, |
| Address constant_pool)); |
| |
| // Read/Modify the code target address in the branch/call instruction at pc. |
| // The isolate argument is unused (and may be nullptr) when skipping flushing. |
| INLINE(static Address target_address_at(Address pc, Address constant_pool)); |
| INLINE(static void set_target_address_at( |
| Isolate* isolate, Address pc, Address constant_pool, Address target, |
| ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED)); |
| |
| // Return the code target address at a call site from the return address |
| // of that call in the instruction stream. |
| INLINE(static Address target_address_from_return_address(Address pc)); |
| |
| // Given the address of the beginning of a call, return the address |
| // in the instruction stream that the call will return from. |
| INLINE(static Address return_address_from_call_start(Address pc)); |
| |
| // This sets the branch destination (which is in the constant pool on ARM). |
| // This is for calls and branches within generated code. |
| inline static void deserialization_set_special_target_at( |
| Isolate* isolate, Address constant_pool_entry, Code* code, |
| Address target); |
| |
| // This sets the internal reference at the pc. |
| inline static void deserialization_set_target_internal_reference_at( |
| Isolate* isolate, Address pc, Address target, |
| RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE); |
| |
| // Here we are patching the address in the constant pool, not the actual call |
| // instruction. The address in the constant pool is the same size as a |
| // pointer. |
| static constexpr int kSpecialTargetSize = kPointerSize; |
| |
| // Size of an instruction. |
| static constexpr int kInstrSize = sizeof(Instr); |
| |
| // Difference between address of current opcode and value read from pc |
| // register. |
| static constexpr int kPcLoadDelta = 8; |
| RegList* GetScratchRegisterList() { return &scratch_register_list_; } |
| |
| // --------------------------------------------------------------------------- |
| // Code generation |
| |
| // Insert the smallest number of nop instructions |
| // possible to align the pc offset to a multiple |
| // of m. m must be a power of 2 (>= 4). |
| void Align(int m); |
| // Insert the smallest number of zero bytes possible to align the pc offset |
| // to a mulitple of m. m must be a power of 2 (>= 2). |
| void DataAlign(int m); |
| // Aligns code to something that's optimal for a jump target for the platform. |
| void CodeTargetAlign(); |
| |
| // Branch instructions |
| void b(int branch_offset, Condition cond = al); |
| void bl(int branch_offset, Condition cond = al); |
| void blx(int branch_offset); // v5 and above |
| void blx(Register target, Condition cond = al); // v5 and above |
| void bx(Register target, Condition cond = al); // v5 and above, plus v4t |
| |
| // Convenience branch instructions using labels |
| void b(Label* L, Condition cond = al); |
| void b(Condition cond, Label* L) { b(L, cond); } |
| void bl(Label* L, Condition cond = al); |
| void bl(Condition cond, Label* L) { bl(L, cond); } |
| void blx(Label* L); // v5 and above |
| |
| // Data-processing instructions |
| |
| void and_(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void eor(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void sub(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| void sub(Register dst, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void rsb(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void add(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| void add(Register dst, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void adc(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void sbc(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void rsc(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void tst(Register src1, const Operand& src2, Condition cond = al); |
| void tst(Register src1, Register src2, Condition cond = al); |
| |
| void teq(Register src1, const Operand& src2, Condition cond = al); |
| |
| void cmp(Register src1, const Operand& src2, Condition cond = al); |
| void cmp(Register src1, Register src2, Condition cond = al); |
| |
| void cmp_raw_immediate(Register src1, int raw_immediate, Condition cond = al); |
| |
| void cmn(Register src1, const Operand& src2, Condition cond = al); |
| |
| void orr(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| void orr(Register dst, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void mov(Register dst, const Operand& src, |
| SBit s = LeaveCC, Condition cond = al); |
| void mov(Register dst, Register src, SBit s = LeaveCC, Condition cond = al); |
| |
| // Load the position of the label relative to the generated code object |
| // pointer in a register. |
| void mov_label_offset(Register dst, Label* label); |
| |
| // ARMv7 instructions for loading a 32 bit immediate in two instructions. |
| // The constant for movw and movt should be in the range 0-0xffff. |
| void movw(Register reg, uint32_t immediate, Condition cond = al); |
| void movt(Register reg, uint32_t immediate, Condition cond = al); |
| |
| void bic(Register dst, Register src1, const Operand& src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void mvn(Register dst, const Operand& src, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| // Shift instructions |
| |
| void asr(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, |
| Condition cond = al); |
| |
| void lsl(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, |
| Condition cond = al); |
| |
| void lsr(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, |
| Condition cond = al); |
| |
| // Multiply instructions |
| |
| void mla(Register dst, Register src1, Register src2, Register srcA, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void mls(Register dst, Register src1, Register src2, Register srcA, |
| Condition cond = al); |
| |
| void sdiv(Register dst, Register src1, Register src2, |
| Condition cond = al); |
| |
| void udiv(Register dst, Register src1, Register src2, Condition cond = al); |
| |
| void mul(Register dst, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void smmla(Register dst, Register src1, Register src2, Register srcA, |
| Condition cond = al); |
| |
| void smmul(Register dst, Register src1, Register src2, Condition cond = al); |
| |
| void smlal(Register dstL, Register dstH, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void smull(Register dstL, Register dstH, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void umlal(Register dstL, Register dstH, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| void umull(Register dstL, Register dstH, Register src1, Register src2, |
| SBit s = LeaveCC, Condition cond = al); |
| |
| // Miscellaneous arithmetic instructions |
| |
| void clz(Register dst, Register src, Condition cond = al); // v5 and above |
| |
| // Saturating instructions. v6 and above. |
| |
| // Unsigned saturate. |
| // |
| // Saturate an optionally shifted signed value to an unsigned range. |
| // |
| // usat dst, #satpos, src |
| // usat dst, #satpos, src, lsl #sh |
| // usat dst, #satpos, src, asr #sh |
| // |
| // Register dst will contain: |
| // |
| // 0, if s < 0 |
| // (1 << satpos) - 1, if s > ((1 << satpos) - 1) |
| // s, otherwise |
| // |
| // where s is the contents of src after shifting (if used.) |
| void usat(Register dst, int satpos, const Operand& src, Condition cond = al); |
| |
| // Bitfield manipulation instructions. v7 and above. |
| |
| void ubfx(Register dst, Register src, int lsb, int width, |
| Condition cond = al); |
| |
| void sbfx(Register dst, Register src, int lsb, int width, |
| Condition cond = al); |
| |
| void bfc(Register dst, int lsb, int width, Condition cond = al); |
| |
| void bfi(Register dst, Register src, int lsb, int width, |
| Condition cond = al); |
| |
| void pkhbt(Register dst, Register src1, const Operand& src2, |
| Condition cond = al); |
| |
| void pkhtb(Register dst, Register src1, const Operand& src2, |
| Condition cond = al); |
| |
| void sxtb(Register dst, Register src, int rotate = 0, Condition cond = al); |
| void sxtab(Register dst, Register src1, Register src2, int rotate = 0, |
| Condition cond = al); |
| void sxth(Register dst, Register src, int rotate = 0, Condition cond = al); |
| void sxtah(Register dst, Register src1, Register src2, int rotate = 0, |
| Condition cond = al); |
| |
| void uxtb(Register dst, Register src, int rotate = 0, Condition cond = al); |
| void uxtab(Register dst, Register src1, Register src2, int rotate = 0, |
| Condition cond = al); |
| void uxtb16(Register dst, Register src, int rotate = 0, Condition cond = al); |
| void uxth(Register dst, Register src, int rotate = 0, Condition cond = al); |
| void uxtah(Register dst, Register src1, Register src2, int rotate = 0, |
| Condition cond = al); |
| |
| // Reverse the bits in a register. |
| void rbit(Register dst, Register src, Condition cond = al); |
| |
| // Status register access instructions |
| |
| void mrs(Register dst, SRegister s, Condition cond = al); |
| void msr(SRegisterFieldMask fields, const Operand& src, Condition cond = al); |
| |
| // Load/Store instructions |
| void ldr(Register dst, const MemOperand& src, Condition cond = al); |
| void str(Register src, const MemOperand& dst, Condition cond = al); |
| void ldrb(Register dst, const MemOperand& src, Condition cond = al); |
| void strb(Register src, const MemOperand& dst, Condition cond = al); |
| void ldrh(Register dst, const MemOperand& src, Condition cond = al); |
| void strh(Register src, const MemOperand& dst, Condition cond = al); |
| void ldrsb(Register dst, const MemOperand& src, Condition cond = al); |
| void ldrsh(Register dst, const MemOperand& src, Condition cond = al); |
| void ldrd(Register dst1, |
| Register dst2, |
| const MemOperand& src, Condition cond = al); |
| void strd(Register src1, |
| Register src2, |
| const MemOperand& dst, Condition cond = al); |
| |
| // Load literal from a pc relative address. |
| void ldr_pcrel(Register dst, int imm12, Condition cond = al); |
| |
| // Load/Store exclusive instructions |
| void ldrex(Register dst, Register src, Condition cond = al); |
| void strex(Register src1, Register src2, Register dst, Condition cond = al); |
| void ldrexb(Register dst, Register src, Condition cond = al); |
| void strexb(Register src1, Register src2, Register dst, Condition cond = al); |
| void ldrexh(Register dst, Register src, Condition cond = al); |
| void strexh(Register src1, Register src2, Register dst, Condition cond = al); |
| |
| // Preload instructions |
| void pld(const MemOperand& address); |
| |
| // Load/Store multiple instructions |
| void ldm(BlockAddrMode am, Register base, RegList dst, Condition cond = al); |
| void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al); |
| |
| // Exception-generating instructions and debugging support |
| void stop(const char* msg, |
| Condition cond = al, |
| int32_t code = kDefaultStopCode); |
| |
| void bkpt(uint32_t imm16); // v5 and above |
| void svc(uint32_t imm24, Condition cond = al); |
| |
| // Synchronization instructions. |
| // On ARMv6, an equivalent CP15 operation will be used. |
| void dmb(BarrierOption option); |
| void dsb(BarrierOption option); |
| void isb(BarrierOption option); |
| |
| // Coprocessor instructions |
| |
| void cdp(Coprocessor coproc, int opcode_1, |
| CRegister crd, CRegister crn, CRegister crm, |
| int opcode_2, Condition cond = al); |
| |
| void cdp2(Coprocessor coproc, int opcode_1, |
| CRegister crd, CRegister crn, CRegister crm, |
| int opcode_2); // v5 and above |
| |
| void mcr(Coprocessor coproc, int opcode_1, |
| Register rd, CRegister crn, CRegister crm, |
| int opcode_2 = 0, Condition cond = al); |
| |
| void mcr2(Coprocessor coproc, int opcode_1, |
| Register rd, CRegister crn, CRegister crm, |
| int opcode_2 = 0); // v5 and above |
| |
| void mrc(Coprocessor coproc, int opcode_1, |
| Register rd, CRegister crn, CRegister crm, |
| int opcode_2 = 0, Condition cond = al); |
| |
| void mrc2(Coprocessor coproc, int opcode_1, |
| Register rd, CRegister crn, CRegister crm, |
| int opcode_2 = 0); // v5 and above |
| |
| void ldc(Coprocessor coproc, CRegister crd, const MemOperand& src, |
| LFlag l = Short, Condition cond = al); |
| void ldc(Coprocessor coproc, CRegister crd, Register base, int option, |
| LFlag l = Short, Condition cond = al); |
| |
| void ldc2(Coprocessor coproc, CRegister crd, const MemOperand& src, |
| LFlag l = Short); // v5 and above |
| void ldc2(Coprocessor coproc, CRegister crd, Register base, int option, |
| LFlag l = Short); // v5 and above |
| |
| // Support for VFP. |
| // All these APIs support S0 to S31 and D0 to D31. |
| |
| void vldr(const DwVfpRegister dst, |
| const Register base, |
| int offset, |
| const Condition cond = al); |
| void vldr(const DwVfpRegister dst, |
| const MemOperand& src, |
| const Condition cond = al); |
| |
| void vldr(const SwVfpRegister dst, |
| const Register base, |
| int offset, |
| const Condition cond = al); |
| void vldr(const SwVfpRegister dst, |
| const MemOperand& src, |
| const Condition cond = al); |
| |
| void vstr(const DwVfpRegister src, |
| const Register base, |
| int offset, |
| const Condition cond = al); |
| void vstr(const DwVfpRegister src, |
| const MemOperand& dst, |
| const Condition cond = al); |
| |
| void vstr(const SwVfpRegister src, |
| const Register base, |
| int offset, |
| const Condition cond = al); |
| void vstr(const SwVfpRegister src, |
| const MemOperand& dst, |
| const Condition cond = al); |
| |
| void vldm(BlockAddrMode am, |
| Register base, |
| DwVfpRegister first, |
| DwVfpRegister last, |
| Condition cond = al); |
| |
| void vstm(BlockAddrMode am, |
| Register base, |
| DwVfpRegister first, |
| DwVfpRegister last, |
| Condition cond = al); |
| |
| void vldm(BlockAddrMode am, |
| Register base, |
| SwVfpRegister first, |
| SwVfpRegister last, |
| Condition cond = al); |
| |
| void vstm(BlockAddrMode am, |
| Register base, |
| SwVfpRegister first, |
| SwVfpRegister last, |
| Condition cond = al); |
| |
| void vmov(const SwVfpRegister dst, Float32 imm); |
| void vmov(const DwVfpRegister dst, |
| Double imm, |
| const Register extra_scratch = no_reg); |
| void vmov(const SwVfpRegister dst, |
| const SwVfpRegister src, |
| const Condition cond = al); |
| void vmov(const DwVfpRegister dst, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| // TODO(bbudge) Replace uses of these with the more general core register to |
| // scalar register vmov's. |
| void vmov(const DwVfpRegister dst, |
| const VmovIndex index, |
| const Register src, |
| const Condition cond = al); |
| void vmov(const Register dst, |
| const VmovIndex index, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| void vmov(const DwVfpRegister dst, |
| const Register src1, |
| const Register src2, |
| const Condition cond = al); |
| void vmov(const Register dst1, |
| const Register dst2, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| void vmov(const SwVfpRegister dst, |
| const Register src, |
| const Condition cond = al); |
| void vmov(const Register dst, |
| const SwVfpRegister src, |
| const Condition cond = al); |
| void vcvt_f64_s32(const DwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f32_s32(const SwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f64_u32(const DwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f32_u32(const SwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_s32_f32(const SwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_u32_f32(const SwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_s32_f64(const SwVfpRegister dst, |
| const DwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_u32_f64(const SwVfpRegister dst, |
| const DwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f64_f32(const DwVfpRegister dst, |
| const SwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f32_f64(const SwVfpRegister dst, |
| const DwVfpRegister src, |
| VFPConversionMode mode = kDefaultRoundToZero, |
| const Condition cond = al); |
| void vcvt_f64_s32(const DwVfpRegister dst, |
| int fraction_bits, |
| const Condition cond = al); |
| |
| void vmrs(const Register dst, const Condition cond = al); |
| void vmsr(const Register dst, const Condition cond = al); |
| |
| void vneg(const DwVfpRegister dst, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| void vneg(const SwVfpRegister dst, const SwVfpRegister src, |
| const Condition cond = al); |
| void vabs(const DwVfpRegister dst, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| void vabs(const SwVfpRegister dst, const SwVfpRegister src, |
| const Condition cond = al); |
| void vadd(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vadd(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vsub(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vsub(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vmul(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vmul(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vmla(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vmla(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vmls(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vmls(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vdiv(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vdiv(const SwVfpRegister dst, const SwVfpRegister src1, |
| const SwVfpRegister src2, const Condition cond = al); |
| void vcmp(const DwVfpRegister src1, |
| const DwVfpRegister src2, |
| const Condition cond = al); |
| void vcmp(const SwVfpRegister src1, const SwVfpRegister src2, |
| const Condition cond = al); |
| void vcmp(const DwVfpRegister src1, |
| const double src2, |
| const Condition cond = al); |
| void vcmp(const SwVfpRegister src1, const float src2, |
| const Condition cond = al); |
| |
| void vmaxnm(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2); |
| void vmaxnm(const SwVfpRegister dst, |
| const SwVfpRegister src1, |
| const SwVfpRegister src2); |
| void vminnm(const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2); |
| void vminnm(const SwVfpRegister dst, |
| const SwVfpRegister src1, |
| const SwVfpRegister src2); |
| |
| // VSEL supports cond in {eq, ne, ge, lt, gt, le, vs, vc}. |
| void vsel(const Condition cond, |
| const DwVfpRegister dst, |
| const DwVfpRegister src1, |
| const DwVfpRegister src2); |
| void vsel(const Condition cond, |
| const SwVfpRegister dst, |
| const SwVfpRegister src1, |
| const SwVfpRegister src2); |
| |
| void vsqrt(const DwVfpRegister dst, |
| const DwVfpRegister src, |
| const Condition cond = al); |
| void vsqrt(const SwVfpRegister dst, const SwVfpRegister src, |
| const Condition cond = al); |
| |
| // ARMv8 rounding instructions. |
| void vrinta(const SwVfpRegister dst, const SwVfpRegister src); |
| void vrinta(const DwVfpRegister dst, const DwVfpRegister src); |
| void vrintn(const SwVfpRegister dst, const SwVfpRegister src); |
| void vrintn(const DwVfpRegister dst, const DwVfpRegister src); |
| void vrintm(const SwVfpRegister dst, const SwVfpRegister src); |
| void vrintm(const DwVfpRegister dst, const DwVfpRegister src); |
| void vrintp(const SwVfpRegister dst, const SwVfpRegister src); |
| void vrintp(const DwVfpRegister dst, const DwVfpRegister src); |
| void vrintz(const SwVfpRegister dst, const SwVfpRegister src, |
| const Condition cond = al); |
| void vrintz(const DwVfpRegister dst, const DwVfpRegister src, |
| const Condition cond = al); |
| |
| // Support for NEON. |
| |
| // All these APIs support D0 to D31 and Q0 to Q15. |
| void vld1(NeonSize size, |
| const NeonListOperand& dst, |
| const NeonMemOperand& src); |
| void vst1(NeonSize size, |
| const NeonListOperand& src, |
| const NeonMemOperand& dst); |
| // dt represents the narrower type |
| void vmovl(NeonDataType dt, QwNeonRegister dst, DwVfpRegister src); |
| // dt represents the narrower type. |
| void vqmovn(NeonDataType dt, DwVfpRegister dst, QwNeonRegister src); |
| |
| // Only unconditional core <-> scalar moves are currently supported. |
| void vmov(NeonDataType dt, DwVfpRegister dst, int index, Register src); |
| void vmov(NeonDataType dt, Register dst, DwVfpRegister src, int index); |
| |
| void vmov(QwNeonRegister dst, QwNeonRegister src); |
| void vdup(NeonSize size, QwNeonRegister dst, Register src); |
| void vdup(NeonSize size, QwNeonRegister dst, DwVfpRegister src, int index); |
| void vdup(NeonSize size, DwVfpRegister dst, DwVfpRegister src, int index); |
| |
| void vcvt_f32_s32(QwNeonRegister dst, QwNeonRegister src); |
| void vcvt_f32_u32(QwNeonRegister dst, QwNeonRegister src); |
| void vcvt_s32_f32(QwNeonRegister dst, QwNeonRegister src); |
| void vcvt_u32_f32(QwNeonRegister dst, QwNeonRegister src); |
| |
| void vmvn(QwNeonRegister dst, QwNeonRegister src); |
| void vswp(DwVfpRegister dst, DwVfpRegister src); |
| void vswp(QwNeonRegister dst, QwNeonRegister src); |
| void vabs(QwNeonRegister dst, QwNeonRegister src); |
| void vabs(NeonSize size, QwNeonRegister dst, QwNeonRegister src); |
| void vneg(QwNeonRegister dst, QwNeonRegister src); |
| void vneg(NeonSize size, QwNeonRegister dst, QwNeonRegister src); |
| |
| void vand(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void veor(DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2); |
| void veor(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vbsl(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vorr(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vadd(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vadd(NeonSize size, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vqadd(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vsub(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vsub(NeonSize size, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vqsub(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vmul(QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vmul(NeonSize size, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vmin(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vmin(NeonDataType dt, QwNeonRegister dst, |
| QwNeonRegister src1, QwNeonRegister src2); |
| void vmax(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vmax(NeonDataType dt, QwNeonRegister dst, |
| QwNeonRegister src1, QwNeonRegister src2); |
| void vpadd(DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2); |
| void vpadd(NeonSize size, DwVfpRegister dst, DwVfpRegister src1, |
| DwVfpRegister src2); |
| void vpmin(NeonDataType dt, DwVfpRegister dst, DwVfpRegister src1, |
| DwVfpRegister src2); |
| void vpmax(NeonDataType dt, DwVfpRegister dst, DwVfpRegister src1, |
| DwVfpRegister src2); |
| void vshl(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src, int shift); |
| void vshr(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src, int shift); |
| void vsli(NeonSize size, DwVfpRegister dst, DwVfpRegister src, int shift); |
| void vsri(NeonSize size, DwVfpRegister dst, DwVfpRegister src, int shift); |
| // vrecpe and vrsqrte only support floating point lanes. |
| void vrecpe(QwNeonRegister dst, QwNeonRegister src); |
| void vrsqrte(QwNeonRegister dst, QwNeonRegister src); |
| void vrecps(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vrsqrts(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vtst(NeonSize size, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vceq(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vceq(NeonSize size, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vcge(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vcge(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vcgt(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2); |
| void vcgt(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, |
| QwNeonRegister src2); |
| void vext(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2, |
| int bytes); |
| void vzip(NeonSize size, DwVfpRegister src1, DwVfpRegister src2); |
| void vzip(NeonSize size, QwNeonRegister src1, QwNeonRegister src2); |
| void vuzp(NeonSize size, DwVfpRegister src1, DwVfpRegister src2); |
| void vuzp(NeonSize size, QwNeonRegister src1, QwNeonRegister src2); |
| void vrev16(NeonSize size, QwNeonRegister dst, QwNeonRegister src); |
| void vrev32(NeonSize size, QwNeonRegister dst, QwNeonRegister src); |
| void vrev64(NeonSize size, QwNeonRegister dst, QwNeonRegister src); |
| void vtrn(NeonSize size, DwVfpRegister src1, DwVfpRegister src2); |
| void vtrn(NeonSize size, QwNeonRegister src1, QwNeonRegister src2); |
| void vtbl(DwVfpRegister dst, const NeonListOperand& list, |
| DwVfpRegister index); |
| void vtbx(DwVfpRegister dst, const NeonListOperand& list, |
| DwVfpRegister index); |
| |
| // Pseudo instructions |
| |
| // Different nop operations are used by the code generator to detect certain |
| // states of the generated code. |
| enum NopMarkerTypes { |
| NON_MARKING_NOP = 0, |
| DEBUG_BREAK_NOP, |
| // IC markers. |
| PROPERTY_ACCESS_INLINED, |
| PROPERTY_ACCESS_INLINED_CONTEXT, |
| PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE, |
| // Helper values. |
| LAST_CODE_MARKER, |
| FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED |
| }; |
| |
| void nop(int type = 0); // 0 is the default non-marking type. |
| |
| void push(Register src, Condition cond = al) { |
| str(src, MemOperand(sp, 4, NegPreIndex), cond); |
| } |
| |
| void pop(Register dst, Condition cond = al) { |
| ldr(dst, MemOperand(sp, 4, PostIndex), cond); |
| } |
| |
| void pop(); |
| |
| void vpush(QwNeonRegister src, Condition cond = al) { |
| vstm(db_w, sp, src.low(), src.high(), cond); |
| } |
| |
| void vpush(DwVfpRegister src, Condition cond = al) { |
| vstm(db_w, sp, src, src, cond); |
| } |
| |
| void vpush(SwVfpRegister src, Condition cond = al) { |
| vstm(db_w, sp, src, src, cond); |
| } |
| |
| void vpop(DwVfpRegister dst, Condition cond = al) { |
| vldm(ia_w, sp, dst, dst, cond); |
| } |
| |
| // Jump unconditionally to given label. |
| void jmp(Label* L) { b(L, al); } |
| |
| // Check the code size generated from label to here. |
| int SizeOfCodeGeneratedSince(Label* label) { |
| return pc_offset() - label->pos(); |
| } |
| |
| // Check the number of instructions generated from label to here. |
| int InstructionsGeneratedSince(Label* label) { |
| return SizeOfCodeGeneratedSince(label) / kInstrSize; |
| } |
| |
| // Check whether an immediate fits an addressing mode 1 instruction. |
| static bool ImmediateFitsAddrMode1Instruction(int32_t imm32); |
| |
| // Check whether an immediate fits an addressing mode 2 instruction. |
| bool ImmediateFitsAddrMode2Instruction(int32_t imm32); |
| |
| // Class for scoping postponing the constant pool generation. |
| class BlockConstPoolScope { |
| public: |
| explicit BlockConstPoolScope(Assembler* assem) : assem_(assem) { |
| assem_->StartBlockConstPool(); |
| } |
| ~BlockConstPoolScope() { |
| assem_->EndBlockConstPool(); |
| } |
| |
| private: |
| Assembler* assem_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(BlockConstPoolScope); |
| }; |
| |
| // Class for blocking sharing of code targets in constant pool. |
| class BlockCodeTargetSharingScope { |
| public: |
| explicit BlockCodeTargetSharingScope(Assembler* assem) : assem_(nullptr) { |
| Open(assem); |
| } |
| // This constructor does not initialize the scope. The user needs to |
| // explicitly call Open() before using it. |
| BlockCodeTargetSharingScope() : assem_(nullptr) {} |
| ~BlockCodeTargetSharingScope() { |
| Close(); |
| } |
| void Open(Assembler* assem) { |
| DCHECK_NULL(assem_); |
| DCHECK_NOT_NULL(assem); |
| assem_ = assem; |
| assem_->StartBlockCodeTargetSharing(); |
| } |
| |
| private: |
| void Close() { |
| if (assem_ != nullptr) { |
| assem_->EndBlockCodeTargetSharing(); |
| } |
| } |
| Assembler* assem_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BlockCodeTargetSharingScope); |
| }; |
| |
| // Record a comment relocation entry that can be used by a disassembler. |
| // Use --code-comments to enable. |
| void RecordComment(const char* msg); |
| |
| // Record a deoptimization reason that can be used by a log or cpu profiler. |
| // Use --trace-deopt to enable. |
| void RecordDeoptReason(DeoptimizeReason reason, SourcePosition position, |
| int id); |
| |
| // Record the emission of a constant pool. |
| // |
| // The emission of constant pool depends on the size of the code generated and |
| // the number of RelocInfo recorded. |
| // The Debug mechanism needs to map code offsets between two versions of a |
| // function, compiled with and without debugger support (see for example |
| // Debug::PrepareForBreakPoints()). |
| // Compiling functions with debugger support generates additional code |
| // (DebugCodegen::GenerateSlot()). This may affect the emission of the |
| // constant pools and cause the version of the code with debugger support to |
| // have constant pools generated in different places. |
| // Recording the position and size of emitted constant pools allows to |
| // correctly compute the offset mappings between the different versions of a |
| // function in all situations. |
| // |
| // The parameter indicates the size of the constant pool (in bytes), including |
| // the marker and branch over the data. |
| void RecordConstPool(int size); |
| |
| // Writes a single byte or word of data in the code stream. Used |
| // for inline tables, e.g., jump-tables. CheckConstantPool() should be |
| // called before any use of db/dd/dq/dp to ensure that constant pools |
| // are not emitted as part of the tables generated. |
| void db(uint8_t data); |
| void dd(uint32_t data); |
| void dq(uint64_t data); |
| void dp(uintptr_t data) { dd(data); } |
| |
| // Emits the address of the code stub's first instruction. |
| void emit_code_stub_address(Code* stub); |
| |
| // Read/patch instructions |
| Instr instr_at(int pos) { return *reinterpret_cast<Instr*>(buffer_ + pos); } |
| void instr_at_put(int pos, Instr instr) { |
| *reinterpret_cast<Instr*>(buffer_ + pos) = instr; |
| } |
| static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); } |
| static void instr_at_put(byte* pc, Instr instr) { |
| *reinterpret_cast<Instr*>(pc) = instr; |
| } |
| static Condition GetCondition(Instr instr); |
| static bool IsBranch(Instr instr); |
| static int GetBranchOffset(Instr instr); |
| static bool IsLdrRegisterImmediate(Instr instr); |
| static bool IsVldrDRegisterImmediate(Instr instr); |
| static int GetLdrRegisterImmediateOffset(Instr instr); |
| static int GetVldrDRegisterImmediateOffset(Instr instr); |
| static Instr SetLdrRegisterImmediateOffset(Instr instr, int offset); |
| static Instr SetVldrDRegisterImmediateOffset(Instr instr, int offset); |
| static bool IsStrRegisterImmediate(Instr instr); |
| static Instr SetStrRegisterImmediateOffset(Instr instr, int offset); |
| static bool IsAddRegisterImmediate(Instr instr); |
| static Instr SetAddRegisterImmediateOffset(Instr instr, int offset); |
| static Register GetRd(Instr instr); |
| static Register GetRn(Instr instr); |
| static Register GetRm(Instr instr); |
| static bool IsPush(Instr instr); |
| static bool IsPop(Instr instr); |
| static bool IsStrRegFpOffset(Instr instr); |
| static bool IsLdrRegFpOffset(Instr instr); |
| static bool IsStrRegFpNegOffset(Instr instr); |
| static bool IsLdrRegFpNegOffset(Instr instr); |
| static bool IsLdrPcImmediateOffset(Instr instr); |
| static bool IsVldrDPcImmediateOffset(Instr instr); |
| static bool IsBlxReg(Instr instr); |
| static bool IsBlxIp(Instr instr); |
| static bool IsTstImmediate(Instr instr); |
| static bool IsCmpRegister(Instr instr); |
| static bool IsCmpImmediate(Instr instr); |
| static Register GetCmpImmediateRegister(Instr instr); |
| static int GetCmpImmediateRawImmediate(Instr instr); |
| static bool IsNop(Instr instr, int type = NON_MARKING_NOP); |
| static bool IsMovImmed(Instr instr); |
| static bool IsOrrImmed(Instr instr); |
| static bool IsMovT(Instr instr); |
| static Instr GetMovTPattern(); |
| static bool IsMovW(Instr instr); |
| static Instr GetMovWPattern(); |
| static Instr EncodeMovwImmediate(uint32_t immediate); |
| static Instr PatchMovwImmediate(Instr instruction, uint32_t immediate); |
| static int DecodeShiftImm(Instr instr); |
| static Instr PatchShiftImm(Instr instr, int immed); |
| |
| // Constants in pools are accessed via pc relative addressing, which can |
| // reach +/-4KB for integer PC-relative loads and +/-1KB for floating-point |
| // PC-relative loads, thereby defining a maximum distance between the |
| // instruction and the accessed constant. |
| static constexpr int kMaxDistToIntPool = 4 * KB; |
| static constexpr int kMaxDistToFPPool = 1 * KB; |
| // All relocations could be integer, it therefore acts as the limit. |
| static constexpr int kMinNumPendingConstants = 4; |
| static constexpr int kMaxNumPending32Constants = |
| kMaxDistToIntPool / kInstrSize; |
| static constexpr int kMaxNumPending64Constants = |
| kMaxDistToFPPool / kInstrSize; |
| |
| // Postpone the generation of the constant pool for the specified number of |
| // instructions. |
| void BlockConstPoolFor(int instructions); |
| |
| // Check if is time to emit a constant pool. |
| void CheckConstPool(bool force_emit, bool require_jump); |
| |
| void MaybeCheckConstPool() { |
| if (pc_offset() >= next_buffer_check_) { |
| CheckConstPool(false, true); |
| } |
| } |
| |
| void PatchConstantPoolAccessInstruction(int pc_offset, int offset, |
| ConstantPoolEntry::Access access, |
| ConstantPoolEntry::Type type) { |
| // No embedded constant pool support. |
| UNREACHABLE(); |
| } |
| |
| protected: |
| int buffer_space() const { return reloc_info_writer.pos() - pc_; } |
| |
| // Decode branch instruction at pos and return branch target pos |
| int target_at(int pos); |
| |
| // Patch branch instruction at pos to branch to given branch target pos |
| void target_at_put(int pos, int target_pos); |
| |
| // Prevent sharing of code target constant pool entries until |
| // EndBlockCodeTargetSharing is called. Calls to this function can be nested |
| // but must be followed by an equal number of call to |
| // EndBlockCodeTargetSharing. |
| void StartBlockCodeTargetSharing() { |
| ++code_target_sharing_blocked_nesting_; |
| } |
| |
| // Resume sharing of constant pool code target entries. Needs to be called |
| // as many times as StartBlockCodeTargetSharing to have an effect. |
| void EndBlockCodeTargetSharing() { |
| --code_target_sharing_blocked_nesting_; |
| } |
| |
| // Prevent contant pool emission until EndBlockConstPool is called. |
| // Calls to this function can be nested but must be followed by an equal |
| // number of call to EndBlockConstpool. |
| void StartBlockConstPool() { |
| if (const_pool_blocked_nesting_++ == 0) { |
| // Prevent constant pool checks happening by setting the next check to |
| // the biggest possible offset. |
| next_buffer_check_ = kMaxInt; |
| } |
| } |
| |
| // Resume constant pool emission. Needs to be called as many times as |
| // StartBlockConstPool to have an effect. |
| void EndBlockConstPool() { |
| if (--const_pool_blocked_nesting_ == 0) { |
| #ifdef DEBUG |
| // Max pool start (if we need a jump and an alignment). |
| int start = pc_offset() + kInstrSize + 2 * kPointerSize; |
| // Check the constant pool hasn't been blocked for too long. |
| DCHECK(pending_32_bit_constants_.empty() || |
| (start + pending_64_bit_constants_.size() * kDoubleSize < |
| static_cast<size_t>(first_const_pool_32_use_ + |
| kMaxDistToIntPool))); |
| DCHECK(pending_64_bit_constants_.empty() || |
| (start < (first_const_pool_64_use_ + kMaxDistToFPPool))); |
| #endif |
| // Two cases: |
| // * no_const_pool_before_ >= next_buffer_check_ and the emission is |
| // still blocked |
| // * no_const_pool_before_ < next_buffer_check_ and the next emit will |
| // trigger a check. |
| next_buffer_check_ = no_const_pool_before_; |
| } |
| } |
| |
| bool is_const_pool_blocked() const { |
| return (const_pool_blocked_nesting_ > 0) || |
| (pc_offset() < no_const_pool_before_); |
| } |
| |
| bool VfpRegisterIsAvailable(DwVfpRegister reg) { |
| DCHECK(reg.is_valid()); |
| return IsEnabled(VFP32DREGS) || |
| (reg.code() < LowDwVfpRegister::kNumRegisters); |
| } |
| |
| bool VfpRegisterIsAvailable(QwNeonRegister reg) { |
| DCHECK(reg.is_valid()); |
| return IsEnabled(VFP32DREGS) || |
| (reg.code() < LowDwVfpRegister::kNumRegisters / 2); |
| } |
| |
| inline void emit(Instr x); |
| |
| // Code generation |
| // The relocation writer's position is at least kGap bytes below the end of |
| // the generated instructions. This is so that multi-instruction sequences do |
| // not have to check for overflow. The same is true for writes of large |
| // relocation info entries. |
| static constexpr int kGap = 32; |
| |
| // Relocation info generation |
| // Each relocation is encoded as a variable size value |
| static constexpr int kMaxRelocSize = RelocInfoWriter::kMaxSize; |
| RelocInfoWriter reloc_info_writer; |
| |
| // ConstantPoolEntry records are used during code generation as temporary |
| // containers for constants and code target addresses until they are emitted |
| // to the constant pool. These records are temporarily stored in a separate |
| // buffer until a constant pool is emitted. |
| // If every instruction in a long sequence is accessing the pool, we need one |
| // pending relocation entry per instruction. |
| |
| // The buffers of pending constant pool entries. |
| std::vector<ConstantPoolEntry> pending_32_bit_constants_; |
| std::vector<ConstantPoolEntry> pending_64_bit_constants_; |
| |
| // Map of address of handle to index in pending_32_bit_constants_. |
| std::map<Address, int> handle_to_index_map_; |
| |
| // Scratch registers available for use by the Assembler. |
| RegList scratch_register_list_; |
| |
| private: |
| // Avoid overflows for displacements etc. |
| static const int kMaximalBufferSize = 512 * MB; |
| |
| int next_buffer_check_; // pc offset of next buffer check |
| |
| // Constant pool generation |
| // Pools are emitted in the instruction stream, preferably after unconditional |
| // jumps or after returns from functions (in dead code locations). |
| // If a long code sequence does not contain unconditional jumps, it is |
| // necessary to emit the constant pool before the pool gets too far from the |
| // location it is accessed from. In this case, we emit a jump over the emitted |
| // constant pool. |
| // Constants in the pool may be addresses of functions that gets relocated; |
| // if so, a relocation info entry is associated to the constant pool entry. |
| |
| // Repeated checking whether the constant pool should be emitted is rather |
| // expensive. By default we only check again once a number of instructions |
| // has been generated. That also means that the sizing of the buffers is not |
| // an exact science, and that we rely on some slop to not overrun buffers. |
| static constexpr int kCheckPoolIntervalInst = 32; |
| static constexpr int kCheckPoolInterval = kCheckPoolIntervalInst * kInstrSize; |
| |
| // Sharing of code target entries may be blocked in some code sequences. |
| int code_target_sharing_blocked_nesting_; |
| bool IsCodeTargetSharingAllowed() const { |
| return code_target_sharing_blocked_nesting_ == 0; |
| } |
| |
| // Emission of the constant pool may be blocked in some code sequences. |
| int const_pool_blocked_nesting_; // Block emission if this is not zero. |
| int no_const_pool_before_; // Block emission before this pc offset. |
| |
| // Keep track of the first instruction requiring a constant pool entry |
| // since the previous constant pool was emitted. |
| int first_const_pool_32_use_; |
| int first_const_pool_64_use_; |
| |
| // The bound position, before this we cannot do instruction elimination. |
| int last_bound_pos_; |
| |
| inline void CheckBuffer(); |
| void GrowBuffer(); |
| |
| // 32-bit immediate values |
| void Move32BitImmediate(Register rd, const Operand& x, Condition cond = al); |
| |
| // Instruction generation |
| void AddrMode1(Instr instr, Register rd, Register rn, const Operand& x); |
| // Attempt to encode operand |x| for instruction |instr| and return true on |
| // success. The result will be encoded in |instr| directly. This method may |
| // change the opcode if deemed beneficial, for instance, MOV may be turned |
| // into MVN, ADD into SUB, AND into BIC, ...etc. The only reason this method |
| // may fail is that the operand is an immediate that cannot be encoded. |
| bool AddrMode1TryEncodeOperand(Instr* instr, const Operand& x); |
| |
| void AddrMode2(Instr instr, Register rd, const MemOperand& x); |
| void AddrMode3(Instr instr, Register rd, const MemOperand& x); |
| void AddrMode4(Instr instr, Register rn, RegList rl); |
| void AddrMode5(Instr instr, CRegister crd, const MemOperand& x); |
| |
| // Labels |
| void print(const Label* L); |
| void bind_to(Label* L, int pos); |
| void next(Label* L); |
| |
| // Record reloc info for current pc_ |
| void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); |
| void ConstantPoolAddEntry(int position, RelocInfo::Mode rmode, |
| intptr_t value); |
| void ConstantPoolAddEntry(int position, Double value); |
| |
| friend class RelocInfo; |
| friend class BlockConstPoolScope; |
| friend class BlockCodeTargetSharingScope; |
| friend class EnsureSpace; |
| |
| // The following functions help with avoiding allocations of embedded heap |
| // objects during the code assembly phase. {RequestHeapObject} records the |
| // need for a future heap number allocation or code stub generation. After |
| // code assembly, {AllocateAndInstallRequestedHeapObjects} will allocate these |
| // objects and place them where they are expected (determined by the pc offset |
| // associated with each request). That is, for each request, it will patch the |
| // dummy heap object handle that we emitted during code assembly with the |
| // actual heap object handle. |
| void RequestHeapObject(HeapObjectRequest request); |
| void AllocateAndInstallRequestedHeapObjects(Isolate* isolate); |
| |
| std::forward_list<HeapObjectRequest> heap_object_requests_; |
| }; |
| |
| constexpr int kNoCodeAgeSequenceLength = 3 * Assembler::kInstrSize; |
| |
| class EnsureSpace BASE_EMBEDDED { |
| public: |
| INLINE(explicit EnsureSpace(Assembler* assembler)); |
| }; |
| |
| class PatchingAssembler : public Assembler { |
| public: |
| PatchingAssembler(IsolateData isolate_data, byte* address, int instructions); |
| ~PatchingAssembler(); |
| |
| void Emit(Address addr); |
| void FlushICache(Isolate* isolate); |
| }; |
| |
| // This scope utility allows scratch registers to be managed safely. The |
| // Assembler's GetScratchRegisterList() is used as a pool of scratch |
| // registers. These registers can be allocated on demand, and will be returned |
| // at the end of the scope. |
| // |
| // When the scope ends, the Assembler's list will be restored to its original |
| // state, even if the list is modified by some other means. Note that this scope |
| // can be nested but the destructors need to run in the opposite order as the |
| // constructors. We do not have assertions for this. |
| class UseScratchRegisterScope { |
| public: |
| explicit UseScratchRegisterScope(Assembler* assembler); |
| ~UseScratchRegisterScope(); |
| |
| // Take a register from the list and return it. |
| Register Acquire(); |
| |
| private: |
| // Currently available scratch registers. |
| RegList* available_; |
| // Available scratch registers at the start of this scope. |
| RegList old_available_; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_ARM_ASSEMBLER_ARM_H_ |