1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/arm/Assembler-arm.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2264 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jit_arm_Assembler_arm_h 1.11 +#define jit_arm_Assembler_arm_h 1.12 + 1.13 +#include "mozilla/ArrayUtils.h" 1.14 +#include "mozilla/Attributes.h" 1.15 +#include "mozilla/MathAlgorithms.h" 1.16 + 1.17 +#include "assembler/assembler/AssemblerBufferWithConstantPool.h" 1.18 +#include "jit/arm/Architecture-arm.h" 1.19 +#include "jit/CompactBuffer.h" 1.20 +#include "jit/IonCode.h" 1.21 +#include "jit/shared/Assembler-shared.h" 1.22 +#include "jit/shared/IonAssemblerBufferWithConstantPools.h" 1.23 + 1.24 +namespace js { 1.25 +namespace jit { 1.26 + 1.27 +//NOTE: there are duplicates in this list! 1.28 +// sometimes we want to specifically refer to the 1.29 +// link register as a link register (bl lr is much 1.30 +// clearer than bl r14). HOWEVER, this register can 1.31 +// easily be a gpr when it is not busy holding the return 1.32 +// address. 1.33 +static MOZ_CONSTEXPR_VAR Register r0 = { Registers::r0 }; 1.34 +static MOZ_CONSTEXPR_VAR Register r1 = { Registers::r1 }; 1.35 +static MOZ_CONSTEXPR_VAR Register r2 = { Registers::r2 }; 1.36 +static MOZ_CONSTEXPR_VAR Register r3 = { Registers::r3 }; 1.37 +static MOZ_CONSTEXPR_VAR Register r4 = { Registers::r4 }; 1.38 +static MOZ_CONSTEXPR_VAR Register r5 = { Registers::r5 }; 1.39 +static MOZ_CONSTEXPR_VAR Register r6 = { Registers::r6 }; 1.40 +static MOZ_CONSTEXPR_VAR Register r7 = { Registers::r7 }; 1.41 +static MOZ_CONSTEXPR_VAR Register r8 = { Registers::r8 }; 1.42 +static MOZ_CONSTEXPR_VAR Register r9 = { Registers::r9 }; 1.43 +static MOZ_CONSTEXPR_VAR Register r10 = { Registers::r10 }; 1.44 +static MOZ_CONSTEXPR_VAR Register r11 = { Registers::r11 }; 1.45 +static MOZ_CONSTEXPR_VAR Register r12 = { Registers::ip }; 1.46 +static MOZ_CONSTEXPR_VAR Register ip = { Registers::ip }; 1.47 +static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp }; 1.48 +static MOZ_CONSTEXPR_VAR Register r14 = { Registers::lr }; 1.49 +static MOZ_CONSTEXPR_VAR Register lr = { Registers::lr }; 1.50 +static MOZ_CONSTEXPR_VAR Register pc = { Registers::pc }; 1.51 + 1.52 +static MOZ_CONSTEXPR_VAR Register ScratchRegister = {Registers::ip}; 1.53 + 1.54 +static MOZ_CONSTEXPR_VAR Register OsrFrameReg = r3; 1.55 +static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = r8; 1.56 +static MOZ_CONSTEXPR_VAR Register CallTempReg0 = r5; 1.57 +static MOZ_CONSTEXPR_VAR Register CallTempReg1 = r6; 1.58 +static MOZ_CONSTEXPR_VAR Register CallTempReg2 = r7; 1.59 +static MOZ_CONSTEXPR_VAR Register CallTempReg3 = r8; 1.60 +static MOZ_CONSTEXPR_VAR Register CallTempReg4 = r0; 1.61 +static MOZ_CONSTEXPR_VAR Register CallTempReg5 = r1; 1.62 + 1.63 +static MOZ_CONSTEXPR_VAR Register IntArgReg0 = r0; 1.64 +static MOZ_CONSTEXPR_VAR Register IntArgReg1 = r1; 1.65 +static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r2; 1.66 +static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r3; 1.67 +static MOZ_CONSTEXPR_VAR Register GlobalReg = r10; 1.68 +static MOZ_CONSTEXPR_VAR Register HeapReg = r11; 1.69 +static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { r5, r6, r7, r8 }; 1.70 +static const uint32_t NumCallTempNonArgRegs = 1.71 + mozilla::ArrayLength(CallTempNonArgRegs); 1.72 +class ABIArgGenerator 1.73 +{ 1.74 + unsigned intRegIndex_; 1.75 + unsigned floatRegIndex_; 1.76 + uint32_t stackOffset_; 1.77 + ABIArg current_; 1.78 + 1.79 + public: 1.80 + ABIArgGenerator(); 1.81 + ABIArg next(MIRType argType); 1.82 + ABIArg ¤t() { return current_; } 1.83 + uint32_t stackBytesConsumedSoFar() const { return stackOffset_; } 1.84 + static const Register NonArgReturnVolatileReg0; 1.85 + static const Register NonArgReturnVolatileReg1; 1.86 +}; 1.87 + 1.88 +static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r1; 1.89 + 1.90 +static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg }; 1.91 +static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg }; 1.92 + 1.93 +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r3; 1.94 +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = r2; 1.95 +static MOZ_CONSTEXPR_VAR Register StackPointer = sp; 1.96 +static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg; 1.97 +static MOZ_CONSTEXPR_VAR Register ReturnReg = r0; 1.98 +static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::d0 }; 1.99 +static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::d15 }; 1.100 + 1.101 +static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::d14 }; 1.102 + 1.103 +// Registers used in the GenerateFFIIonExit Enable Activation block. 1.104 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4; 1.105 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0; 1.106 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1; 1.107 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2; 1.108 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3; 1.109 + 1.110 +// Registers used in the GenerateFFIIonExit Disable Activation block. 1.111 +// None of these may be the second scratch register (lr). 1.112 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2; 1.113 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3; 1.114 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0; 1.115 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1; 1.116 +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4; 1.117 + 1.118 + 1.119 +static MOZ_CONSTEXPR_VAR FloatRegister d0 = {FloatRegisters::d0}; 1.120 +static MOZ_CONSTEXPR_VAR FloatRegister d1 = {FloatRegisters::d1}; 1.121 +static MOZ_CONSTEXPR_VAR FloatRegister d2 = {FloatRegisters::d2}; 1.122 +static MOZ_CONSTEXPR_VAR FloatRegister d3 = {FloatRegisters::d3}; 1.123 +static MOZ_CONSTEXPR_VAR FloatRegister d4 = {FloatRegisters::d4}; 1.124 +static MOZ_CONSTEXPR_VAR FloatRegister d5 = {FloatRegisters::d5}; 1.125 +static MOZ_CONSTEXPR_VAR FloatRegister d6 = {FloatRegisters::d6}; 1.126 +static MOZ_CONSTEXPR_VAR FloatRegister d7 = {FloatRegisters::d7}; 1.127 +static MOZ_CONSTEXPR_VAR FloatRegister d8 = {FloatRegisters::d8}; 1.128 +static MOZ_CONSTEXPR_VAR FloatRegister d9 = {FloatRegisters::d9}; 1.129 +static MOZ_CONSTEXPR_VAR FloatRegister d10 = {FloatRegisters::d10}; 1.130 +static MOZ_CONSTEXPR_VAR FloatRegister d11 = {FloatRegisters::d11}; 1.131 +static MOZ_CONSTEXPR_VAR FloatRegister d12 = {FloatRegisters::d12}; 1.132 +static MOZ_CONSTEXPR_VAR FloatRegister d13 = {FloatRegisters::d13}; 1.133 +static MOZ_CONSTEXPR_VAR FloatRegister d14 = {FloatRegisters::d14}; 1.134 +static MOZ_CONSTEXPR_VAR FloatRegister d15 = {FloatRegisters::d15}; 1.135 + 1.136 +// For maximal awesomeness, 8 should be sufficent. 1.137 +// ldrd/strd (dual-register load/store) operate in a single cycle 1.138 +// when the address they are dealing with is 8 byte aligned. 1.139 +// Also, the ARM abi wants the stack to be 8 byte aligned at 1.140 +// function boundaries. I'm trying to make sure this is always true. 1.141 +static const uint32_t StackAlignment = 8; 1.142 +static const uint32_t CodeAlignment = 8; 1.143 +static const bool StackKeptAligned = true; 1.144 +static const uint32_t NativeFrameSize = sizeof(void*); 1.145 +static const uint32_t AlignmentAtPrologue = 0; 1.146 +static const uint32_t AlignmentMidPrologue = 4; 1.147 + 1.148 + 1.149 +static const Scale ScalePointer = TimesFour; 1.150 + 1.151 +class Instruction; 1.152 +class InstBranchImm; 1.153 +uint32_t RM(Register r); 1.154 +uint32_t RS(Register r); 1.155 +uint32_t RD(Register r); 1.156 +uint32_t RT(Register r); 1.157 +uint32_t RN(Register r); 1.158 + 1.159 +uint32_t maybeRD(Register r); 1.160 +uint32_t maybeRT(Register r); 1.161 +uint32_t maybeRN(Register r); 1.162 + 1.163 +Register toRN (Instruction &i); 1.164 +Register toRM (Instruction &i); 1.165 +Register toRD (Instruction &i); 1.166 +Register toR (Instruction &i); 1.167 + 1.168 +class VFPRegister; 1.169 +uint32_t VD(VFPRegister vr); 1.170 +uint32_t VN(VFPRegister vr); 1.171 +uint32_t VM(VFPRegister vr); 1.172 + 1.173 +class VFPRegister 1.174 +{ 1.175 + public: 1.176 + // What type of data is being stored in this register? 1.177 + // UInt / Int are specifically for vcvt, where we need 1.178 + // to know how the data is supposed to be converted. 1.179 + enum RegType { 1.180 + Double = 0x0, 1.181 + Single = 0x1, 1.182 + UInt = 0x2, 1.183 + Int = 0x3 1.184 + }; 1.185 + 1.186 + protected: 1.187 + RegType kind : 2; 1.188 + // ARM doesn't have more than 32 registers... 1.189 + // don't take more bits than we'll need. 1.190 + // Presently, I don't have plans to address the upper 1.191 + // and lower halves of the double registers seprately, so 1.192 + // 5 bits should suffice. If I do decide to address them seprately 1.193 + // (vmov, I'm looking at you), I will likely specify it as a separate 1.194 + // field. 1.195 + uint32_t _code : 5; 1.196 + bool _isInvalid : 1; 1.197 + bool _isMissing : 1; 1.198 + 1.199 + VFPRegister(int r, RegType k) 1.200 + : kind(k), _code (r), _isInvalid(false), _isMissing(false) 1.201 + { } 1.202 + 1.203 + public: 1.204 + VFPRegister() 1.205 + : _isInvalid(true), _isMissing(false) 1.206 + { } 1.207 + 1.208 + VFPRegister(bool b) 1.209 + : _isInvalid(false), _isMissing(b) 1.210 + { } 1.211 + 1.212 + VFPRegister(FloatRegister fr) 1.213 + : kind(Double), _code(fr.code()), _isInvalid(false), _isMissing(false) 1.214 + { 1.215 + JS_ASSERT(_code == (unsigned)fr.code()); 1.216 + } 1.217 + 1.218 + VFPRegister(FloatRegister fr, RegType k) 1.219 + : kind(k), _code (fr.code()), _isInvalid(false), _isMissing(false) 1.220 + { 1.221 + JS_ASSERT(_code == (unsigned)fr.code()); 1.222 + } 1.223 + bool isDouble() const { return kind == Double; } 1.224 + bool isSingle() const { return kind == Single; } 1.225 + bool isFloat() const { return (kind == Double) || (kind == Single); } 1.226 + bool isInt() const { return (kind == UInt) || (kind == Int); } 1.227 + bool isSInt() const { return kind == Int; } 1.228 + bool isUInt() const { return kind == UInt; } 1.229 + bool equiv(VFPRegister other) const { return other.kind == kind; } 1.230 + size_t size() const { return (kind == Double) ? 8 : 4; } 1.231 + bool isInvalid(); 1.232 + bool isMissing(); 1.233 + 1.234 + VFPRegister doubleOverlay() const; 1.235 + VFPRegister singleOverlay() const; 1.236 + VFPRegister sintOverlay() const; 1.237 + VFPRegister uintOverlay() const; 1.238 + 1.239 + struct VFPRegIndexSplit; 1.240 + VFPRegIndexSplit encode(); 1.241 + 1.242 + // for serializing values 1.243 + struct VFPRegIndexSplit { 1.244 + const uint32_t block : 4; 1.245 + const uint32_t bit : 1; 1.246 + 1.247 + private: 1.248 + friend VFPRegIndexSplit js::jit::VFPRegister::encode(); 1.249 + 1.250 + VFPRegIndexSplit (uint32_t block_, uint32_t bit_) 1.251 + : block(block_), bit(bit_) 1.252 + { 1.253 + JS_ASSERT (block == block_); 1.254 + JS_ASSERT(bit == bit_); 1.255 + } 1.256 + }; 1.257 + 1.258 + uint32_t code() const { 1.259 + return _code; 1.260 + } 1.261 +}; 1.262 + 1.263 +// For being passed into the generic vfp instruction generator when 1.264 +// there is an instruction that only takes two registers 1.265 +extern VFPRegister NoVFPRegister; 1.266 + 1.267 +struct ImmTag : public Imm32 1.268 +{ 1.269 + ImmTag(JSValueTag mask) 1.270 + : Imm32(int32_t(mask)) 1.271 + { } 1.272 +}; 1.273 + 1.274 +struct ImmType : public ImmTag 1.275 +{ 1.276 + ImmType(JSValueType type) 1.277 + : ImmTag(JSVAL_TYPE_TO_TAG(type)) 1.278 + { } 1.279 +}; 1.280 + 1.281 +enum Index { 1.282 + Offset = 0 << 21 | 1<<24, 1.283 + PreIndex = 1<<21 | 1 << 24, 1.284 + PostIndex = 0 << 21 | 0 << 24 1.285 + // The docs were rather unclear on this. it sounds like 1.286 + // 1<<21 | 0 << 24 encodes dtrt 1.287 +}; 1.288 + 1.289 +// Seriously, wtf arm 1.290 +enum IsImmOp2_ { 1.291 + IsImmOp2 = 1 << 25, 1.292 + IsNotImmOp2 = 0 << 25 1.293 +}; 1.294 +enum IsImmDTR_ { 1.295 + IsImmDTR = 0 << 25, 1.296 + IsNotImmDTR = 1 << 25 1.297 +}; 1.298 +// For the extra memory operations, ldrd, ldrsb, ldrh 1.299 +enum IsImmEDTR_ { 1.300 + IsImmEDTR = 1 << 22, 1.301 + IsNotImmEDTR = 0 << 22 1.302 +}; 1.303 + 1.304 + 1.305 +enum ShiftType { 1.306 + LSL = 0, // << 5 1.307 + LSR = 1, // << 5 1.308 + ASR = 2, // << 5 1.309 + ROR = 3, // << 5 1.310 + RRX = ROR // RRX is encoded as ROR with a 0 offset. 1.311 +}; 1.312 + 1.313 +// The actual codes that get set by instructions 1.314 +// and the codes that are checked by the conditions below. 1.315 +struct ConditionCodes 1.316 +{ 1.317 + bool Zero : 1; 1.318 + bool Overflow : 1; 1.319 + bool Carry : 1; 1.320 + bool Minus : 1; 1.321 +}; 1.322 + 1.323 +// Modes for STM/LDM. 1.324 +// Names are the suffixes applied to 1.325 +// the instruction. 1.326 +enum DTMMode { 1.327 + A = 0 << 24, // empty / after 1.328 + B = 1 << 24, // full / before 1.329 + D = 0 << 23, // decrement 1.330 + I = 1 << 23, // increment 1.331 + DA = D | A, 1.332 + DB = D | B, 1.333 + IA = I | A, 1.334 + IB = I | B 1.335 +}; 1.336 + 1.337 +enum DTMWriteBack { 1.338 + WriteBack = 1 << 21, 1.339 + NoWriteBack = 0 << 21 1.340 +}; 1.341 + 1.342 +enum SetCond_ { 1.343 + SetCond = 1 << 20, 1.344 + NoSetCond = 0 << 20 1.345 +}; 1.346 +enum LoadStore { 1.347 + IsLoad = 1 << 20, 1.348 + IsStore = 0 << 20 1.349 +}; 1.350 +// You almost never want to use this directly. 1.351 +// Instead, you wantto pass in a signed constant, 1.352 +// and let this bit be implicitly set for you. 1.353 +// this is however, necessary if we want a negative index 1.354 +enum IsUp_ { 1.355 + IsUp = 1 << 23, 1.356 + IsDown = 0 << 23 1.357 +}; 1.358 +enum ALUOp { 1.359 + op_mov = 0xd << 21, 1.360 + op_mvn = 0xf << 21, 1.361 + op_and = 0x0 << 21, 1.362 + op_bic = 0xe << 21, 1.363 + op_eor = 0x1 << 21, 1.364 + op_orr = 0xc << 21, 1.365 + op_adc = 0x5 << 21, 1.366 + op_add = 0x4 << 21, 1.367 + op_sbc = 0x6 << 21, 1.368 + op_sub = 0x2 << 21, 1.369 + op_rsb = 0x3 << 21, 1.370 + op_rsc = 0x7 << 21, 1.371 + op_cmn = 0xb << 21, 1.372 + op_cmp = 0xa << 21, 1.373 + op_teq = 0x9 << 21, 1.374 + op_tst = 0x8 << 21, 1.375 + op_invalid = -1 1.376 +}; 1.377 + 1.378 + 1.379 +enum MULOp { 1.380 + opm_mul = 0 << 21, 1.381 + opm_mla = 1 << 21, 1.382 + opm_umaal = 2 << 21, 1.383 + opm_mls = 3 << 21, 1.384 + opm_umull = 4 << 21, 1.385 + opm_umlal = 5 << 21, 1.386 + opm_smull = 6 << 21, 1.387 + opm_smlal = 7 << 21 1.388 +}; 1.389 +enum BranchTag { 1.390 + op_b = 0x0a000000, 1.391 + op_b_mask = 0x0f000000, 1.392 + op_b_dest_mask = 0x00ffffff, 1.393 + op_bl = 0x0b000000, 1.394 + op_blx = 0x012fff30, 1.395 + op_bx = 0x012fff10 1.396 +}; 1.397 + 1.398 +// Just like ALUOp, but for the vfp instruction set. 1.399 +enum VFPOp { 1.400 + opv_mul = 0x2 << 20, 1.401 + opv_add = 0x3 << 20, 1.402 + opv_sub = 0x3 << 20 | 0x1 << 6, 1.403 + opv_div = 0x8 << 20, 1.404 + opv_mov = 0xB << 20 | 0x1 << 6, 1.405 + opv_abs = 0xB << 20 | 0x3 << 6, 1.406 + opv_neg = 0xB << 20 | 0x1 << 6 | 0x1 << 16, 1.407 + opv_sqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16, 1.408 + opv_cmp = 0xB << 20 | 0x1 << 6 | 0x4 << 16, 1.409 + opv_cmpz = 0xB << 20 | 0x1 << 6 | 0x5 << 16 1.410 +}; 1.411 +// Negate the operation, AND negate the immediate that we were passed in. 1.412 +ALUOp ALUNeg(ALUOp op, Register dest, Imm32 *imm, Register *negDest); 1.413 +bool can_dbl(ALUOp op); 1.414 +bool condsAreSafe(ALUOp op); 1.415 +// If there is a variant of op that has a dest (think cmp/sub) 1.416 +// return that variant of it. 1.417 +ALUOp getDestVariant(ALUOp op); 1.418 + 1.419 +static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data); 1.420 +static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0); 1.421 +// All of these classes exist solely to shuffle data into the various operands. 1.422 +// For example Operand2 can be an imm8, a register-shifted-by-a-constant or 1.423 +// a register-shifted-by-a-register. I represent this in C++ by having a 1.424 +// base class Operand2, which just stores the 32 bits of data as they will be 1.425 +// encoded in the instruction. You cannot directly create an Operand2 1.426 +// since it is tricky, and not entirely sane to do so. Instead, you create 1.427 +// one of its child classes, e.g. Imm8. Imm8's constructor takes a single 1.428 +// integer argument. Imm8 will verify that its argument can be encoded 1.429 +// as an ARM 12 bit imm8, encode it using an Imm8data, and finally call 1.430 +// its parent's (Operand2) constructor with the Imm8data. The Operand2 1.431 +// constructor will then call the Imm8data's encode() function to extract 1.432 +// the raw bits from it. In the future, we should be able to extract 1.433 +// data from the Operand2 by asking it for its component Imm8data 1.434 +// structures. The reason this is so horribly round-about is I wanted 1.435 +// to have Imm8 and RegisterShiftedRegister inherit directly from Operand2 1.436 +// but have all of them take up only a single word of storage. 1.437 +// I also wanted to avoid passing around raw integers at all 1.438 +// since they are error prone. 1.439 +class Op2Reg; 1.440 +class O2RegImmShift; 1.441 +class O2RegRegShift; 1.442 +namespace datastore { 1.443 +struct Reg 1.444 +{ 1.445 + // the "second register" 1.446 + uint32_t RM : 4; 1.447 + // do we get another register for shifting 1.448 + uint32_t RRS : 1; 1.449 + ShiftType Type : 2; 1.450 + // I'd like this to be a more sensible encoding, but that would 1.451 + // need to be a struct and that would not pack :( 1.452 + uint32_t ShiftAmount : 5; 1.453 + uint32_t pad : 20; 1.454 + 1.455 + Reg(uint32_t rm, ShiftType type, uint32_t rsr, uint32_t shiftamount) 1.456 + : RM(rm), RRS(rsr), Type(type), ShiftAmount(shiftamount), pad(0) 1.457 + { } 1.458 + 1.459 + uint32_t encode() { 1.460 + return RM | RRS << 4 | Type << 5 | ShiftAmount << 7; 1.461 + } 1.462 + explicit Reg(const Op2Reg &op) { 1.463 + memcpy(this, &op, sizeof(*this)); 1.464 + } 1.465 +}; 1.466 + 1.467 +// Op2 has a mode labelled "<imm8m>", which is arm's magical 1.468 +// immediate encoding. Some instructions actually get 8 bits of 1.469 +// data, which is called Imm8Data below. These should have edit 1.470 +// distance > 1, but this is how it is for now. 1.471 +struct Imm8mData 1.472 +{ 1.473 + private: 1.474 + uint32_t data : 8; 1.475 + uint32_t rot : 4; 1.476 + // Throw in an extra bit that will be 1 if we can't encode this 1.477 + // properly. if we can encode it properly, a simple "|" will still 1.478 + // suffice to meld it into the instruction. 1.479 + uint32_t buff : 19; 1.480 + public: 1.481 + uint32_t invalid : 1; 1.482 + 1.483 + uint32_t encode() { 1.484 + JS_ASSERT(!invalid); 1.485 + return data | rot << 8; 1.486 + }; 1.487 + 1.488 + // Default constructor makes an invalid immediate. 1.489 + Imm8mData() 1.490 + : data(0xff), rot(0xf), invalid(1) 1.491 + { } 1.492 + 1.493 + Imm8mData(uint32_t data_, uint32_t rot_) 1.494 + : data(data_), rot(rot_), invalid(0) 1.495 + { 1.496 + JS_ASSERT(data == data_); 1.497 + JS_ASSERT(rot == rot_); 1.498 + } 1.499 +}; 1.500 + 1.501 +struct Imm8Data 1.502 +{ 1.503 + private: 1.504 + uint32_t imm4L : 4; 1.505 + uint32_t pad : 4; 1.506 + uint32_t imm4H : 4; 1.507 + 1.508 + public: 1.509 + uint32_t encode() { 1.510 + return imm4L | (imm4H << 8); 1.511 + }; 1.512 + Imm8Data(uint32_t imm) : imm4L(imm&0xf), imm4H(imm>>4) { 1.513 + JS_ASSERT(imm <= 0xff); 1.514 + } 1.515 +}; 1.516 + 1.517 +// VLDR/VSTR take an 8 bit offset, which is implicitly left shifted 1.518 +// by 2. 1.519 +struct Imm8VFPOffData 1.520 +{ 1.521 + private: 1.522 + uint32_t data; 1.523 + 1.524 + public: 1.525 + uint32_t encode() { 1.526 + return data; 1.527 + }; 1.528 + Imm8VFPOffData(uint32_t imm) : data (imm) { 1.529 + JS_ASSERT((imm & ~(0xff)) == 0); 1.530 + } 1.531 +}; 1.532 + 1.533 +// ARM can magically encode 256 very special immediates to be moved 1.534 +// into a register. 1.535 +struct Imm8VFPImmData 1.536 +{ 1.537 + private: 1.538 + uint32_t imm4L : 4; 1.539 + uint32_t pad : 12; 1.540 + uint32_t imm4H : 4; 1.541 + int32_t isInvalid : 12; 1.542 + 1.543 + public: 1.544 + Imm8VFPImmData() 1.545 + : imm4L(-1U & 0xf), imm4H(-1U & 0xf), isInvalid(-1) 1.546 + { } 1.547 + 1.548 + Imm8VFPImmData(uint32_t imm) 1.549 + : imm4L(imm&0xf), imm4H(imm>>4), isInvalid(0) 1.550 + { 1.551 + JS_ASSERT(imm <= 0xff); 1.552 + } 1.553 + 1.554 + uint32_t encode() { 1.555 + if (isInvalid != 0) 1.556 + return -1; 1.557 + return imm4L | (imm4H << 16); 1.558 + }; 1.559 +}; 1.560 + 1.561 +struct Imm12Data 1.562 +{ 1.563 + uint32_t data : 12; 1.564 + uint32_t encode() { 1.565 + return data; 1.566 + } 1.567 + 1.568 + Imm12Data(uint32_t imm) 1.569 + : data(imm) 1.570 + { 1.571 + JS_ASSERT(data == imm); 1.572 + } 1.573 + 1.574 +}; 1.575 + 1.576 +struct RIS 1.577 +{ 1.578 + uint32_t ShiftAmount : 5; 1.579 + uint32_t encode () { 1.580 + return ShiftAmount; 1.581 + } 1.582 + 1.583 + RIS(uint32_t imm) 1.584 + : ShiftAmount(imm) 1.585 + { 1.586 + JS_ASSERT(ShiftAmount == imm); 1.587 + } 1.588 + explicit RIS(Reg r) : ShiftAmount(r.ShiftAmount) {} 1.589 +}; 1.590 + 1.591 +struct RRS 1.592 +{ 1.593 + uint32_t MustZero : 1; 1.594 + // the register that holds the shift amount 1.595 + uint32_t RS : 4; 1.596 + 1.597 + RRS(uint32_t rs) 1.598 + : RS(rs) 1.599 + { 1.600 + JS_ASSERT(rs == RS); 1.601 + } 1.602 + 1.603 + uint32_t encode () { 1.604 + return RS << 1; 1.605 + } 1.606 +}; 1.607 + 1.608 +} // namespace datastore 1.609 + 1.610 +class MacroAssemblerARM; 1.611 +class Operand; 1.612 +class Operand2 1.613 +{ 1.614 + friend class Operand; 1.615 + friend class MacroAssemblerARM; 1.616 + friend class InstALU; 1.617 + public: 1.618 + uint32_t oper : 31; 1.619 + uint32_t invalid : 1; 1.620 + bool isO2Reg() { 1.621 + return !(oper & IsImmOp2); 1.622 + } 1.623 + Op2Reg toOp2Reg(); 1.624 + bool isImm8() { 1.625 + return oper & IsImmOp2; 1.626 + } 1.627 + 1.628 + protected: 1.629 + Operand2(datastore::Imm8mData base) 1.630 + : oper(base.invalid ? -1 : (base.encode() | (uint32_t)IsImmOp2)), 1.631 + invalid(base.invalid) 1.632 + { } 1.633 + 1.634 + Operand2(datastore::Reg base) 1.635 + : oper(base.encode() | (uint32_t)IsNotImmOp2) 1.636 + { } 1.637 + 1.638 + private: 1.639 + Operand2(int blob) 1.640 + : oper(blob) 1.641 + { } 1.642 + 1.643 + public: 1.644 + uint32_t encode() { 1.645 + return oper; 1.646 + } 1.647 +}; 1.648 + 1.649 +class Imm8 : public Operand2 1.650 +{ 1.651 + public: 1.652 + static datastore::Imm8mData encodeImm(uint32_t imm) { 1.653 + // mozilla::CountLeadingZeroes32(imm) requires imm != 0. 1.654 + if (imm == 0) 1.655 + return datastore::Imm8mData(0, 0); 1.656 + int left = mozilla::CountLeadingZeroes32(imm) & 30; 1.657 + // See if imm is a simple value that can be encoded with a rotate of 0. 1.658 + // This is effectively imm <= 0xff, but I assume this can be optimized 1.659 + // more 1.660 + if (left >= 24) 1.661 + return datastore::Imm8mData(imm, 0); 1.662 + 1.663 + // Mask out the 8 bits following the first bit that we found, see if we 1.664 + // have 0 yet. 1.665 + int no_imm = imm & ~(0xff << (24 - left)); 1.666 + if (no_imm == 0) { 1.667 + return datastore::Imm8mData(imm >> (24 - left), ((8+left) >> 1)); 1.668 + } 1.669 + // Look for the most signifigant bit set, once again. 1.670 + int right = 32 - (mozilla::CountLeadingZeroes32(no_imm) & 30); 1.671 + // If it is in the bottom 8 bits, there is a chance that this is a 1.672 + // wraparound case. 1.673 + if (right >= 8) 1.674 + return datastore::Imm8mData(); 1.675 + // Rather than masking out bits and checking for 0, just rotate the 1.676 + // immediate that we were passed in, and see if it fits into 8 bits. 1.677 + unsigned int mask = imm << (8 - right) | imm >> (24 + right); 1.678 + if (mask <= 0xff) 1.679 + return datastore::Imm8mData(mask, (8-right) >> 1); 1.680 + return datastore::Imm8mData(); 1.681 + } 1.682 + // pair template? 1.683 + struct TwoImm8mData 1.684 + { 1.685 + datastore::Imm8mData fst, snd; 1.686 + 1.687 + TwoImm8mData() 1.688 + : fst(), snd() 1.689 + { } 1.690 + 1.691 + TwoImm8mData(datastore::Imm8mData _fst, datastore::Imm8mData _snd) 1.692 + : fst(_fst), snd(_snd) 1.693 + { } 1.694 + }; 1.695 + 1.696 + static TwoImm8mData encodeTwoImms(uint32_t); 1.697 + Imm8(uint32_t imm) 1.698 + : Operand2(encodeImm(imm)) 1.699 + { } 1.700 +}; 1.701 + 1.702 +class Op2Reg : public Operand2 1.703 +{ 1.704 + public: 1.705 + Op2Reg(Register rm, ShiftType type, datastore::RIS shiftImm) 1.706 + : Operand2(datastore::Reg(rm.code(), type, 0, shiftImm.encode())) 1.707 + { } 1.708 + 1.709 + Op2Reg(Register rm, ShiftType type, datastore::RRS shiftReg) 1.710 + : Operand2(datastore::Reg(rm.code(), type, 1, shiftReg.encode())) 1.711 + { } 1.712 + bool isO2RegImmShift() { 1.713 + datastore::Reg r(*this); 1.714 + return !r.RRS; 1.715 + } 1.716 + O2RegImmShift toO2RegImmShift(); 1.717 + bool isO2RegRegShift() { 1.718 + datastore::Reg r(*this); 1.719 + return r.RRS; 1.720 + } 1.721 + O2RegRegShift toO2RegRegShift(); 1.722 + 1.723 + bool checkType(ShiftType type) { 1.724 + datastore::Reg r(*this); 1.725 + return r.Type == type; 1.726 + } 1.727 + bool checkRM(Register rm) { 1.728 + datastore::Reg r(*this); 1.729 + return r.RM == rm.code(); 1.730 + } 1.731 + bool getRM(Register *rm) { 1.732 + datastore::Reg r(*this); 1.733 + *rm = Register::FromCode(r.RM); 1.734 + return true; 1.735 + } 1.736 +}; 1.737 + 1.738 +class O2RegImmShift : public Op2Reg 1.739 +{ 1.740 + public: 1.741 + O2RegImmShift(Register rn, ShiftType type, uint32_t shift) 1.742 + : Op2Reg(rn, type, datastore::RIS(shift)) 1.743 + { } 1.744 + int getShift() { 1.745 + datastore::Reg r(*this); 1.746 + datastore::RIS ris(r); 1.747 + return ris.ShiftAmount; 1.748 + } 1.749 +}; 1.750 + 1.751 +class O2RegRegShift : public Op2Reg 1.752 +{ 1.753 + public: 1.754 + O2RegRegShift(Register rn, ShiftType type, Register rs) 1.755 + : Op2Reg(rn, type, datastore::RRS(rs.code())) 1.756 + { } 1.757 +}; 1.758 + 1.759 +O2RegImmShift O2Reg(Register r); 1.760 +O2RegImmShift lsl (Register r, int amt); 1.761 +O2RegImmShift lsr (Register r, int amt); 1.762 +O2RegImmShift asr (Register r, int amt); 1.763 +O2RegImmShift rol (Register r, int amt); 1.764 +O2RegImmShift ror (Register r, int amt); 1.765 + 1.766 +O2RegRegShift lsl (Register r, Register amt); 1.767 +O2RegRegShift lsr (Register r, Register amt); 1.768 +O2RegRegShift asr (Register r, Register amt); 1.769 +O2RegRegShift ror (Register r, Register amt); 1.770 + 1.771 +// An offset from a register to be used for ldr/str. This should include 1.772 +// the sign bit, since ARM has "signed-magnitude" offsets. That is it encodes 1.773 +// an unsigned offset, then the instruction specifies if the offset is positive 1.774 +// or negative. The +/- bit is necessary if the instruction set wants to be 1.775 +// able to have a negative register offset e.g. ldr pc, [r1,-r2]; 1.776 +class DtrOff 1.777 +{ 1.778 + uint32_t data; 1.779 + 1.780 + protected: 1.781 + DtrOff(datastore::Imm12Data immdata, IsUp_ iu) 1.782 + : data(immdata.encode() | (uint32_t)IsImmDTR | ((uint32_t)iu)) 1.783 + { } 1.784 + 1.785 + DtrOff(datastore::Reg reg, IsUp_ iu = IsUp) 1.786 + : data(reg.encode() | (uint32_t) IsNotImmDTR | iu) 1.787 + { } 1.788 + 1.789 + public: 1.790 + uint32_t encode() { return data; } 1.791 +}; 1.792 + 1.793 +class DtrOffImm : public DtrOff 1.794 +{ 1.795 + public: 1.796 + DtrOffImm(int32_t imm) 1.797 + : DtrOff(datastore::Imm12Data(mozilla::Abs(imm)), imm >= 0 ? IsUp : IsDown) 1.798 + { 1.799 + JS_ASSERT(mozilla::Abs(imm) < 4096); 1.800 + } 1.801 +}; 1.802 + 1.803 +class DtrOffReg : public DtrOff 1.804 +{ 1.805 + // These are designed to be called by a constructor of a subclass. 1.806 + // Constructing the necessary RIS/RRS structures are annoying 1.807 + protected: 1.808 + DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm, IsUp_ iu = IsUp) 1.809 + : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()), iu) 1.810 + { } 1.811 + 1.812 + DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg, IsUp_ iu = IsUp) 1.813 + : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()), iu) 1.814 + { } 1.815 +}; 1.816 + 1.817 +class DtrRegImmShift : public DtrOffReg 1.818 +{ 1.819 + public: 1.820 + DtrRegImmShift(Register rn, ShiftType type, uint32_t shift, IsUp_ iu = IsUp) 1.821 + : DtrOffReg(rn, type, datastore::RIS(shift), iu) 1.822 + { } 1.823 +}; 1.824 + 1.825 +class DtrRegRegShift : public DtrOffReg 1.826 +{ 1.827 + public: 1.828 + DtrRegRegShift(Register rn, ShiftType type, Register rs, IsUp_ iu = IsUp) 1.829 + : DtrOffReg(rn, type, datastore::RRS(rs.code()), iu) 1.830 + { } 1.831 +}; 1.832 + 1.833 +// we will frequently want to bundle a register with its offset so that we have 1.834 +// an "operand" to a load instruction. 1.835 +class DTRAddr 1.836 +{ 1.837 + uint32_t data; 1.838 + 1.839 + public: 1.840 + DTRAddr(Register reg, DtrOff dtr) 1.841 + : data(dtr.encode() | (reg.code() << 16)) 1.842 + { } 1.843 + 1.844 + uint32_t encode() { 1.845 + return data; 1.846 + } 1.847 + Register getBase() { 1.848 + return Register::FromCode((data >> 16) &0xf); 1.849 + } 1.850 + private: 1.851 + friend class Operand; 1.852 + DTRAddr(uint32_t blob) 1.853 + : data(blob) 1.854 + { } 1.855 +}; 1.856 + 1.857 +// Offsets for the extended data transfer instructions: 1.858 +// ldrsh, ldrd, ldrsb, etc. 1.859 +class EDtrOff 1.860 +{ 1.861 + uint32_t data; 1.862 + 1.863 + protected: 1.864 + EDtrOff(datastore::Imm8Data imm8, IsUp_ iu = IsUp) 1.865 + : data(imm8.encode() | IsImmEDTR | (uint32_t)iu) 1.866 + { } 1.867 + 1.868 + EDtrOff(Register rm, IsUp_ iu = IsUp) 1.869 + : data(rm.code() | IsNotImmEDTR | iu) 1.870 + { } 1.871 + 1.872 + public: 1.873 + uint32_t encode() { 1.874 + return data; 1.875 + } 1.876 +}; 1.877 + 1.878 +class EDtrOffImm : public EDtrOff 1.879 +{ 1.880 + public: 1.881 + EDtrOffImm(int32_t imm) 1.882 + : EDtrOff(datastore::Imm8Data(mozilla::Abs(imm)), (imm >= 0) ? IsUp : IsDown) 1.883 + { 1.884 + JS_ASSERT(mozilla::Abs(imm) < 256); 1.885 + } 1.886 +}; 1.887 + 1.888 +// this is the most-derived class, since the extended data 1.889 +// transfer instructions don't support any sort of modifying the 1.890 +// "index" operand 1.891 +class EDtrOffReg : public EDtrOff 1.892 +{ 1.893 + public: 1.894 + EDtrOffReg(Register rm) 1.895 + : EDtrOff(rm) 1.896 + { } 1.897 +}; 1.898 + 1.899 +class EDtrAddr 1.900 +{ 1.901 + uint32_t data; 1.902 + 1.903 + public: 1.904 + EDtrAddr(Register r, EDtrOff off) 1.905 + : data(RN(r) | off.encode()) 1.906 + { } 1.907 + 1.908 + uint32_t encode() { 1.909 + return data; 1.910 + } 1.911 +}; 1.912 + 1.913 +class VFPOff 1.914 +{ 1.915 + uint32_t data; 1.916 + 1.917 + protected: 1.918 + VFPOff(datastore::Imm8VFPOffData imm, IsUp_ isup) 1.919 + : data(imm.encode() | (uint32_t)isup) 1.920 + { } 1.921 + 1.922 + public: 1.923 + uint32_t encode() { 1.924 + return data; 1.925 + } 1.926 +}; 1.927 + 1.928 +class VFPOffImm : public VFPOff 1.929 +{ 1.930 + public: 1.931 + VFPOffImm(int32_t imm) 1.932 + : VFPOff(datastore::Imm8VFPOffData(mozilla::Abs(imm) / 4), imm < 0 ? IsDown : IsUp) 1.933 + { 1.934 + JS_ASSERT(mozilla::Abs(imm) <= 255 * 4); 1.935 + } 1.936 +}; 1.937 +class VFPAddr 1.938 +{ 1.939 + friend class Operand; 1.940 + 1.941 + uint32_t data; 1.942 + 1.943 + protected: 1.944 + VFPAddr(uint32_t blob) 1.945 + : data(blob) 1.946 + { } 1.947 + 1.948 + public: 1.949 + VFPAddr(Register base, VFPOff off) 1.950 + : data(RN(base) | off.encode()) 1.951 + { } 1.952 + 1.953 + uint32_t encode() { 1.954 + return data; 1.955 + } 1.956 +}; 1.957 + 1.958 +class VFPImm { 1.959 + uint32_t data; 1.960 + 1.961 + public: 1.962 + static const VFPImm one; 1.963 + 1.964 + VFPImm(uint32_t topWordOfDouble); 1.965 + 1.966 + uint32_t encode() { 1.967 + return data; 1.968 + } 1.969 + bool isValid() { 1.970 + return data != -1U; 1.971 + } 1.972 +}; 1.973 + 1.974 +// A BOffImm is an immediate that is used for branches. Namely, it is the offset that will 1.975 +// be encoded in the branch instruction. This is the only sane way of constructing a branch. 1.976 +class BOffImm 1.977 +{ 1.978 + uint32_t data; 1.979 + 1.980 + public: 1.981 + uint32_t encode() { 1.982 + return data; 1.983 + } 1.984 + int32_t decode() { 1.985 + return ((((int32_t)data) << 8) >> 6) + 8; 1.986 + } 1.987 + 1.988 + explicit BOffImm(int offset) 1.989 + : data ((offset - 8) >> 2 & 0x00ffffff) 1.990 + { 1.991 + JS_ASSERT((offset & 0x3) == 0); 1.992 + if (!isInRange(offset)) 1.993 + CrashAtUnhandlableOOM("BOffImm"); 1.994 + } 1.995 + static bool isInRange(int offset) 1.996 + { 1.997 + if ((offset - 8) < -33554432) 1.998 + return false; 1.999 + if ((offset - 8) > 33554428) 1.1000 + return false; 1.1001 + return true; 1.1002 + } 1.1003 + static const int INVALID = 0x00800000; 1.1004 + BOffImm() 1.1005 + : data(INVALID) 1.1006 + { } 1.1007 + 1.1008 + bool isInvalid() { 1.1009 + return data == uint32_t(INVALID); 1.1010 + } 1.1011 + Instruction *getDest(Instruction *src); 1.1012 + 1.1013 + private: 1.1014 + friend class InstBranchImm; 1.1015 + BOffImm(Instruction &inst); 1.1016 +}; 1.1017 + 1.1018 +class Imm16 1.1019 +{ 1.1020 + uint32_t lower : 12; 1.1021 + uint32_t pad : 4; 1.1022 + uint32_t upper : 4; 1.1023 + uint32_t invalid : 12; 1.1024 + 1.1025 + public: 1.1026 + Imm16(); 1.1027 + Imm16(uint32_t imm); 1.1028 + Imm16(Instruction &inst); 1.1029 + 1.1030 + uint32_t encode() { 1.1031 + return lower | upper << 16; 1.1032 + } 1.1033 + uint32_t decode() { 1.1034 + return lower | upper << 12; 1.1035 + } 1.1036 + 1.1037 + bool isInvalid () { 1.1038 + return invalid; 1.1039 + } 1.1040 +}; 1.1041 + 1.1042 +/* I would preffer that these do not exist, since there are essentially 1.1043 +* no instructions that would ever take more than one of these, however, 1.1044 +* the MIR wants to only have one type of arguments to functions, so bugger. 1.1045 +*/ 1.1046 +class Operand 1.1047 +{ 1.1048 + // the encoding of registers is the same for OP2, DTR and EDTR 1.1049 + // yet the type system doesn't let us express this, so choices 1.1050 + // must be made. 1.1051 + public: 1.1052 + enum Tag_ { 1.1053 + OP2, 1.1054 + MEM, 1.1055 + FOP 1.1056 + }; 1.1057 + 1.1058 + private: 1.1059 + Tag_ Tag : 3; 1.1060 + uint32_t reg : 5; 1.1061 + int32_t offset; 1.1062 + uint32_t data; 1.1063 + 1.1064 + public: 1.1065 + Operand (Register reg_) 1.1066 + : Tag(OP2), reg(reg_.code()) 1.1067 + { } 1.1068 + 1.1069 + Operand (FloatRegister freg) 1.1070 + : Tag(FOP), reg(freg.code()) 1.1071 + { } 1.1072 + 1.1073 + Operand (Register base, Imm32 off) 1.1074 + : Tag(MEM), reg(base.code()), offset(off.value) 1.1075 + { } 1.1076 + 1.1077 + Operand (Register base, int32_t off) 1.1078 + : Tag(MEM), reg(base.code()), offset(off) 1.1079 + { } 1.1080 + 1.1081 + Operand (const Address &addr) 1.1082 + : Tag(MEM), reg(addr.base.code()), offset(addr.offset) 1.1083 + { } 1.1084 + 1.1085 + Tag_ getTag() const { 1.1086 + return Tag; 1.1087 + } 1.1088 + 1.1089 + Operand2 toOp2() const { 1.1090 + JS_ASSERT(Tag == OP2); 1.1091 + return O2Reg(Register::FromCode(reg)); 1.1092 + } 1.1093 + 1.1094 + Register toReg() const { 1.1095 + JS_ASSERT(Tag == OP2); 1.1096 + return Register::FromCode(reg); 1.1097 + } 1.1098 + 1.1099 + void toAddr(Register *r, Imm32 *dest) const { 1.1100 + JS_ASSERT(Tag == MEM); 1.1101 + *r = Register::FromCode(reg); 1.1102 + *dest = Imm32(offset); 1.1103 + } 1.1104 + Address toAddress() const { 1.1105 + return Address(Register::FromCode(reg), offset); 1.1106 + } 1.1107 + int32_t disp() const { 1.1108 + JS_ASSERT(Tag == MEM); 1.1109 + return offset; 1.1110 + } 1.1111 + 1.1112 + int32_t base() const { 1.1113 + JS_ASSERT(Tag == MEM); 1.1114 + return reg; 1.1115 + } 1.1116 + Register baseReg() const { 1.1117 + return Register::FromCode(reg); 1.1118 + } 1.1119 + DTRAddr toDTRAddr() const { 1.1120 + return DTRAddr(baseReg(), DtrOffImm(offset)); 1.1121 + } 1.1122 + VFPAddr toVFPAddr() const { 1.1123 + return VFPAddr(baseReg(), VFPOffImm(offset)); 1.1124 + } 1.1125 +}; 1.1126 + 1.1127 +void 1.1128 +PatchJump(CodeLocationJump &jump_, CodeLocationLabel label); 1.1129 +class InstructionIterator; 1.1130 +class Assembler; 1.1131 +typedef js::jit::AssemblerBufferWithConstantPool<1024, 4, Instruction, Assembler, 1> ARMBuffer; 1.1132 + 1.1133 +class Assembler : public AssemblerShared 1.1134 +{ 1.1135 + public: 1.1136 + // ARM conditional constants 1.1137 + enum ARMCondition { 1.1138 + EQ = 0x00000000, // Zero 1.1139 + NE = 0x10000000, // Non-zero 1.1140 + CS = 0x20000000, 1.1141 + CC = 0x30000000, 1.1142 + MI = 0x40000000, 1.1143 + PL = 0x50000000, 1.1144 + VS = 0x60000000, 1.1145 + VC = 0x70000000, 1.1146 + HI = 0x80000000, 1.1147 + LS = 0x90000000, 1.1148 + GE = 0xa0000000, 1.1149 + LT = 0xb0000000, 1.1150 + GT = 0xc0000000, 1.1151 + LE = 0xd0000000, 1.1152 + AL = 0xe0000000 1.1153 + }; 1.1154 + 1.1155 + enum Condition { 1.1156 + Equal = EQ, 1.1157 + NotEqual = NE, 1.1158 + Above = HI, 1.1159 + AboveOrEqual = CS, 1.1160 + Below = CC, 1.1161 + BelowOrEqual = LS, 1.1162 + GreaterThan = GT, 1.1163 + GreaterThanOrEqual = GE, 1.1164 + LessThan = LT, 1.1165 + LessThanOrEqual = LE, 1.1166 + Overflow = VS, 1.1167 + Signed = MI, 1.1168 + NotSigned = PL, 1.1169 + Zero = EQ, 1.1170 + NonZero = NE, 1.1171 + Always = AL, 1.1172 + 1.1173 + VFP_NotEqualOrUnordered = NE, 1.1174 + VFP_Equal = EQ, 1.1175 + VFP_Unordered = VS, 1.1176 + VFP_NotUnordered = VC, 1.1177 + VFP_GreaterThanOrEqualOrUnordered = CS, 1.1178 + VFP_GreaterThanOrEqual = GE, 1.1179 + VFP_GreaterThanOrUnordered = HI, 1.1180 + VFP_GreaterThan = GT, 1.1181 + VFP_LessThanOrEqualOrUnordered = LE, 1.1182 + VFP_LessThanOrEqual = LS, 1.1183 + VFP_LessThanOrUnordered = LT, 1.1184 + VFP_LessThan = CC // MI is valid too. 1.1185 + }; 1.1186 + 1.1187 + // Bit set when a DoubleCondition does not map to a single ARM condition. 1.1188 + // The macro assembler has to special-case these conditions, or else 1.1189 + // ConditionFromDoubleCondition will complain. 1.1190 + static const int DoubleConditionBitSpecial = 0x1; 1.1191 + 1.1192 + enum DoubleCondition { 1.1193 + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. 1.1194 + DoubleOrdered = VFP_NotUnordered, 1.1195 + DoubleEqual = VFP_Equal, 1.1196 + DoubleNotEqual = VFP_NotEqualOrUnordered | DoubleConditionBitSpecial, 1.1197 + DoubleGreaterThan = VFP_GreaterThan, 1.1198 + DoubleGreaterThanOrEqual = VFP_GreaterThanOrEqual, 1.1199 + DoubleLessThan = VFP_LessThan, 1.1200 + DoubleLessThanOrEqual = VFP_LessThanOrEqual, 1.1201 + // If either operand is NaN, these conditions always evaluate to true. 1.1202 + DoubleUnordered = VFP_Unordered, 1.1203 + DoubleEqualOrUnordered = VFP_Equal | DoubleConditionBitSpecial, 1.1204 + DoubleNotEqualOrUnordered = VFP_NotEqualOrUnordered, 1.1205 + DoubleGreaterThanOrUnordered = VFP_GreaterThanOrUnordered, 1.1206 + DoubleGreaterThanOrEqualOrUnordered = VFP_GreaterThanOrEqualOrUnordered, 1.1207 + DoubleLessThanOrUnordered = VFP_LessThanOrUnordered, 1.1208 + DoubleLessThanOrEqualOrUnordered = VFP_LessThanOrEqualOrUnordered 1.1209 + }; 1.1210 + 1.1211 + Condition getCondition(uint32_t inst) { 1.1212 + return (Condition) (0xf0000000 & inst); 1.1213 + } 1.1214 + static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) { 1.1215 + JS_ASSERT(!(cond & DoubleConditionBitSpecial)); 1.1216 + return static_cast<Condition>(cond); 1.1217 + } 1.1218 + 1.1219 + // :( this should be protected, but since CodeGenerator 1.1220 + // wants to use it, It needs to go out here :( 1.1221 + 1.1222 + BufferOffset nextOffset() { 1.1223 + return m_buffer.nextOffset(); 1.1224 + } 1.1225 + 1.1226 + protected: 1.1227 + BufferOffset labelOffset (Label *l) { 1.1228 + return BufferOffset(l->bound()); 1.1229 + } 1.1230 + 1.1231 + Instruction * editSrc (BufferOffset bo) { 1.1232 + return m_buffer.getInst(bo); 1.1233 + } 1.1234 + public: 1.1235 + void resetCounter(); 1.1236 + uint32_t actualOffset(uint32_t) const; 1.1237 + uint32_t actualIndex(uint32_t) const; 1.1238 + static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index); 1.1239 + BufferOffset actualOffset(BufferOffset) const; 1.1240 + protected: 1.1241 + 1.1242 + // structure for fixing up pc-relative loads/jumps when a the machine code 1.1243 + // gets moved (executable copy, gc, etc.) 1.1244 + struct RelativePatch 1.1245 + { 1.1246 + void *target; 1.1247 + Relocation::Kind kind; 1.1248 + RelativePatch(void *target, Relocation::Kind kind) 1.1249 + : target(target), kind(kind) 1.1250 + { } 1.1251 + }; 1.1252 + 1.1253 + // TODO: this should actually be a pool-like object 1.1254 + // It is currently a big hack, and probably shouldn't exist 1.1255 + js::Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_; 1.1256 + js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_; 1.1257 + js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpJumpRelocations_; 1.1258 + js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpDataRelocations_; 1.1259 + js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpPreBarriers_; 1.1260 + 1.1261 + CompactBufferWriter jumpRelocations_; 1.1262 + CompactBufferWriter dataRelocations_; 1.1263 + CompactBufferWriter relocations_; 1.1264 + CompactBufferWriter preBarriers_; 1.1265 + 1.1266 + bool enoughMemory_; 1.1267 + 1.1268 + //typedef JSC::AssemblerBufferWithConstantPool<1024, 4, 4, js::jit::Assembler> ARMBuffer; 1.1269 + ARMBuffer m_buffer; 1.1270 + 1.1271 + // There is now a semi-unified interface for instruction generation. 1.1272 + // During assembly, there is an active buffer that instructions are 1.1273 + // being written into, but later, we may wish to modify instructions 1.1274 + // that have already been created. In order to do this, we call the 1.1275 + // same assembly function, but pass it a destination address, which 1.1276 + // will be overwritten with a new instruction. In order to do this very 1.1277 + // after assembly buffers no longer exist, when calling with a third 1.1278 + // dest parameter, a this object is still needed. dummy always happens 1.1279 + // to be null, but we shouldn't be looking at it in any case. 1.1280 + static Assembler *dummy; 1.1281 + mozilla::Array<Pool, 4> pools_; 1.1282 + Pool *int32Pool; 1.1283 + Pool *doublePool; 1.1284 + 1.1285 + public: 1.1286 + Assembler() 1.1287 + : enoughMemory_(true), 1.1288 + m_buffer(4, 4, 0, &pools_[0], 8), 1.1289 + int32Pool(m_buffer.getPool(1)), 1.1290 + doublePool(m_buffer.getPool(0)), 1.1291 + isFinished(false), 1.1292 + dtmActive(false), 1.1293 + dtmCond(Always) 1.1294 + { 1.1295 + } 1.1296 + 1.1297 + // We need to wait until an AutoIonContextAlloc is created by the 1.1298 + // IonMacroAssembler, before allocating any space. 1.1299 + void initWithAllocator() { 1.1300 + m_buffer.initWithAllocator(); 1.1301 + 1.1302 + // Set up the backwards double region 1.1303 + new (&pools_[2]) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, true); 1.1304 + // Set up the backwards 32 bit region 1.1305 + new (&pools_[3]) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, true, true); 1.1306 + // Set up the forwards double region 1.1307 + new (doublePool) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]); 1.1308 + // Set up the forwards 32 bit region 1.1309 + new (int32Pool) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, false, true, &pools_[3]); 1.1310 + for (int i = 0; i < 4; i++) { 1.1311 + if (pools_[i].poolData == nullptr) { 1.1312 + m_buffer.fail_oom(); 1.1313 + return; 1.1314 + } 1.1315 + } 1.1316 + } 1.1317 + 1.1318 + static Condition InvertCondition(Condition cond); 1.1319 + 1.1320 + // MacroAssemblers hold onto gcthings, so they are traced by the GC. 1.1321 + void trace(JSTracer *trc); 1.1322 + void writeRelocation(BufferOffset src) { 1.1323 + tmpJumpRelocations_.append(src); 1.1324 + } 1.1325 + 1.1326 + // As opposed to x86/x64 version, the data relocation has to be executed 1.1327 + // before to recover the pointer, and not after. 1.1328 + void writeDataRelocation(const ImmGCPtr &ptr) { 1.1329 + if (ptr.value) 1.1330 + tmpDataRelocations_.append(nextOffset()); 1.1331 + } 1.1332 + void writePrebarrierOffset(CodeOffsetLabel label) { 1.1333 + tmpPreBarriers_.append(BufferOffset(label.offset())); 1.1334 + } 1.1335 + 1.1336 + enum RelocBranchStyle { 1.1337 + B_MOVWT, 1.1338 + B_LDR_BX, 1.1339 + B_LDR, 1.1340 + B_MOVW_ADD 1.1341 + }; 1.1342 + 1.1343 + enum RelocStyle { 1.1344 + L_MOVWT, 1.1345 + L_LDR 1.1346 + }; 1.1347 + 1.1348 + public: 1.1349 + // Given the start of a Control Flow sequence, grab the value that is finally branched to 1.1350 + // given the start of a function that loads an address into a register get the address that 1.1351 + // ends up in the register. 1.1352 + template <class Iter> 1.1353 + static const uint32_t * getCF32Target(Iter *iter); 1.1354 + 1.1355 + static uintptr_t getPointer(uint8_t *); 1.1356 + template <class Iter> 1.1357 + static const uint32_t * getPtr32Target(Iter *iter, Register *dest = nullptr, RelocStyle *rs = nullptr); 1.1358 + 1.1359 + bool oom() const; 1.1360 + 1.1361 + void setPrinter(Sprinter *sp) { 1.1362 + } 1.1363 + 1.1364 + private: 1.1365 + bool isFinished; 1.1366 + public: 1.1367 + void finish(); 1.1368 + void executableCopy(void *buffer); 1.1369 + void copyJumpRelocationTable(uint8_t *dest); 1.1370 + void copyDataRelocationTable(uint8_t *dest); 1.1371 + void copyPreBarrierTable(uint8_t *dest); 1.1372 + 1.1373 + bool addCodeLabel(CodeLabel label); 1.1374 + size_t numCodeLabels() const { 1.1375 + return codeLabels_.length(); 1.1376 + } 1.1377 + CodeLabel codeLabel(size_t i) { 1.1378 + return codeLabels_[i]; 1.1379 + } 1.1380 + 1.1381 + // Size of the instruction stream, in bytes. 1.1382 + size_t size() const; 1.1383 + // Size of the jump relocation table, in bytes. 1.1384 + size_t jumpRelocationTableBytes() const; 1.1385 + size_t dataRelocationTableBytes() const; 1.1386 + size_t preBarrierTableBytes() const; 1.1387 + 1.1388 + // Size of the data table, in bytes. 1.1389 + size_t bytesNeeded() const; 1.1390 + 1.1391 + // Write a blob of binary into the instruction stream *OR* 1.1392 + // into a destination address. If dest is nullptr (the default), then the 1.1393 + // instruction gets written into the instruction stream. If dest is not null 1.1394 + // it is interpreted as a pointer to the location that we want the 1.1395 + // instruction to be written. 1.1396 + BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr); 1.1397 + // A static variant for the cases where we don't want to have an assembler 1.1398 + // object at all. Normally, you would use the dummy (nullptr) object. 1.1399 + static void writeInstStatic(uint32_t x, uint32_t *dest); 1.1400 + 1.1401 + public: 1.1402 + void writeCodePointer(AbsoluteLabel *label); 1.1403 + 1.1404 + BufferOffset align(int alignment); 1.1405 + BufferOffset as_nop(); 1.1406 + BufferOffset as_alu(Register dest, Register src1, Operand2 op2, 1.1407 + ALUOp op, SetCond_ sc = NoSetCond, Condition c = Always, Instruction *instdest = nullptr); 1.1408 + 1.1409 + BufferOffset as_mov(Register dest, 1.1410 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always, Instruction *instdest = nullptr); 1.1411 + BufferOffset as_mvn(Register dest, Operand2 op2, 1.1412 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1413 + // logical operations 1.1414 + BufferOffset as_and(Register dest, Register src1, 1.1415 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1416 + BufferOffset as_bic(Register dest, Register src1, 1.1417 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1418 + BufferOffset as_eor(Register dest, Register src1, 1.1419 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1420 + BufferOffset as_orr(Register dest, Register src1, 1.1421 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1422 + // mathematical operations 1.1423 + BufferOffset as_adc(Register dest, Register src1, 1.1424 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1425 + BufferOffset as_add(Register dest, Register src1, 1.1426 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1427 + BufferOffset as_sbc(Register dest, Register src1, 1.1428 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1429 + BufferOffset as_sub(Register dest, Register src1, 1.1430 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1431 + BufferOffset as_rsb(Register dest, Register src1, 1.1432 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1433 + BufferOffset as_rsc(Register dest, Register src1, 1.1434 + Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always); 1.1435 + // test operations 1.1436 + BufferOffset as_cmn(Register src1, Operand2 op2, 1.1437 + Condition c = Always); 1.1438 + BufferOffset as_cmp(Register src1, Operand2 op2, 1.1439 + Condition c = Always); 1.1440 + BufferOffset as_teq(Register src1, Operand2 op2, 1.1441 + Condition c = Always); 1.1442 + BufferOffset as_tst(Register src1, Operand2 op2, 1.1443 + Condition c = Always); 1.1444 + 1.1445 + // Not quite ALU worthy, but useful none the less: 1.1446 + // These also have the isue of these being formatted 1.1447 + // completly differently from the standard ALU operations. 1.1448 + BufferOffset as_movw(Register dest, Imm16 imm, Condition c = Always, Instruction *pos = nullptr); 1.1449 + BufferOffset as_movt(Register dest, Imm16 imm, Condition c = Always, Instruction *pos = nullptr); 1.1450 + 1.1451 + BufferOffset as_genmul(Register d1, Register d2, Register rm, Register rn, 1.1452 + MULOp op, SetCond_ sc, Condition c = Always); 1.1453 + BufferOffset as_mul(Register dest, Register src1, Register src2, 1.1454 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1455 + BufferOffset as_mla(Register dest, Register acc, Register src1, Register src2, 1.1456 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1457 + BufferOffset as_umaal(Register dest1, Register dest2, Register src1, Register src2, 1.1458 + Condition c = Always); 1.1459 + BufferOffset as_mls(Register dest, Register acc, Register src1, Register src2, 1.1460 + Condition c = Always); 1.1461 + BufferOffset as_umull(Register dest1, Register dest2, Register src1, Register src2, 1.1462 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1463 + BufferOffset as_umlal(Register dest1, Register dest2, Register src1, Register src2, 1.1464 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1465 + BufferOffset as_smull(Register dest1, Register dest2, Register src1, Register src2, 1.1466 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1467 + BufferOffset as_smlal(Register dest1, Register dest2, Register src1, Register src2, 1.1468 + SetCond_ sc = NoSetCond, Condition c = Always); 1.1469 + 1.1470 + BufferOffset as_sdiv(Register dest, Register num, Register div, Condition c = Always); 1.1471 + BufferOffset as_udiv(Register dest, Register num, Register div, Condition c = Always); 1.1472 + 1.1473 + // Data transfer instructions: ldr, str, ldrb, strb. 1.1474 + // Using an int to differentiate between 8 bits and 32 bits is 1.1475 + // overkill, but meh 1.1476 + BufferOffset as_dtr(LoadStore ls, int size, Index mode, 1.1477 + Register rt, DTRAddr addr, Condition c = Always, uint32_t *dest = nullptr); 1.1478 + // Handles all of the other integral data transferring functions: 1.1479 + // ldrsb, ldrsh, ldrd, etc. 1.1480 + // size is given in bits. 1.1481 + BufferOffset as_extdtr(LoadStore ls, int size, bool IsSigned, Index mode, 1.1482 + Register rt, EDtrAddr addr, Condition c = Always, uint32_t *dest = nullptr); 1.1483 + 1.1484 + BufferOffset as_dtm(LoadStore ls, Register rn, uint32_t mask, 1.1485 + DTMMode mode, DTMWriteBack wb, Condition c = Always); 1.1486 + //overwrite a pool entry with new data. 1.1487 + void as_WritePoolEntry(Instruction *addr, Condition c, uint32_t data); 1.1488 + // load a 32 bit immediate from a pool into a register 1.1489 + BufferOffset as_Imm32Pool(Register dest, uint32_t value, Condition c = Always); 1.1490 + // make a patchable jump that can target the entire 32 bit address space. 1.1491 + BufferOffset as_BranchPool(uint32_t value, RepatchLabel *label, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always); 1.1492 + 1.1493 + // load a 64 bit floating point immediate from a pool into a register 1.1494 + BufferOffset as_FImm64Pool(VFPRegister dest, double value, Condition c = Always); 1.1495 + // load a 32 bit floating point immediate from a pool into a register 1.1496 + BufferOffset as_FImm32Pool(VFPRegister dest, float value, Condition c = Always); 1.1497 + 1.1498 + // Control flow stuff: 1.1499 + 1.1500 + // bx can *only* branch to a register 1.1501 + // never to an immediate. 1.1502 + BufferOffset as_bx(Register r, Condition c = Always, bool isPatchable = false); 1.1503 + 1.1504 + // Branch can branch to an immediate *or* to a register. 1.1505 + // Branches to immediates are pc relative, branches to registers 1.1506 + // are absolute 1.1507 + BufferOffset as_b(BOffImm off, Condition c, bool isPatchable = false); 1.1508 + 1.1509 + BufferOffset as_b(Label *l, Condition c = Always, bool isPatchable = false); 1.1510 + BufferOffset as_b(BOffImm off, Condition c, BufferOffset inst); 1.1511 + 1.1512 + // blx can go to either an immediate or a register. 1.1513 + // When blx'ing to a register, we change processor mode 1.1514 + // depending on the low bit of the register 1.1515 + // when blx'ing to an immediate, we *always* change processor state. 1.1516 + BufferOffset as_blx(Label *l); 1.1517 + 1.1518 + BufferOffset as_blx(Register r, Condition c = Always); 1.1519 + BufferOffset as_bl(BOffImm off, Condition c); 1.1520 + // bl can only branch+link to an immediate, never to a register 1.1521 + // it never changes processor state 1.1522 + BufferOffset as_bl(); 1.1523 + // bl #imm can have a condition code, blx #imm cannot. 1.1524 + // blx reg can be conditional. 1.1525 + BufferOffset as_bl(Label *l, Condition c); 1.1526 + BufferOffset as_bl(BOffImm off, Condition c, BufferOffset inst); 1.1527 + 1.1528 + BufferOffset as_mrs(Register r, Condition c = Always); 1.1529 + BufferOffset as_msr(Register r, Condition c = Always); 1.1530 + // VFP instructions! 1.1531 + private: 1.1532 + 1.1533 + enum vfp_size { 1.1534 + isDouble = 1 << 8, 1.1535 + isSingle = 0 << 8 1.1536 + }; 1.1537 + 1.1538 + BufferOffset writeVFPInst(vfp_size sz, uint32_t blob, uint32_t *dest=nullptr); 1.1539 + // Unityped variants: all registers hold the same (ieee754 single/double) 1.1540 + // notably not included are vcvt; vmov vd, #imm; vmov rt, vn. 1.1541 + BufferOffset as_vfp_float(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1542 + VFPOp op, Condition c = Always); 1.1543 + 1.1544 + public: 1.1545 + BufferOffset as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1546 + Condition c = Always); 1.1547 + 1.1548 + BufferOffset as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1549 + Condition c = Always); 1.1550 + 1.1551 + BufferOffset as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1552 + Condition c = Always); 1.1553 + 1.1554 + BufferOffset as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1555 + Condition c = Always); 1.1556 + 1.1557 + BufferOffset as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1558 + Condition c = Always); 1.1559 + 1.1560 + BufferOffset as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1561 + Condition c = Always); 1.1562 + 1.1563 + BufferOffset as_vneg(VFPRegister vd, VFPRegister vm, Condition c = Always); 1.1564 + 1.1565 + BufferOffset as_vsqrt(VFPRegister vd, VFPRegister vm, Condition c = Always); 1.1566 + 1.1567 + BufferOffset as_vabs(VFPRegister vd, VFPRegister vm, Condition c = Always); 1.1568 + 1.1569 + BufferOffset as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm, 1.1570 + Condition c = Always); 1.1571 + 1.1572 + BufferOffset as_vcmp(VFPRegister vd, VFPRegister vm, 1.1573 + Condition c = Always); 1.1574 + BufferOffset as_vcmpz(VFPRegister vd, Condition c = Always); 1.1575 + 1.1576 + // specifically, a move between two same sized-registers 1.1577 + BufferOffset as_vmov(VFPRegister vd, VFPRegister vsrc, Condition c = Always); 1.1578 + /*xfer between Core and VFP*/ 1.1579 + enum FloatToCore_ { 1.1580 + FloatToCore = 1 << 20, 1.1581 + CoreToFloat = 0 << 20 1.1582 + }; 1.1583 + 1.1584 + private: 1.1585 + enum VFPXferSize { 1.1586 + WordTransfer = 0x02000010, 1.1587 + DoubleTransfer = 0x00400010 1.1588 + }; 1.1589 + 1.1590 + public: 1.1591 + // Unlike the next function, moving between the core registers and vfp 1.1592 + // registers can't be *that* properly typed. Namely, since I don't want to 1.1593 + // munge the type VFPRegister to also include core registers. Thus, the core 1.1594 + // and vfp registers are passed in based on their type, and src/dest is 1.1595 + // determined by the float2core. 1.1596 + 1.1597 + BufferOffset as_vxfer(Register vt1, Register vt2, VFPRegister vm, FloatToCore_ f2c, 1.1598 + Condition c = Always, int idx = 0); 1.1599 + 1.1600 + // our encoding actually allows just the src and the dest (and theiyr types) 1.1601 + // to uniquely specify the encoding that we are going to use. 1.1602 + BufferOffset as_vcvt(VFPRegister vd, VFPRegister vm, bool useFPSCR = false, 1.1603 + Condition c = Always); 1.1604 + // hard coded to a 32 bit fixed width result for now 1.1605 + BufferOffset as_vcvtFixed(VFPRegister vd, bool isSigned, uint32_t fixedPoint, bool toFixed, Condition c = Always); 1.1606 + 1.1607 + /* xfer between VFP and memory*/ 1.1608 + BufferOffset as_vdtr(LoadStore ls, VFPRegister vd, VFPAddr addr, 1.1609 + Condition c = Always /* vfp doesn't have a wb option*/, 1.1610 + uint32_t *dest = nullptr); 1.1611 + 1.1612 + // VFP's ldm/stm work differently from the standard arm ones. 1.1613 + // You can only transfer a range 1.1614 + 1.1615 + BufferOffset as_vdtm(LoadStore st, Register rn, VFPRegister vd, int length, 1.1616 + /*also has update conditions*/Condition c = Always); 1.1617 + 1.1618 + BufferOffset as_vimm(VFPRegister vd, VFPImm imm, Condition c = Always); 1.1619 + 1.1620 + BufferOffset as_vmrs(Register r, Condition c = Always); 1.1621 + BufferOffset as_vmsr(Register r, Condition c = Always); 1.1622 + // label operations 1.1623 + bool nextLink(BufferOffset b, BufferOffset *next); 1.1624 + void bind(Label *label, BufferOffset boff = BufferOffset()); 1.1625 + void bind(RepatchLabel *label); 1.1626 + uint32_t currentOffset() { 1.1627 + return nextOffset().getOffset(); 1.1628 + } 1.1629 + void retarget(Label *label, Label *target); 1.1630 + // I'm going to pretend this doesn't exist for now. 1.1631 + void retarget(Label *label, void *target, Relocation::Kind reloc); 1.1632 + 1.1633 + void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address); 1.1634 + 1.1635 + // See Bind 1.1636 + size_t labelOffsetToPatchOffset(size_t offset) { 1.1637 + return actualOffset(offset); 1.1638 + } 1.1639 + 1.1640 + void call(Label *label); 1.1641 + void call(void *target); 1.1642 + 1.1643 + void as_bkpt(); 1.1644 + 1.1645 + public: 1.1646 + static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); 1.1647 + static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); 1.1648 + 1.1649 + protected: 1.1650 + void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { 1.1651 + enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind)); 1.1652 + if (kind == Relocation::JITCODE) 1.1653 + writeRelocation(src); 1.1654 + } 1.1655 + 1.1656 + public: 1.1657 + // The buffer is about to be linked, make sure any constant pools or excess 1.1658 + // bookkeeping has been flushed to the instruction stream. 1.1659 + void flush() { 1.1660 + JS_ASSERT(!isFinished); 1.1661 + m_buffer.flushPool(); 1.1662 + return; 1.1663 + } 1.1664 + 1.1665 + // Copy the assembly code to the given buffer, and perform any pending 1.1666 + // relocations relying on the target address. 1.1667 + void executableCopy(uint8_t *buffer); 1.1668 + 1.1669 + // Actual assembly emitting functions. 1.1670 + 1.1671 + // Since I can't think of a reasonable default for the mode, I'm going to 1.1672 + // leave it as a required argument. 1.1673 + void startDataTransferM(LoadStore ls, Register rm, 1.1674 + DTMMode mode, DTMWriteBack update = NoWriteBack, 1.1675 + Condition c = Always) 1.1676 + { 1.1677 + JS_ASSERT(!dtmActive); 1.1678 + dtmUpdate = update; 1.1679 + dtmBase = rm; 1.1680 + dtmLoadStore = ls; 1.1681 + dtmLastReg = -1; 1.1682 + dtmRegBitField = 0; 1.1683 + dtmActive = 1; 1.1684 + dtmCond = c; 1.1685 + dtmMode = mode; 1.1686 + } 1.1687 + 1.1688 + void transferReg(Register rn) { 1.1689 + JS_ASSERT(dtmActive); 1.1690 + JS_ASSERT(rn.code() > dtmLastReg); 1.1691 + dtmRegBitField |= 1 << rn.code(); 1.1692 + if (dtmLoadStore == IsLoad && rn.code() == 13 && dtmBase.code() == 13) { 1.1693 + MOZ_ASSUME_UNREACHABLE("ARM Spec says this is invalid"); 1.1694 + } 1.1695 + } 1.1696 + void finishDataTransfer() { 1.1697 + dtmActive = false; 1.1698 + as_dtm(dtmLoadStore, dtmBase, dtmRegBitField, dtmMode, dtmUpdate, dtmCond); 1.1699 + } 1.1700 + 1.1701 + void startFloatTransferM(LoadStore ls, Register rm, 1.1702 + DTMMode mode, DTMWriteBack update = NoWriteBack, 1.1703 + Condition c = Always) 1.1704 + { 1.1705 + JS_ASSERT(!dtmActive); 1.1706 + dtmActive = true; 1.1707 + dtmUpdate = update; 1.1708 + dtmLoadStore = ls; 1.1709 + dtmBase = rm; 1.1710 + dtmCond = c; 1.1711 + dtmLastReg = -1; 1.1712 + dtmMode = mode; 1.1713 + dtmDelta = 0; 1.1714 + } 1.1715 + void transferFloatReg(VFPRegister rn) 1.1716 + { 1.1717 + if (dtmLastReg == -1) { 1.1718 + vdtmFirstReg = rn.code(); 1.1719 + } else { 1.1720 + if (dtmDelta == 0) { 1.1721 + dtmDelta = rn.code() - dtmLastReg; 1.1722 + JS_ASSERT(dtmDelta == 1 || dtmDelta == -1); 1.1723 + } 1.1724 + JS_ASSERT(dtmLastReg >= 0); 1.1725 + JS_ASSERT(rn.code() == unsigned(dtmLastReg) + dtmDelta); 1.1726 + } 1.1727 + dtmLastReg = rn.code(); 1.1728 + } 1.1729 + void finishFloatTransfer() { 1.1730 + JS_ASSERT(dtmActive); 1.1731 + dtmActive = false; 1.1732 + JS_ASSERT(dtmLastReg != -1); 1.1733 + dtmDelta = dtmDelta ? dtmDelta : 1; 1.1734 + // fencepost problem. 1.1735 + int len = dtmDelta * (dtmLastReg - vdtmFirstReg) + 1; 1.1736 + as_vdtm(dtmLoadStore, dtmBase, 1.1737 + VFPRegister(FloatRegister::FromCode(Min(vdtmFirstReg, dtmLastReg))), 1.1738 + len, dtmCond); 1.1739 + } 1.1740 + 1.1741 + private: 1.1742 + int dtmRegBitField; 1.1743 + int vdtmFirstReg; 1.1744 + int dtmLastReg; 1.1745 + int dtmDelta; 1.1746 + Register dtmBase; 1.1747 + DTMWriteBack dtmUpdate; 1.1748 + DTMMode dtmMode; 1.1749 + LoadStore dtmLoadStore; 1.1750 + bool dtmActive; 1.1751 + Condition dtmCond; 1.1752 + 1.1753 + public: 1.1754 + enum { 1.1755 + padForAlign8 = (int)0x00, 1.1756 + padForAlign16 = (int)0x0000, 1.1757 + padForAlign32 = (int)0xe12fff7f // 'bkpt 0xffff' 1.1758 + }; 1.1759 + 1.1760 + // API for speaking with the IonAssemblerBufferWithConstantPools 1.1761 + // generate an initial placeholder instruction that we want to later fix up 1.1762 + static void insertTokenIntoTag(uint32_t size, uint8_t *load, int32_t token); 1.1763 + // take the stub value that was written in before, and write in an actual load 1.1764 + // using the index we'd computed previously as well as the address of the pool start. 1.1765 + static bool patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); 1.1766 + // this is a callback for when we have filled a pool, and MUST flush it now. 1.1767 + // The pool requires the assembler to place a branch past the pool, and it 1.1768 + // calls this function. 1.1769 + static uint32_t placeConstantPoolBarrier(int offset); 1.1770 + // END API 1.1771 + 1.1772 + // move our entire pool into the instruction stream 1.1773 + // This is to force an opportunistic dump of the pool, prefferably when it 1.1774 + // is more convenient to do a dump. 1.1775 + void dumpPool(); 1.1776 + void flushBuffer(); 1.1777 + void enterNoPool(); 1.1778 + void leaveNoPool(); 1.1779 + // this should return a BOffImm, but I didn't want to require everyplace that used the 1.1780 + // AssemblerBuffer to make that class. 1.1781 + static ptrdiff_t getBranchOffset(const Instruction *i); 1.1782 + static void retargetNearBranch(Instruction *i, int offset, Condition cond, bool final = true); 1.1783 + static void retargetNearBranch(Instruction *i, int offset, bool final = true); 1.1784 + static void retargetFarBranch(Instruction *i, uint8_t **slot, uint8_t *dest, Condition cond); 1.1785 + 1.1786 + static void writePoolHeader(uint8_t *start, Pool *p, bool isNatural); 1.1787 + static void writePoolFooter(uint8_t *start, Pool *p, bool isNatural); 1.1788 + static void writePoolGuard(BufferOffset branch, Instruction *inst, BufferOffset dest); 1.1789 + 1.1790 + 1.1791 + static uint32_t patchWrite_NearCallSize(); 1.1792 + static uint32_t nopSize() { return 4; } 1.1793 + static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); 1.1794 + static void patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, 1.1795 + PatchedImmPtr expectedValue); 1.1796 + static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, 1.1797 + ImmPtr expectedValue); 1.1798 + static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); 1.1799 + static uint32_t alignDoubleArg(uint32_t offset) { 1.1800 + return (offset+1)&~1; 1.1801 + } 1.1802 + static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = nullptr); 1.1803 + // Toggle a jmp or cmp emitted by toggledJump(). 1.1804 + 1.1805 + static void ToggleToJmp(CodeLocationLabel inst_); 1.1806 + static void ToggleToCmp(CodeLocationLabel inst_); 1.1807 + 1.1808 + static void ToggleCall(CodeLocationLabel inst_, bool enabled); 1.1809 + 1.1810 + static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst); 1.1811 + void processCodeLabels(uint8_t *rawCode); 1.1812 + bool bailed() { 1.1813 + return m_buffer.bail(); 1.1814 + } 1.1815 +}; // Assembler 1.1816 + 1.1817 +// An Instruction is a structure for both encoding and decoding any and all ARM instructions. 1.1818 +// many classes have not been implemented thusfar. 1.1819 +class Instruction 1.1820 +{ 1.1821 + uint32_t data; 1.1822 + 1.1823 + protected: 1.1824 + // This is not for defaulting to always, this is for instructions that 1.1825 + // cannot be made conditional, and have the usually invalid 4b1111 cond field 1.1826 + Instruction (uint32_t data_, bool fake = false) : data(data_ | 0xf0000000) { 1.1827 + JS_ASSERT (fake || ((data_ & 0xf0000000) == 0)); 1.1828 + } 1.1829 + // Standard constructor 1.1830 + Instruction (uint32_t data_, Assembler::Condition c) : data(data_ | (uint32_t) c) { 1.1831 + JS_ASSERT ((data_ & 0xf0000000) == 0); 1.1832 + } 1.1833 + // You should never create an instruction directly. You should create a 1.1834 + // more specific instruction which will eventually call one of these 1.1835 + // constructors for you. 1.1836 + public: 1.1837 + uint32_t encode() const { 1.1838 + return data; 1.1839 + } 1.1840 + // Check if this instruction is really a particular case 1.1841 + template <class C> 1.1842 + bool is() const { return C::isTHIS(*this); } 1.1843 + 1.1844 + // safely get a more specific variant of this pointer 1.1845 + template <class C> 1.1846 + C *as() const { return C::asTHIS(*this); } 1.1847 + 1.1848 + const Instruction & operator=(const Instruction &src) { 1.1849 + data = src.data; 1.1850 + return *this; 1.1851 + } 1.1852 + // Since almost all instructions have condition codes, the condition 1.1853 + // code extractor resides in the base class. 1.1854 + void extractCond(Assembler::Condition *c) { 1.1855 + if (data >> 28 != 0xf ) 1.1856 + *c = (Assembler::Condition)(data & 0xf0000000); 1.1857 + } 1.1858 + // Get the next instruction in the instruction stream. 1.1859 + // This does neat things like ignoreconstant pools and their guards. 1.1860 + Instruction *next(); 1.1861 + 1.1862 + // Sometimes, an api wants a uint32_t (or a pointer to it) rather than 1.1863 + // an instruction. raw() just coerces this into a pointer to a uint32_t 1.1864 + const uint32_t *raw() const { return &data; } 1.1865 + uint32_t size() const { return 4; } 1.1866 +}; // Instruction 1.1867 + 1.1868 +// make sure that it is the right size 1.1869 +JS_STATIC_ASSERT(sizeof(Instruction) == 4); 1.1870 + 1.1871 +// Data Transfer Instructions 1.1872 +class InstDTR : public Instruction 1.1873 +{ 1.1874 + public: 1.1875 + enum IsByte_ { 1.1876 + IsByte = 0x00400000, 1.1877 + IsWord = 0x00000000 1.1878 + }; 1.1879 + static const int IsDTR = 0x04000000; 1.1880 + static const int IsDTRMask = 0x0c000000; 1.1881 + 1.1882 + // TODO: Replace the initialization with something that is safer. 1.1883 + InstDTR(LoadStore ls, IsByte_ ib, Index mode, Register rt, DTRAddr addr, Assembler::Condition c) 1.1884 + : Instruction(ls | ib | mode | RT(rt) | addr.encode() | IsDTR, c) 1.1885 + { } 1.1886 + 1.1887 + static bool isTHIS(const Instruction &i); 1.1888 + static InstDTR *asTHIS(const Instruction &i); 1.1889 + 1.1890 +}; 1.1891 +JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(Instruction)); 1.1892 + 1.1893 +class InstLDR : public InstDTR 1.1894 +{ 1.1895 + public: 1.1896 + InstLDR(Index mode, Register rt, DTRAddr addr, Assembler::Condition c) 1.1897 + : InstDTR(IsLoad, IsWord, mode, rt, addr, c) 1.1898 + { } 1.1899 + static bool isTHIS(const Instruction &i); 1.1900 + static InstLDR *asTHIS(const Instruction &i); 1.1901 + 1.1902 +}; 1.1903 +JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(InstLDR)); 1.1904 + 1.1905 +class InstNOP : public Instruction 1.1906 +{ 1.1907 + static const uint32_t NopInst = 0x0320f000; 1.1908 + 1.1909 + public: 1.1910 + InstNOP() 1.1911 + : Instruction(NopInst, Assembler::Always) 1.1912 + { } 1.1913 + 1.1914 + static bool isTHIS(const Instruction &i); 1.1915 + static InstNOP *asTHIS(Instruction &i); 1.1916 +}; 1.1917 + 1.1918 +// Branching to a register, or calling a register 1.1919 +class InstBranchReg : public Instruction 1.1920 +{ 1.1921 + protected: 1.1922 + // Don't use BranchTag yourself, use a derived instruction. 1.1923 + enum BranchTag { 1.1924 + IsBX = 0x012fff10, 1.1925 + IsBLX = 0x012fff30 1.1926 + }; 1.1927 + static const uint32_t IsBRegMask = 0x0ffffff0; 1.1928 + InstBranchReg(BranchTag tag, Register rm, Assembler::Condition c) 1.1929 + : Instruction(tag | rm.code(), c) 1.1930 + { } 1.1931 + public: 1.1932 + static bool isTHIS (const Instruction &i); 1.1933 + static InstBranchReg *asTHIS (const Instruction &i); 1.1934 + // Get the register that is being branched to 1.1935 + void extractDest(Register *dest); 1.1936 + // Make sure we are branching to a pre-known register 1.1937 + bool checkDest(Register dest); 1.1938 +}; 1.1939 +JS_STATIC_ASSERT(sizeof(InstBranchReg) == sizeof(Instruction)); 1.1940 + 1.1941 +// Branching to an immediate offset, or calling an immediate offset 1.1942 +class InstBranchImm : public Instruction 1.1943 +{ 1.1944 + protected: 1.1945 + enum BranchTag { 1.1946 + IsB = 0x0a000000, 1.1947 + IsBL = 0x0b000000 1.1948 + }; 1.1949 + static const uint32_t IsBImmMask = 0x0f000000; 1.1950 + 1.1951 + InstBranchImm(BranchTag tag, BOffImm off, Assembler::Condition c) 1.1952 + : Instruction(tag | off.encode(), c) 1.1953 + { } 1.1954 + 1.1955 + public: 1.1956 + static bool isTHIS (const Instruction &i); 1.1957 + static InstBranchImm *asTHIS (const Instruction &i); 1.1958 + void extractImm(BOffImm *dest); 1.1959 +}; 1.1960 +JS_STATIC_ASSERT(sizeof(InstBranchImm) == sizeof(Instruction)); 1.1961 + 1.1962 +// Very specific branching instructions. 1.1963 +class InstBXReg : public InstBranchReg 1.1964 +{ 1.1965 + public: 1.1966 + static bool isTHIS (const Instruction &i); 1.1967 + static InstBXReg *asTHIS (const Instruction &i); 1.1968 +}; 1.1969 +class InstBLXReg : public InstBranchReg 1.1970 +{ 1.1971 + public: 1.1972 + InstBLXReg(Register reg, Assembler::Condition c) 1.1973 + : InstBranchReg(IsBLX, reg, c) 1.1974 + { } 1.1975 + 1.1976 + static bool isTHIS (const Instruction &i); 1.1977 + static InstBLXReg *asTHIS (const Instruction &i); 1.1978 +}; 1.1979 +class InstBImm : public InstBranchImm 1.1980 +{ 1.1981 + public: 1.1982 + InstBImm(BOffImm off, Assembler::Condition c) 1.1983 + : InstBranchImm(IsB, off, c) 1.1984 + { } 1.1985 + 1.1986 + static bool isTHIS (const Instruction &i); 1.1987 + static InstBImm *asTHIS (const Instruction &i); 1.1988 +}; 1.1989 +class InstBLImm : public InstBranchImm 1.1990 +{ 1.1991 + public: 1.1992 + InstBLImm(BOffImm off, Assembler::Condition c) 1.1993 + : InstBranchImm(IsBL, off, c) 1.1994 + { } 1.1995 + 1.1996 + static bool isTHIS (const Instruction &i); 1.1997 + static InstBLImm *asTHIS (Instruction &i); 1.1998 +}; 1.1999 + 1.2000 +// Both movw and movt. The layout of both the immediate and the destination 1.2001 +// register is the same so the code is being shared. 1.2002 +class InstMovWT : public Instruction 1.2003 +{ 1.2004 + protected: 1.2005 + enum WT { 1.2006 + IsW = 0x03000000, 1.2007 + IsT = 0x03400000 1.2008 + }; 1.2009 + static const uint32_t IsWTMask = 0x0ff00000; 1.2010 + 1.2011 + InstMovWT(Register rd, Imm16 imm, WT wt, Assembler::Condition c) 1.2012 + : Instruction (RD(rd) | imm.encode() | wt, c) 1.2013 + { } 1.2014 + 1.2015 + public: 1.2016 + void extractImm(Imm16 *dest); 1.2017 + void extractDest(Register *dest); 1.2018 + bool checkImm(Imm16 dest); 1.2019 + bool checkDest(Register dest); 1.2020 + 1.2021 + static bool isTHIS (Instruction &i); 1.2022 + static InstMovWT *asTHIS (Instruction &i); 1.2023 + 1.2024 +}; 1.2025 +JS_STATIC_ASSERT(sizeof(InstMovWT) == sizeof(Instruction)); 1.2026 + 1.2027 +class InstMovW : public InstMovWT 1.2028 +{ 1.2029 + public: 1.2030 + InstMovW (Register rd, Imm16 imm, Assembler::Condition c) 1.2031 + : InstMovWT(rd, imm, IsW, c) 1.2032 + { } 1.2033 + 1.2034 + static bool isTHIS (const Instruction &i); 1.2035 + static InstMovW *asTHIS (const Instruction &i); 1.2036 +}; 1.2037 + 1.2038 +class InstMovT : public InstMovWT 1.2039 +{ 1.2040 + public: 1.2041 + InstMovT (Register rd, Imm16 imm, Assembler::Condition c) 1.2042 + : InstMovWT(rd, imm, IsT, c) 1.2043 + { } 1.2044 + static bool isTHIS (const Instruction &i); 1.2045 + static InstMovT *asTHIS (const Instruction &i); 1.2046 +}; 1.2047 + 1.2048 +class InstALU : public Instruction 1.2049 +{ 1.2050 + static const int32_t ALUMask = 0xc << 24; 1.2051 + public: 1.2052 + InstALU (Register rd, Register rn, Operand2 op2, ALUOp op, SetCond_ sc, Assembler::Condition c) 1.2053 + : Instruction(maybeRD(rd) | maybeRN(rn) | op2.encode() | op | sc, c) 1.2054 + { } 1.2055 + static bool isTHIS (const Instruction &i); 1.2056 + static InstALU *asTHIS (const Instruction &i); 1.2057 + void extractOp(ALUOp *ret); 1.2058 + bool checkOp(ALUOp op); 1.2059 + void extractDest(Register *ret); 1.2060 + bool checkDest(Register rd); 1.2061 + void extractOp1(Register *ret); 1.2062 + bool checkOp1(Register rn); 1.2063 + Operand2 extractOp2(); 1.2064 +}; 1.2065 + 1.2066 +class InstCMP : public InstALU 1.2067 +{ 1.2068 + public: 1.2069 + static bool isTHIS (const Instruction &i); 1.2070 + static InstCMP *asTHIS (const Instruction &i); 1.2071 +}; 1.2072 + 1.2073 +class InstMOV : public InstALU 1.2074 +{ 1.2075 + public: 1.2076 + static bool isTHIS (const Instruction &i); 1.2077 + static InstMOV *asTHIS (const Instruction &i); 1.2078 +}; 1.2079 + 1.2080 + 1.2081 +class InstructionIterator { 1.2082 + private: 1.2083 + Instruction *i; 1.2084 + public: 1.2085 + InstructionIterator(Instruction *i_); 1.2086 + Instruction *next() { 1.2087 + i = i->next(); 1.2088 + return cur(); 1.2089 + } 1.2090 + Instruction *cur() const { 1.2091 + return i; 1.2092 + } 1.2093 +}; 1.2094 + 1.2095 +static const uint32_t NumIntArgRegs = 4; 1.2096 +static const uint32_t NumFloatArgRegs = 8; 1.2097 + 1.2098 +static inline bool 1.2099 +GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out) 1.2100 +{ 1.2101 + if (usedIntArgs >= NumIntArgRegs) 1.2102 + return false; 1.2103 + *out = Register::FromCode(usedIntArgs); 1.2104 + return true; 1.2105 +} 1.2106 + 1.2107 +// Get a register in which we plan to put a quantity that will be used as an 1.2108 +// integer argument. This differs from GetIntArgReg in that if we have no more 1.2109 +// actual argument registers to use we will fall back on using whatever 1.2110 +// CallTempReg* don't overlap the argument registers, and only fail once those 1.2111 +// run out too. 1.2112 +static inline bool 1.2113 +GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out) 1.2114 +{ 1.2115 + if (GetIntArgReg(usedIntArgs, usedFloatArgs, out)) 1.2116 + return true; 1.2117 + // Unfortunately, we have to assume things about the point at which 1.2118 + // GetIntArgReg returns false, because we need to know how many registers it 1.2119 + // can allocate. 1.2120 + usedIntArgs -= NumIntArgRegs; 1.2121 + if (usedIntArgs >= NumCallTempNonArgRegs) 1.2122 + return false; 1.2123 + *out = CallTempNonArgRegs[usedIntArgs]; 1.2124 + return true; 1.2125 +} 1.2126 + 1.2127 + 1.2128 +#if !defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR) 1.2129 + 1.2130 +static inline uint32_t 1.2131 +GetArgStackDisp(uint32_t arg) 1.2132 +{ 1.2133 + JS_ASSERT(!useHardFpABI()); 1.2134 + JS_ASSERT(arg >= NumIntArgRegs); 1.2135 + return (arg - NumIntArgRegs) * sizeof(intptr_t); 1.2136 +} 1.2137 + 1.2138 +#endif 1.2139 + 1.2140 + 1.2141 +#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR) 1.2142 + 1.2143 +static inline bool 1.2144 +GetFloatArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, FloatRegister *out) 1.2145 +{ 1.2146 + JS_ASSERT(useHardFpABI()); 1.2147 + if (usedFloatArgs >= NumFloatArgRegs) 1.2148 + return false; 1.2149 + *out = FloatRegister::FromCode(usedFloatArgs); 1.2150 + return true; 1.2151 +} 1.2152 + 1.2153 +static inline uint32_t 1.2154 +GetIntArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) 1.2155 +{ 1.2156 + JS_ASSERT(useHardFpABI()); 1.2157 + JS_ASSERT(usedIntArgs >= NumIntArgRegs); 1.2158 + uint32_t doubleSlots = Max(0, (int32_t)usedFloatArgs - (int32_t)NumFloatArgRegs); 1.2159 + doubleSlots *= 2; 1.2160 + int intSlots = usedIntArgs - NumIntArgRegs; 1.2161 + return (intSlots + doubleSlots + *padding) * sizeof(intptr_t); 1.2162 +} 1.2163 + 1.2164 +static inline uint32_t 1.2165 +GetFloat32ArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) 1.2166 +{ 1.2167 + JS_ASSERT(useHardFpABI()); 1.2168 + JS_ASSERT(usedFloatArgs >= NumFloatArgRegs); 1.2169 + uint32_t intSlots = 0; 1.2170 + if (usedIntArgs > NumIntArgRegs) 1.2171 + intSlots = usedIntArgs - NumIntArgRegs; 1.2172 + uint32_t float32Slots = usedFloatArgs - NumFloatArgRegs; 1.2173 + return (intSlots + float32Slots + *padding) * sizeof(intptr_t); 1.2174 +} 1.2175 + 1.2176 +static inline uint32_t 1.2177 +GetDoubleArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding) 1.2178 +{ 1.2179 + JS_ASSERT(useHardFpABI()); 1.2180 + JS_ASSERT(usedFloatArgs >= NumFloatArgRegs); 1.2181 + uint32_t intSlots = 0; 1.2182 + if (usedIntArgs > NumIntArgRegs) { 1.2183 + intSlots = usedIntArgs - NumIntArgRegs; 1.2184 + // update the amount of padding required. 1.2185 + *padding += (*padding + usedIntArgs) % 2; 1.2186 + } 1.2187 + uint32_t doubleSlots = usedFloatArgs - NumFloatArgRegs; 1.2188 + doubleSlots *= 2; 1.2189 + return (intSlots + doubleSlots + *padding) * sizeof(intptr_t); 1.2190 +} 1.2191 + 1.2192 +#endif 1.2193 + 1.2194 + 1.2195 + 1.2196 +class DoubleEncoder { 1.2197 + uint32_t rep(bool b, uint32_t count) { 1.2198 + uint32_t ret = 0; 1.2199 + for (uint32_t i = 0; i < count; i++) 1.2200 + ret = (ret << 1) | b; 1.2201 + return ret; 1.2202 + } 1.2203 + 1.2204 + uint32_t encode(uint8_t value) { 1.2205 + //ARM ARM "VFP modified immediate constants" 1.2206 + // aBbbbbbb bbcdefgh 000... 1.2207 + // we want to return the top 32 bits of the double 1.2208 + // the rest are 0. 1.2209 + bool a = value >> 7; 1.2210 + bool b = value >> 6 & 1; 1.2211 + bool B = !b; 1.2212 + uint32_t cdefgh = value & 0x3f; 1.2213 + return a << 31 | 1.2214 + B << 30 | 1.2215 + rep(b, 8) << 22 | 1.2216 + cdefgh << 16; 1.2217 + } 1.2218 + 1.2219 + struct DoubleEntry 1.2220 + { 1.2221 + uint32_t dblTop; 1.2222 + datastore::Imm8VFPImmData data; 1.2223 + 1.2224 + DoubleEntry() 1.2225 + : dblTop(-1) 1.2226 + { } 1.2227 + DoubleEntry(uint32_t dblTop_, datastore::Imm8VFPImmData data_) 1.2228 + : dblTop(dblTop_), data(data_) 1.2229 + { } 1.2230 + }; 1.2231 + 1.2232 + mozilla::Array<DoubleEntry, 256> table; 1.2233 + 1.2234 + public: 1.2235 + DoubleEncoder() 1.2236 + { 1.2237 + for (int i = 0; i < 256; i++) { 1.2238 + table[i] = DoubleEntry(encode(i), datastore::Imm8VFPImmData(i)); 1.2239 + } 1.2240 + } 1.2241 + 1.2242 + bool lookup(uint32_t top, datastore::Imm8VFPImmData *ret) { 1.2243 + for (int i = 0; i < 256; i++) { 1.2244 + if (table[i].dblTop == top) { 1.2245 + *ret = table[i].data; 1.2246 + return true; 1.2247 + } 1.2248 + } 1.2249 + return false; 1.2250 + } 1.2251 +}; 1.2252 + 1.2253 +class AutoForbidPools { 1.2254 + Assembler *masm_; 1.2255 + public: 1.2256 + AutoForbidPools(Assembler *masm) : masm_(masm) { 1.2257 + masm_->enterNoPool(); 1.2258 + } 1.2259 + ~AutoForbidPools() { 1.2260 + masm_->leaveNoPool(); 1.2261 + } 1.2262 +}; 1.2263 + 1.2264 +} // namespace jit 1.2265 +} // namespace js 1.2266 + 1.2267 +#endif /* jit_arm_Assembler_arm_h */