blob: 9998e638badb8e6849a0467447448e0e9aa441ee [file] [log] [blame]
// 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;
// Single word VFP register.
struct SwVfpRegister {
bool is_valid() const { return 0 <= code_ && code_ < 32; }
bool is(SwVfpRegister reg) const { return code_ == reg.code_; }
int code() const {
ASSERT(is_valid());
return code_;
}
int bit() const {
ASSERT(is_valid());
return 1 << code_;
}
int code_;
};
// Double word VFP register.
struct DwVfpRegister {
// Supporting d0 to d15, can be later extended to d31.
bool is_valid() const { return 0 <= code_ && code_ < 16; }
bool is(DwVfpRegister reg) const { return code_ == reg.code_; }
int code() const {
ASSERT(is_valid());
return code_;
}
int bit() const {
ASSERT(is_valid());
return 1 << code_;
}
int code_;
};
// Support for VFP registers s0 to s31 (d0 to d15).
// Note that "s(N):s(N+1)" is the same as "d(N/2)".
extern SwVfpRegister s0;
extern SwVfpRegister s1;
extern SwVfpRegister s2;
extern SwVfpRegister s3;
extern SwVfpRegister s4;
extern SwVfpRegister s5;
extern SwVfpRegister s6;
extern SwVfpRegister s7;
extern SwVfpRegister s8;
extern SwVfpRegister s9;
extern SwVfpRegister s10;
extern SwVfpRegister s11;
extern SwVfpRegister s12;
extern SwVfpRegister s13;
extern SwVfpRegister s14;
extern SwVfpRegister s15;
extern SwVfpRegister s16;
extern SwVfpRegister s17;
extern SwVfpRegister s18;
extern SwVfpRegister s19;
extern SwVfpRegister s20;
extern SwVfpRegister s21;
extern SwVfpRegister s22;
extern SwVfpRegister s23;
extern SwVfpRegister s24;
extern SwVfpRegister s25;
extern SwVfpRegister s26;
extern SwVfpRegister s27;
extern SwVfpRegister s28;
extern SwVfpRegister s29;
extern SwVfpRegister s30;
extern SwVfpRegister s31;
extern DwVfpRegister d0;
extern DwVfpRegister d1;
extern DwVfpRegister d2;
extern DwVfpRegister d3;
extern DwVfpRegister d4;
extern DwVfpRegister d5;
extern DwVfpRegister d6;
extern DwVfpRegister d7;
extern DwVfpRegister d8;
extern DwVfpRegister d9;
extern DwVfpRegister d10;
extern DwVfpRegister d11;
extern DwVfpRegister d12;
extern DwVfpRegister d13;
extern DwVfpRegister d14;
extern DwVfpRegister 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 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(const DwVfpRegister dst,
const SwVfpRegister src,
const Condition cond = al);
void vcvt(const SwVfpRegister 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 vdiv(const DwVfpRegister dst,
const DwVfpRegister src1,
const DwVfpRegister src2,
const Condition cond = al);
void vcmp(const DwVfpRegister src1,
const DwVfpRegister 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_