js/src/jit/arm/Assembler-arm.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef jit_arm_Assembler_arm_h
michael@0 8 #define jit_arm_Assembler_arm_h
michael@0 9
michael@0 10 #include "mozilla/ArrayUtils.h"
michael@0 11 #include "mozilla/Attributes.h"
michael@0 12 #include "mozilla/MathAlgorithms.h"
michael@0 13
michael@0 14 #include "assembler/assembler/AssemblerBufferWithConstantPool.h"
michael@0 15 #include "jit/arm/Architecture-arm.h"
michael@0 16 #include "jit/CompactBuffer.h"
michael@0 17 #include "jit/IonCode.h"
michael@0 18 #include "jit/shared/Assembler-shared.h"
michael@0 19 #include "jit/shared/IonAssemblerBufferWithConstantPools.h"
michael@0 20
michael@0 21 namespace js {
michael@0 22 namespace jit {
michael@0 23
michael@0 24 //NOTE: there are duplicates in this list!
michael@0 25 // sometimes we want to specifically refer to the
michael@0 26 // link register as a link register (bl lr is much
michael@0 27 // clearer than bl r14). HOWEVER, this register can
michael@0 28 // easily be a gpr when it is not busy holding the return
michael@0 29 // address.
michael@0 30 static MOZ_CONSTEXPR_VAR Register r0 = { Registers::r0 };
michael@0 31 static MOZ_CONSTEXPR_VAR Register r1 = { Registers::r1 };
michael@0 32 static MOZ_CONSTEXPR_VAR Register r2 = { Registers::r2 };
michael@0 33 static MOZ_CONSTEXPR_VAR Register r3 = { Registers::r3 };
michael@0 34 static MOZ_CONSTEXPR_VAR Register r4 = { Registers::r4 };
michael@0 35 static MOZ_CONSTEXPR_VAR Register r5 = { Registers::r5 };
michael@0 36 static MOZ_CONSTEXPR_VAR Register r6 = { Registers::r6 };
michael@0 37 static MOZ_CONSTEXPR_VAR Register r7 = { Registers::r7 };
michael@0 38 static MOZ_CONSTEXPR_VAR Register r8 = { Registers::r8 };
michael@0 39 static MOZ_CONSTEXPR_VAR Register r9 = { Registers::r9 };
michael@0 40 static MOZ_CONSTEXPR_VAR Register r10 = { Registers::r10 };
michael@0 41 static MOZ_CONSTEXPR_VAR Register r11 = { Registers::r11 };
michael@0 42 static MOZ_CONSTEXPR_VAR Register r12 = { Registers::ip };
michael@0 43 static MOZ_CONSTEXPR_VAR Register ip = { Registers::ip };
michael@0 44 static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp };
michael@0 45 static MOZ_CONSTEXPR_VAR Register r14 = { Registers::lr };
michael@0 46 static MOZ_CONSTEXPR_VAR Register lr = { Registers::lr };
michael@0 47 static MOZ_CONSTEXPR_VAR Register pc = { Registers::pc };
michael@0 48
michael@0 49 static MOZ_CONSTEXPR_VAR Register ScratchRegister = {Registers::ip};
michael@0 50
michael@0 51 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = r3;
michael@0 52 static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = r8;
michael@0 53 static MOZ_CONSTEXPR_VAR Register CallTempReg0 = r5;
michael@0 54 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = r6;
michael@0 55 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = r7;
michael@0 56 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = r8;
michael@0 57 static MOZ_CONSTEXPR_VAR Register CallTempReg4 = r0;
michael@0 58 static MOZ_CONSTEXPR_VAR Register CallTempReg5 = r1;
michael@0 59
michael@0 60 static MOZ_CONSTEXPR_VAR Register IntArgReg0 = r0;
michael@0 61 static MOZ_CONSTEXPR_VAR Register IntArgReg1 = r1;
michael@0 62 static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r2;
michael@0 63 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r3;
michael@0 64 static MOZ_CONSTEXPR_VAR Register GlobalReg = r10;
michael@0 65 static MOZ_CONSTEXPR_VAR Register HeapReg = r11;
michael@0 66 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { r5, r6, r7, r8 };
michael@0 67 static const uint32_t NumCallTempNonArgRegs =
michael@0 68 mozilla::ArrayLength(CallTempNonArgRegs);
michael@0 69 class ABIArgGenerator
michael@0 70 {
michael@0 71 unsigned intRegIndex_;
michael@0 72 unsigned floatRegIndex_;
michael@0 73 uint32_t stackOffset_;
michael@0 74 ABIArg current_;
michael@0 75
michael@0 76 public:
michael@0 77 ABIArgGenerator();
michael@0 78 ABIArg next(MIRType argType);
michael@0 79 ABIArg &current() { return current_; }
michael@0 80 uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
michael@0 81 static const Register NonArgReturnVolatileReg0;
michael@0 82 static const Register NonArgReturnVolatileReg1;
michael@0 83 };
michael@0 84
michael@0 85 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r1;
michael@0 86
michael@0 87 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
michael@0 88 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
michael@0 89
michael@0 90 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r3;
michael@0 91 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = r2;
michael@0 92 static MOZ_CONSTEXPR_VAR Register StackPointer = sp;
michael@0 93 static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg;
michael@0 94 static MOZ_CONSTEXPR_VAR Register ReturnReg = r0;
michael@0 95 static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::d0 };
michael@0 96 static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::d15 };
michael@0 97
michael@0 98 static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::d14 };
michael@0 99
michael@0 100 // Registers used in the GenerateFFIIonExit Enable Activation block.
michael@0 101 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4;
michael@0 102 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0;
michael@0 103 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1;
michael@0 104 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2;
michael@0 105 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3;
michael@0 106
michael@0 107 // Registers used in the GenerateFFIIonExit Disable Activation block.
michael@0 108 // None of these may be the second scratch register (lr).
michael@0 109 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
michael@0 110 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
michael@0 111 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
michael@0 112 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
michael@0 113 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
michael@0 114
michael@0 115
michael@0 116 static MOZ_CONSTEXPR_VAR FloatRegister d0 = {FloatRegisters::d0};
michael@0 117 static MOZ_CONSTEXPR_VAR FloatRegister d1 = {FloatRegisters::d1};
michael@0 118 static MOZ_CONSTEXPR_VAR FloatRegister d2 = {FloatRegisters::d2};
michael@0 119 static MOZ_CONSTEXPR_VAR FloatRegister d3 = {FloatRegisters::d3};
michael@0 120 static MOZ_CONSTEXPR_VAR FloatRegister d4 = {FloatRegisters::d4};
michael@0 121 static MOZ_CONSTEXPR_VAR FloatRegister d5 = {FloatRegisters::d5};
michael@0 122 static MOZ_CONSTEXPR_VAR FloatRegister d6 = {FloatRegisters::d6};
michael@0 123 static MOZ_CONSTEXPR_VAR FloatRegister d7 = {FloatRegisters::d7};
michael@0 124 static MOZ_CONSTEXPR_VAR FloatRegister d8 = {FloatRegisters::d8};
michael@0 125 static MOZ_CONSTEXPR_VAR FloatRegister d9 = {FloatRegisters::d9};
michael@0 126 static MOZ_CONSTEXPR_VAR FloatRegister d10 = {FloatRegisters::d10};
michael@0 127 static MOZ_CONSTEXPR_VAR FloatRegister d11 = {FloatRegisters::d11};
michael@0 128 static MOZ_CONSTEXPR_VAR FloatRegister d12 = {FloatRegisters::d12};
michael@0 129 static MOZ_CONSTEXPR_VAR FloatRegister d13 = {FloatRegisters::d13};
michael@0 130 static MOZ_CONSTEXPR_VAR FloatRegister d14 = {FloatRegisters::d14};
michael@0 131 static MOZ_CONSTEXPR_VAR FloatRegister d15 = {FloatRegisters::d15};
michael@0 132
michael@0 133 // For maximal awesomeness, 8 should be sufficent.
michael@0 134 // ldrd/strd (dual-register load/store) operate in a single cycle
michael@0 135 // when the address they are dealing with is 8 byte aligned.
michael@0 136 // Also, the ARM abi wants the stack to be 8 byte aligned at
michael@0 137 // function boundaries. I'm trying to make sure this is always true.
michael@0 138 static const uint32_t StackAlignment = 8;
michael@0 139 static const uint32_t CodeAlignment = 8;
michael@0 140 static const bool StackKeptAligned = true;
michael@0 141 static const uint32_t NativeFrameSize = sizeof(void*);
michael@0 142 static const uint32_t AlignmentAtPrologue = 0;
michael@0 143 static const uint32_t AlignmentMidPrologue = 4;
michael@0 144
michael@0 145
michael@0 146 static const Scale ScalePointer = TimesFour;
michael@0 147
michael@0 148 class Instruction;
michael@0 149 class InstBranchImm;
michael@0 150 uint32_t RM(Register r);
michael@0 151 uint32_t RS(Register r);
michael@0 152 uint32_t RD(Register r);
michael@0 153 uint32_t RT(Register r);
michael@0 154 uint32_t RN(Register r);
michael@0 155
michael@0 156 uint32_t maybeRD(Register r);
michael@0 157 uint32_t maybeRT(Register r);
michael@0 158 uint32_t maybeRN(Register r);
michael@0 159
michael@0 160 Register toRN (Instruction &i);
michael@0 161 Register toRM (Instruction &i);
michael@0 162 Register toRD (Instruction &i);
michael@0 163 Register toR (Instruction &i);
michael@0 164
michael@0 165 class VFPRegister;
michael@0 166 uint32_t VD(VFPRegister vr);
michael@0 167 uint32_t VN(VFPRegister vr);
michael@0 168 uint32_t VM(VFPRegister vr);
michael@0 169
michael@0 170 class VFPRegister
michael@0 171 {
michael@0 172 public:
michael@0 173 // What type of data is being stored in this register?
michael@0 174 // UInt / Int are specifically for vcvt, where we need
michael@0 175 // to know how the data is supposed to be converted.
michael@0 176 enum RegType {
michael@0 177 Double = 0x0,
michael@0 178 Single = 0x1,
michael@0 179 UInt = 0x2,
michael@0 180 Int = 0x3
michael@0 181 };
michael@0 182
michael@0 183 protected:
michael@0 184 RegType kind : 2;
michael@0 185 // ARM doesn't have more than 32 registers...
michael@0 186 // don't take more bits than we'll need.
michael@0 187 // Presently, I don't have plans to address the upper
michael@0 188 // and lower halves of the double registers seprately, so
michael@0 189 // 5 bits should suffice. If I do decide to address them seprately
michael@0 190 // (vmov, I'm looking at you), I will likely specify it as a separate
michael@0 191 // field.
michael@0 192 uint32_t _code : 5;
michael@0 193 bool _isInvalid : 1;
michael@0 194 bool _isMissing : 1;
michael@0 195
michael@0 196 VFPRegister(int r, RegType k)
michael@0 197 : kind(k), _code (r), _isInvalid(false), _isMissing(false)
michael@0 198 { }
michael@0 199
michael@0 200 public:
michael@0 201 VFPRegister()
michael@0 202 : _isInvalid(true), _isMissing(false)
michael@0 203 { }
michael@0 204
michael@0 205 VFPRegister(bool b)
michael@0 206 : _isInvalid(false), _isMissing(b)
michael@0 207 { }
michael@0 208
michael@0 209 VFPRegister(FloatRegister fr)
michael@0 210 : kind(Double), _code(fr.code()), _isInvalid(false), _isMissing(false)
michael@0 211 {
michael@0 212 JS_ASSERT(_code == (unsigned)fr.code());
michael@0 213 }
michael@0 214
michael@0 215 VFPRegister(FloatRegister fr, RegType k)
michael@0 216 : kind(k), _code (fr.code()), _isInvalid(false), _isMissing(false)
michael@0 217 {
michael@0 218 JS_ASSERT(_code == (unsigned)fr.code());
michael@0 219 }
michael@0 220 bool isDouble() const { return kind == Double; }
michael@0 221 bool isSingle() const { return kind == Single; }
michael@0 222 bool isFloat() const { return (kind == Double) || (kind == Single); }
michael@0 223 bool isInt() const { return (kind == UInt) || (kind == Int); }
michael@0 224 bool isSInt() const { return kind == Int; }
michael@0 225 bool isUInt() const { return kind == UInt; }
michael@0 226 bool equiv(VFPRegister other) const { return other.kind == kind; }
michael@0 227 size_t size() const { return (kind == Double) ? 8 : 4; }
michael@0 228 bool isInvalid();
michael@0 229 bool isMissing();
michael@0 230
michael@0 231 VFPRegister doubleOverlay() const;
michael@0 232 VFPRegister singleOverlay() const;
michael@0 233 VFPRegister sintOverlay() const;
michael@0 234 VFPRegister uintOverlay() const;
michael@0 235
michael@0 236 struct VFPRegIndexSplit;
michael@0 237 VFPRegIndexSplit encode();
michael@0 238
michael@0 239 // for serializing values
michael@0 240 struct VFPRegIndexSplit {
michael@0 241 const uint32_t block : 4;
michael@0 242 const uint32_t bit : 1;
michael@0 243
michael@0 244 private:
michael@0 245 friend VFPRegIndexSplit js::jit::VFPRegister::encode();
michael@0 246
michael@0 247 VFPRegIndexSplit (uint32_t block_, uint32_t bit_)
michael@0 248 : block(block_), bit(bit_)
michael@0 249 {
michael@0 250 JS_ASSERT (block == block_);
michael@0 251 JS_ASSERT(bit == bit_);
michael@0 252 }
michael@0 253 };
michael@0 254
michael@0 255 uint32_t code() const {
michael@0 256 return _code;
michael@0 257 }
michael@0 258 };
michael@0 259
michael@0 260 // For being passed into the generic vfp instruction generator when
michael@0 261 // there is an instruction that only takes two registers
michael@0 262 extern VFPRegister NoVFPRegister;
michael@0 263
michael@0 264 struct ImmTag : public Imm32
michael@0 265 {
michael@0 266 ImmTag(JSValueTag mask)
michael@0 267 : Imm32(int32_t(mask))
michael@0 268 { }
michael@0 269 };
michael@0 270
michael@0 271 struct ImmType : public ImmTag
michael@0 272 {
michael@0 273 ImmType(JSValueType type)
michael@0 274 : ImmTag(JSVAL_TYPE_TO_TAG(type))
michael@0 275 { }
michael@0 276 };
michael@0 277
michael@0 278 enum Index {
michael@0 279 Offset = 0 << 21 | 1<<24,
michael@0 280 PreIndex = 1<<21 | 1 << 24,
michael@0 281 PostIndex = 0 << 21 | 0 << 24
michael@0 282 // The docs were rather unclear on this. it sounds like
michael@0 283 // 1<<21 | 0 << 24 encodes dtrt
michael@0 284 };
michael@0 285
michael@0 286 // Seriously, wtf arm
michael@0 287 enum IsImmOp2_ {
michael@0 288 IsImmOp2 = 1 << 25,
michael@0 289 IsNotImmOp2 = 0 << 25
michael@0 290 };
michael@0 291 enum IsImmDTR_ {
michael@0 292 IsImmDTR = 0 << 25,
michael@0 293 IsNotImmDTR = 1 << 25
michael@0 294 };
michael@0 295 // For the extra memory operations, ldrd, ldrsb, ldrh
michael@0 296 enum IsImmEDTR_ {
michael@0 297 IsImmEDTR = 1 << 22,
michael@0 298 IsNotImmEDTR = 0 << 22
michael@0 299 };
michael@0 300
michael@0 301
michael@0 302 enum ShiftType {
michael@0 303 LSL = 0, // << 5
michael@0 304 LSR = 1, // << 5
michael@0 305 ASR = 2, // << 5
michael@0 306 ROR = 3, // << 5
michael@0 307 RRX = ROR // RRX is encoded as ROR with a 0 offset.
michael@0 308 };
michael@0 309
michael@0 310 // The actual codes that get set by instructions
michael@0 311 // and the codes that are checked by the conditions below.
michael@0 312 struct ConditionCodes
michael@0 313 {
michael@0 314 bool Zero : 1;
michael@0 315 bool Overflow : 1;
michael@0 316 bool Carry : 1;
michael@0 317 bool Minus : 1;
michael@0 318 };
michael@0 319
michael@0 320 // Modes for STM/LDM.
michael@0 321 // Names are the suffixes applied to
michael@0 322 // the instruction.
michael@0 323 enum DTMMode {
michael@0 324 A = 0 << 24, // empty / after
michael@0 325 B = 1 << 24, // full / before
michael@0 326 D = 0 << 23, // decrement
michael@0 327 I = 1 << 23, // increment
michael@0 328 DA = D | A,
michael@0 329 DB = D | B,
michael@0 330 IA = I | A,
michael@0 331 IB = I | B
michael@0 332 };
michael@0 333
michael@0 334 enum DTMWriteBack {
michael@0 335 WriteBack = 1 << 21,
michael@0 336 NoWriteBack = 0 << 21
michael@0 337 };
michael@0 338
michael@0 339 enum SetCond_ {
michael@0 340 SetCond = 1 << 20,
michael@0 341 NoSetCond = 0 << 20
michael@0 342 };
michael@0 343 enum LoadStore {
michael@0 344 IsLoad = 1 << 20,
michael@0 345 IsStore = 0 << 20
michael@0 346 };
michael@0 347 // You almost never want to use this directly.
michael@0 348 // Instead, you wantto pass in a signed constant,
michael@0 349 // and let this bit be implicitly set for you.
michael@0 350 // this is however, necessary if we want a negative index
michael@0 351 enum IsUp_ {
michael@0 352 IsUp = 1 << 23,
michael@0 353 IsDown = 0 << 23
michael@0 354 };
michael@0 355 enum ALUOp {
michael@0 356 op_mov = 0xd << 21,
michael@0 357 op_mvn = 0xf << 21,
michael@0 358 op_and = 0x0 << 21,
michael@0 359 op_bic = 0xe << 21,
michael@0 360 op_eor = 0x1 << 21,
michael@0 361 op_orr = 0xc << 21,
michael@0 362 op_adc = 0x5 << 21,
michael@0 363 op_add = 0x4 << 21,
michael@0 364 op_sbc = 0x6 << 21,
michael@0 365 op_sub = 0x2 << 21,
michael@0 366 op_rsb = 0x3 << 21,
michael@0 367 op_rsc = 0x7 << 21,
michael@0 368 op_cmn = 0xb << 21,
michael@0 369 op_cmp = 0xa << 21,
michael@0 370 op_teq = 0x9 << 21,
michael@0 371 op_tst = 0x8 << 21,
michael@0 372 op_invalid = -1
michael@0 373 };
michael@0 374
michael@0 375
michael@0 376 enum MULOp {
michael@0 377 opm_mul = 0 << 21,
michael@0 378 opm_mla = 1 << 21,
michael@0 379 opm_umaal = 2 << 21,
michael@0 380 opm_mls = 3 << 21,
michael@0 381 opm_umull = 4 << 21,
michael@0 382 opm_umlal = 5 << 21,
michael@0 383 opm_smull = 6 << 21,
michael@0 384 opm_smlal = 7 << 21
michael@0 385 };
michael@0 386 enum BranchTag {
michael@0 387 op_b = 0x0a000000,
michael@0 388 op_b_mask = 0x0f000000,
michael@0 389 op_b_dest_mask = 0x00ffffff,
michael@0 390 op_bl = 0x0b000000,
michael@0 391 op_blx = 0x012fff30,
michael@0 392 op_bx = 0x012fff10
michael@0 393 };
michael@0 394
michael@0 395 // Just like ALUOp, but for the vfp instruction set.
michael@0 396 enum VFPOp {
michael@0 397 opv_mul = 0x2 << 20,
michael@0 398 opv_add = 0x3 << 20,
michael@0 399 opv_sub = 0x3 << 20 | 0x1 << 6,
michael@0 400 opv_div = 0x8 << 20,
michael@0 401 opv_mov = 0xB << 20 | 0x1 << 6,
michael@0 402 opv_abs = 0xB << 20 | 0x3 << 6,
michael@0 403 opv_neg = 0xB << 20 | 0x1 << 6 | 0x1 << 16,
michael@0 404 opv_sqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16,
michael@0 405 opv_cmp = 0xB << 20 | 0x1 << 6 | 0x4 << 16,
michael@0 406 opv_cmpz = 0xB << 20 | 0x1 << 6 | 0x5 << 16
michael@0 407 };
michael@0 408 // Negate the operation, AND negate the immediate that we were passed in.
michael@0 409 ALUOp ALUNeg(ALUOp op, Register dest, Imm32 *imm, Register *negDest);
michael@0 410 bool can_dbl(ALUOp op);
michael@0 411 bool condsAreSafe(ALUOp op);
michael@0 412 // If there is a variant of op that has a dest (think cmp/sub)
michael@0 413 // return that variant of it.
michael@0 414 ALUOp getDestVariant(ALUOp op);
michael@0 415
michael@0 416 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
michael@0 417 static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0);
michael@0 418 // All of these classes exist solely to shuffle data into the various operands.
michael@0 419 // For example Operand2 can be an imm8, a register-shifted-by-a-constant or
michael@0 420 // a register-shifted-by-a-register. I represent this in C++ by having a
michael@0 421 // base class Operand2, which just stores the 32 bits of data as they will be
michael@0 422 // encoded in the instruction. You cannot directly create an Operand2
michael@0 423 // since it is tricky, and not entirely sane to do so. Instead, you create
michael@0 424 // one of its child classes, e.g. Imm8. Imm8's constructor takes a single
michael@0 425 // integer argument. Imm8 will verify that its argument can be encoded
michael@0 426 // as an ARM 12 bit imm8, encode it using an Imm8data, and finally call
michael@0 427 // its parent's (Operand2) constructor with the Imm8data. The Operand2
michael@0 428 // constructor will then call the Imm8data's encode() function to extract
michael@0 429 // the raw bits from it. In the future, we should be able to extract
michael@0 430 // data from the Operand2 by asking it for its component Imm8data
michael@0 431 // structures. The reason this is so horribly round-about is I wanted
michael@0 432 // to have Imm8 and RegisterShiftedRegister inherit directly from Operand2
michael@0 433 // but have all of them take up only a single word of storage.
michael@0 434 // I also wanted to avoid passing around raw integers at all
michael@0 435 // since they are error prone.
michael@0 436 class Op2Reg;
michael@0 437 class O2RegImmShift;
michael@0 438 class O2RegRegShift;
michael@0 439 namespace datastore {
michael@0 440 struct Reg
michael@0 441 {
michael@0 442 // the "second register"
michael@0 443 uint32_t RM : 4;
michael@0 444 // do we get another register for shifting
michael@0 445 uint32_t RRS : 1;
michael@0 446 ShiftType Type : 2;
michael@0 447 // I'd like this to be a more sensible encoding, but that would
michael@0 448 // need to be a struct and that would not pack :(
michael@0 449 uint32_t ShiftAmount : 5;
michael@0 450 uint32_t pad : 20;
michael@0 451
michael@0 452 Reg(uint32_t rm, ShiftType type, uint32_t rsr, uint32_t shiftamount)
michael@0 453 : RM(rm), RRS(rsr), Type(type), ShiftAmount(shiftamount), pad(0)
michael@0 454 { }
michael@0 455
michael@0 456 uint32_t encode() {
michael@0 457 return RM | RRS << 4 | Type << 5 | ShiftAmount << 7;
michael@0 458 }
michael@0 459 explicit Reg(const Op2Reg &op) {
michael@0 460 memcpy(this, &op, sizeof(*this));
michael@0 461 }
michael@0 462 };
michael@0 463
michael@0 464 // Op2 has a mode labelled "<imm8m>", which is arm's magical
michael@0 465 // immediate encoding. Some instructions actually get 8 bits of
michael@0 466 // data, which is called Imm8Data below. These should have edit
michael@0 467 // distance > 1, but this is how it is for now.
michael@0 468 struct Imm8mData
michael@0 469 {
michael@0 470 private:
michael@0 471 uint32_t data : 8;
michael@0 472 uint32_t rot : 4;
michael@0 473 // Throw in an extra bit that will be 1 if we can't encode this
michael@0 474 // properly. if we can encode it properly, a simple "|" will still
michael@0 475 // suffice to meld it into the instruction.
michael@0 476 uint32_t buff : 19;
michael@0 477 public:
michael@0 478 uint32_t invalid : 1;
michael@0 479
michael@0 480 uint32_t encode() {
michael@0 481 JS_ASSERT(!invalid);
michael@0 482 return data | rot << 8;
michael@0 483 };
michael@0 484
michael@0 485 // Default constructor makes an invalid immediate.
michael@0 486 Imm8mData()
michael@0 487 : data(0xff), rot(0xf), invalid(1)
michael@0 488 { }
michael@0 489
michael@0 490 Imm8mData(uint32_t data_, uint32_t rot_)
michael@0 491 : data(data_), rot(rot_), invalid(0)
michael@0 492 {
michael@0 493 JS_ASSERT(data == data_);
michael@0 494 JS_ASSERT(rot == rot_);
michael@0 495 }
michael@0 496 };
michael@0 497
michael@0 498 struct Imm8Data
michael@0 499 {
michael@0 500 private:
michael@0 501 uint32_t imm4L : 4;
michael@0 502 uint32_t pad : 4;
michael@0 503 uint32_t imm4H : 4;
michael@0 504
michael@0 505 public:
michael@0 506 uint32_t encode() {
michael@0 507 return imm4L | (imm4H << 8);
michael@0 508 };
michael@0 509 Imm8Data(uint32_t imm) : imm4L(imm&0xf), imm4H(imm>>4) {
michael@0 510 JS_ASSERT(imm <= 0xff);
michael@0 511 }
michael@0 512 };
michael@0 513
michael@0 514 // VLDR/VSTR take an 8 bit offset, which is implicitly left shifted
michael@0 515 // by 2.
michael@0 516 struct Imm8VFPOffData
michael@0 517 {
michael@0 518 private:
michael@0 519 uint32_t data;
michael@0 520
michael@0 521 public:
michael@0 522 uint32_t encode() {
michael@0 523 return data;
michael@0 524 };
michael@0 525 Imm8VFPOffData(uint32_t imm) : data (imm) {
michael@0 526 JS_ASSERT((imm & ~(0xff)) == 0);
michael@0 527 }
michael@0 528 };
michael@0 529
michael@0 530 // ARM can magically encode 256 very special immediates to be moved
michael@0 531 // into a register.
michael@0 532 struct Imm8VFPImmData
michael@0 533 {
michael@0 534 private:
michael@0 535 uint32_t imm4L : 4;
michael@0 536 uint32_t pad : 12;
michael@0 537 uint32_t imm4H : 4;
michael@0 538 int32_t isInvalid : 12;
michael@0 539
michael@0 540 public:
michael@0 541 Imm8VFPImmData()
michael@0 542 : imm4L(-1U & 0xf), imm4H(-1U & 0xf), isInvalid(-1)
michael@0 543 { }
michael@0 544
michael@0 545 Imm8VFPImmData(uint32_t imm)
michael@0 546 : imm4L(imm&0xf), imm4H(imm>>4), isInvalid(0)
michael@0 547 {
michael@0 548 JS_ASSERT(imm <= 0xff);
michael@0 549 }
michael@0 550
michael@0 551 uint32_t encode() {
michael@0 552 if (isInvalid != 0)
michael@0 553 return -1;
michael@0 554 return imm4L | (imm4H << 16);
michael@0 555 };
michael@0 556 };
michael@0 557
michael@0 558 struct Imm12Data
michael@0 559 {
michael@0 560 uint32_t data : 12;
michael@0 561 uint32_t encode() {
michael@0 562 return data;
michael@0 563 }
michael@0 564
michael@0 565 Imm12Data(uint32_t imm)
michael@0 566 : data(imm)
michael@0 567 {
michael@0 568 JS_ASSERT(data == imm);
michael@0 569 }
michael@0 570
michael@0 571 };
michael@0 572
michael@0 573 struct RIS
michael@0 574 {
michael@0 575 uint32_t ShiftAmount : 5;
michael@0 576 uint32_t encode () {
michael@0 577 return ShiftAmount;
michael@0 578 }
michael@0 579
michael@0 580 RIS(uint32_t imm)
michael@0 581 : ShiftAmount(imm)
michael@0 582 {
michael@0 583 JS_ASSERT(ShiftAmount == imm);
michael@0 584 }
michael@0 585 explicit RIS(Reg r) : ShiftAmount(r.ShiftAmount) {}
michael@0 586 };
michael@0 587
michael@0 588 struct RRS
michael@0 589 {
michael@0 590 uint32_t MustZero : 1;
michael@0 591 // the register that holds the shift amount
michael@0 592 uint32_t RS : 4;
michael@0 593
michael@0 594 RRS(uint32_t rs)
michael@0 595 : RS(rs)
michael@0 596 {
michael@0 597 JS_ASSERT(rs == RS);
michael@0 598 }
michael@0 599
michael@0 600 uint32_t encode () {
michael@0 601 return RS << 1;
michael@0 602 }
michael@0 603 };
michael@0 604
michael@0 605 } // namespace datastore
michael@0 606
michael@0 607 class MacroAssemblerARM;
michael@0 608 class Operand;
michael@0 609 class Operand2
michael@0 610 {
michael@0 611 friend class Operand;
michael@0 612 friend class MacroAssemblerARM;
michael@0 613 friend class InstALU;
michael@0 614 public:
michael@0 615 uint32_t oper : 31;
michael@0 616 uint32_t invalid : 1;
michael@0 617 bool isO2Reg() {
michael@0 618 return !(oper & IsImmOp2);
michael@0 619 }
michael@0 620 Op2Reg toOp2Reg();
michael@0 621 bool isImm8() {
michael@0 622 return oper & IsImmOp2;
michael@0 623 }
michael@0 624
michael@0 625 protected:
michael@0 626 Operand2(datastore::Imm8mData base)
michael@0 627 : oper(base.invalid ? -1 : (base.encode() | (uint32_t)IsImmOp2)),
michael@0 628 invalid(base.invalid)
michael@0 629 { }
michael@0 630
michael@0 631 Operand2(datastore::Reg base)
michael@0 632 : oper(base.encode() | (uint32_t)IsNotImmOp2)
michael@0 633 { }
michael@0 634
michael@0 635 private:
michael@0 636 Operand2(int blob)
michael@0 637 : oper(blob)
michael@0 638 { }
michael@0 639
michael@0 640 public:
michael@0 641 uint32_t encode() {
michael@0 642 return oper;
michael@0 643 }
michael@0 644 };
michael@0 645
michael@0 646 class Imm8 : public Operand2
michael@0 647 {
michael@0 648 public:
michael@0 649 static datastore::Imm8mData encodeImm(uint32_t imm) {
michael@0 650 // mozilla::CountLeadingZeroes32(imm) requires imm != 0.
michael@0 651 if (imm == 0)
michael@0 652 return datastore::Imm8mData(0, 0);
michael@0 653 int left = mozilla::CountLeadingZeroes32(imm) & 30;
michael@0 654 // See if imm is a simple value that can be encoded with a rotate of 0.
michael@0 655 // This is effectively imm <= 0xff, but I assume this can be optimized
michael@0 656 // more
michael@0 657 if (left >= 24)
michael@0 658 return datastore::Imm8mData(imm, 0);
michael@0 659
michael@0 660 // Mask out the 8 bits following the first bit that we found, see if we
michael@0 661 // have 0 yet.
michael@0 662 int no_imm = imm & ~(0xff << (24 - left));
michael@0 663 if (no_imm == 0) {
michael@0 664 return datastore::Imm8mData(imm >> (24 - left), ((8+left) >> 1));
michael@0 665 }
michael@0 666 // Look for the most signifigant bit set, once again.
michael@0 667 int right = 32 - (mozilla::CountLeadingZeroes32(no_imm) & 30);
michael@0 668 // If it is in the bottom 8 bits, there is a chance that this is a
michael@0 669 // wraparound case.
michael@0 670 if (right >= 8)
michael@0 671 return datastore::Imm8mData();
michael@0 672 // Rather than masking out bits and checking for 0, just rotate the
michael@0 673 // immediate that we were passed in, and see if it fits into 8 bits.
michael@0 674 unsigned int mask = imm << (8 - right) | imm >> (24 + right);
michael@0 675 if (mask <= 0xff)
michael@0 676 return datastore::Imm8mData(mask, (8-right) >> 1);
michael@0 677 return datastore::Imm8mData();
michael@0 678 }
michael@0 679 // pair template?
michael@0 680 struct TwoImm8mData
michael@0 681 {
michael@0 682 datastore::Imm8mData fst, snd;
michael@0 683
michael@0 684 TwoImm8mData()
michael@0 685 : fst(), snd()
michael@0 686 { }
michael@0 687
michael@0 688 TwoImm8mData(datastore::Imm8mData _fst, datastore::Imm8mData _snd)
michael@0 689 : fst(_fst), snd(_snd)
michael@0 690 { }
michael@0 691 };
michael@0 692
michael@0 693 static TwoImm8mData encodeTwoImms(uint32_t);
michael@0 694 Imm8(uint32_t imm)
michael@0 695 : Operand2(encodeImm(imm))
michael@0 696 { }
michael@0 697 };
michael@0 698
michael@0 699 class Op2Reg : public Operand2
michael@0 700 {
michael@0 701 public:
michael@0 702 Op2Reg(Register rm, ShiftType type, datastore::RIS shiftImm)
michael@0 703 : Operand2(datastore::Reg(rm.code(), type, 0, shiftImm.encode()))
michael@0 704 { }
michael@0 705
michael@0 706 Op2Reg(Register rm, ShiftType type, datastore::RRS shiftReg)
michael@0 707 : Operand2(datastore::Reg(rm.code(), type, 1, shiftReg.encode()))
michael@0 708 { }
michael@0 709 bool isO2RegImmShift() {
michael@0 710 datastore::Reg r(*this);
michael@0 711 return !r.RRS;
michael@0 712 }
michael@0 713 O2RegImmShift toO2RegImmShift();
michael@0 714 bool isO2RegRegShift() {
michael@0 715 datastore::Reg r(*this);
michael@0 716 return r.RRS;
michael@0 717 }
michael@0 718 O2RegRegShift toO2RegRegShift();
michael@0 719
michael@0 720 bool checkType(ShiftType type) {
michael@0 721 datastore::Reg r(*this);
michael@0 722 return r.Type == type;
michael@0 723 }
michael@0 724 bool checkRM(Register rm) {
michael@0 725 datastore::Reg r(*this);
michael@0 726 return r.RM == rm.code();
michael@0 727 }
michael@0 728 bool getRM(Register *rm) {
michael@0 729 datastore::Reg r(*this);
michael@0 730 *rm = Register::FromCode(r.RM);
michael@0 731 return true;
michael@0 732 }
michael@0 733 };
michael@0 734
michael@0 735 class O2RegImmShift : public Op2Reg
michael@0 736 {
michael@0 737 public:
michael@0 738 O2RegImmShift(Register rn, ShiftType type, uint32_t shift)
michael@0 739 : Op2Reg(rn, type, datastore::RIS(shift))
michael@0 740 { }
michael@0 741 int getShift() {
michael@0 742 datastore::Reg r(*this);
michael@0 743 datastore::RIS ris(r);
michael@0 744 return ris.ShiftAmount;
michael@0 745 }
michael@0 746 };
michael@0 747
michael@0 748 class O2RegRegShift : public Op2Reg
michael@0 749 {
michael@0 750 public:
michael@0 751 O2RegRegShift(Register rn, ShiftType type, Register rs)
michael@0 752 : Op2Reg(rn, type, datastore::RRS(rs.code()))
michael@0 753 { }
michael@0 754 };
michael@0 755
michael@0 756 O2RegImmShift O2Reg(Register r);
michael@0 757 O2RegImmShift lsl (Register r, int amt);
michael@0 758 O2RegImmShift lsr (Register r, int amt);
michael@0 759 O2RegImmShift asr (Register r, int amt);
michael@0 760 O2RegImmShift rol (Register r, int amt);
michael@0 761 O2RegImmShift ror (Register r, int amt);
michael@0 762
michael@0 763 O2RegRegShift lsl (Register r, Register amt);
michael@0 764 O2RegRegShift lsr (Register r, Register amt);
michael@0 765 O2RegRegShift asr (Register r, Register amt);
michael@0 766 O2RegRegShift ror (Register r, Register amt);
michael@0 767
michael@0 768 // An offset from a register to be used for ldr/str. This should include
michael@0 769 // the sign bit, since ARM has "signed-magnitude" offsets. That is it encodes
michael@0 770 // an unsigned offset, then the instruction specifies if the offset is positive
michael@0 771 // or negative. The +/- bit is necessary if the instruction set wants to be
michael@0 772 // able to have a negative register offset e.g. ldr pc, [r1,-r2];
michael@0 773 class DtrOff
michael@0 774 {
michael@0 775 uint32_t data;
michael@0 776
michael@0 777 protected:
michael@0 778 DtrOff(datastore::Imm12Data immdata, IsUp_ iu)
michael@0 779 : data(immdata.encode() | (uint32_t)IsImmDTR | ((uint32_t)iu))
michael@0 780 { }
michael@0 781
michael@0 782 DtrOff(datastore::Reg reg, IsUp_ iu = IsUp)
michael@0 783 : data(reg.encode() | (uint32_t) IsNotImmDTR | iu)
michael@0 784 { }
michael@0 785
michael@0 786 public:
michael@0 787 uint32_t encode() { return data; }
michael@0 788 };
michael@0 789
michael@0 790 class DtrOffImm : public DtrOff
michael@0 791 {
michael@0 792 public:
michael@0 793 DtrOffImm(int32_t imm)
michael@0 794 : DtrOff(datastore::Imm12Data(mozilla::Abs(imm)), imm >= 0 ? IsUp : IsDown)
michael@0 795 {
michael@0 796 JS_ASSERT(mozilla::Abs(imm) < 4096);
michael@0 797 }
michael@0 798 };
michael@0 799
michael@0 800 class DtrOffReg : public DtrOff
michael@0 801 {
michael@0 802 // These are designed to be called by a constructor of a subclass.
michael@0 803 // Constructing the necessary RIS/RRS structures are annoying
michael@0 804 protected:
michael@0 805 DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm, IsUp_ iu = IsUp)
michael@0 806 : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()), iu)
michael@0 807 { }
michael@0 808
michael@0 809 DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg, IsUp_ iu = IsUp)
michael@0 810 : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()), iu)
michael@0 811 { }
michael@0 812 };
michael@0 813
michael@0 814 class DtrRegImmShift : public DtrOffReg
michael@0 815 {
michael@0 816 public:
michael@0 817 DtrRegImmShift(Register rn, ShiftType type, uint32_t shift, IsUp_ iu = IsUp)
michael@0 818 : DtrOffReg(rn, type, datastore::RIS(shift), iu)
michael@0 819 { }
michael@0 820 };
michael@0 821
michael@0 822 class DtrRegRegShift : public DtrOffReg
michael@0 823 {
michael@0 824 public:
michael@0 825 DtrRegRegShift(Register rn, ShiftType type, Register rs, IsUp_ iu = IsUp)
michael@0 826 : DtrOffReg(rn, type, datastore::RRS(rs.code()), iu)
michael@0 827 { }
michael@0 828 };
michael@0 829
michael@0 830 // we will frequently want to bundle a register with its offset so that we have
michael@0 831 // an "operand" to a load instruction.
michael@0 832 class DTRAddr
michael@0 833 {
michael@0 834 uint32_t data;
michael@0 835
michael@0 836 public:
michael@0 837 DTRAddr(Register reg, DtrOff dtr)
michael@0 838 : data(dtr.encode() | (reg.code() << 16))
michael@0 839 { }
michael@0 840
michael@0 841 uint32_t encode() {
michael@0 842 return data;
michael@0 843 }
michael@0 844 Register getBase() {
michael@0 845 return Register::FromCode((data >> 16) &0xf);
michael@0 846 }
michael@0 847 private:
michael@0 848 friend class Operand;
michael@0 849 DTRAddr(uint32_t blob)
michael@0 850 : data(blob)
michael@0 851 { }
michael@0 852 };
michael@0 853
michael@0 854 // Offsets for the extended data transfer instructions:
michael@0 855 // ldrsh, ldrd, ldrsb, etc.
michael@0 856 class EDtrOff
michael@0 857 {
michael@0 858 uint32_t data;
michael@0 859
michael@0 860 protected:
michael@0 861 EDtrOff(datastore::Imm8Data imm8, IsUp_ iu = IsUp)
michael@0 862 : data(imm8.encode() | IsImmEDTR | (uint32_t)iu)
michael@0 863 { }
michael@0 864
michael@0 865 EDtrOff(Register rm, IsUp_ iu = IsUp)
michael@0 866 : data(rm.code() | IsNotImmEDTR | iu)
michael@0 867 { }
michael@0 868
michael@0 869 public:
michael@0 870 uint32_t encode() {
michael@0 871 return data;
michael@0 872 }
michael@0 873 };
michael@0 874
michael@0 875 class EDtrOffImm : public EDtrOff
michael@0 876 {
michael@0 877 public:
michael@0 878 EDtrOffImm(int32_t imm)
michael@0 879 : EDtrOff(datastore::Imm8Data(mozilla::Abs(imm)), (imm >= 0) ? IsUp : IsDown)
michael@0 880 {
michael@0 881 JS_ASSERT(mozilla::Abs(imm) < 256);
michael@0 882 }
michael@0 883 };
michael@0 884
michael@0 885 // this is the most-derived class, since the extended data
michael@0 886 // transfer instructions don't support any sort of modifying the
michael@0 887 // "index" operand
michael@0 888 class EDtrOffReg : public EDtrOff
michael@0 889 {
michael@0 890 public:
michael@0 891 EDtrOffReg(Register rm)
michael@0 892 : EDtrOff(rm)
michael@0 893 { }
michael@0 894 };
michael@0 895
michael@0 896 class EDtrAddr
michael@0 897 {
michael@0 898 uint32_t data;
michael@0 899
michael@0 900 public:
michael@0 901 EDtrAddr(Register r, EDtrOff off)
michael@0 902 : data(RN(r) | off.encode())
michael@0 903 { }
michael@0 904
michael@0 905 uint32_t encode() {
michael@0 906 return data;
michael@0 907 }
michael@0 908 };
michael@0 909
michael@0 910 class VFPOff
michael@0 911 {
michael@0 912 uint32_t data;
michael@0 913
michael@0 914 protected:
michael@0 915 VFPOff(datastore::Imm8VFPOffData imm, IsUp_ isup)
michael@0 916 : data(imm.encode() | (uint32_t)isup)
michael@0 917 { }
michael@0 918
michael@0 919 public:
michael@0 920 uint32_t encode() {
michael@0 921 return data;
michael@0 922 }
michael@0 923 };
michael@0 924
michael@0 925 class VFPOffImm : public VFPOff
michael@0 926 {
michael@0 927 public:
michael@0 928 VFPOffImm(int32_t imm)
michael@0 929 : VFPOff(datastore::Imm8VFPOffData(mozilla::Abs(imm) / 4), imm < 0 ? IsDown : IsUp)
michael@0 930 {
michael@0 931 JS_ASSERT(mozilla::Abs(imm) <= 255 * 4);
michael@0 932 }
michael@0 933 };
michael@0 934 class VFPAddr
michael@0 935 {
michael@0 936 friend class Operand;
michael@0 937
michael@0 938 uint32_t data;
michael@0 939
michael@0 940 protected:
michael@0 941 VFPAddr(uint32_t blob)
michael@0 942 : data(blob)
michael@0 943 { }
michael@0 944
michael@0 945 public:
michael@0 946 VFPAddr(Register base, VFPOff off)
michael@0 947 : data(RN(base) | off.encode())
michael@0 948 { }
michael@0 949
michael@0 950 uint32_t encode() {
michael@0 951 return data;
michael@0 952 }
michael@0 953 };
michael@0 954
michael@0 955 class VFPImm {
michael@0 956 uint32_t data;
michael@0 957
michael@0 958 public:
michael@0 959 static const VFPImm one;
michael@0 960
michael@0 961 VFPImm(uint32_t topWordOfDouble);
michael@0 962
michael@0 963 uint32_t encode() {
michael@0 964 return data;
michael@0 965 }
michael@0 966 bool isValid() {
michael@0 967 return data != -1U;
michael@0 968 }
michael@0 969 };
michael@0 970
michael@0 971 // A BOffImm is an immediate that is used for branches. Namely, it is the offset that will
michael@0 972 // be encoded in the branch instruction. This is the only sane way of constructing a branch.
michael@0 973 class BOffImm
michael@0 974 {
michael@0 975 uint32_t data;
michael@0 976
michael@0 977 public:
michael@0 978 uint32_t encode() {
michael@0 979 return data;
michael@0 980 }
michael@0 981 int32_t decode() {
michael@0 982 return ((((int32_t)data) << 8) >> 6) + 8;
michael@0 983 }
michael@0 984
michael@0 985 explicit BOffImm(int offset)
michael@0 986 : data ((offset - 8) >> 2 & 0x00ffffff)
michael@0 987 {
michael@0 988 JS_ASSERT((offset & 0x3) == 0);
michael@0 989 if (!isInRange(offset))
michael@0 990 CrashAtUnhandlableOOM("BOffImm");
michael@0 991 }
michael@0 992 static bool isInRange(int offset)
michael@0 993 {
michael@0 994 if ((offset - 8) < -33554432)
michael@0 995 return false;
michael@0 996 if ((offset - 8) > 33554428)
michael@0 997 return false;
michael@0 998 return true;
michael@0 999 }
michael@0 1000 static const int INVALID = 0x00800000;
michael@0 1001 BOffImm()
michael@0 1002 : data(INVALID)
michael@0 1003 { }
michael@0 1004
michael@0 1005 bool isInvalid() {
michael@0 1006 return data == uint32_t(INVALID);
michael@0 1007 }
michael@0 1008 Instruction *getDest(Instruction *src);
michael@0 1009
michael@0 1010 private:
michael@0 1011 friend class InstBranchImm;
michael@0 1012 BOffImm(Instruction &inst);
michael@0 1013 };
michael@0 1014
michael@0 1015 class Imm16
michael@0 1016 {
michael@0 1017 uint32_t lower : 12;
michael@0 1018 uint32_t pad : 4;
michael@0 1019 uint32_t upper : 4;
michael@0 1020 uint32_t invalid : 12;
michael@0 1021
michael@0 1022 public:
michael@0 1023 Imm16();
michael@0 1024 Imm16(uint32_t imm);
michael@0 1025 Imm16(Instruction &inst);
michael@0 1026
michael@0 1027 uint32_t encode() {
michael@0 1028 return lower | upper << 16;
michael@0 1029 }
michael@0 1030 uint32_t decode() {
michael@0 1031 return lower | upper << 12;
michael@0 1032 }
michael@0 1033
michael@0 1034 bool isInvalid () {
michael@0 1035 return invalid;
michael@0 1036 }
michael@0 1037 };
michael@0 1038
michael@0 1039 /* I would preffer that these do not exist, since there are essentially
michael@0 1040 * no instructions that would ever take more than one of these, however,
michael@0 1041 * the MIR wants to only have one type of arguments to functions, so bugger.
michael@0 1042 */
michael@0 1043 class Operand
michael@0 1044 {
michael@0 1045 // the encoding of registers is the same for OP2, DTR and EDTR
michael@0 1046 // yet the type system doesn't let us express this, so choices
michael@0 1047 // must be made.
michael@0 1048 public:
michael@0 1049 enum Tag_ {
michael@0 1050 OP2,
michael@0 1051 MEM,
michael@0 1052 FOP
michael@0 1053 };
michael@0 1054
michael@0 1055 private:
michael@0 1056 Tag_ Tag : 3;
michael@0 1057 uint32_t reg : 5;
michael@0 1058 int32_t offset;
michael@0 1059 uint32_t data;
michael@0 1060
michael@0 1061 public:
michael@0 1062 Operand (Register reg_)
michael@0 1063 : Tag(OP2), reg(reg_.code())
michael@0 1064 { }
michael@0 1065
michael@0 1066 Operand (FloatRegister freg)
michael@0 1067 : Tag(FOP), reg(freg.code())
michael@0 1068 { }
michael@0 1069
michael@0 1070 Operand (Register base, Imm32 off)
michael@0 1071 : Tag(MEM), reg(base.code()), offset(off.value)
michael@0 1072 { }
michael@0 1073
michael@0 1074 Operand (Register base, int32_t off)
michael@0 1075 : Tag(MEM), reg(base.code()), offset(off)
michael@0 1076 { }
michael@0 1077
michael@0 1078 Operand (const Address &addr)
michael@0 1079 : Tag(MEM), reg(addr.base.code()), offset(addr.offset)
michael@0 1080 { }
michael@0 1081
michael@0 1082 Tag_ getTag() const {
michael@0 1083 return Tag;
michael@0 1084 }
michael@0 1085
michael@0 1086 Operand2 toOp2() const {
michael@0 1087 JS_ASSERT(Tag == OP2);
michael@0 1088 return O2Reg(Register::FromCode(reg));
michael@0 1089 }
michael@0 1090
michael@0 1091 Register toReg() const {
michael@0 1092 JS_ASSERT(Tag == OP2);
michael@0 1093 return Register::FromCode(reg);
michael@0 1094 }
michael@0 1095
michael@0 1096 void toAddr(Register *r, Imm32 *dest) const {
michael@0 1097 JS_ASSERT(Tag == MEM);
michael@0 1098 *r = Register::FromCode(reg);
michael@0 1099 *dest = Imm32(offset);
michael@0 1100 }
michael@0 1101 Address toAddress() const {
michael@0 1102 return Address(Register::FromCode(reg), offset);
michael@0 1103 }
michael@0 1104 int32_t disp() const {
michael@0 1105 JS_ASSERT(Tag == MEM);
michael@0 1106 return offset;
michael@0 1107 }
michael@0 1108
michael@0 1109 int32_t base() const {
michael@0 1110 JS_ASSERT(Tag == MEM);
michael@0 1111 return reg;
michael@0 1112 }
michael@0 1113 Register baseReg() const {
michael@0 1114 return Register::FromCode(reg);
michael@0 1115 }
michael@0 1116 DTRAddr toDTRAddr() const {
michael@0 1117 return DTRAddr(baseReg(), DtrOffImm(offset));
michael@0 1118 }
michael@0 1119 VFPAddr toVFPAddr() const {
michael@0 1120 return VFPAddr(baseReg(), VFPOffImm(offset));
michael@0 1121 }
michael@0 1122 };
michael@0 1123
michael@0 1124 void
michael@0 1125 PatchJump(CodeLocationJump &jump_, CodeLocationLabel label);
michael@0 1126 class InstructionIterator;
michael@0 1127 class Assembler;
michael@0 1128 typedef js::jit::AssemblerBufferWithConstantPool<1024, 4, Instruction, Assembler, 1> ARMBuffer;
michael@0 1129
michael@0 1130 class Assembler : public AssemblerShared
michael@0 1131 {
michael@0 1132 public:
michael@0 1133 // ARM conditional constants
michael@0 1134 enum ARMCondition {
michael@0 1135 EQ = 0x00000000, // Zero
michael@0 1136 NE = 0x10000000, // Non-zero
michael@0 1137 CS = 0x20000000,
michael@0 1138 CC = 0x30000000,
michael@0 1139 MI = 0x40000000,
michael@0 1140 PL = 0x50000000,
michael@0 1141 VS = 0x60000000,
michael@0 1142 VC = 0x70000000,
michael@0 1143 HI = 0x80000000,
michael@0 1144 LS = 0x90000000,
michael@0 1145 GE = 0xa0000000,
michael@0 1146 LT = 0xb0000000,
michael@0 1147 GT = 0xc0000000,
michael@0 1148 LE = 0xd0000000,
michael@0 1149 AL = 0xe0000000
michael@0 1150 };
michael@0 1151
michael@0 1152 enum Condition {
michael@0 1153 Equal = EQ,
michael@0 1154 NotEqual = NE,
michael@0 1155 Above = HI,
michael@0 1156 AboveOrEqual = CS,
michael@0 1157 Below = CC,
michael@0 1158 BelowOrEqual = LS,
michael@0 1159 GreaterThan = GT,
michael@0 1160 GreaterThanOrEqual = GE,
michael@0 1161 LessThan = LT,
michael@0 1162 LessThanOrEqual = LE,
michael@0 1163 Overflow = VS,
michael@0 1164 Signed = MI,
michael@0 1165 NotSigned = PL,
michael@0 1166 Zero = EQ,
michael@0 1167 NonZero = NE,
michael@0 1168 Always = AL,
michael@0 1169
michael@0 1170 VFP_NotEqualOrUnordered = NE,
michael@0 1171 VFP_Equal = EQ,
michael@0 1172 VFP_Unordered = VS,
michael@0 1173 VFP_NotUnordered = VC,
michael@0 1174 VFP_GreaterThanOrEqualOrUnordered = CS,
michael@0 1175 VFP_GreaterThanOrEqual = GE,
michael@0 1176 VFP_GreaterThanOrUnordered = HI,
michael@0 1177 VFP_GreaterThan = GT,
michael@0 1178 VFP_LessThanOrEqualOrUnordered = LE,
michael@0 1179 VFP_LessThanOrEqual = LS,
michael@0 1180 VFP_LessThanOrUnordered = LT,
michael@0 1181 VFP_LessThan = CC // MI is valid too.
michael@0 1182 };
michael@0 1183
michael@0 1184 // Bit set when a DoubleCondition does not map to a single ARM condition.
michael@0 1185 // The macro assembler has to special-case these conditions, or else
michael@0 1186 // ConditionFromDoubleCondition will complain.
michael@0 1187 static const int DoubleConditionBitSpecial = 0x1;
michael@0 1188
michael@0 1189 enum DoubleCondition {
michael@0 1190 // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
michael@0 1191 DoubleOrdered = VFP_NotUnordered,
michael@0 1192 DoubleEqual = VFP_Equal,
michael@0 1193 DoubleNotEqual = VFP_NotEqualOrUnordered | DoubleConditionBitSpecial,
michael@0 1194 DoubleGreaterThan = VFP_GreaterThan,
michael@0 1195 DoubleGreaterThanOrEqual = VFP_GreaterThanOrEqual,
michael@0 1196 DoubleLessThan = VFP_LessThan,
michael@0 1197 DoubleLessThanOrEqual = VFP_LessThanOrEqual,
michael@0 1198 // If either operand is NaN, these conditions always evaluate to true.
michael@0 1199 DoubleUnordered = VFP_Unordered,
michael@0 1200 DoubleEqualOrUnordered = VFP_Equal | DoubleConditionBitSpecial,
michael@0 1201 DoubleNotEqualOrUnordered = VFP_NotEqualOrUnordered,
michael@0 1202 DoubleGreaterThanOrUnordered = VFP_GreaterThanOrUnordered,
michael@0 1203 DoubleGreaterThanOrEqualOrUnordered = VFP_GreaterThanOrEqualOrUnordered,
michael@0 1204 DoubleLessThanOrUnordered = VFP_LessThanOrUnordered,
michael@0 1205 DoubleLessThanOrEqualOrUnordered = VFP_LessThanOrEqualOrUnordered
michael@0 1206 };
michael@0 1207
michael@0 1208 Condition getCondition(uint32_t inst) {
michael@0 1209 return (Condition) (0xf0000000 & inst);
michael@0 1210 }
michael@0 1211 static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) {
michael@0 1212 JS_ASSERT(!(cond & DoubleConditionBitSpecial));
michael@0 1213 return static_cast<Condition>(cond);
michael@0 1214 }
michael@0 1215
michael@0 1216 // :( this should be protected, but since CodeGenerator
michael@0 1217 // wants to use it, It needs to go out here :(
michael@0 1218
michael@0 1219 BufferOffset nextOffset() {
michael@0 1220 return m_buffer.nextOffset();
michael@0 1221 }
michael@0 1222
michael@0 1223 protected:
michael@0 1224 BufferOffset labelOffset (Label *l) {
michael@0 1225 return BufferOffset(l->bound());
michael@0 1226 }
michael@0 1227
michael@0 1228 Instruction * editSrc (BufferOffset bo) {
michael@0 1229 return m_buffer.getInst(bo);
michael@0 1230 }
michael@0 1231 public:
michael@0 1232 void resetCounter();
michael@0 1233 uint32_t actualOffset(uint32_t) const;
michael@0 1234 uint32_t actualIndex(uint32_t) const;
michael@0 1235 static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index);
michael@0 1236 BufferOffset actualOffset(BufferOffset) const;
michael@0 1237 protected:
michael@0 1238
michael@0 1239 // structure for fixing up pc-relative loads/jumps when a the machine code
michael@0 1240 // gets moved (executable copy, gc, etc.)
michael@0 1241 struct RelativePatch
michael@0 1242 {
michael@0 1243 void *target;
michael@0 1244 Relocation::Kind kind;
michael@0 1245 RelativePatch(void *target, Relocation::Kind kind)
michael@0 1246 : target(target), kind(kind)
michael@0 1247 { }
michael@0 1248 };
michael@0 1249
michael@0 1250 // TODO: this should actually be a pool-like object
michael@0 1251 // It is currently a big hack, and probably shouldn't exist
michael@0 1252 js::Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
michael@0 1253 js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
michael@0 1254 js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpJumpRelocations_;
michael@0 1255 js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpDataRelocations_;
michael@0 1256 js::Vector<BufferOffset, 0, SystemAllocPolicy> tmpPreBarriers_;
michael@0 1257
michael@0 1258 CompactBufferWriter jumpRelocations_;
michael@0 1259 CompactBufferWriter dataRelocations_;
michael@0 1260 CompactBufferWriter relocations_;
michael@0 1261 CompactBufferWriter preBarriers_;
michael@0 1262
michael@0 1263 bool enoughMemory_;
michael@0 1264
michael@0 1265 //typedef JSC::AssemblerBufferWithConstantPool<1024, 4, 4, js::jit::Assembler> ARMBuffer;
michael@0 1266 ARMBuffer m_buffer;
michael@0 1267
michael@0 1268 // There is now a semi-unified interface for instruction generation.
michael@0 1269 // During assembly, there is an active buffer that instructions are
michael@0 1270 // being written into, but later, we may wish to modify instructions
michael@0 1271 // that have already been created. In order to do this, we call the
michael@0 1272 // same assembly function, but pass it a destination address, which
michael@0 1273 // will be overwritten with a new instruction. In order to do this very
michael@0 1274 // after assembly buffers no longer exist, when calling with a third
michael@0 1275 // dest parameter, a this object is still needed. dummy always happens
michael@0 1276 // to be null, but we shouldn't be looking at it in any case.
michael@0 1277 static Assembler *dummy;
michael@0 1278 mozilla::Array<Pool, 4> pools_;
michael@0 1279 Pool *int32Pool;
michael@0 1280 Pool *doublePool;
michael@0 1281
michael@0 1282 public:
michael@0 1283 Assembler()
michael@0 1284 : enoughMemory_(true),
michael@0 1285 m_buffer(4, 4, 0, &pools_[0], 8),
michael@0 1286 int32Pool(m_buffer.getPool(1)),
michael@0 1287 doublePool(m_buffer.getPool(0)),
michael@0 1288 isFinished(false),
michael@0 1289 dtmActive(false),
michael@0 1290 dtmCond(Always)
michael@0 1291 {
michael@0 1292 }
michael@0 1293
michael@0 1294 // We need to wait until an AutoIonContextAlloc is created by the
michael@0 1295 // IonMacroAssembler, before allocating any space.
michael@0 1296 void initWithAllocator() {
michael@0 1297 m_buffer.initWithAllocator();
michael@0 1298
michael@0 1299 // Set up the backwards double region
michael@0 1300 new (&pools_[2]) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, true);
michael@0 1301 // Set up the backwards 32 bit region
michael@0 1302 new (&pools_[3]) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, true, true);
michael@0 1303 // Set up the forwards double region
michael@0 1304 new (doublePool) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]);
michael@0 1305 // Set up the forwards 32 bit region
michael@0 1306 new (int32Pool) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, false, true, &pools_[3]);
michael@0 1307 for (int i = 0; i < 4; i++) {
michael@0 1308 if (pools_[i].poolData == nullptr) {
michael@0 1309 m_buffer.fail_oom();
michael@0 1310 return;
michael@0 1311 }
michael@0 1312 }
michael@0 1313 }
michael@0 1314
michael@0 1315 static Condition InvertCondition(Condition cond);
michael@0 1316
michael@0 1317 // MacroAssemblers hold onto gcthings, so they are traced by the GC.
michael@0 1318 void trace(JSTracer *trc);
michael@0 1319 void writeRelocation(BufferOffset src) {
michael@0 1320 tmpJumpRelocations_.append(src);
michael@0 1321 }
michael@0 1322
michael@0 1323 // As opposed to x86/x64 version, the data relocation has to be executed
michael@0 1324 // before to recover the pointer, and not after.
michael@0 1325 void writeDataRelocation(const ImmGCPtr &ptr) {
michael@0 1326 if (ptr.value)
michael@0 1327 tmpDataRelocations_.append(nextOffset());
michael@0 1328 }
michael@0 1329 void writePrebarrierOffset(CodeOffsetLabel label) {
michael@0 1330 tmpPreBarriers_.append(BufferOffset(label.offset()));
michael@0 1331 }
michael@0 1332
michael@0 1333 enum RelocBranchStyle {
michael@0 1334 B_MOVWT,
michael@0 1335 B_LDR_BX,
michael@0 1336 B_LDR,
michael@0 1337 B_MOVW_ADD
michael@0 1338 };
michael@0 1339
michael@0 1340 enum RelocStyle {
michael@0 1341 L_MOVWT,
michael@0 1342 L_LDR
michael@0 1343 };
michael@0 1344
michael@0 1345 public:
michael@0 1346 // Given the start of a Control Flow sequence, grab the value that is finally branched to
michael@0 1347 // given the start of a function that loads an address into a register get the address that
michael@0 1348 // ends up in the register.
michael@0 1349 template <class Iter>
michael@0 1350 static const uint32_t * getCF32Target(Iter *iter);
michael@0 1351
michael@0 1352 static uintptr_t getPointer(uint8_t *);
michael@0 1353 template <class Iter>
michael@0 1354 static const uint32_t * getPtr32Target(Iter *iter, Register *dest = nullptr, RelocStyle *rs = nullptr);
michael@0 1355
michael@0 1356 bool oom() const;
michael@0 1357
michael@0 1358 void setPrinter(Sprinter *sp) {
michael@0 1359 }
michael@0 1360
michael@0 1361 private:
michael@0 1362 bool isFinished;
michael@0 1363 public:
michael@0 1364 void finish();
michael@0 1365 void executableCopy(void *buffer);
michael@0 1366 void copyJumpRelocationTable(uint8_t *dest);
michael@0 1367 void copyDataRelocationTable(uint8_t *dest);
michael@0 1368 void copyPreBarrierTable(uint8_t *dest);
michael@0 1369
michael@0 1370 bool addCodeLabel(CodeLabel label);
michael@0 1371 size_t numCodeLabels() const {
michael@0 1372 return codeLabels_.length();
michael@0 1373 }
michael@0 1374 CodeLabel codeLabel(size_t i) {
michael@0 1375 return codeLabels_[i];
michael@0 1376 }
michael@0 1377
michael@0 1378 // Size of the instruction stream, in bytes.
michael@0 1379 size_t size() const;
michael@0 1380 // Size of the jump relocation table, in bytes.
michael@0 1381 size_t jumpRelocationTableBytes() const;
michael@0 1382 size_t dataRelocationTableBytes() const;
michael@0 1383 size_t preBarrierTableBytes() const;
michael@0 1384
michael@0 1385 // Size of the data table, in bytes.
michael@0 1386 size_t bytesNeeded() const;
michael@0 1387
michael@0 1388 // Write a blob of binary into the instruction stream *OR*
michael@0 1389 // into a destination address. If dest is nullptr (the default), then the
michael@0 1390 // instruction gets written into the instruction stream. If dest is not null
michael@0 1391 // it is interpreted as a pointer to the location that we want the
michael@0 1392 // instruction to be written.
michael@0 1393 BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr);
michael@0 1394 // A static variant for the cases where we don't want to have an assembler
michael@0 1395 // object at all. Normally, you would use the dummy (nullptr) object.
michael@0 1396 static void writeInstStatic(uint32_t x, uint32_t *dest);
michael@0 1397
michael@0 1398 public:
michael@0 1399 void writeCodePointer(AbsoluteLabel *label);
michael@0 1400
michael@0 1401 BufferOffset align(int alignment);
michael@0 1402 BufferOffset as_nop();
michael@0 1403 BufferOffset as_alu(Register dest, Register src1, Operand2 op2,
michael@0 1404 ALUOp op, SetCond_ sc = NoSetCond, Condition c = Always, Instruction *instdest = nullptr);
michael@0 1405
michael@0 1406 BufferOffset as_mov(Register dest,
michael@0 1407 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always, Instruction *instdest = nullptr);
michael@0 1408 BufferOffset as_mvn(Register dest, Operand2 op2,
michael@0 1409 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1410 // logical operations
michael@0 1411 BufferOffset as_and(Register dest, Register src1,
michael@0 1412 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1413 BufferOffset as_bic(Register dest, Register src1,
michael@0 1414 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1415 BufferOffset as_eor(Register dest, Register src1,
michael@0 1416 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1417 BufferOffset as_orr(Register dest, Register src1,
michael@0 1418 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1419 // mathematical operations
michael@0 1420 BufferOffset as_adc(Register dest, Register src1,
michael@0 1421 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1422 BufferOffset as_add(Register dest, Register src1,
michael@0 1423 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1424 BufferOffset as_sbc(Register dest, Register src1,
michael@0 1425 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1426 BufferOffset as_sub(Register dest, Register src1,
michael@0 1427 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1428 BufferOffset as_rsb(Register dest, Register src1,
michael@0 1429 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1430 BufferOffset as_rsc(Register dest, Register src1,
michael@0 1431 Operand2 op2, SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1432 // test operations
michael@0 1433 BufferOffset as_cmn(Register src1, Operand2 op2,
michael@0 1434 Condition c = Always);
michael@0 1435 BufferOffset as_cmp(Register src1, Operand2 op2,
michael@0 1436 Condition c = Always);
michael@0 1437 BufferOffset as_teq(Register src1, Operand2 op2,
michael@0 1438 Condition c = Always);
michael@0 1439 BufferOffset as_tst(Register src1, Operand2 op2,
michael@0 1440 Condition c = Always);
michael@0 1441
michael@0 1442 // Not quite ALU worthy, but useful none the less:
michael@0 1443 // These also have the isue of these being formatted
michael@0 1444 // completly differently from the standard ALU operations.
michael@0 1445 BufferOffset as_movw(Register dest, Imm16 imm, Condition c = Always, Instruction *pos = nullptr);
michael@0 1446 BufferOffset as_movt(Register dest, Imm16 imm, Condition c = Always, Instruction *pos = nullptr);
michael@0 1447
michael@0 1448 BufferOffset as_genmul(Register d1, Register d2, Register rm, Register rn,
michael@0 1449 MULOp op, SetCond_ sc, Condition c = Always);
michael@0 1450 BufferOffset as_mul(Register dest, Register src1, Register src2,
michael@0 1451 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1452 BufferOffset as_mla(Register dest, Register acc, Register src1, Register src2,
michael@0 1453 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1454 BufferOffset as_umaal(Register dest1, Register dest2, Register src1, Register src2,
michael@0 1455 Condition c = Always);
michael@0 1456 BufferOffset as_mls(Register dest, Register acc, Register src1, Register src2,
michael@0 1457 Condition c = Always);
michael@0 1458 BufferOffset as_umull(Register dest1, Register dest2, Register src1, Register src2,
michael@0 1459 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1460 BufferOffset as_umlal(Register dest1, Register dest2, Register src1, Register src2,
michael@0 1461 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1462 BufferOffset as_smull(Register dest1, Register dest2, Register src1, Register src2,
michael@0 1463 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1464 BufferOffset as_smlal(Register dest1, Register dest2, Register src1, Register src2,
michael@0 1465 SetCond_ sc = NoSetCond, Condition c = Always);
michael@0 1466
michael@0 1467 BufferOffset as_sdiv(Register dest, Register num, Register div, Condition c = Always);
michael@0 1468 BufferOffset as_udiv(Register dest, Register num, Register div, Condition c = Always);
michael@0 1469
michael@0 1470 // Data transfer instructions: ldr, str, ldrb, strb.
michael@0 1471 // Using an int to differentiate between 8 bits and 32 bits is
michael@0 1472 // overkill, but meh
michael@0 1473 BufferOffset as_dtr(LoadStore ls, int size, Index mode,
michael@0 1474 Register rt, DTRAddr addr, Condition c = Always, uint32_t *dest = nullptr);
michael@0 1475 // Handles all of the other integral data transferring functions:
michael@0 1476 // ldrsb, ldrsh, ldrd, etc.
michael@0 1477 // size is given in bits.
michael@0 1478 BufferOffset as_extdtr(LoadStore ls, int size, bool IsSigned, Index mode,
michael@0 1479 Register rt, EDtrAddr addr, Condition c = Always, uint32_t *dest = nullptr);
michael@0 1480
michael@0 1481 BufferOffset as_dtm(LoadStore ls, Register rn, uint32_t mask,
michael@0 1482 DTMMode mode, DTMWriteBack wb, Condition c = Always);
michael@0 1483 //overwrite a pool entry with new data.
michael@0 1484 void as_WritePoolEntry(Instruction *addr, Condition c, uint32_t data);
michael@0 1485 // load a 32 bit immediate from a pool into a register
michael@0 1486 BufferOffset as_Imm32Pool(Register dest, uint32_t value, Condition c = Always);
michael@0 1487 // make a patchable jump that can target the entire 32 bit address space.
michael@0 1488 BufferOffset as_BranchPool(uint32_t value, RepatchLabel *label, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always);
michael@0 1489
michael@0 1490 // load a 64 bit floating point immediate from a pool into a register
michael@0 1491 BufferOffset as_FImm64Pool(VFPRegister dest, double value, Condition c = Always);
michael@0 1492 // load a 32 bit floating point immediate from a pool into a register
michael@0 1493 BufferOffset as_FImm32Pool(VFPRegister dest, float value, Condition c = Always);
michael@0 1494
michael@0 1495 // Control flow stuff:
michael@0 1496
michael@0 1497 // bx can *only* branch to a register
michael@0 1498 // never to an immediate.
michael@0 1499 BufferOffset as_bx(Register r, Condition c = Always, bool isPatchable = false);
michael@0 1500
michael@0 1501 // Branch can branch to an immediate *or* to a register.
michael@0 1502 // Branches to immediates are pc relative, branches to registers
michael@0 1503 // are absolute
michael@0 1504 BufferOffset as_b(BOffImm off, Condition c, bool isPatchable = false);
michael@0 1505
michael@0 1506 BufferOffset as_b(Label *l, Condition c = Always, bool isPatchable = false);
michael@0 1507 BufferOffset as_b(BOffImm off, Condition c, BufferOffset inst);
michael@0 1508
michael@0 1509 // blx can go to either an immediate or a register.
michael@0 1510 // When blx'ing to a register, we change processor mode
michael@0 1511 // depending on the low bit of the register
michael@0 1512 // when blx'ing to an immediate, we *always* change processor state.
michael@0 1513 BufferOffset as_blx(Label *l);
michael@0 1514
michael@0 1515 BufferOffset as_blx(Register r, Condition c = Always);
michael@0 1516 BufferOffset as_bl(BOffImm off, Condition c);
michael@0 1517 // bl can only branch+link to an immediate, never to a register
michael@0 1518 // it never changes processor state
michael@0 1519 BufferOffset as_bl();
michael@0 1520 // bl #imm can have a condition code, blx #imm cannot.
michael@0 1521 // blx reg can be conditional.
michael@0 1522 BufferOffset as_bl(Label *l, Condition c);
michael@0 1523 BufferOffset as_bl(BOffImm off, Condition c, BufferOffset inst);
michael@0 1524
michael@0 1525 BufferOffset as_mrs(Register r, Condition c = Always);
michael@0 1526 BufferOffset as_msr(Register r, Condition c = Always);
michael@0 1527 // VFP instructions!
michael@0 1528 private:
michael@0 1529
michael@0 1530 enum vfp_size {
michael@0 1531 isDouble = 1 << 8,
michael@0 1532 isSingle = 0 << 8
michael@0 1533 };
michael@0 1534
michael@0 1535 BufferOffset writeVFPInst(vfp_size sz, uint32_t blob, uint32_t *dest=nullptr);
michael@0 1536 // Unityped variants: all registers hold the same (ieee754 single/double)
michael@0 1537 // notably not included are vcvt; vmov vd, #imm; vmov rt, vn.
michael@0 1538 BufferOffset as_vfp_float(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1539 VFPOp op, Condition c = Always);
michael@0 1540
michael@0 1541 public:
michael@0 1542 BufferOffset as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1543 Condition c = Always);
michael@0 1544
michael@0 1545 BufferOffset as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1546 Condition c = Always);
michael@0 1547
michael@0 1548 BufferOffset as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1549 Condition c = Always);
michael@0 1550
michael@0 1551 BufferOffset as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1552 Condition c = Always);
michael@0 1553
michael@0 1554 BufferOffset as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1555 Condition c = Always);
michael@0 1556
michael@0 1557 BufferOffset as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1558 Condition c = Always);
michael@0 1559
michael@0 1560 BufferOffset as_vneg(VFPRegister vd, VFPRegister vm, Condition c = Always);
michael@0 1561
michael@0 1562 BufferOffset as_vsqrt(VFPRegister vd, VFPRegister vm, Condition c = Always);
michael@0 1563
michael@0 1564 BufferOffset as_vabs(VFPRegister vd, VFPRegister vm, Condition c = Always);
michael@0 1565
michael@0 1566 BufferOffset as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm,
michael@0 1567 Condition c = Always);
michael@0 1568
michael@0 1569 BufferOffset as_vcmp(VFPRegister vd, VFPRegister vm,
michael@0 1570 Condition c = Always);
michael@0 1571 BufferOffset as_vcmpz(VFPRegister vd, Condition c = Always);
michael@0 1572
michael@0 1573 // specifically, a move between two same sized-registers
michael@0 1574 BufferOffset as_vmov(VFPRegister vd, VFPRegister vsrc, Condition c = Always);
michael@0 1575 /*xfer between Core and VFP*/
michael@0 1576 enum FloatToCore_ {
michael@0 1577 FloatToCore = 1 << 20,
michael@0 1578 CoreToFloat = 0 << 20
michael@0 1579 };
michael@0 1580
michael@0 1581 private:
michael@0 1582 enum VFPXferSize {
michael@0 1583 WordTransfer = 0x02000010,
michael@0 1584 DoubleTransfer = 0x00400010
michael@0 1585 };
michael@0 1586
michael@0 1587 public:
michael@0 1588 // Unlike the next function, moving between the core registers and vfp
michael@0 1589 // registers can't be *that* properly typed. Namely, since I don't want to
michael@0 1590 // munge the type VFPRegister to also include core registers. Thus, the core
michael@0 1591 // and vfp registers are passed in based on their type, and src/dest is
michael@0 1592 // determined by the float2core.
michael@0 1593
michael@0 1594 BufferOffset as_vxfer(Register vt1, Register vt2, VFPRegister vm, FloatToCore_ f2c,
michael@0 1595 Condition c = Always, int idx = 0);
michael@0 1596
michael@0 1597 // our encoding actually allows just the src and the dest (and theiyr types)
michael@0 1598 // to uniquely specify the encoding that we are going to use.
michael@0 1599 BufferOffset as_vcvt(VFPRegister vd, VFPRegister vm, bool useFPSCR = false,
michael@0 1600 Condition c = Always);
michael@0 1601 // hard coded to a 32 bit fixed width result for now
michael@0 1602 BufferOffset as_vcvtFixed(VFPRegister vd, bool isSigned, uint32_t fixedPoint, bool toFixed, Condition c = Always);
michael@0 1603
michael@0 1604 /* xfer between VFP and memory*/
michael@0 1605 BufferOffset as_vdtr(LoadStore ls, VFPRegister vd, VFPAddr addr,
michael@0 1606 Condition c = Always /* vfp doesn't have a wb option*/,
michael@0 1607 uint32_t *dest = nullptr);
michael@0 1608
michael@0 1609 // VFP's ldm/stm work differently from the standard arm ones.
michael@0 1610 // You can only transfer a range
michael@0 1611
michael@0 1612 BufferOffset as_vdtm(LoadStore st, Register rn, VFPRegister vd, int length,
michael@0 1613 /*also has update conditions*/Condition c = Always);
michael@0 1614
michael@0 1615 BufferOffset as_vimm(VFPRegister vd, VFPImm imm, Condition c = Always);
michael@0 1616
michael@0 1617 BufferOffset as_vmrs(Register r, Condition c = Always);
michael@0 1618 BufferOffset as_vmsr(Register r, Condition c = Always);
michael@0 1619 // label operations
michael@0 1620 bool nextLink(BufferOffset b, BufferOffset *next);
michael@0 1621 void bind(Label *label, BufferOffset boff = BufferOffset());
michael@0 1622 void bind(RepatchLabel *label);
michael@0 1623 uint32_t currentOffset() {
michael@0 1624 return nextOffset().getOffset();
michael@0 1625 }
michael@0 1626 void retarget(Label *label, Label *target);
michael@0 1627 // I'm going to pretend this doesn't exist for now.
michael@0 1628 void retarget(Label *label, void *target, Relocation::Kind reloc);
michael@0 1629
michael@0 1630 void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address);
michael@0 1631
michael@0 1632 // See Bind
michael@0 1633 size_t labelOffsetToPatchOffset(size_t offset) {
michael@0 1634 return actualOffset(offset);
michael@0 1635 }
michael@0 1636
michael@0 1637 void call(Label *label);
michael@0 1638 void call(void *target);
michael@0 1639
michael@0 1640 void as_bkpt();
michael@0 1641
michael@0 1642 public:
michael@0 1643 static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
michael@0 1644 static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
michael@0 1645
michael@0 1646 protected:
michael@0 1647 void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
michael@0 1648 enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind));
michael@0 1649 if (kind == Relocation::JITCODE)
michael@0 1650 writeRelocation(src);
michael@0 1651 }
michael@0 1652
michael@0 1653 public:
michael@0 1654 // The buffer is about to be linked, make sure any constant pools or excess
michael@0 1655 // bookkeeping has been flushed to the instruction stream.
michael@0 1656 void flush() {
michael@0 1657 JS_ASSERT(!isFinished);
michael@0 1658 m_buffer.flushPool();
michael@0 1659 return;
michael@0 1660 }
michael@0 1661
michael@0 1662 // Copy the assembly code to the given buffer, and perform any pending
michael@0 1663 // relocations relying on the target address.
michael@0 1664 void executableCopy(uint8_t *buffer);
michael@0 1665
michael@0 1666 // Actual assembly emitting functions.
michael@0 1667
michael@0 1668 // Since I can't think of a reasonable default for the mode, I'm going to
michael@0 1669 // leave it as a required argument.
michael@0 1670 void startDataTransferM(LoadStore ls, Register rm,
michael@0 1671 DTMMode mode, DTMWriteBack update = NoWriteBack,
michael@0 1672 Condition c = Always)
michael@0 1673 {
michael@0 1674 JS_ASSERT(!dtmActive);
michael@0 1675 dtmUpdate = update;
michael@0 1676 dtmBase = rm;
michael@0 1677 dtmLoadStore = ls;
michael@0 1678 dtmLastReg = -1;
michael@0 1679 dtmRegBitField = 0;
michael@0 1680 dtmActive = 1;
michael@0 1681 dtmCond = c;
michael@0 1682 dtmMode = mode;
michael@0 1683 }
michael@0 1684
michael@0 1685 void transferReg(Register rn) {
michael@0 1686 JS_ASSERT(dtmActive);
michael@0 1687 JS_ASSERT(rn.code() > dtmLastReg);
michael@0 1688 dtmRegBitField |= 1 << rn.code();
michael@0 1689 if (dtmLoadStore == IsLoad && rn.code() == 13 && dtmBase.code() == 13) {
michael@0 1690 MOZ_ASSUME_UNREACHABLE("ARM Spec says this is invalid");
michael@0 1691 }
michael@0 1692 }
michael@0 1693 void finishDataTransfer() {
michael@0 1694 dtmActive = false;
michael@0 1695 as_dtm(dtmLoadStore, dtmBase, dtmRegBitField, dtmMode, dtmUpdate, dtmCond);
michael@0 1696 }
michael@0 1697
michael@0 1698 void startFloatTransferM(LoadStore ls, Register rm,
michael@0 1699 DTMMode mode, DTMWriteBack update = NoWriteBack,
michael@0 1700 Condition c = Always)
michael@0 1701 {
michael@0 1702 JS_ASSERT(!dtmActive);
michael@0 1703 dtmActive = true;
michael@0 1704 dtmUpdate = update;
michael@0 1705 dtmLoadStore = ls;
michael@0 1706 dtmBase = rm;
michael@0 1707 dtmCond = c;
michael@0 1708 dtmLastReg = -1;
michael@0 1709 dtmMode = mode;
michael@0 1710 dtmDelta = 0;
michael@0 1711 }
michael@0 1712 void transferFloatReg(VFPRegister rn)
michael@0 1713 {
michael@0 1714 if (dtmLastReg == -1) {
michael@0 1715 vdtmFirstReg = rn.code();
michael@0 1716 } else {
michael@0 1717 if (dtmDelta == 0) {
michael@0 1718 dtmDelta = rn.code() - dtmLastReg;
michael@0 1719 JS_ASSERT(dtmDelta == 1 || dtmDelta == -1);
michael@0 1720 }
michael@0 1721 JS_ASSERT(dtmLastReg >= 0);
michael@0 1722 JS_ASSERT(rn.code() == unsigned(dtmLastReg) + dtmDelta);
michael@0 1723 }
michael@0 1724 dtmLastReg = rn.code();
michael@0 1725 }
michael@0 1726 void finishFloatTransfer() {
michael@0 1727 JS_ASSERT(dtmActive);
michael@0 1728 dtmActive = false;
michael@0 1729 JS_ASSERT(dtmLastReg != -1);
michael@0 1730 dtmDelta = dtmDelta ? dtmDelta : 1;
michael@0 1731 // fencepost problem.
michael@0 1732 int len = dtmDelta * (dtmLastReg - vdtmFirstReg) + 1;
michael@0 1733 as_vdtm(dtmLoadStore, dtmBase,
michael@0 1734 VFPRegister(FloatRegister::FromCode(Min(vdtmFirstReg, dtmLastReg))),
michael@0 1735 len, dtmCond);
michael@0 1736 }
michael@0 1737
michael@0 1738 private:
michael@0 1739 int dtmRegBitField;
michael@0 1740 int vdtmFirstReg;
michael@0 1741 int dtmLastReg;
michael@0 1742 int dtmDelta;
michael@0 1743 Register dtmBase;
michael@0 1744 DTMWriteBack dtmUpdate;
michael@0 1745 DTMMode dtmMode;
michael@0 1746 LoadStore dtmLoadStore;
michael@0 1747 bool dtmActive;
michael@0 1748 Condition dtmCond;
michael@0 1749
michael@0 1750 public:
michael@0 1751 enum {
michael@0 1752 padForAlign8 = (int)0x00,
michael@0 1753 padForAlign16 = (int)0x0000,
michael@0 1754 padForAlign32 = (int)0xe12fff7f // 'bkpt 0xffff'
michael@0 1755 };
michael@0 1756
michael@0 1757 // API for speaking with the IonAssemblerBufferWithConstantPools
michael@0 1758 // generate an initial placeholder instruction that we want to later fix up
michael@0 1759 static void insertTokenIntoTag(uint32_t size, uint8_t *load, int32_t token);
michael@0 1760 // take the stub value that was written in before, and write in an actual load
michael@0 1761 // using the index we'd computed previously as well as the address of the pool start.
michael@0 1762 static bool patchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
michael@0 1763 // this is a callback for when we have filled a pool, and MUST flush it now.
michael@0 1764 // The pool requires the assembler to place a branch past the pool, and it
michael@0 1765 // calls this function.
michael@0 1766 static uint32_t placeConstantPoolBarrier(int offset);
michael@0 1767 // END API
michael@0 1768
michael@0 1769 // move our entire pool into the instruction stream
michael@0 1770 // This is to force an opportunistic dump of the pool, prefferably when it
michael@0 1771 // is more convenient to do a dump.
michael@0 1772 void dumpPool();
michael@0 1773 void flushBuffer();
michael@0 1774 void enterNoPool();
michael@0 1775 void leaveNoPool();
michael@0 1776 // this should return a BOffImm, but I didn't want to require everyplace that used the
michael@0 1777 // AssemblerBuffer to make that class.
michael@0 1778 static ptrdiff_t getBranchOffset(const Instruction *i);
michael@0 1779 static void retargetNearBranch(Instruction *i, int offset, Condition cond, bool final = true);
michael@0 1780 static void retargetNearBranch(Instruction *i, int offset, bool final = true);
michael@0 1781 static void retargetFarBranch(Instruction *i, uint8_t **slot, uint8_t *dest, Condition cond);
michael@0 1782
michael@0 1783 static void writePoolHeader(uint8_t *start, Pool *p, bool isNatural);
michael@0 1784 static void writePoolFooter(uint8_t *start, Pool *p, bool isNatural);
michael@0 1785 static void writePoolGuard(BufferOffset branch, Instruction *inst, BufferOffset dest);
michael@0 1786
michael@0 1787
michael@0 1788 static uint32_t patchWrite_NearCallSize();
michael@0 1789 static uint32_t nopSize() { return 4; }
michael@0 1790 static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
michael@0 1791 static void patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
michael@0 1792 PatchedImmPtr expectedValue);
michael@0 1793 static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
michael@0 1794 ImmPtr expectedValue);
michael@0 1795 static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
michael@0 1796 static uint32_t alignDoubleArg(uint32_t offset) {
michael@0 1797 return (offset+1)&~1;
michael@0 1798 }
michael@0 1799 static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = nullptr);
michael@0 1800 // Toggle a jmp or cmp emitted by toggledJump().
michael@0 1801
michael@0 1802 static void ToggleToJmp(CodeLocationLabel inst_);
michael@0 1803 static void ToggleToCmp(CodeLocationLabel inst_);
michael@0 1804
michael@0 1805 static void ToggleCall(CodeLocationLabel inst_, bool enabled);
michael@0 1806
michael@0 1807 static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst);
michael@0 1808 void processCodeLabels(uint8_t *rawCode);
michael@0 1809 bool bailed() {
michael@0 1810 return m_buffer.bail();
michael@0 1811 }
michael@0 1812 }; // Assembler
michael@0 1813
michael@0 1814 // An Instruction is a structure for both encoding and decoding any and all ARM instructions.
michael@0 1815 // many classes have not been implemented thusfar.
michael@0 1816 class Instruction
michael@0 1817 {
michael@0 1818 uint32_t data;
michael@0 1819
michael@0 1820 protected:
michael@0 1821 // This is not for defaulting to always, this is for instructions that
michael@0 1822 // cannot be made conditional, and have the usually invalid 4b1111 cond field
michael@0 1823 Instruction (uint32_t data_, bool fake = false) : data(data_ | 0xf0000000) {
michael@0 1824 JS_ASSERT (fake || ((data_ & 0xf0000000) == 0));
michael@0 1825 }
michael@0 1826 // Standard constructor
michael@0 1827 Instruction (uint32_t data_, Assembler::Condition c) : data(data_ | (uint32_t) c) {
michael@0 1828 JS_ASSERT ((data_ & 0xf0000000) == 0);
michael@0 1829 }
michael@0 1830 // You should never create an instruction directly. You should create a
michael@0 1831 // more specific instruction which will eventually call one of these
michael@0 1832 // constructors for you.
michael@0 1833 public:
michael@0 1834 uint32_t encode() const {
michael@0 1835 return data;
michael@0 1836 }
michael@0 1837 // Check if this instruction is really a particular case
michael@0 1838 template <class C>
michael@0 1839 bool is() const { return C::isTHIS(*this); }
michael@0 1840
michael@0 1841 // safely get a more specific variant of this pointer
michael@0 1842 template <class C>
michael@0 1843 C *as() const { return C::asTHIS(*this); }
michael@0 1844
michael@0 1845 const Instruction & operator=(const Instruction &src) {
michael@0 1846 data = src.data;
michael@0 1847 return *this;
michael@0 1848 }
michael@0 1849 // Since almost all instructions have condition codes, the condition
michael@0 1850 // code extractor resides in the base class.
michael@0 1851 void extractCond(Assembler::Condition *c) {
michael@0 1852 if (data >> 28 != 0xf )
michael@0 1853 *c = (Assembler::Condition)(data & 0xf0000000);
michael@0 1854 }
michael@0 1855 // Get the next instruction in the instruction stream.
michael@0 1856 // This does neat things like ignoreconstant pools and their guards.
michael@0 1857 Instruction *next();
michael@0 1858
michael@0 1859 // Sometimes, an api wants a uint32_t (or a pointer to it) rather than
michael@0 1860 // an instruction. raw() just coerces this into a pointer to a uint32_t
michael@0 1861 const uint32_t *raw() const { return &data; }
michael@0 1862 uint32_t size() const { return 4; }
michael@0 1863 }; // Instruction
michael@0 1864
michael@0 1865 // make sure that it is the right size
michael@0 1866 JS_STATIC_ASSERT(sizeof(Instruction) == 4);
michael@0 1867
michael@0 1868 // Data Transfer Instructions
michael@0 1869 class InstDTR : public Instruction
michael@0 1870 {
michael@0 1871 public:
michael@0 1872 enum IsByte_ {
michael@0 1873 IsByte = 0x00400000,
michael@0 1874 IsWord = 0x00000000
michael@0 1875 };
michael@0 1876 static const int IsDTR = 0x04000000;
michael@0 1877 static const int IsDTRMask = 0x0c000000;
michael@0 1878
michael@0 1879 // TODO: Replace the initialization with something that is safer.
michael@0 1880 InstDTR(LoadStore ls, IsByte_ ib, Index mode, Register rt, DTRAddr addr, Assembler::Condition c)
michael@0 1881 : Instruction(ls | ib | mode | RT(rt) | addr.encode() | IsDTR, c)
michael@0 1882 { }
michael@0 1883
michael@0 1884 static bool isTHIS(const Instruction &i);
michael@0 1885 static InstDTR *asTHIS(const Instruction &i);
michael@0 1886
michael@0 1887 };
michael@0 1888 JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(Instruction));
michael@0 1889
michael@0 1890 class InstLDR : public InstDTR
michael@0 1891 {
michael@0 1892 public:
michael@0 1893 InstLDR(Index mode, Register rt, DTRAddr addr, Assembler::Condition c)
michael@0 1894 : InstDTR(IsLoad, IsWord, mode, rt, addr, c)
michael@0 1895 { }
michael@0 1896 static bool isTHIS(const Instruction &i);
michael@0 1897 static InstLDR *asTHIS(const Instruction &i);
michael@0 1898
michael@0 1899 };
michael@0 1900 JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(InstLDR));
michael@0 1901
michael@0 1902 class InstNOP : public Instruction
michael@0 1903 {
michael@0 1904 static const uint32_t NopInst = 0x0320f000;
michael@0 1905
michael@0 1906 public:
michael@0 1907 InstNOP()
michael@0 1908 : Instruction(NopInst, Assembler::Always)
michael@0 1909 { }
michael@0 1910
michael@0 1911 static bool isTHIS(const Instruction &i);
michael@0 1912 static InstNOP *asTHIS(Instruction &i);
michael@0 1913 };
michael@0 1914
michael@0 1915 // Branching to a register, or calling a register
michael@0 1916 class InstBranchReg : public Instruction
michael@0 1917 {
michael@0 1918 protected:
michael@0 1919 // Don't use BranchTag yourself, use a derived instruction.
michael@0 1920 enum BranchTag {
michael@0 1921 IsBX = 0x012fff10,
michael@0 1922 IsBLX = 0x012fff30
michael@0 1923 };
michael@0 1924 static const uint32_t IsBRegMask = 0x0ffffff0;
michael@0 1925 InstBranchReg(BranchTag tag, Register rm, Assembler::Condition c)
michael@0 1926 : Instruction(tag | rm.code(), c)
michael@0 1927 { }
michael@0 1928 public:
michael@0 1929 static bool isTHIS (const Instruction &i);
michael@0 1930 static InstBranchReg *asTHIS (const Instruction &i);
michael@0 1931 // Get the register that is being branched to
michael@0 1932 void extractDest(Register *dest);
michael@0 1933 // Make sure we are branching to a pre-known register
michael@0 1934 bool checkDest(Register dest);
michael@0 1935 };
michael@0 1936 JS_STATIC_ASSERT(sizeof(InstBranchReg) == sizeof(Instruction));
michael@0 1937
michael@0 1938 // Branching to an immediate offset, or calling an immediate offset
michael@0 1939 class InstBranchImm : public Instruction
michael@0 1940 {
michael@0 1941 protected:
michael@0 1942 enum BranchTag {
michael@0 1943 IsB = 0x0a000000,
michael@0 1944 IsBL = 0x0b000000
michael@0 1945 };
michael@0 1946 static const uint32_t IsBImmMask = 0x0f000000;
michael@0 1947
michael@0 1948 InstBranchImm(BranchTag tag, BOffImm off, Assembler::Condition c)
michael@0 1949 : Instruction(tag | off.encode(), c)
michael@0 1950 { }
michael@0 1951
michael@0 1952 public:
michael@0 1953 static bool isTHIS (const Instruction &i);
michael@0 1954 static InstBranchImm *asTHIS (const Instruction &i);
michael@0 1955 void extractImm(BOffImm *dest);
michael@0 1956 };
michael@0 1957 JS_STATIC_ASSERT(sizeof(InstBranchImm) == sizeof(Instruction));
michael@0 1958
michael@0 1959 // Very specific branching instructions.
michael@0 1960 class InstBXReg : public InstBranchReg
michael@0 1961 {
michael@0 1962 public:
michael@0 1963 static bool isTHIS (const Instruction &i);
michael@0 1964 static InstBXReg *asTHIS (const Instruction &i);
michael@0 1965 };
michael@0 1966 class InstBLXReg : public InstBranchReg
michael@0 1967 {
michael@0 1968 public:
michael@0 1969 InstBLXReg(Register reg, Assembler::Condition c)
michael@0 1970 : InstBranchReg(IsBLX, reg, c)
michael@0 1971 { }
michael@0 1972
michael@0 1973 static bool isTHIS (const Instruction &i);
michael@0 1974 static InstBLXReg *asTHIS (const Instruction &i);
michael@0 1975 };
michael@0 1976 class InstBImm : public InstBranchImm
michael@0 1977 {
michael@0 1978 public:
michael@0 1979 InstBImm(BOffImm off, Assembler::Condition c)
michael@0 1980 : InstBranchImm(IsB, off, c)
michael@0 1981 { }
michael@0 1982
michael@0 1983 static bool isTHIS (const Instruction &i);
michael@0 1984 static InstBImm *asTHIS (const Instruction &i);
michael@0 1985 };
michael@0 1986 class InstBLImm : public InstBranchImm
michael@0 1987 {
michael@0 1988 public:
michael@0 1989 InstBLImm(BOffImm off, Assembler::Condition c)
michael@0 1990 : InstBranchImm(IsBL, off, c)
michael@0 1991 { }
michael@0 1992
michael@0 1993 static bool isTHIS (const Instruction &i);
michael@0 1994 static InstBLImm *asTHIS (Instruction &i);
michael@0 1995 };
michael@0 1996
michael@0 1997 // Both movw and movt. The layout of both the immediate and the destination
michael@0 1998 // register is the same so the code is being shared.
michael@0 1999 class InstMovWT : public Instruction
michael@0 2000 {
michael@0 2001 protected:
michael@0 2002 enum WT {
michael@0 2003 IsW = 0x03000000,
michael@0 2004 IsT = 0x03400000
michael@0 2005 };
michael@0 2006 static const uint32_t IsWTMask = 0x0ff00000;
michael@0 2007
michael@0 2008 InstMovWT(Register rd, Imm16 imm, WT wt, Assembler::Condition c)
michael@0 2009 : Instruction (RD(rd) | imm.encode() | wt, c)
michael@0 2010 { }
michael@0 2011
michael@0 2012 public:
michael@0 2013 void extractImm(Imm16 *dest);
michael@0 2014 void extractDest(Register *dest);
michael@0 2015 bool checkImm(Imm16 dest);
michael@0 2016 bool checkDest(Register dest);
michael@0 2017
michael@0 2018 static bool isTHIS (Instruction &i);
michael@0 2019 static InstMovWT *asTHIS (Instruction &i);
michael@0 2020
michael@0 2021 };
michael@0 2022 JS_STATIC_ASSERT(sizeof(InstMovWT) == sizeof(Instruction));
michael@0 2023
michael@0 2024 class InstMovW : public InstMovWT
michael@0 2025 {
michael@0 2026 public:
michael@0 2027 InstMovW (Register rd, Imm16 imm, Assembler::Condition c)
michael@0 2028 : InstMovWT(rd, imm, IsW, c)
michael@0 2029 { }
michael@0 2030
michael@0 2031 static bool isTHIS (const Instruction &i);
michael@0 2032 static InstMovW *asTHIS (const Instruction &i);
michael@0 2033 };
michael@0 2034
michael@0 2035 class InstMovT : public InstMovWT
michael@0 2036 {
michael@0 2037 public:
michael@0 2038 InstMovT (Register rd, Imm16 imm, Assembler::Condition c)
michael@0 2039 : InstMovWT(rd, imm, IsT, c)
michael@0 2040 { }
michael@0 2041 static bool isTHIS (const Instruction &i);
michael@0 2042 static InstMovT *asTHIS (const Instruction &i);
michael@0 2043 };
michael@0 2044
michael@0 2045 class InstALU : public Instruction
michael@0 2046 {
michael@0 2047 static const int32_t ALUMask = 0xc << 24;
michael@0 2048 public:
michael@0 2049 InstALU (Register rd, Register rn, Operand2 op2, ALUOp op, SetCond_ sc, Assembler::Condition c)
michael@0 2050 : Instruction(maybeRD(rd) | maybeRN(rn) | op2.encode() | op | sc, c)
michael@0 2051 { }
michael@0 2052 static bool isTHIS (const Instruction &i);
michael@0 2053 static InstALU *asTHIS (const Instruction &i);
michael@0 2054 void extractOp(ALUOp *ret);
michael@0 2055 bool checkOp(ALUOp op);
michael@0 2056 void extractDest(Register *ret);
michael@0 2057 bool checkDest(Register rd);
michael@0 2058 void extractOp1(Register *ret);
michael@0 2059 bool checkOp1(Register rn);
michael@0 2060 Operand2 extractOp2();
michael@0 2061 };
michael@0 2062
michael@0 2063 class InstCMP : public InstALU
michael@0 2064 {
michael@0 2065 public:
michael@0 2066 static bool isTHIS (const Instruction &i);
michael@0 2067 static InstCMP *asTHIS (const Instruction &i);
michael@0 2068 };
michael@0 2069
michael@0 2070 class InstMOV : public InstALU
michael@0 2071 {
michael@0 2072 public:
michael@0 2073 static bool isTHIS (const Instruction &i);
michael@0 2074 static InstMOV *asTHIS (const Instruction &i);
michael@0 2075 };
michael@0 2076
michael@0 2077
michael@0 2078 class InstructionIterator {
michael@0 2079 private:
michael@0 2080 Instruction *i;
michael@0 2081 public:
michael@0 2082 InstructionIterator(Instruction *i_);
michael@0 2083 Instruction *next() {
michael@0 2084 i = i->next();
michael@0 2085 return cur();
michael@0 2086 }
michael@0 2087 Instruction *cur() const {
michael@0 2088 return i;
michael@0 2089 }
michael@0 2090 };
michael@0 2091
michael@0 2092 static const uint32_t NumIntArgRegs = 4;
michael@0 2093 static const uint32_t NumFloatArgRegs = 8;
michael@0 2094
michael@0 2095 static inline bool
michael@0 2096 GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
michael@0 2097 {
michael@0 2098 if (usedIntArgs >= NumIntArgRegs)
michael@0 2099 return false;
michael@0 2100 *out = Register::FromCode(usedIntArgs);
michael@0 2101 return true;
michael@0 2102 }
michael@0 2103
michael@0 2104 // Get a register in which we plan to put a quantity that will be used as an
michael@0 2105 // integer argument. This differs from GetIntArgReg in that if we have no more
michael@0 2106 // actual argument registers to use we will fall back on using whatever
michael@0 2107 // CallTempReg* don't overlap the argument registers, and only fail once those
michael@0 2108 // run out too.
michael@0 2109 static inline bool
michael@0 2110 GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
michael@0 2111 {
michael@0 2112 if (GetIntArgReg(usedIntArgs, usedFloatArgs, out))
michael@0 2113 return true;
michael@0 2114 // Unfortunately, we have to assume things about the point at which
michael@0 2115 // GetIntArgReg returns false, because we need to know how many registers it
michael@0 2116 // can allocate.
michael@0 2117 usedIntArgs -= NumIntArgRegs;
michael@0 2118 if (usedIntArgs >= NumCallTempNonArgRegs)
michael@0 2119 return false;
michael@0 2120 *out = CallTempNonArgRegs[usedIntArgs];
michael@0 2121 return true;
michael@0 2122 }
michael@0 2123
michael@0 2124
michael@0 2125 #if !defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
michael@0 2126
michael@0 2127 static inline uint32_t
michael@0 2128 GetArgStackDisp(uint32_t arg)
michael@0 2129 {
michael@0 2130 JS_ASSERT(!useHardFpABI());
michael@0 2131 JS_ASSERT(arg >= NumIntArgRegs);
michael@0 2132 return (arg - NumIntArgRegs) * sizeof(intptr_t);
michael@0 2133 }
michael@0 2134
michael@0 2135 #endif
michael@0 2136
michael@0 2137
michael@0 2138 #if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
michael@0 2139
michael@0 2140 static inline bool
michael@0 2141 GetFloatArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, FloatRegister *out)
michael@0 2142 {
michael@0 2143 JS_ASSERT(useHardFpABI());
michael@0 2144 if (usedFloatArgs >= NumFloatArgRegs)
michael@0 2145 return false;
michael@0 2146 *out = FloatRegister::FromCode(usedFloatArgs);
michael@0 2147 return true;
michael@0 2148 }
michael@0 2149
michael@0 2150 static inline uint32_t
michael@0 2151 GetIntArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
michael@0 2152 {
michael@0 2153 JS_ASSERT(useHardFpABI());
michael@0 2154 JS_ASSERT(usedIntArgs >= NumIntArgRegs);
michael@0 2155 uint32_t doubleSlots = Max(0, (int32_t)usedFloatArgs - (int32_t)NumFloatArgRegs);
michael@0 2156 doubleSlots *= 2;
michael@0 2157 int intSlots = usedIntArgs - NumIntArgRegs;
michael@0 2158 return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
michael@0 2159 }
michael@0 2160
michael@0 2161 static inline uint32_t
michael@0 2162 GetFloat32ArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
michael@0 2163 {
michael@0 2164 JS_ASSERT(useHardFpABI());
michael@0 2165 JS_ASSERT(usedFloatArgs >= NumFloatArgRegs);
michael@0 2166 uint32_t intSlots = 0;
michael@0 2167 if (usedIntArgs > NumIntArgRegs)
michael@0 2168 intSlots = usedIntArgs - NumIntArgRegs;
michael@0 2169 uint32_t float32Slots = usedFloatArgs - NumFloatArgRegs;
michael@0 2170 return (intSlots + float32Slots + *padding) * sizeof(intptr_t);
michael@0 2171 }
michael@0 2172
michael@0 2173 static inline uint32_t
michael@0 2174 GetDoubleArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
michael@0 2175 {
michael@0 2176 JS_ASSERT(useHardFpABI());
michael@0 2177 JS_ASSERT(usedFloatArgs >= NumFloatArgRegs);
michael@0 2178 uint32_t intSlots = 0;
michael@0 2179 if (usedIntArgs > NumIntArgRegs) {
michael@0 2180 intSlots = usedIntArgs - NumIntArgRegs;
michael@0 2181 // update the amount of padding required.
michael@0 2182 *padding += (*padding + usedIntArgs) % 2;
michael@0 2183 }
michael@0 2184 uint32_t doubleSlots = usedFloatArgs - NumFloatArgRegs;
michael@0 2185 doubleSlots *= 2;
michael@0 2186 return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
michael@0 2187 }
michael@0 2188
michael@0 2189 #endif
michael@0 2190
michael@0 2191
michael@0 2192
michael@0 2193 class DoubleEncoder {
michael@0 2194 uint32_t rep(bool b, uint32_t count) {
michael@0 2195 uint32_t ret = 0;
michael@0 2196 for (uint32_t i = 0; i < count; i++)
michael@0 2197 ret = (ret << 1) | b;
michael@0 2198 return ret;
michael@0 2199 }
michael@0 2200
michael@0 2201 uint32_t encode(uint8_t value) {
michael@0 2202 //ARM ARM "VFP modified immediate constants"
michael@0 2203 // aBbbbbbb bbcdefgh 000...
michael@0 2204 // we want to return the top 32 bits of the double
michael@0 2205 // the rest are 0.
michael@0 2206 bool a = value >> 7;
michael@0 2207 bool b = value >> 6 & 1;
michael@0 2208 bool B = !b;
michael@0 2209 uint32_t cdefgh = value & 0x3f;
michael@0 2210 return a << 31 |
michael@0 2211 B << 30 |
michael@0 2212 rep(b, 8) << 22 |
michael@0 2213 cdefgh << 16;
michael@0 2214 }
michael@0 2215
michael@0 2216 struct DoubleEntry
michael@0 2217 {
michael@0 2218 uint32_t dblTop;
michael@0 2219 datastore::Imm8VFPImmData data;
michael@0 2220
michael@0 2221 DoubleEntry()
michael@0 2222 : dblTop(-1)
michael@0 2223 { }
michael@0 2224 DoubleEntry(uint32_t dblTop_, datastore::Imm8VFPImmData data_)
michael@0 2225 : dblTop(dblTop_), data(data_)
michael@0 2226 { }
michael@0 2227 };
michael@0 2228
michael@0 2229 mozilla::Array<DoubleEntry, 256> table;
michael@0 2230
michael@0 2231 public:
michael@0 2232 DoubleEncoder()
michael@0 2233 {
michael@0 2234 for (int i = 0; i < 256; i++) {
michael@0 2235 table[i] = DoubleEntry(encode(i), datastore::Imm8VFPImmData(i));
michael@0 2236 }
michael@0 2237 }
michael@0 2238
michael@0 2239 bool lookup(uint32_t top, datastore::Imm8VFPImmData *ret) {
michael@0 2240 for (int i = 0; i < 256; i++) {
michael@0 2241 if (table[i].dblTop == top) {
michael@0 2242 *ret = table[i].data;
michael@0 2243 return true;
michael@0 2244 }
michael@0 2245 }
michael@0 2246 return false;
michael@0 2247 }
michael@0 2248 };
michael@0 2249
michael@0 2250 class AutoForbidPools {
michael@0 2251 Assembler *masm_;
michael@0 2252 public:
michael@0 2253 AutoForbidPools(Assembler *masm) : masm_(masm) {
michael@0 2254 masm_->enterNoPool();
michael@0 2255 }
michael@0 2256 ~AutoForbidPools() {
michael@0 2257 masm_->leaveNoPool();
michael@0 2258 }
michael@0 2259 };
michael@0 2260
michael@0 2261 } // namespace jit
michael@0 2262 } // namespace js
michael@0 2263
michael@0 2264 #endif /* jit_arm_Assembler_arm_h */

mercurial