// 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 #include #include "src/arm/constants-arm.h" #include "src/assembler.h" #include "src/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. // These constants are used in several locations, including static initializers const int kRegister_no_reg_Code = -1; const int kRegister_r0_Code = 0; const int kRegister_r1_Code = 1; const int kRegister_r2_Code = 2; const int kRegister_r3_Code = 3; const int kRegister_r4_Code = 4; const int kRegister_r5_Code = 5; const int kRegister_r6_Code = 6; const int kRegister_r7_Code = 7; const int kRegister_r8_Code = 8; const int kRegister_r9_Code = 9; const int kRegister_r10_Code = 10; const int kRegister_fp_Code = 11; const int kRegister_ip_Code = 12; const int kRegister_sp_Code = 13; const int kRegister_lr_Code = 14; const int kRegister_pc_Code = 15; // Core register struct Register { static const int kNumRegisters = 16; static const int kMaxNumAllocatableRegisters = FLAG_enable_ool_constant_pool ? 8 : 9; static const int kSizeInBytes = 4; inline static int NumAllocatableRegisters(); static int ToAllocationIndex(Register reg) { DCHECK(reg.code() < kMaxNumAllocatableRegisters); return reg.code(); } static Register FromAllocationIndex(int index) { DCHECK(index >= 0 && index < kMaxNumAllocatableRegisters); return from_code(index); } static const char* AllocationIndexToString(int index) { DCHECK(index >= 0 && index < kMaxNumAllocatableRegisters); const char* const names[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", }; if (FLAG_enable_ool_constant_pool && (index >= 7)) { return names[index + 1]; } return names[index]; } static Register from_code(int code) { Register r = { code }; return r; } bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; } bool is(Register reg) const { return code_ == reg.code_; } int code() const { DCHECK(is_valid()); return code_; } int bit() const { DCHECK(is_valid()); return 1 << code_; } void set_code(int code) { code_ = code; DCHECK(is_valid()); } // Unfortunately we can't make this private in a struct. int code_; }; const Register no_reg = { kRegister_no_reg_Code }; const Register r0 = { kRegister_r0_Code }; const Register r1 = { kRegister_r1_Code }; const Register r2 = { kRegister_r2_Code }; const Register r3 = { kRegister_r3_Code }; const Register r4 = { kRegister_r4_Code }; const Register r5 = { kRegister_r5_Code }; const Register r6 = { kRegister_r6_Code }; // Used as constant pool pointer register if FLAG_enable_ool_constant_pool. const Register r7 = { kRegister_r7_Code }; // Used as context register. const Register r8 = { kRegister_r8_Code }; // Used as lithium codegen scratch register. const Register r9 = { kRegister_r9_Code }; // Used as roots register. const Register r10 = { kRegister_r10_Code }; const Register fp = { kRegister_fp_Code }; const Register ip = { kRegister_ip_Code }; const Register sp = { kRegister_sp_Code }; const Register lr = { kRegister_lr_Code }; const Register pc = { kRegister_pc_Code }; // Single word VFP register. struct SwVfpRegister { static const int kSizeInBytes = 4; bool is_valid() const { return 0 <= code_ && code_ < 32; } bool is(SwVfpRegister reg) const { return code_ == reg.code_; } int code() const { DCHECK(is_valid()); return code_; } int bit() const { DCHECK(is_valid()); return 1 << code_; } void split_code(int* vm, int* m) const { DCHECK(is_valid()); *m = code_ & 0x1; *vm = code_ >> 1; } int code_; }; // Double word VFP register. struct DwVfpRegister { static const int kMaxNumRegisters = 32; // 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 const int kNumReservedRegisters = 2; static const int kMaxNumAllocatableRegisters = kMaxNumRegisters - kNumReservedRegisters; static const int kSizeInBytes = 8; // Note: the number of registers can be different at snapshot and run-time. // Any code included in the snapshot must be able to run both with 16 or 32 // registers. inline static int NumRegisters(); inline static int NumReservedRegisters(); inline static int NumAllocatableRegisters(); inline static int ToAllocationIndex(DwVfpRegister reg); static const char* AllocationIndexToString(int index); inline static DwVfpRegister FromAllocationIndex(int index); static DwVfpRegister from_code(int code) { DwVfpRegister r = { code }; return r; } bool is_valid() const { return 0 <= code_ && code_ < kMaxNumRegisters; } bool is(DwVfpRegister reg) const { return code_ == reg.code_; } int code() const { DCHECK(is_valid()); return code_; } int bit() const { DCHECK(is_valid()); return 1 << code_; } void split_code(int* vm, int* m) const { DCHECK(is_valid()); *m = (code_ & 0x10) >> 4; *vm = code_ & 0x0F; } int code_; }; typedef DwVfpRegister DoubleRegister; // Double word VFP register d0-15. struct LowDwVfpRegister { public: static const int kMaxNumLowRegisters = 16; operator DwVfpRegister() const { DwVfpRegister r = { code_ }; return r; } static LowDwVfpRegister from_code(int code) { LowDwVfpRegister r = { code }; return r; } bool is_valid() const { return 0 <= code_ && code_ < kMaxNumLowRegisters; } bool is(DwVfpRegister reg) const { return code_ == reg.code_; } bool is(LowDwVfpRegister reg) const { return code_ == reg.code_; } int code() const { DCHECK(is_valid()); return code_; } SwVfpRegister low() const { SwVfpRegister reg; reg.code_ = code_ * 2; DCHECK(reg.is_valid()); return reg; } SwVfpRegister high() const { SwVfpRegister reg; reg.code_ = (code_ * 2) + 1; DCHECK(reg.is_valid()); return reg; } int code_; }; // Quad word NEON register. struct QwNeonRegister { static const int kMaxNumRegisters = 16; static QwNeonRegister from_code(int code) { QwNeonRegister r = { code }; return r; } bool is_valid() const { return (0 <= code_) && (code_ < kMaxNumRegisters); } bool is(QwNeonRegister reg) const { return code_ == reg.code_; } int code() const { DCHECK(is_valid()); return code_; } void split_code(int* vm, int* m) const { DCHECK(is_valid()); int encoded_code = code_ << 1; *m = (encoded_code & 0x10) >> 4; *vm = encoded_code & 0x0F; } int code_; }; typedef QwNeonRegister QuadRegister; // 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)". const SwVfpRegister s0 = { 0 }; const SwVfpRegister s1 = { 1 }; const SwVfpRegister s2 = { 2 }; const SwVfpRegister s3 = { 3 }; const SwVfpRegister s4 = { 4 }; const SwVfpRegister s5 = { 5 }; const SwVfpRegister s6 = { 6 }; const SwVfpRegister s7 = { 7 }; const SwVfpRegister s8 = { 8 }; const SwVfpRegister s9 = { 9 }; const SwVfpRegister s10 = { 10 }; const SwVfpRegister s11 = { 11 }; const SwVfpRegister s12 = { 12 }; const SwVfpRegister s13 = { 13 }; const SwVfpRegister s14 = { 14 }; const SwVfpRegister s15 = { 15 }; const SwVfpRegister s16 = { 16 }; const SwVfpRegister s17 = { 17 }; const SwVfpRegister s18 = { 18 }; const SwVfpRegister s19 = { 19 }; const SwVfpRegister s20 = { 20 }; const SwVfpRegister s21 = { 21 }; const SwVfpRegister s22 = { 22 }; const SwVfpRegister s23 = { 23 }; const SwVfpRegister s24 = { 24 }; const SwVfpRegister s25 = { 25 }; const SwVfpRegister s26 = { 26 }; const SwVfpRegister s27 = { 27 }; const SwVfpRegister s28 = { 28 }; const SwVfpRegister s29 = { 29 }; const SwVfpRegister s30 = { 30 }; const SwVfpRegister s31 = { 31 }; const DwVfpRegister no_dreg = { -1 }; const LowDwVfpRegister d0 = { 0 }; const LowDwVfpRegister d1 = { 1 }; const LowDwVfpRegister d2 = { 2 }; const LowDwVfpRegister d3 = { 3 }; const LowDwVfpRegister d4 = { 4 }; const LowDwVfpRegister d5 = { 5 }; const LowDwVfpRegister d6 = { 6 }; const LowDwVfpRegister d7 = { 7 }; const LowDwVfpRegister d8 = { 8 }; const LowDwVfpRegister d9 = { 9 }; const LowDwVfpRegister d10 = { 10 }; const LowDwVfpRegister d11 = { 11 }; const LowDwVfpRegister d12 = { 12 }; const LowDwVfpRegister d13 = { 13 }; const LowDwVfpRegister d14 = { 14 }; const LowDwVfpRegister d15 = { 15 }; const DwVfpRegister d16 = { 16 }; const DwVfpRegister d17 = { 17 }; const DwVfpRegister d18 = { 18 }; const DwVfpRegister d19 = { 19 }; const DwVfpRegister d20 = { 20 }; const DwVfpRegister d21 = { 21 }; const DwVfpRegister d22 = { 22 }; const DwVfpRegister d23 = { 23 }; const DwVfpRegister d24 = { 24 }; const DwVfpRegister d25 = { 25 }; const DwVfpRegister d26 = { 26 }; const DwVfpRegister d27 = { 27 }; const DwVfpRegister d28 = { 28 }; const DwVfpRegister d29 = { 29 }; const DwVfpRegister d30 = { 30 }; const DwVfpRegister d31 = { 31 }; const QwNeonRegister q0 = { 0 }; const QwNeonRegister q1 = { 1 }; const QwNeonRegister q2 = { 2 }; const QwNeonRegister q3 = { 3 }; const QwNeonRegister q4 = { 4 }; const QwNeonRegister q5 = { 5 }; const QwNeonRegister q6 = { 6 }; const QwNeonRegister q7 = { 7 }; const QwNeonRegister q8 = { 8 }; const QwNeonRegister q9 = { 9 }; const QwNeonRegister q10 = { 10 }; const QwNeonRegister q11 = { 11 }; const QwNeonRegister q12 = { 12 }; const QwNeonRegister q13 = { 13 }; const QwNeonRegister q14 = { 14 }; const QwNeonRegister q15 = { 15 }; // Aliases for double registers. Defined using #define instead of // "static const DwVfpRegister&" because Clang complains otherwise when a // compilation unit that includes this header doesn't use the variables. #define kFirstCalleeSavedDoubleReg d8 #define kLastCalleeSavedDoubleReg d15 #define kDoubleRegZero d14 #define kScratchDoubleReg 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 { DCHECK(is_valid()); return code_; } int bit() const { DCHECK(is_valid()); return 1 << code_; } // Unfortunately we can't make this private in a struct. int code_; }; const CRegister no_creg = { -1 }; const CRegister cr0 = { 0 }; const CRegister cr1 = { 1 }; const CRegister cr2 = { 2 }; const CRegister cr3 = { 3 }; const CRegister cr4 = { 4 }; const CRegister cr5 = { 5 }; const CRegister cr6 = { 6 }; const CRegister cr7 = { 7 }; const CRegister cr8 = { 8 }; const CRegister cr9 = { 9 }; const CRegister cr10 = { 10 }; const CRegister cr11 = { 11 }; const CRegister cr12 = { 12 }; const CRegister cr13 = { 13 }; const CRegister cr14 = { 14 }; const CRegister cr15 = { 15 }; // 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()) { return Operand(static_cast(0)); } INLINE(explicit Operand(const ExternalReference& f)); explicit Operand(Handle handle); INLINE(explicit Operand(Smi* value)); // rm INLINE(explicit Operand(Register rm)); // rm 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 rs explicit Operand(Register rm, ShiftOp shift_op, Register rs); // Return true if this is a register operand. INLINE(bool is_reg() const); // 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 the ip 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 instructions_required(const Assembler* assembler, Instr instr = 0) const; bool must_output_reloc_info(const Assembler* assembler) const; inline int32_t immediate() const { DCHECK(!rm_.is_valid()); return imm32_; } Register rm() const { return rm_; } Register rs() const { return rs_; } ShiftOp shift_op() const { return shift_op_; } 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_imm] Offset/NegOffset // [rn +/- rm shift_imm]! PreIndex/NegPreIndex // [rn], +/- rm 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_.is(no_reg)); offset_ = offset; } uint32_t offset() const { DCHECK(rm_.is(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 registers_count = 1); DoubleRegister base() const { return base_; } NeonListType type() const { return type_; } private: DoubleRegister base_; NeonListType type_; }; // Class used to build a constant pool. class ConstantPoolBuilder BASE_EMBEDDED { public: ConstantPoolBuilder(); ConstantPoolArray::LayoutSection AddEntry(Assembler* assm, const RelocInfo& rinfo); void Relocate(int pc_delta); bool IsEmpty(); Handle New(Isolate* isolate); void Populate(Assembler* assm, ConstantPoolArray* constant_pool); inline ConstantPoolArray::LayoutSection current_section() const { return current_section_; } inline ConstantPoolArray::NumberOfEntries* number_of_entries( ConstantPoolArray::LayoutSection section) { return &number_of_entries_[section]; } inline ConstantPoolArray::NumberOfEntries* small_entries() { return number_of_entries(ConstantPoolArray::SMALL_SECTION); } inline ConstantPoolArray::NumberOfEntries* extended_entries() { return number_of_entries(ConstantPoolArray::EXTENDED_SECTION); } private: struct ConstantPoolEntry { ConstantPoolEntry(RelocInfo rinfo, ConstantPoolArray::LayoutSection section, int merged_index) : rinfo_(rinfo), section_(section), merged_index_(merged_index) {} RelocInfo rinfo_; ConstantPoolArray::LayoutSection section_; int merged_index_; }; ConstantPoolArray::Type GetConstantPoolType(RelocInfo::Mode rmode); std::vector entries_; ConstantPoolArray::LayoutSection current_section_; ConstantPoolArray::NumberOfEntries number_of_entries_[2]; }; struct VmovIndex { unsigned char index; }; const VmovIndex VmovIndexLo = { 0 }; const 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 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(Isolate* isolate, 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(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); // 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, ConstantPoolArray* constant_pool)); // Read/Modify the code target address in the branch/call instruction at pc. INLINE(static Address target_address_at(Address pc, ConstantPoolArray* constant_pool)); INLINE(static void set_target_address_at(Address pc, ConstantPoolArray* constant_pool, Address target, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED)); INLINE(static Address target_address_at(Address pc, Code* code)) { ConstantPoolArray* constant_pool = code ? code->constant_pool() : NULL; return target_address_at(pc, constant_pool); } INLINE(static void set_target_address_at(Address pc, Code* code, Address target, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED)) { ConstantPoolArray* constant_pool = code ? code->constant_pool() : NULL; set_target_address_at(pc, constant_pool, target, icache_flush_mode); } // 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)); // Return the code target address of the patch debug break slot INLINE(static Address break_address_from_return_address(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( Address constant_pool_entry, Code* code, Address 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 kSpecialTargetSize = kPointerSize; // Size of an instruction. static const int kInstrSize = sizeof(Instr); // Distance between start of patched return sequence and the emitted address // to jump to. // Patched return sequence is: // ldr ip, [pc, #0] @ emited address and start // blx ip static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize; // Distance between start of patched debug break slot and the emitted address // to jump to. // Patched debug break slot code is: // ldr ip, [pc, #0] @ emited address and start // blx ip static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize; static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstrSize; // Difference between address of current opcode and value read from pc // register. static const int kPcLoadDelta = 8; static const int kJSReturnSequenceInstructions = 4; static const int kDebugBreakSlotInstructions = 3; static const int kDebugBreakSlotLength = kDebugBreakSlotInstructions * kInstrSize; // --------------------------------------------------------------------------- // 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); // 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) { 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 add(Register dst, Register src1, Register src2, SBit s = LeaveCC, Condition cond = al) { add(dst, src1, Operand(src2), s, cond); } 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 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) { 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); } // 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) { if (src2.is_reg()) { mov(dst, Operand(src1, ASR, src2.rm()), s, cond); } else { mov(dst, Operand(src1, ASR, src2.immediate()), s, cond); } } void lsl(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, Condition cond = al) { if (src2.is_reg()) { mov(dst, Operand(src1, LSL, src2.rm()), s, cond); } else { mov(dst, Operand(src1, LSL, src2.immediate()), s, cond); } } void lsr(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, Condition cond = al) { if (src2.is_reg()) { mov(dst, Operand(src1, LSR, src2.rm()), s, cond); } else { mov(dst, Operand(src1, LSR, src2.immediate()), s, cond); } } // 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 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 uxtb(Register dst, const Operand& src, Condition cond = al); void uxtab(Register dst, Register src1, const Operand& src2, Condition cond = al); void uxtb16(Register dst, const Operand& 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); // 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); // 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 DwVfpRegister dst, double imm, const Register 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); 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_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 vneg(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond = al); void vabs(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond = al); void vadd(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vsub(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vmul(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vmla(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vmls(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vdiv(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vcmp(const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void vcmp(const DwVfpRegister src1, const double src2, const Condition cond = al); void vmrs(const Register dst, const Condition cond = al); void vmsr(const Register dst, const Condition cond = al); void vsqrt(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); void vmovl(NeonDataType dt, QwNeonRegister dst, DwVfpRegister src); // 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() { add(sp, sp, Operand(kPointerSize)); } // 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); }; // Debugging // Mark address of the ExitJSFrame code. void RecordJSReturn(); // Mark address of a debug break slot. void RecordDebugBreakSlot(); // Record the AST id of the CallIC being compiled, so that it can be placed // in the relocation information. void SetRecordedAstId(TypeFeedbackId ast_id) { DCHECK(recorded_ast_id_.IsNone()); recorded_ast_id_ = ast_id; } TypeFeedbackId RecordedAstId() { DCHECK(!recorded_ast_id_.IsNone()); return recorded_ast_id_; } void ClearRecordedAstId() { recorded_ast_id_ = TypeFeedbackId::None(); } // Record a comment relocation entry that can be used by a disassembler. // Use --code-comments to enable. void RecordComment(const char* msg); // 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. The constant pool should be // emitted before any use of db and dd to ensure that constant pools // are not emitted as part of the tables generated. void db(uint8_t data); void dd(uint32_t data); // Emits the address of the code stub's first instruction. void emit_code_stub_address(Code* stub); PositionsRecorder* positions_recorder() { return &positions_recorder_; } // Read/patch instructions Instr instr_at(int pos) { return *reinterpret_cast(buffer_ + pos); } void instr_at_put(int pos, Instr instr) { *reinterpret_cast(buffer_ + pos) = instr; } static Instr instr_at(byte* pc) { return *reinterpret_cast(pc); } static void instr_at_put(byte* pc, Instr instr) { *reinterpret_cast(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 Instr GetConsantPoolLoadPattern(); static Instr GetConsantPoolLoadMask(); static bool IsLdrPpRegOffset(Instr instr); static Instr GetLdrPpRegOffsetPattern(); static bool IsLdrPpImmediateOffset(Instr instr); static bool IsVldrDPpImmediateOffset(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 const int kMaxDistToIntPool = 4*KB; static const int kMaxDistToFPPool = 1*KB; // All relocations could be integer, it therefore acts as the limit. static const int kMaxNumPending32RelocInfo = kMaxDistToIntPool/kInstrSize; static const int kMaxNumPending64RelocInfo = 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); // Allocate a constant pool of the correct size for the generated code. Handle NewConstantPool(Isolate* isolate); // Generate the constant pool for the generated code. void PopulateConstantPool(ConstantPoolArray* constant_pool); bool is_constant_pool_available() const { return constant_pool_available_; } bool use_extended_constant_pool() const { return constant_pool_builder_.current_section() == ConstantPoolArray::EXTENDED_SECTION; } protected: // Relocation for a type-recording IC has the AST id added to it. This // member variable is a way to pass the information from the call site to // the relocation info. TypeFeedbackId recorded_ast_id_; 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 contant pool emission until EndBlockConstPool is called. // Call 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. Need to be called as many time 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((num_pending_32_bit_reloc_info_ == 0) || (start + num_pending_64_bit_reloc_info_ * kDoubleSize < (first_const_pool_32_use_ + kMaxDistToIntPool))); DCHECK((num_pending_64_bit_reloc_info_ == 0) || (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_); } void set_constant_pool_available(bool available) { constant_pool_available_ = available; } private: 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; // 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 kCheckPoolIntervalInst = 32; static const int kCheckPoolInterval = kCheckPoolIntervalInst * kInstrSize; // 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_; // 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. // The buffers of pending relocation info. RelocInfo pending_32_bit_reloc_info_[kMaxNumPending32RelocInfo]; RelocInfo pending_64_bit_reloc_info_[kMaxNumPending64RelocInfo]; // Number of pending reloc info entries in the 32 bits buffer. int num_pending_32_bit_reloc_info_; // Number of pending reloc info entries in the 64 bits buffer. int num_pending_64_bit_reloc_info_; ConstantPoolBuilder constant_pool_builder_; // The bound position, before this we cannot do instruction elimination. int last_bound_pos_; // Indicates whether the constant pool can be accessed, which is only possible // if the pp register points to the current code object's constant pool. bool constant_pool_available_; // Code emission inline void CheckBuffer(); void GrowBuffer(); inline void emit(Instr x); // 32-bit immediate values void move_32_bit_immediate(Register rd, const Operand& x, Condition cond = al); // 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 next(Label* L); enum UseConstantPoolMode { USE_CONSTANT_POOL, DONT_USE_CONSTANT_POOL }; // Record reloc info for current pc_ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); void RecordRelocInfo(const RelocInfo& rinfo); ConstantPoolArray::LayoutSection ConstantPoolAddEntry(const RelocInfo& rinfo); friend class RelocInfo; friend class CodePatcher; friend class BlockConstPoolScope; friend class FrameAndConstantPoolScope; friend class ConstantPoolUnavailableScope; PositionsRecorder positions_recorder_; friend class PositionsRecorder; friend class EnsureSpace; }; class EnsureSpace BASE_EMBEDDED { public: explicit EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); } }; } } // namespace v8::internal #endif // V8_ARM_ASSEMBLER_ARM_H_