Wed, 31 Dec 2014 06:09:35 +0100
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 ¤t() { 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 */ |