Index: src/arm/assembler-thumb2.h |
=================================================================== |
--- src/arm/assembler-thumb2.h (revision 0) |
+++ src/arm/assembler-thumb2.h (revision 0) |
@@ -0,0 +1,1001 @@ |
+// 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 2006-2008 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_THUMB2_H_ |
+#define V8_ARM_ASSEMBLER_THUMB2_H_ |
+#include <stdio.h> |
+#include "assembler.h" |
+#include "serialize.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+// CPU Registers. |
+// |
+// 1) We would prefer to use an enum, but enum values are assignment- |
+// compatible with int, which has caused code-generation bugs. |
+// |
+// 2) We would prefer to use a class instead of a struct but we don't like |
+// the register initialization to depend on the particular initialization |
+// order (which appears to be different on OS X, Linux, and Windows for the |
+// installed versions of C++ we tried). Using a struct permits C-style |
+// "initialization". Also, the Register objects cannot be const as this |
+// forces initialization stubs in MSVC, making us dependent on initialization |
+// order. |
+// |
+// 3) By not using an enum, we are possibly preventing the compiler from |
+// doing certain constant folds, which may significantly reduce the |
+// code generated for some assembly instructions (because they boil down |
+// to a few constants). If this is a problem, we could change the code |
+// such that we use an enum in optimized mode, and the struct in debug |
+// mode. This way we get the compile-time error checking in debug mode |
+// and best performance in optimized code. |
+// |
+// Core register |
+struct Register { |
+ bool is_valid() const { return 0 <= code_ && code_ < 16; } |
+ bool is(Register reg) const { return code_ == reg.code_; } |
+ int code() const { |
+ ASSERT(is_valid()); |
+ return code_; |
+ } |
+ int bit() const { |
+ ASSERT(is_valid()); |
+ return 1 << code_; |
+ } |
+ |
+ // (unfortunately we can't make this private in a struct) |
+ int code_; |
+}; |
+ |
+ |
+extern Register no_reg; |
+extern Register r0; |
+extern Register r1; |
+extern Register r2; |
+extern Register r3; |
+extern Register r4; |
+extern Register r5; |
+extern Register r6; |
+extern Register r7; |
+extern Register r8; |
+extern Register r9; |
+extern Register r10; |
+extern Register fp; |
+extern Register ip; |
+extern Register sp; |
+extern Register lr; |
+extern Register pc; |
+ |
+// Support for VFP registers s0 to s32 (d0 to d16). |
+// Note that "sN:sM" is the same as "dN/2". |
+extern Register s0; |
+extern Register s1; |
+extern Register s2; |
+extern Register s3; |
+extern Register s4; |
+extern Register s5; |
+extern Register s6; |
+extern Register s7; |
+extern Register s8; |
+extern Register s9; |
+extern Register s10; |
+extern Register s11; |
+extern Register s12; |
+extern Register s13; |
+extern Register s14; |
+extern Register s15; |
+extern Register s16; |
+extern Register s17; |
+extern Register s18; |
+extern Register s19; |
+extern Register s20; |
+extern Register s21; |
+extern Register s22; |
+extern Register s23; |
+extern Register s24; |
+extern Register s25; |
+extern Register s26; |
+extern Register s27; |
+extern Register s28; |
+extern Register s29; |
+extern Register s30; |
+extern Register s31; |
+ |
+extern Register d0; |
+extern Register d1; |
+extern Register d2; |
+extern Register d3; |
+extern Register d4; |
+extern Register d5; |
+extern Register d6; |
+extern Register d7; |
+extern Register d8; |
+extern Register d9; |
+extern Register d10; |
+extern Register d11; |
+extern Register d12; |
+extern Register d13; |
+extern Register d14; |
+extern Register d15; |
+ |
+// Coprocessor register |
+struct CRegister { |
+ bool is_valid() const { return 0 <= code_ && code_ < 16; } |
+ bool is(CRegister creg) const { return code_ == creg.code_; } |
+ int code() const { |
+ ASSERT(is_valid()); |
+ return code_; |
+ } |
+ int bit() const { |
+ ASSERT(is_valid()); |
+ return 1 << code_; |
+ } |
+ |
+ // (unfortunately we can't make this private in a struct) |
+ int code_; |
+}; |
+ |
+ |
+extern CRegister no_creg; |
+extern CRegister cr0; |
+extern CRegister cr1; |
+extern CRegister cr2; |
+extern CRegister cr3; |
+extern CRegister cr4; |
+extern CRegister cr5; |
+extern CRegister cr6; |
+extern CRegister cr7; |
+extern CRegister cr8; |
+extern CRegister cr9; |
+extern CRegister cr10; |
+extern CRegister cr11; |
+extern CRegister cr12; |
+extern CRegister cr13; |
+extern CRegister cr14; |
+extern CRegister cr15; |
+ |
+ |
+// 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 |
+}; |
+ |
+ |
+// Condition field in instructions |
+enum Condition { |
+ eq = 0 << 28, // Z set equal. |
+ ne = 1 << 28, // Z clear not equal. |
+ nz = 1 << 28, // Z clear not zero. |
+ cs = 2 << 28, // C set carry set. |
+ hs = 2 << 28, // C set unsigned higher or same. |
+ cc = 3 << 28, // C clear carry clear. |
+ lo = 3 << 28, // C clear unsigned lower. |
+ mi = 4 << 28, // N set negative. |
+ pl = 5 << 28, // N clear positive or zero. |
+ vs = 6 << 28, // V set overflow. |
+ vc = 7 << 28, // V clear no overflow. |
+ hi = 8 << 28, // C set, Z clear unsigned higher. |
+ ls = 9 << 28, // C clear or Z set unsigned lower or same. |
+ ge = 10 << 28, // N == V greater or equal. |
+ lt = 11 << 28, // N != V less than. |
+ gt = 12 << 28, // Z clear, N == V greater than. |
+ le = 13 << 28, // Z set or N != V less then or equal |
+ al = 14 << 28 // always. |
+}; |
+ |
+ |
+// Returns the equivalent of !cc. |
+INLINE(Condition NegateCondition(Condition cc)); |
+ |
+ |
+// Corresponds to transposing the operands of a comparison. |
+inline Condition ReverseCondition(Condition cc) { |
+ switch (cc) { |
+ case lo: |
+ return hi; |
+ case hi: |
+ return lo; |
+ case hs: |
+ return ls; |
+ case ls: |
+ return hs; |
+ case lt: |
+ return gt; |
+ case gt: |
+ return lt; |
+ case ge: |
+ return le; |
+ case le: |
+ return ge; |
+ default: |
+ return cc; |
+ }; |
+} |
+ |
+ |
+// Branch hints are not used on the ARM. They are defined so that they can |
+// appear in shared function signatures, but will be ignored in ARM |
+// implementations. |
+enum Hint { no_hint }; |
+ |
+// Hints are not used on the arm. Negating is trivial. |
+inline Hint NegateHint(Hint ignored) { return no_hint; } |
+ |
+ |
+// ----------------------------------------------------------------------------- |
+// Addressing modes and instruction variants |
+ |
+// Shifter operand shift operation |
+enum ShiftOp { |
+ LSL = 0 << 5, |
+ LSR = 1 << 5, |
+ ASR = 2 << 5, |
+ ROR = 3 << 5, |
+ RRX = -1 |
+}; |
+ |
+ |
+// Condition code updating mode |
+enum SBit { |
+ SetCC = 1 << 20, // set condition code |
+ LeaveCC = 0 << 20 // leave condition code unchanged |
+}; |
+ |
+ |
+// Status register selection |
+enum SRegister { |
+ CPSR = 0 << 22, |
+ SPSR = 1 << 22 |
+}; |
+ |
+ |
+// Status register fields |
+enum SRegisterField { |
+ CPSR_c = CPSR | 1 << 16, |
+ CPSR_x = CPSR | 1 << 17, |
+ CPSR_s = CPSR | 1 << 18, |
+ CPSR_f = CPSR | 1 << 19, |
+ SPSR_c = SPSR | 1 << 16, |
+ SPSR_x = SPSR | 1 << 17, |
+ SPSR_s = SPSR | 1 << 18, |
+ SPSR_f = SPSR | 1 << 19 |
+}; |
+ |
+// Status register field mask (or'ed SRegisterField enum values) |
+typedef uint32_t SRegisterFieldMask; |
+ |
+ |
+// Memory operand addressing mode |
+enum AddrMode { |
+ // bit encoding P U W |
+ Offset = (8|4|0) << 21, // offset (without writeback to base) |
+ PreIndex = (8|4|1) << 21, // pre-indexed addressing with writeback |
+ PostIndex = (0|4|0) << 21, // post-indexed addressing with writeback |
+ NegOffset = (8|0|0) << 21, // negative offset (without writeback to base) |
+ NegPreIndex = (8|0|1) << 21, // negative pre-indexed with writeback |
+ NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback |
+}; |
+ |
+ |
+// Load/store multiple addressing mode |
+enum BlockAddrMode { |
+ // bit encoding P U W |
+ da = (0|0|0) << 21, // decrement after |
+ ia = (0|4|0) << 21, // increment after |
+ db = (8|0|0) << 21, // decrement before |
+ ib = (8|4|0) << 21, // increment before |
+ da_w = (0|0|1) << 21, // decrement after with writeback to base |
+ ia_w = (0|4|1) << 21, // increment after with writeback to base |
+ db_w = (8|0|1) << 21, // decrement before with writeback to base |
+ ib_w = (8|4|1) << 21 // increment before with writeback to base |
+}; |
+ |
+ |
+// Coprocessor load/store operand size |
+enum LFlag { |
+ Long = 1 << 22, // long load/store coprocessor |
+ Short = 0 << 22 // short load/store coprocessor |
+}; |
+ |
+ |
+// ----------------------------------------------------------------------------- |
+// 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::NONE)); |
+ INLINE(explicit Operand(const ExternalReference& f)); |
+ INLINE(explicit Operand(const char* s)); |
+ INLINE(explicit Operand(Object** opp)); |
+ INLINE(explicit Operand(Context** cpp)); |
+ explicit Operand(Handle<Object> 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); |
+ |
+ // rm <shift_op> rs |
+ explicit Operand(Register rm, ShiftOp shift_op, Register rs); |
+ |
+ // Return true if this is a register operand. |
+ INLINE(bool is_reg() const); |
+ |
+ Register rm() const { return rm_; } |
+ |
+ private: |
+ Register rm_; |
+ Register rs_; |
+ ShiftOp shift_op_; |
+ int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg |
+ int32_t imm32_; // valid if rm_ == no_reg |
+ 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 register ip 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); |
+ |
+ 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; |
+}; |
+ |
+// CpuFeatures keeps track of which features are supported by the target CPU. |
+// Supported features must be enabled by a Scope before use. |
+class CpuFeatures : public AllStatic { |
+ public: |
+ // Detect features of the target CPU. Set safe defaults if the serializer |
+ // is enabled (snapshots must be portable). |
+ static void Probe(); |
+ |
+ // Check whether a feature is supported by the target CPU. |
+ static bool IsSupported(CpuFeature f) { |
+ if (f == VFP3 && !FLAG_enable_vfp3) return false; |
+ return (supported_ & (1u << f)) != 0; |
+ } |
+ |
+ // Check whether a feature is currently enabled. |
+ static bool IsEnabled(CpuFeature f) { |
+ return (enabled_ & (1u << f)) != 0; |
+ } |
+ |
+ // Enable a specified feature within a scope. |
+ class Scope BASE_EMBEDDED { |
+#ifdef DEBUG |
+ public: |
+ explicit Scope(CpuFeature f) { |
+ ASSERT(CpuFeatures::IsSupported(f)); |
+ ASSERT(!Serializer::enabled() || |
+ (found_by_runtime_probing_ & (1u << f)) == 0); |
+ old_enabled_ = CpuFeatures::enabled_; |
+ CpuFeatures::enabled_ |= 1u << f; |
+ } |
+ ~Scope() { CpuFeatures::enabled_ = old_enabled_; } |
+ private: |
+ unsigned old_enabled_; |
+#else |
+ public: |
+ explicit Scope(CpuFeature f) {} |
+#endif |
+ }; |
+ |
+ private: |
+ static unsigned supported_; |
+ static unsigned enabled_; |
+ static unsigned found_by_runtime_probing_; |
+}; |
+ |
+ |
+typedef int32_t Instr; |
+ |
+ |
+extern const Instr kMovLrPc; |
+extern const Instr kLdrPCPattern; |
+ |
+ |
+class Assembler : public Malloced { |
+ 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 NULL, 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 NULL, 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(void* buffer, int buffer_size); |
+ ~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(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, bool jump_elimination_allowed); |
+ |
+ // Puts a labels target address at the given position. |
+ // The high 8 bits are set to zero. |
+ void label_at_put(Label* L, int at_offset); |
+ |
+ // Return the address in the constant pool of the code target address used by |
+ // the branch/call instruction at pc. |
+ INLINE(static Address target_address_address_at(Address pc)); |
+ |
+ // Read/Modify the code target address in the branch/call instruction at pc. |
+ INLINE(static Address target_address_at(Address pc)); |
+ INLINE(static void set_target_address_at(Address pc, Address target)); |
+ |
+ // 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 set_target_at(Address constant_pool_entry, Address target); |
+ |
+ // This sets the branch destination (which is in the constant pool on ARM). |
+ // This is for calls and branches to runtime code. |
+ inline static void set_external_target_at(Address constant_pool_entry, |
+ Address target) { |
+ set_target_at(constant_pool_entry, target); |
+ } |
+ |
+ // 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 const int kCallTargetSize = kPointerSize; |
+ static const int kExternalTargetSize = kPointerSize; |
+ |
+ // Size of an instruction. |
+ static const int kInstrSize = sizeof(Instr); |
+ |
+ // Distance between the instruction referring to the address of the call |
+ // target (ldr pc, [target addr in const pool]) and the return address |
+ static const int kCallTargetAddressOffset = kInstrSize; |
+ |
+ // Distance between start of patched return sequence and the emitted address |
+ // to jump to. |
+ static const int kPatchReturnSequenceAddressOffset = kInstrSize; |
+ |
+ // Difference between address of current opcode and value read from pc |
+ // register. |
+ static const int kPcLoadDelta = 8; |
+ |
+ static const int kJSReturnSequenceLength = 4; |
+ |
+ // --------------------------------------------------------------------------- |
+ // 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); |
+ |
+ // 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) { |
+ b(branch_offset(L, cond == al), cond); |
+ } |
+ void b(Condition cond, Label* L) { b(branch_offset(L, cond == al), cond); } |
+ void bl(Label* L, Condition cond = al) { bl(branch_offset(L, false), cond); } |
+ void bl(Condition cond, Label* L) { bl(branch_offset(L, false), cond); } |
+ void blx(Label* L) { blx(branch_offset(L, false)); } // 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) { |
+ sub(dst, src1, Operand(src2), s, cond); |
+ } |
+ |
+ 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 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) { |
+ tst(src1, Operand(src2), cond); |
+ } |
+ |
+ 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) { |
+ cmp(src1, Operand(src2), cond); |
+ } |
+ |
+ 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) { |
+ orr(dst, src1, Operand(src2), s, cond); |
+ } |
+ |
+ 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) { |
+ mov(dst, Operand(src), s, cond); |
+ } |
+ |
+ 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); |
+ |
+ // Multiply instructions |
+ |
+ void mla(Register dst, Register src1, Register src2, Register srcA, |
+ SBit s = LeaveCC, Condition cond = al); |
+ |
+ void mul(Register dst, Register src1, Register src2, |
+ SBit s = LeaveCC, 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 |
+ |
+ // 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); |
+ |
+ // 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); |
+ |
+ // Semaphore instructions |
+ void swp(Register dst, Register src, Register base, Condition cond = al); |
+ void swpb(Register dst, Register src, Register base, Condition cond = al); |
+ |
+ // Exception-generating instructions and debugging support |
+ void stop(const char* msg); |
+ |
+ void bkpt(uint32_t imm16); // v5 and above |
+ void swi(uint32_t imm24, Condition cond = al); |
+ |
+ // 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 |
+ |
+ void stc(Coprocessor coproc, CRegister crd, const MemOperand& dst, |
+ LFlag l = Short, Condition cond = al); |
+ void stc(Coprocessor coproc, CRegister crd, Register base, int option, |
+ LFlag l = Short, Condition cond = al); |
+ |
+ void stc2(Coprocessor coproc, CRegister crd, const MemOperand& dst, |
+ LFlag l = Short); // v5 and above |
+ void stc2(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 D15. |
+ // Currently these APIs do not support extended D registers, i.e, D16 to D31. |
+ // However, some simple modifications can allow |
+ // these APIs to support D16 to D31. |
+ |
+ void fmdrr(const Register dst, |
+ const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fmrrd(const Register dst1, |
+ const Register dst2, |
+ const Register src, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fmsr(const Register dst, |
+ const Register src, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fmrs(const Register dst, |
+ const Register src, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fsitod(const Register dst, |
+ const Register src, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void ftosid(const Register dst, |
+ const Register src, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ |
+ void faddd(const Register dst, |
+ const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fsubd(const Register dst, |
+ const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fmuld(const Register dst, |
+ const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fdivd(const Register dst, |
+ const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void fcmp(const Register src1, |
+ const Register src2, |
+ const SBit s = LeaveCC, |
+ const Condition cond = al); |
+ void vmrs(const Register dst, |
+ const Condition cond = al); |
+ |
+ // Pseudo instructions |
+ void nop() { mov(r0, Operand(r0)); } |
+ |
+ 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() { |
+ add(sp, sp, Operand(kPointerSize)); |
+ } |
+ |
+ // Load effective address of memory operand x into register dst |
+ void lea(Register dst, const MemOperand& x, |
+ SBit s = LeaveCC, Condition cond = al); |
+ |
+ // Jump unconditionally to given label. |
+ void jmp(Label* L) { b(L, al); } |
+ |
+ // Check the code size generated from label to here. |
+ int InstructionsGeneratedSince(Label* l) { |
+ return (pc_offset() - l->pos()) / kInstrSize; |
+ } |
+ |
+ // Check whether an immediate fits an addressing mode 1 instruction. |
+ bool ImmediateFitsAddrMode1Instruction(int32_t imm32); |
+ |
+ // Postpone the generation of the constant pool for the specified number of |
+ // instructions. |
+ void BlockConstPoolFor(int instructions); |
+ |
+ // Debugging |
+ |
+ // Mark address of the ExitJSFrame code. |
+ void RecordJSReturn(); |
+ |
+ // Record a comment relocation entry that can be used by a disassembler. |
+ // Use --debug_code to enable. |
+ void RecordComment(const char* msg); |
+ |
+ void RecordPosition(int pos); |
+ void RecordStatementPosition(int pos); |
+ void WriteRecordedPositions(); |
+ |
+ int pc_offset() const { return pc_ - buffer_; } |
+ int current_position() const { return current_position_; } |
+ int current_statement_position() const { return current_position_; } |
+ |
+ protected: |
+ int buffer_space() const { return reloc_info_writer.pos() - pc_; } |
+ |
+ // Read/patch instructions |
+ static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); } |
+ void instr_at_put(byte* pc, Instr instr) { |
+ *reinterpret_cast<Instr*>(pc) = instr; |
+ } |
+ 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; |
+ } |
+ |
+ // 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); |
+ |
+ // Check if is time to emit a constant pool for pending reloc info entries |
+ void CheckConstPool(bool force_emit, bool require_jump); |
+ |
+ // Block the emission of the constant pool before pc_offset |
+ void BlockConstPoolBefore(int pc_offset) { |
+ if (no_const_pool_before_ < pc_offset) no_const_pool_before_ = pc_offset; |
+ } |
+ |
+ private: |
+ // Code buffer: |
+ // The buffer into which code and relocation info are generated. |
+ byte* buffer_; |
+ int buffer_size_; |
+ // True if the assembler owns the buffer, false if buffer is external. |
+ bool own_buffer_; |
+ |
+ // Buffer size and constant pool distance are checked together at regular |
+ // intervals of kBufferCheckInterval emitted bytes |
+ static const int kBufferCheckInterval = 1*KB/2; |
+ int next_buffer_check_; // pc offset of next buffer check |
+ |
+ // 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 const int kGap = 32; |
+ byte* pc_; // the program counter; moves forward |
+ |
+ // 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 const int kCheckConstIntervalInst = 32; |
+ static const int kCheckConstInterval = kCheckConstIntervalInst * kInstrSize; |
+ |
+ |
+ // Pools are emitted after function return and in dead code at (more or less) |
+ // regular intervals of kDistBetweenPools bytes |
+ static const int kDistBetweenPools = 1*KB; |
+ |
+ // Constants in pools are accessed via pc relative addressing, which can |
+ // reach +/-4KB thereby defining a maximum distance between the instruction |
+ // and the accessed constant. We satisfy this constraint by limiting the |
+ // distance between pools. |
+ static const int kMaxDistBetweenPools = 4*KB - 2*kBufferCheckInterval; |
+ |
+ // Emission of the constant pool may be blocked in some code sequences |
+ int no_const_pool_before_; // block emission before this pc offset |
+ |
+ // Keep track of the last emitted pool to guarantee a maximal distance |
+ int last_const_pool_end_; // pc offset following the last constant pool |
+ |
+ // Relocation info generation |
+ // Each relocation is encoded as a variable size value |
+ static const int kMaxRelocSize = RelocInfoWriter::kMaxSize; |
+ RelocInfoWriter reloc_info_writer; |
+ // Relocation info records are also used during code generation as temporary |
+ // containers for constants and code target addresses until they are emitted |
+ // to the constant pool. These pending relocation info 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. |
+ static const int kMaxNumPRInfo = kMaxDistBetweenPools/kInstrSize; |
+ RelocInfo prinfo_[kMaxNumPRInfo]; // the buffer of pending relocation info |
+ int num_prinfo_; // number of pending reloc info entries in the buffer |
+ |
+ // The bound position, before this we cannot do instruction elimination. |
+ int last_bound_pos_; |
+ |
+ // source position information |
+ int current_position_; |
+ int current_statement_position_; |
+ int written_position_; |
+ int written_statement_position_; |
+ |
+ // Code emission |
+ inline void CheckBuffer(); |
+ void GrowBuffer(); |
+ inline void emit(Instr x); |
+ |
+ // Instruction generation |
+ void addrmod1(Instr instr, Register rn, Register rd, const Operand& x); |
+ void addrmod2(Instr instr, Register rd, const MemOperand& x); |
+ void addrmod3(Instr instr, Register rd, const MemOperand& x); |
+ void addrmod4(Instr instr, Register rn, RegList rl); |
+ void addrmod5(Instr instr, CRegister crd, const MemOperand& x); |
+ |
+ // Labels |
+ void print(Label* L); |
+ void bind_to(Label* L, int pos); |
+ void link_to(Label* L, Label* appendix); |
+ void next(Label* L); |
+ |
+ // Record reloc info for current pc_ |
+ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); |
+ |
+ friend class RegExpMacroAssemblerARM; |
+ friend class RelocInfo; |
+ friend class CodePatcher; |
+}; |
+ |
+} } // namespace v8::internal |
+ |
+#endif // V8_ARM_ASSEMBLER_THUMB2_H_ |
+ |