1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef jit_arm_Assembler_arm_h
8 #define jit_arm_Assembler_arm_h
9
10 #include "mozilla/Attributes.h"
11 #include "mozilla/MathAlgorithms.h"
12
13 #include <algorithm>
14 #include <iterator>
15
16 #include "jit/arm/Architecture-arm.h"
17 #include "jit/arm/disasm/Disasm-arm.h"
18 #include "jit/CompactBuffer.h"
19 #include "jit/JitCode.h"
20 #include "jit/shared/Assembler-shared.h"
21 #include "jit/shared/Disassembler-shared.h"
22 #include "jit/shared/IonAssemblerBufferWithConstantPools.h"
23 #include "wasm/WasmTypeDecls.h"
24
25 union PoolHintPun;
26
27 namespace js {
28 namespace jit {
29
30 using LiteralDoc = DisassemblerSpew::LiteralDoc;
31 using LabelDoc = DisassemblerSpew::LabelDoc;
32
33 // NOTE: there are duplicates in this list! Sometimes we want to specifically
34 // refer to the link register as a link register (bl lr is much clearer than bl
35 // r14). HOWEVER, this register can easily be a gpr when it is not busy holding
36 // the return address.
37 static constexpr Register r0{Registers::r0};
38 static constexpr Register r1{Registers::r1};
39 static constexpr Register r2{Registers::r2};
40 static constexpr Register r3{Registers::r3};
41 static constexpr Register r4{Registers::r4};
42 static constexpr Register r5{Registers::r5};
43 static constexpr Register r6{Registers::r6};
44 static constexpr Register r7{Registers::r7};
45 static constexpr Register r8{Registers::r8};
46 static constexpr Register r9{Registers::r9};
47 static constexpr Register r10{Registers::r10};
48 static constexpr Register r11{Registers::r11};
49 static constexpr Register r12{Registers::ip};
50 static constexpr Register ip{Registers::ip};
51 static constexpr Register sp{Registers::sp};
52 static constexpr Register r14{Registers::lr};
53 static constexpr Register lr{Registers::lr};
54 static constexpr Register pc{Registers::pc};
55
56 static constexpr Register ScratchRegister{Registers::ip};
57
58 // Helper class for ScratchRegister usage. Asserts that only one piece
59 // of code thinks it has exclusive ownership of the scratch register.
60 struct ScratchRegisterScope : public AutoRegisterScope {
ScratchRegisterScopeScratchRegisterScope61 explicit ScratchRegisterScope(MacroAssembler& masm)
62 : AutoRegisterScope(masm, ScratchRegister) {}
63 };
64
65 struct SecondScratchRegisterScope : public AutoRegisterScope {
66 explicit SecondScratchRegisterScope(MacroAssembler& masm);
67 };
68
69 static constexpr Register OsrFrameReg = r3;
70 static constexpr Register CallTempReg0 = r5;
71 static constexpr Register CallTempReg1 = r6;
72 static constexpr Register CallTempReg2 = r7;
73 static constexpr Register CallTempReg3 = r8;
74 static constexpr Register CallTempReg4 = r0;
75 static constexpr Register CallTempReg5 = r1;
76
77 static constexpr Register IntArgReg0 = r0;
78 static constexpr Register IntArgReg1 = r1;
79 static constexpr Register IntArgReg2 = r2;
80 static constexpr Register IntArgReg3 = r3;
81 static constexpr Register HeapReg = r10;
82 static constexpr Register CallTempNonArgRegs[] = {r5, r6, r7, r8};
83 static const uint32_t NumCallTempNonArgRegs = std::size(CallTempNonArgRegs);
84
85 // These register assignments for the 64-bit atomic ops are frequently too
86 // constraining, but we have no way of expressing looser constraints to the
87 // register allocator.
88
89 // CompareExchange: Any two odd/even pairs would do for `new` and `out`, and any
90 // pair would do for `old`, so long as none of them overlap.
91
92 static constexpr Register CmpXchgOldLo = r4;
93 static constexpr Register CmpXchgOldHi = r5;
94 static constexpr Register64 CmpXchgOld64 =
95 Register64(CmpXchgOldHi, CmpXchgOldLo);
96 static constexpr Register CmpXchgNewLo = IntArgReg2;
97 static constexpr Register CmpXchgNewHi = IntArgReg3;
98 static constexpr Register64 CmpXchgNew64 =
99 Register64(CmpXchgNewHi, CmpXchgNewLo);
100 static constexpr Register CmpXchgOutLo = IntArgReg0;
101 static constexpr Register CmpXchgOutHi = IntArgReg1;
102 static constexpr Register64 CmpXchgOut64 =
103 Register64(CmpXchgOutHi, CmpXchgOutLo);
104
105 // Exchange: Any two non-equal odd/even pairs would do for `new` and `out`.
106
107 static constexpr Register XchgNewLo = IntArgReg2;
108 static constexpr Register XchgNewHi = IntArgReg3;
109 static constexpr Register64 XchgNew64 = Register64(XchgNewHi, XchgNewLo);
110 static constexpr Register XchgOutLo = IntArgReg0;
111 static constexpr Register XchgOutHi = IntArgReg1;
112
113 // Atomic rmw operations: Any two odd/even pairs would do for `tmp` and `out`,
114 // and any pair would do for `val`, so long as none of them overlap.
115
116 static constexpr Register FetchOpValLo = r4;
117 static constexpr Register FetchOpValHi = r5;
118 static constexpr Register64 FetchOpVal64 =
119 Register64(FetchOpValHi, FetchOpValLo);
120 static constexpr Register FetchOpTmpLo = IntArgReg2;
121 static constexpr Register FetchOpTmpHi = IntArgReg3;
122 static constexpr Register64 FetchOpTmp64 =
123 Register64(FetchOpTmpHi, FetchOpTmpLo);
124 static constexpr Register FetchOpOutLo = IntArgReg0;
125 static constexpr Register FetchOpOutHi = IntArgReg1;
126 static constexpr Register64 FetchOpOut64 =
127 Register64(FetchOpOutHi, FetchOpOutLo);
128
129 class ABIArgGenerator {
130 unsigned intRegIndex_;
131 unsigned floatRegIndex_;
132 uint32_t stackOffset_;
133 ABIArg current_;
134
135 // ARM can either use HardFp (use float registers for float arguments), or
136 // SoftFp (use general registers for float arguments) ABI. We keep this
137 // switch as a runtime switch because wasm always use the HardFp back-end
138 // while the calls to native functions have to use the one provided by the
139 // system.
140 bool useHardFp_;
141
142 ABIArg softNext(MIRType argType);
143 ABIArg hardNext(MIRType argType);
144
145 public:
146 ABIArgGenerator();
147
setUseHardFp(bool useHardFp)148 void setUseHardFp(bool useHardFp) {
149 MOZ_ASSERT(intRegIndex_ == 0 && floatRegIndex_ == 0);
150 useHardFp_ = useHardFp;
151 }
152 ABIArg next(MIRType argType);
current()153 ABIArg& current() { return current_; }
stackBytesConsumedSoFar()154 uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
increaseStackOffset(uint32_t bytes)155 void increaseStackOffset(uint32_t bytes) { stackOffset_ += bytes; }
156 };
157
158 bool IsUnaligned(const wasm::MemoryAccessDesc& access);
159
160 // These registers may be volatile or nonvolatile.
161 static constexpr Register ABINonArgReg0 = r4;
162 static constexpr Register ABINonArgReg1 = r5;
163 static constexpr Register ABINonArgReg2 = r6;
164 static constexpr Register ABINonArgReg3 = r7;
165
166 // This register may be volatile or nonvolatile. Avoid d15 which is the
167 // ScratchDoubleReg_.
168 static constexpr FloatRegister ABINonArgDoubleReg{FloatRegisters::d8,
169 VFPRegister::Double};
170
171 // These registers may be volatile or nonvolatile.
172 // Note: these three registers are all guaranteed to be different
173 static constexpr Register ABINonArgReturnReg0 = r4;
174 static constexpr Register ABINonArgReturnReg1 = r5;
175 static constexpr Register ABINonVolatileReg = r6;
176
177 // This register is guaranteed to be clobberable during the prologue and
178 // epilogue of an ABI call which must preserve both ABI argument, return
179 // and non-volatile registers.
180 static constexpr Register ABINonArgReturnVolatileReg = lr;
181
182 // TLS pointer argument register for WebAssembly functions. This must not alias
183 // any other register used for passing function arguments or return values.
184 // Preserved by WebAssembly functions.
185 static constexpr Register WasmTlsReg = r9;
186
187 // Registers used for wasm table calls. These registers must be disjoint
188 // from the ABI argument registers, WasmTlsReg and each other.
189 static constexpr Register WasmTableCallScratchReg0 = ABINonArgReg0;
190 static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
191 static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
192 static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
193
194 // Register used as a scratch along the return path in the fast js -> wasm stub
195 // code. This must not overlap ReturnReg, JSReturnOperand, or WasmTlsReg. It
196 // must be a volatile register.
197 static constexpr Register WasmJitEntryReturnScratch = r5;
198
199 static constexpr Register PreBarrierReg = r1;
200
201 static constexpr Register InterpreterPCReg = r9;
202
203 static constexpr Register InvalidReg{Registers::invalid_reg};
204 static constexpr FloatRegister InvalidFloatReg;
205
206 static constexpr Register JSReturnReg_Type = r3;
207 static constexpr Register JSReturnReg_Data = r2;
208 static constexpr Register StackPointer = sp;
209 static constexpr Register FramePointer = r11;
210 static constexpr Register ReturnReg = r0;
211 static constexpr Register64 ReturnReg64(r1, r0);
212
213 // The attribute '__value_in_regs' alters the calling convention of a function
214 // so that a structure of up to four elements can be returned via the argument
215 // registers rather than being written to memory.
216 static constexpr Register ReturnRegVal0 = IntArgReg0;
217 static constexpr Register ReturnRegVal1 = IntArgReg1;
218 static constexpr Register ReturnRegVal2 = IntArgReg2;
219 static constexpr Register ReturnRegVal3 = IntArgReg3;
220
221 static constexpr FloatRegister ReturnFloat32Reg = {FloatRegisters::d0,
222 VFPRegister::Single};
223 static constexpr FloatRegister ReturnDoubleReg = {FloatRegisters::d0,
224 VFPRegister::Double};
225 static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
226 static constexpr FloatRegister ScratchFloat32Reg_ = {FloatRegisters::s30,
227 VFPRegister::Single};
228 static constexpr FloatRegister ScratchDoubleReg_ = {FloatRegisters::d15,
229 VFPRegister::Double};
230 static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
231 static constexpr FloatRegister ScratchUIntReg = {FloatRegisters::d15,
232 VFPRegister::UInt};
233 static constexpr FloatRegister ScratchIntReg = {FloatRegisters::d15,
234 VFPRegister::Int};
235
236 // Do not reference ScratchFloat32Reg_ directly, use ScratchFloat32Scope
237 // instead.
238 struct ScratchFloat32Scope : public AutoFloatRegisterScope {
ScratchFloat32ScopeScratchFloat32Scope239 explicit ScratchFloat32Scope(MacroAssembler& masm)
240 : AutoFloatRegisterScope(masm, ScratchFloat32Reg_) {}
241 };
242
243 // Do not reference ScratchDoubleReg_ directly, use ScratchDoubleScope instead.
244 struct ScratchDoubleScope : public AutoFloatRegisterScope {
ScratchDoubleScopeScratchDoubleScope245 explicit ScratchDoubleScope(MacroAssembler& masm)
246 : AutoFloatRegisterScope(masm, ScratchDoubleReg_) {}
247 };
248
249 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
250 static constexpr Register RegExpMatcherRegExpReg = CallTempReg0;
251 static constexpr Register RegExpMatcherStringReg = CallTempReg1;
252 static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2;
253
254 // Registerd used in RegExpTester instruction (do not use ReturnReg).
255 static constexpr Register RegExpTesterRegExpReg = CallTempReg0;
256 static constexpr Register RegExpTesterStringReg = CallTempReg1;
257 static constexpr Register RegExpTesterLastIndexReg = CallTempReg2;
258
259 static constexpr FloatRegister d0 = {FloatRegisters::d0, VFPRegister::Double};
260 static constexpr FloatRegister d1 = {FloatRegisters::d1, VFPRegister::Double};
261 static constexpr FloatRegister d2 = {FloatRegisters::d2, VFPRegister::Double};
262 static constexpr FloatRegister d3 = {FloatRegisters::d3, VFPRegister::Double};
263 static constexpr FloatRegister d4 = {FloatRegisters::d4, VFPRegister::Double};
264 static constexpr FloatRegister d5 = {FloatRegisters::d5, VFPRegister::Double};
265 static constexpr FloatRegister d6 = {FloatRegisters::d6, VFPRegister::Double};
266 static constexpr FloatRegister d7 = {FloatRegisters::d7, VFPRegister::Double};
267 static constexpr FloatRegister d8 = {FloatRegisters::d8, VFPRegister::Double};
268 static constexpr FloatRegister d9 = {FloatRegisters::d9, VFPRegister::Double};
269 static constexpr FloatRegister d10 = {FloatRegisters::d10, VFPRegister::Double};
270 static constexpr FloatRegister d11 = {FloatRegisters::d11, VFPRegister::Double};
271 static constexpr FloatRegister d12 = {FloatRegisters::d12, VFPRegister::Double};
272 static constexpr FloatRegister d13 = {FloatRegisters::d13, VFPRegister::Double};
273 static constexpr FloatRegister d14 = {FloatRegisters::d14, VFPRegister::Double};
274 static constexpr FloatRegister d15 = {FloatRegisters::d15, VFPRegister::Double};
275
276 // For maximal awesomeness, 8 should be sufficent. ldrd/strd (dual-register
277 // load/store) operate in a single cycle when the address they are dealing with
278 // is 8 byte aligned. Also, the ARM abi wants the stack to be 8 byte aligned at
279 // function boundaries. I'm trying to make sure this is always true.
280 static constexpr uint32_t ABIStackAlignment = 8;
281 static constexpr uint32_t CodeAlignment = 8;
282 static constexpr uint32_t JitStackAlignment = 8;
283
284 static constexpr uint32_t JitStackValueAlignment =
285 JitStackAlignment / sizeof(Value);
286 static_assert(JitStackAlignment % sizeof(Value) == 0 &&
287 JitStackValueAlignment >= 1,
288 "Stack alignment should be a non-zero multiple of sizeof(Value)");
289
290 static constexpr uint32_t SimdMemoryAlignment = 8;
291
292 static_assert(CodeAlignment % SimdMemoryAlignment == 0,
293 "Code alignment should be larger than any of the alignments "
294 "which are used for "
295 "the constant sections of the code buffer. Thus it should be "
296 "larger than the "
297 "alignment for SIMD constants.");
298
299 static_assert(JitStackAlignment % SimdMemoryAlignment == 0,
300 "Stack alignment should be larger than any of the alignments "
301 "which are used for "
302 "spilled values. Thus it should be larger than the alignment "
303 "for SIMD accesses.");
304
305 static const uint32_t WasmStackAlignment = SimdMemoryAlignment;
306 static const uint32_t WasmTrapInstructionLength = 4;
307
308 // See comments in wasm::GenerateFunctionPrologue. The difference between these
309 // is the size of the largest callable prologue on the platform.
310 static constexpr uint32_t WasmCheckedCallEntryOffset = 0u;
311 static constexpr uint32_t WasmCheckedTailEntryOffset = 12u;
312
313 static const Scale ScalePointer = TimesFour;
314
315 class Instruction;
316 class InstBranchImm;
317 uint32_t RM(Register r);
318 uint32_t RS(Register r);
319 uint32_t RD(Register r);
320 uint32_t RT(Register r);
321 uint32_t RN(Register r);
322
323 uint32_t maybeRD(Register r);
324 uint32_t maybeRT(Register r);
325 uint32_t maybeRN(Register r);
326
327 Register toRN(Instruction i);
328 Register toRM(Instruction i);
329 Register toRD(Instruction i);
330 Register toR(Instruction i);
331
332 class VFPRegister;
333 uint32_t VD(VFPRegister vr);
334 uint32_t VN(VFPRegister vr);
335 uint32_t VM(VFPRegister vr);
336
337 // For being passed into the generic vfp instruction generator when there is an
338 // instruction that only takes two registers.
339 static constexpr VFPRegister NoVFPRegister(VFPRegister::Double, 0, false, true);
340
341 struct ImmTag : public Imm32 {
ImmTagImmTag342 explicit ImmTag(JSValueTag mask) : Imm32(int32_t(mask)) {}
343 };
344
345 struct ImmType : public ImmTag {
ImmTypeImmType346 explicit ImmType(JSValueType type) : ImmTag(JSVAL_TYPE_TO_TAG(type)) {}
347 };
348
349 enum Index {
350 Offset = 0 << 21 | 1 << 24,
351 PreIndex = 1 << 21 | 1 << 24,
352 PostIndex = 0 << 21 | 0 << 24
353 // The docs were rather unclear on this. It sounds like
354 // 1 << 21 | 0 << 24 encodes dtrt.
355 };
356
357 enum IsImmOp2_ { IsImmOp2 = 1 << 25, IsNotImmOp2 = 0 << 25 };
358 enum IsImmDTR_ { IsImmDTR = 0 << 25, IsNotImmDTR = 1 << 25 };
359 // For the extra memory operations, ldrd, ldrsb, ldrh.
360 enum IsImmEDTR_ { IsImmEDTR = 1 << 22, IsNotImmEDTR = 0 << 22 };
361
362 enum ShiftType {
363 LSL = 0, // << 5
364 LSR = 1, // << 5
365 ASR = 2, // << 5
366 ROR = 3, // << 5
367 RRX = ROR // RRX is encoded as ROR with a 0 offset.
368 };
369
370 // Modes for STM/LDM. Names are the suffixes applied to the instruction.
371 enum DTMMode {
372 A = 0 << 24, // empty / after
373 B = 1 << 24, // full / before
374 D = 0 << 23, // decrement
375 I = 1 << 23, // increment
376 DA = D | A,
377 DB = D | B,
378 IA = I | A,
379 IB = I | B
380 };
381
382 enum DTMWriteBack { WriteBack = 1 << 21, NoWriteBack = 0 << 21 };
383
384 // Condition code updating mode.
385 enum SBit {
386 SetCC = 1 << 20, // Set condition code.
387 LeaveCC = 0 << 20 // Leave condition code unchanged.
388 };
389
390 enum LoadStore { IsLoad = 1 << 20, IsStore = 0 << 20 };
391
392 // You almost never want to use this directly. Instead, you wantto pass in a
393 // signed constant, and let this bit be implicitly set for you. This is however,
394 // necessary if we want a negative index.
395 enum IsUp_ { IsUp = 1 << 23, IsDown = 0 << 23 };
396 enum ALUOp {
397 OpMov = 0xd << 21,
398 OpMvn = 0xf << 21,
399 OpAnd = 0x0 << 21,
400 OpBic = 0xe << 21,
401 OpEor = 0x1 << 21,
402 OpOrr = 0xc << 21,
403 OpAdc = 0x5 << 21,
404 OpAdd = 0x4 << 21,
405 OpSbc = 0x6 << 21,
406 OpSub = 0x2 << 21,
407 OpRsb = 0x3 << 21,
408 OpRsc = 0x7 << 21,
409 OpCmn = 0xb << 21,
410 OpCmp = 0xa << 21,
411 OpTeq = 0x9 << 21,
412 OpTst = 0x8 << 21,
413 OpInvalid = -1
414 };
415
416 enum MULOp {
417 OpmMul = 0 << 21,
418 OpmMla = 1 << 21,
419 OpmUmaal = 2 << 21,
420 OpmMls = 3 << 21,
421 OpmUmull = 4 << 21,
422 OpmUmlal = 5 << 21,
423 OpmSmull = 6 << 21,
424 OpmSmlal = 7 << 21
425 };
426 enum BranchTag {
427 OpB = 0x0a000000,
428 OpBMask = 0x0f000000,
429 OpBDestMask = 0x00ffffff,
430 OpBl = 0x0b000000,
431 OpBlx = 0x012fff30,
432 OpBx = 0x012fff10
433 };
434
435 // Just like ALUOp, but for the vfp instruction set.
436 enum VFPOp {
437 OpvMul = 0x2 << 20,
438 OpvAdd = 0x3 << 20,
439 OpvSub = 0x3 << 20 | 0x1 << 6,
440 OpvDiv = 0x8 << 20,
441 OpvMov = 0xB << 20 | 0x1 << 6,
442 OpvAbs = 0xB << 20 | 0x3 << 6,
443 OpvNeg = 0xB << 20 | 0x1 << 6 | 0x1 << 16,
444 OpvSqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16,
445 OpvCmp = 0xB << 20 | 0x1 << 6 | 0x4 << 16,
446 OpvCmpz = 0xB << 20 | 0x1 << 6 | 0x5 << 16
447 };
448
449 // Negate the operation, AND negate the immediate that we were passed in.
450 ALUOp ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm,
451 Register* negDest);
452 bool can_dbl(ALUOp op);
453 bool condsAreSafe(ALUOp op);
454
455 // If there is a variant of op that has a dest (think cmp/sub) return that
456 // variant of it.
457 ALUOp getDestVariant(ALUOp op);
458
459 static constexpr ValueOperand JSReturnOperand{JSReturnReg_Type,
460 JSReturnReg_Data};
461 static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0);
462
463 // All of these classes exist solely to shuffle data into the various operands.
464 // For example Operand2 can be an imm8, a register-shifted-by-a-constant or a
465 // register-shifted-by-a-register. We represent this in C++ by having a base
466 // class Operand2, which just stores the 32 bits of data as they will be encoded
467 // in the instruction. You cannot directly create an Operand2 since it is
468 // tricky, and not entirely sane to do so. Instead, you create one of its child
469 // classes, e.g. Imm8. Imm8's constructor takes a single integer argument. Imm8
470 // will verify that its argument can be encoded as an ARM 12 bit imm8, encode it
471 // using an Imm8data, and finally call its parent's (Operand2) constructor with
472 // the Imm8data. The Operand2 constructor will then call the Imm8data's encode()
473 // function to extract the raw bits from it.
474 //
475 // In the future, we should be able to extract data from the Operand2 by asking
476 // it for its component Imm8data structures. The reason this is so horribly
477 // round-about is we wanted to have Imm8 and RegisterShiftedRegister inherit
478 // directly from Operand2 but have all of them take up only a single word of
479 // storage. We also wanted to avoid passing around raw integers at all since
480 // they are error prone.
481 class Op2Reg;
482 class O2RegImmShift;
483 class O2RegRegShift;
484
485 namespace datastore {
486
487 class Reg {
488 // The "second register".
489 uint32_t rm_ : 4;
490 // Do we get another register for shifting.
491 uint32_t rrs_ : 1;
492 uint32_t type_ : 2;
493 // We'd like this to be a more sensible encoding, but that would need to be
494 // a struct and that would not pack :(
495 uint32_t shiftAmount_ : 5;
496
497 protected:
498 // Mark as a protected field to avoid unused private field warnings.
499 uint32_t pad_ : 20;
500
501 public:
Reg(uint32_t rm,ShiftType type,uint32_t rsr,uint32_t shiftAmount)502 Reg(uint32_t rm, ShiftType type, uint32_t rsr, uint32_t shiftAmount)
503 : rm_(rm), rrs_(rsr), type_(type), shiftAmount_(shiftAmount), pad_(0) {}
Reg(const Op2Reg & op)504 explicit Reg(const Op2Reg& op) { memcpy(this, &op, sizeof(*this)); }
505
shiftAmount()506 uint32_t shiftAmount() const { return shiftAmount_; }
507
encode()508 uint32_t encode() const {
509 return rm_ | (rrs_ << 4) | (type_ << 5) | (shiftAmount_ << 7);
510 }
511 };
512
513 // Op2 has a mode labelled "<imm8m>", which is arm's magical immediate encoding.
514 // Some instructions actually get 8 bits of data, which is called Imm8Data
515 // below. These should have edit distance > 1, but this is how it is for now.
516 class Imm8mData {
517 uint32_t data_ : 8;
518 uint32_t rot_ : 4;
519
520 protected:
521 // Mark as a protected field to avoid unused private field warnings.
522 uint32_t buff_ : 19;
523
524 private:
525 // Throw in an extra bit that will be 1 if we can't encode this properly.
526 // if we can encode it properly, a simple "|" will still suffice to meld it
527 // into the instruction.
528 uint32_t invalid_ : 1;
529
530 public:
531 // Default constructor makes an invalid immediate.
Imm8mData()532 Imm8mData() : data_(0xff), rot_(0xf), buff_(0), invalid_(true) {}
533
Imm8mData(uint32_t data,uint32_t rot)534 Imm8mData(uint32_t data, uint32_t rot)
535 : data_(data), rot_(rot), buff_(0), invalid_(false) {
536 MOZ_ASSERT(data == data_);
537 MOZ_ASSERT(rot == rot_);
538 }
539
invalid()540 bool invalid() const { return invalid_; }
541
encode()542 uint32_t encode() const {
543 MOZ_ASSERT(!invalid_);
544 return data_ | (rot_ << 8);
545 };
546 };
547
548 class Imm8Data {
549 uint32_t imm4L_ : 4;
550
551 protected:
552 // Mark as a protected field to avoid unused private field warnings.
553 uint32_t pad_ : 4;
554
555 private:
556 uint32_t imm4H_ : 4;
557
558 public:
Imm8Data(uint32_t imm)559 explicit Imm8Data(uint32_t imm) : imm4L_(imm & 0xf), imm4H_(imm >> 4) {
560 MOZ_ASSERT(imm <= 0xff);
561 }
562
encode()563 uint32_t encode() const { return imm4L_ | (imm4H_ << 8); };
564 };
565
566 // VLDR/VSTR take an 8 bit offset, which is implicitly left shifted by 2.
567 class Imm8VFPOffData {
568 uint32_t data_;
569
570 public:
Imm8VFPOffData(uint32_t imm)571 explicit Imm8VFPOffData(uint32_t imm) : data_(imm) {
572 MOZ_ASSERT((imm & ~(0xff)) == 0);
573 }
encode()574 uint32_t encode() const { return data_; };
575 };
576
577 // ARM can magically encode 256 very special immediates to be moved into a
578 // register.
579 struct Imm8VFPImmData {
580 // This structure's members are public and it has no constructor to
581 // initialize them, for a very special reason. Were this structure to
582 // have a constructor, the initialization for DoubleEncoder's internal
583 // table (see below) would require a rather large static constructor on
584 // some of our supported compilers. The known solution to this is to mark
585 // the constructor constexpr, but, again, some of our supported
586 // compilers don't support constexpr! So we are reduced to public
587 // members and eschewing a constructor in hopes that the initialization
588 // of DoubleEncoder's table is correct.
589 uint32_t imm4L : 4;
590 uint32_t imm4H : 4;
591 int32_t isInvalid : 24;
592
encodeImm8VFPImmData593 uint32_t encode() const {
594 // This assert is an attempting at ensuring that we don't create random
595 // instances of this structure and then asking to encode() it.
596 MOZ_ASSERT(isInvalid == 0);
597 return imm4L | (imm4H << 16);
598 };
599 };
600
601 class Imm12Data {
602 uint32_t data_ : 12;
603
604 public:
Imm12Data(uint32_t imm)605 explicit Imm12Data(uint32_t imm) : data_(imm) { MOZ_ASSERT(data_ == imm); }
606
encode()607 uint32_t encode() const { return data_; }
608 };
609
610 class RIS {
611 uint32_t shiftAmount_ : 5;
612
613 public:
RIS(uint32_t imm)614 explicit RIS(uint32_t imm) : shiftAmount_(imm) {
615 MOZ_ASSERT(shiftAmount_ == imm);
616 }
617
RIS(Reg r)618 explicit RIS(Reg r) : shiftAmount_(r.shiftAmount()) {}
619
encode()620 uint32_t encode() const { return shiftAmount_; }
621 };
622
623 class RRS {
624 protected:
625 // Mark as a protected field to avoid unused private field warnings.
626 uint32_t mustZero_ : 1;
627
628 private:
629 // The register that holds the shift amount.
630 uint32_t rs_ : 4;
631
632 public:
RRS(uint32_t rs)633 explicit RRS(uint32_t rs) : rs_(rs) { MOZ_ASSERT(rs_ == rs); }
634
encode()635 uint32_t encode() const { return rs_ << 1; }
636 };
637
638 } // namespace datastore
639
640 class MacroAssemblerARM;
641 class Operand;
642
643 class Operand2 {
644 friend class Operand;
645 friend class MacroAssemblerARM;
646 friend class InstALU;
647
648 uint32_t oper_ : 31;
649 uint32_t invalid_ : 1;
650
651 protected:
Operand2(datastore::Imm8mData base)652 explicit Operand2(datastore::Imm8mData base)
653 : oper_(base.invalid() ? -1 : (base.encode() | uint32_t(IsImmOp2))),
654 invalid_(base.invalid()) {}
655
Operand2(datastore::Reg base)656 explicit Operand2(datastore::Reg base)
657 : oper_(base.encode() | uint32_t(IsNotImmOp2)), invalid_(false) {}
658
659 private:
Operand2(uint32_t blob)660 explicit Operand2(uint32_t blob) : oper_(blob), invalid_(false) {}
661
662 public:
isO2Reg()663 bool isO2Reg() const { return !(oper_ & IsImmOp2); }
664
665 Op2Reg toOp2Reg() const;
666
isImm8()667 bool isImm8() const { return oper_ & IsImmOp2; }
668
invalid()669 bool invalid() const { return invalid_; }
670
encode()671 uint32_t encode() const { return oper_; }
672 };
673
674 class Imm8 : public Operand2 {
675 public:
Imm8(uint32_t imm)676 explicit Imm8(uint32_t imm) : Operand2(EncodeImm(imm)) {}
677
EncodeImm(uint32_t imm)678 static datastore::Imm8mData EncodeImm(uint32_t imm) {
679 // RotateLeft below may not be called with a shift of zero.
680 if (imm <= 0xFF) {
681 return datastore::Imm8mData(imm, 0);
682 }
683
684 // An encodable integer has a maximum of 8 contiguous set bits,
685 // with an optional wrapped left rotation to even bit positions.
686 for (int rot = 1; rot < 16; rot++) {
687 uint32_t rotimm = mozilla::RotateLeft(imm, rot * 2);
688 if (rotimm <= 0xFF) {
689 return datastore::Imm8mData(rotimm, rot);
690 }
691 }
692 return datastore::Imm8mData();
693 }
694
695 // Pair template?
696 struct TwoImm8mData {
697 datastore::Imm8mData fst_, snd_;
698
699 TwoImm8mData() = default;
700
TwoImm8mDataTwoImm8mData701 TwoImm8mData(datastore::Imm8mData fst, datastore::Imm8mData snd)
702 : fst_(fst), snd_(snd) {}
703
fstTwoImm8mData704 datastore::Imm8mData fst() const { return fst_; }
sndTwoImm8mData705 datastore::Imm8mData snd() const { return snd_; }
706 };
707
708 static TwoImm8mData EncodeTwoImms(uint32_t);
709 };
710
711 class Op2Reg : public Operand2 {
712 public:
Op2Reg(Register rm,ShiftType type,datastore::RIS shiftImm)713 explicit Op2Reg(Register rm, ShiftType type, datastore::RIS shiftImm)
714 : Operand2(datastore::Reg(rm.code(), type, 0, shiftImm.encode())) {}
715
Op2Reg(Register rm,ShiftType type,datastore::RRS shiftReg)716 explicit Op2Reg(Register rm, ShiftType type, datastore::RRS shiftReg)
717 : Operand2(datastore::Reg(rm.code(), type, 1, shiftReg.encode())) {}
718 };
719
720 static_assert(sizeof(Op2Reg) == sizeof(datastore::Reg),
721 "datastore::Reg(const Op2Reg&) constructor relies on Reg/Op2Reg "
722 "having same size");
723
724 class O2RegImmShift : public Op2Reg {
725 public:
O2RegImmShift(Register rn,ShiftType type,uint32_t shift)726 explicit O2RegImmShift(Register rn, ShiftType type, uint32_t shift)
727 : Op2Reg(rn, type, datastore::RIS(shift)) {}
728 };
729
730 class O2RegRegShift : public Op2Reg {
731 public:
O2RegRegShift(Register rn,ShiftType type,Register rs)732 explicit O2RegRegShift(Register rn, ShiftType type, Register rs)
733 : Op2Reg(rn, type, datastore::RRS(rs.code())) {}
734 };
735
736 O2RegImmShift O2Reg(Register r);
737 O2RegImmShift lsl(Register r, int amt);
738 O2RegImmShift lsr(Register r, int amt);
739 O2RegImmShift asr(Register r, int amt);
740 O2RegImmShift rol(Register r, int amt);
741 O2RegImmShift ror(Register r, int amt);
742
743 O2RegRegShift lsl(Register r, Register amt);
744 O2RegRegShift lsr(Register r, Register amt);
745 O2RegRegShift asr(Register r, Register amt);
746 O2RegRegShift ror(Register r, Register amt);
747
748 // An offset from a register to be used for ldr/str. This should include the
749 // sign bit, since ARM has "signed-magnitude" offsets. That is it encodes an
750 // unsigned offset, then the instruction specifies if the offset is positive or
751 // negative. The +/- bit is necessary if the instruction set wants to be able to
752 // have a negative register offset e.g. ldr pc, [r1,-r2];
753 class DtrOff {
754 uint32_t data_;
755
756 protected:
DtrOff(datastore::Imm12Data immdata,IsUp_ iu)757 explicit DtrOff(datastore::Imm12Data immdata, IsUp_ iu)
758 : data_(immdata.encode() | uint32_t(IsImmDTR) | uint32_t(iu)) {}
759
760 explicit DtrOff(datastore::Reg reg, IsUp_ iu = IsUp)
761 : data_(reg.encode() | uint32_t(IsNotImmDTR) | iu) {}
762
763 public:
encode()764 uint32_t encode() const { return data_; }
765 };
766
767 class DtrOffImm : public DtrOff {
768 public:
DtrOffImm(int32_t imm)769 explicit DtrOffImm(int32_t imm)
770 : DtrOff(datastore::Imm12Data(mozilla::Abs(imm)),
771 imm >= 0 ? IsUp : IsDown) {
772 MOZ_ASSERT(mozilla::Abs(imm) < 4096);
773 }
774 };
775
776 class DtrOffReg : public DtrOff {
777 // These are designed to be called by a constructor of a subclass.
778 // Constructing the necessary RIS/RRS structures is annoying.
779
780 protected:
781 explicit DtrOffReg(Register rn, ShiftType type, datastore::RIS shiftImm,
782 IsUp_ iu = IsUp)
783 : DtrOff(datastore::Reg(rn.code(), type, 0, shiftImm.encode()), iu) {}
784
785 explicit DtrOffReg(Register rn, ShiftType type, datastore::RRS shiftReg,
786 IsUp_ iu = IsUp)
787 : DtrOff(datastore::Reg(rn.code(), type, 1, shiftReg.encode()), iu) {}
788 };
789
790 class DtrRegImmShift : public DtrOffReg {
791 public:
792 explicit DtrRegImmShift(Register rn, ShiftType type, uint32_t shift,
793 IsUp_ iu = IsUp)
DtrOffReg(rn,type,datastore::RIS (shift),iu)794 : DtrOffReg(rn, type, datastore::RIS(shift), iu) {}
795 };
796
797 class DtrRegRegShift : public DtrOffReg {
798 public:
799 explicit DtrRegRegShift(Register rn, ShiftType type, Register rs,
800 IsUp_ iu = IsUp)
801 : DtrOffReg(rn, type, datastore::RRS(rs.code()), iu) {}
802 };
803
804 // We will frequently want to bundle a register with its offset so that we have
805 // an "operand" to a load instruction.
806 class DTRAddr {
807 friend class Operand;
808
809 uint32_t data_;
810
811 public:
DTRAddr(Register reg,DtrOff dtr)812 explicit DTRAddr(Register reg, DtrOff dtr)
813 : data_(dtr.encode() | (reg.code() << 16)) {}
814
encode()815 uint32_t encode() const { return data_; }
816
getBase()817 Register getBase() const { return Register::FromCode((data_ >> 16) & 0xf); }
818 };
819
820 // Offsets for the extended data transfer instructions:
821 // ldrsh, ldrd, ldrsb, etc.
822 class EDtrOff {
823 uint32_t data_;
824
825 protected:
826 explicit EDtrOff(datastore::Imm8Data imm8, IsUp_ iu = IsUp)
827 : data_(imm8.encode() | IsImmEDTR | uint32_t(iu)) {}
828
829 explicit EDtrOff(Register rm, IsUp_ iu = IsUp)
830 : data_(rm.code() | IsNotImmEDTR | iu) {}
831
832 public:
encode()833 uint32_t encode() const { return data_; }
834 };
835
836 class EDtrOffImm : public EDtrOff {
837 public:
EDtrOffImm(int32_t imm)838 explicit EDtrOffImm(int32_t imm)
839 : EDtrOff(datastore::Imm8Data(mozilla::Abs(imm)),
840 (imm >= 0) ? IsUp : IsDown) {
841 MOZ_ASSERT(mozilla::Abs(imm) < 256);
842 }
843 };
844
845 // This is the most-derived class, since the extended data transfer instructions
846 // don't support any sort of modifying the "index" operand.
847 class EDtrOffReg : public EDtrOff {
848 public:
EDtrOffReg(Register rm)849 explicit EDtrOffReg(Register rm) : EDtrOff(rm) {}
850 };
851
852 class EDtrAddr {
853 uint32_t data_;
854
855 public:
EDtrAddr(Register r,EDtrOff off)856 explicit EDtrAddr(Register r, EDtrOff off) : data_(RN(r) | off.encode()) {}
857
encode()858 uint32_t encode() const { return data_; }
859 #ifdef DEBUG
maybeOffsetRegister()860 Register maybeOffsetRegister() const {
861 if (data_ & IsImmEDTR) {
862 return InvalidReg;
863 }
864 return Register::FromCode(data_ & 0xf);
865 }
866 #endif
867 };
868
869 class VFPOff {
870 uint32_t data_;
871
872 protected:
VFPOff(datastore::Imm8VFPOffData imm,IsUp_ isup)873 explicit VFPOff(datastore::Imm8VFPOffData imm, IsUp_ isup)
874 : data_(imm.encode() | uint32_t(isup)) {}
875
876 public:
encode()877 uint32_t encode() const { return data_; }
878 };
879
880 class VFPOffImm : public VFPOff {
881 public:
VFPOffImm(int32_t imm)882 explicit VFPOffImm(int32_t imm)
883 : VFPOff(datastore::Imm8VFPOffData(mozilla::Abs(imm) / 4),
884 imm < 0 ? IsDown : IsUp) {
885 MOZ_ASSERT(mozilla::Abs(imm) <= 255 * 4);
886 }
887 };
888
889 class VFPAddr {
890 friend class Operand;
891
892 uint32_t data_;
893
894 public:
VFPAddr(Register base,VFPOff off)895 explicit VFPAddr(Register base, VFPOff off)
896 : data_(RN(base) | off.encode()) {}
897
encode()898 uint32_t encode() const { return data_; }
899 };
900
901 class VFPImm {
902 uint32_t data_;
903
904 public:
905 explicit VFPImm(uint32_t topWordOfDouble);
906
907 static const VFPImm One;
908
encode()909 uint32_t encode() const { return data_; }
isValid()910 bool isValid() const { return data_ != (~0U); }
911 };
912
913 // A BOffImm is an immediate that is used for branches. Namely, it is the offset
914 // that will be encoded in the branch instruction. This is the only sane way of
915 // constructing a branch.
916 class BOffImm {
917 friend class InstBranchImm;
918
919 uint32_t data_;
920
921 public:
BOffImm(int offset)922 explicit BOffImm(int offset) : data_((offset - 8) >> 2 & 0x00ffffff) {
923 MOZ_ASSERT((offset & 0x3) == 0);
924 if (!IsInRange(offset)) {
925 MOZ_CRASH("BOffImm offset out of range");
926 }
927 }
928
BOffImm()929 explicit BOffImm() : data_(INVALID) {}
930
931 private:
932 explicit BOffImm(const Instruction& inst);
933
934 public:
935 static const uint32_t INVALID = 0x00800000;
936
encode()937 uint32_t encode() const { return data_; }
decode()938 int32_t decode() const { return ((int32_t(data_) << 8) >> 6) + 8; }
939
IsInRange(int offset)940 static bool IsInRange(int offset) {
941 if ((offset - 8) < -33554432) {
942 return false;
943 }
944 if ((offset - 8) > 33554428) {
945 return false;
946 }
947 return true;
948 }
949
isInvalid()950 bool isInvalid() const { return data_ == INVALID; }
951 Instruction* getDest(Instruction* src) const;
952 };
953
954 class Imm16 {
955 uint32_t lower_ : 12;
956
957 protected:
958 // Mark as a protected field to avoid unused private field warnings.
959 uint32_t pad_ : 4;
960
961 private:
962 uint32_t upper_ : 4;
963 uint32_t invalid_ : 12;
964
965 public:
966 explicit Imm16();
967 explicit Imm16(uint32_t imm);
968 explicit Imm16(Instruction& inst);
969
encode()970 uint32_t encode() const { return lower_ | (upper_ << 16); }
decode()971 uint32_t decode() const { return lower_ | (upper_ << 12); }
972
isInvalid()973 bool isInvalid() const { return invalid_; }
974 };
975
976 // I would preffer that these do not exist, since there are essentially no
977 // instructions that would ever take more than one of these, however, the MIR
978 // wants to only have one type of arguments to functions, so bugger.
979 class Operand {
980 // The encoding of registers is the same for OP2, DTR and EDTR yet the type
981 // system doesn't let us express this, so choices must be made.
982 public:
983 enum class Tag : uint8_t { OP2, MEM, FOP };
984
985 private:
986 uint32_t tag_ : 8;
987 uint32_t reg_ : 5;
988 int32_t offset_;
989
990 protected:
Operand(Tag tag,uint32_t regCode,int32_t offset)991 Operand(Tag tag, uint32_t regCode, int32_t offset)
992 : tag_(static_cast<uint32_t>(tag)), reg_(regCode), offset_(offset) {}
993
994 public:
Operand(Register reg)995 explicit Operand(Register reg) : Operand(Tag::OP2, reg.code(), 0) {}
996
Operand(FloatRegister freg)997 explicit Operand(FloatRegister freg) : Operand(Tag::FOP, freg.code(), 0) {}
998
Operand(Register base,Imm32 off)999 explicit Operand(Register base, Imm32 off)
1000 : Operand(Tag::MEM, base.code(), off.value) {}
1001
Operand(Register base,int32_t off)1002 explicit Operand(Register base, int32_t off)
1003 : Operand(Tag::MEM, base.code(), off) {}
1004
Operand(const Address & addr)1005 explicit Operand(const Address& addr)
1006 : Operand(Tag::MEM, addr.base.code(), addr.offset) {}
1007
1008 public:
tag()1009 Tag tag() const { return static_cast<Tag>(tag_); }
1010
toOp2()1011 Operand2 toOp2() const {
1012 MOZ_ASSERT(tag() == Tag::OP2);
1013 return O2Reg(Register::FromCode(reg_));
1014 }
1015
toReg()1016 Register toReg() const {
1017 MOZ_ASSERT(tag() == Tag::OP2);
1018 return Register::FromCode(reg_);
1019 }
1020
toAddress()1021 Address toAddress() const {
1022 MOZ_ASSERT(tag() == Tag::MEM);
1023 return Address(Register::FromCode(reg_), offset_);
1024 }
disp()1025 int32_t disp() const {
1026 MOZ_ASSERT(tag() == Tag::MEM);
1027 return offset_;
1028 }
1029
base()1030 int32_t base() const {
1031 MOZ_ASSERT(tag() == Tag::MEM);
1032 return reg_;
1033 }
baseReg()1034 Register baseReg() const {
1035 MOZ_ASSERT(tag() == Tag::MEM);
1036 return Register::FromCode(reg_);
1037 }
toDTRAddr()1038 DTRAddr toDTRAddr() const {
1039 MOZ_ASSERT(tag() == Tag::MEM);
1040 return DTRAddr(baseReg(), DtrOffImm(offset_));
1041 }
toVFPAddr()1042 VFPAddr toVFPAddr() const {
1043 MOZ_ASSERT(tag() == Tag::MEM);
1044 return VFPAddr(baseReg(), VFPOffImm(offset_));
1045 }
1046 };
1047
firstHalf()1048 inline Imm32 Imm64::firstHalf() const { return low(); }
1049
secondHalf()1050 inline Imm32 Imm64::secondHalf() const { return hi(); }
1051
1052 class InstructionIterator {
1053 private:
1054 Instruction* inst_;
1055
1056 public:
InstructionIterator(Instruction * inst)1057 explicit InstructionIterator(Instruction* inst) : inst_(inst) {
1058 maybeSkipAutomaticInstructions();
1059 }
1060
1061 // Advances to the next intentionally-inserted instruction.
1062 Instruction* next();
1063
1064 // Advances past any automatically-inserted instructions.
1065 Instruction* maybeSkipAutomaticInstructions();
1066
cur()1067 Instruction* cur() const { return inst_; }
1068
1069 protected:
1070 // Advances past the given number of instruction-length bytes.
1071 inline void advanceRaw(ptrdiff_t instructions = 1);
1072 };
1073
1074 class Assembler;
1075 typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction,
1076 Assembler>
1077 ARMBuffer;
1078
1079 class Assembler : public AssemblerShared {
1080 public:
1081 // ARM conditional constants:
1082 enum ARMCondition : uint32_t {
1083 EQ = 0x00000000, // Zero
1084 NE = 0x10000000, // Non-zero
1085 CS = 0x20000000,
1086 CC = 0x30000000,
1087 MI = 0x40000000,
1088 PL = 0x50000000,
1089 VS = 0x60000000,
1090 VC = 0x70000000,
1091 HI = 0x80000000,
1092 LS = 0x90000000,
1093 GE = 0xa0000000,
1094 LT = 0xb0000000,
1095 GT = 0xc0000000,
1096 LE = 0xd0000000,
1097 AL = 0xe0000000
1098 };
1099
1100 enum Condition : uint32_t {
1101 Equal = EQ,
1102 NotEqual = NE,
1103 Above = HI,
1104 AboveOrEqual = CS,
1105 Below = CC,
1106 BelowOrEqual = LS,
1107 GreaterThan = GT,
1108 GreaterThanOrEqual = GE,
1109 LessThan = LT,
1110 LessThanOrEqual = LE,
1111 Overflow = VS,
1112 CarrySet = CS,
1113 CarryClear = CC,
1114 Signed = MI,
1115 NotSigned = PL,
1116 Zero = EQ,
1117 NonZero = NE,
1118 Always = AL,
1119
1120 VFP_NotEqualOrUnordered = NE,
1121 VFP_Equal = EQ,
1122 VFP_Unordered = VS,
1123 VFP_NotUnordered = VC,
1124 VFP_GreaterThanOrEqualOrUnordered = CS,
1125 VFP_GreaterThanOrEqual = GE,
1126 VFP_GreaterThanOrUnordered = HI,
1127 VFP_GreaterThan = GT,
1128 VFP_LessThanOrEqualOrUnordered = LE,
1129 VFP_LessThanOrEqual = LS,
1130 VFP_LessThanOrUnordered = LT,
1131 VFP_LessThan = CC // MI is valid too.
1132 };
1133
1134 // Bit set when a DoubleCondition does not map to a single ARM condition.
1135 // The macro assembler has to special-case these conditions, or else
1136 // ConditionFromDoubleCondition will complain.
1137 static const int DoubleConditionBitSpecial = 0x1;
1138
1139 enum DoubleCondition : uint32_t {
1140 // These conditions will only evaluate to true if the comparison is
1141 // ordered - i.e. neither operand is NaN.
1142 DoubleOrdered = VFP_NotUnordered,
1143 DoubleEqual = VFP_Equal,
1144 DoubleNotEqual = VFP_NotEqualOrUnordered | DoubleConditionBitSpecial,
1145 DoubleGreaterThan = VFP_GreaterThan,
1146 DoubleGreaterThanOrEqual = VFP_GreaterThanOrEqual,
1147 DoubleLessThan = VFP_LessThan,
1148 DoubleLessThanOrEqual = VFP_LessThanOrEqual,
1149 // If either operand is NaN, these conditions always evaluate to true.
1150 DoubleUnordered = VFP_Unordered,
1151 DoubleEqualOrUnordered = VFP_Equal | DoubleConditionBitSpecial,
1152 DoubleNotEqualOrUnordered = VFP_NotEqualOrUnordered,
1153 DoubleGreaterThanOrUnordered = VFP_GreaterThanOrUnordered,
1154 DoubleGreaterThanOrEqualOrUnordered = VFP_GreaterThanOrEqualOrUnordered,
1155 DoubleLessThanOrUnordered = VFP_LessThanOrUnordered,
1156 DoubleLessThanOrEqualOrUnordered = VFP_LessThanOrEqualOrUnordered
1157 };
1158
getCondition(uint32_t inst)1159 Condition getCondition(uint32_t inst) {
1160 return (Condition)(0xf0000000 & inst);
1161 }
ConditionFromDoubleCondition(DoubleCondition cond)1162 static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) {
1163 MOZ_ASSERT(!(cond & DoubleConditionBitSpecial));
1164 return static_cast<Condition>(cond);
1165 }
1166
1167 enum BarrierOption {
1168 BarrierSY = 15, // Full system barrier
1169 BarrierST = 14 // StoreStore barrier
1170 };
1171
1172 // This should be protected, but since CodeGenerator wants to use it, it
1173 // needs to go out here :(
1174
nextOffset()1175 BufferOffset nextOffset() { return m_buffer.nextOffset(); }
1176
1177 protected:
1178 // Shim around AssemblerBufferWithConstantPools::allocEntry.
1179 BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
1180 PoolHintPun& php, uint8_t* data,
1181 const LiteralDoc& doc = LiteralDoc(),
1182 ARMBuffer::PoolEntry* pe = nullptr,
1183 bool loadToPC = false);
1184
editSrc(BufferOffset bo)1185 Instruction* editSrc(BufferOffset bo) { return m_buffer.getInst(bo); }
1186
1187 #ifdef JS_DISASM_ARM
1188 typedef disasm::EmbeddedVector<char, disasm::ReasonableBufferSize>
1189 DisasmBuffer;
1190
1191 static void disassembleInstruction(const Instruction* i,
1192 DisasmBuffer& buffer);
1193
1194 void initDisassembler();
1195 void finishDisassembler();
1196 void spew(Instruction* i);
1197 void spewBranch(Instruction* i, const LabelDoc& target);
1198 void spewLiteralLoad(PoolHintPun& php, bool loadToPC, const Instruction* offs,
1199 const LiteralDoc& doc);
1200 #endif
1201
1202 public:
1203 void resetCounter();
1204 static uint32_t NopFill;
1205 static uint32_t GetNopFill();
1206 static uint32_t AsmPoolMaxOffset;
1207 static uint32_t GetPoolMaxOffset();
1208
1209 protected:
1210 // Structure for fixing up pc-relative loads/jumps when a the machine code
1211 // gets moved (executable copy, gc, etc.).
1212 class RelativePatch {
1213 void* target_;
1214 RelocationKind kind_;
1215
1216 public:
RelativePatch(void * target,RelocationKind kind)1217 RelativePatch(void* target, RelocationKind kind)
1218 : target_(target), kind_(kind) {}
target()1219 void* target() const { return target_; }
kind()1220 RelocationKind kind() const { return kind_; }
1221 };
1222
1223 // TODO: this should actually be a pool-like object. It is currently a big
1224 // hack, and probably shouldn't exist.
1225 js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
1226
1227 CompactBufferWriter jumpRelocations_;
1228 CompactBufferWriter dataRelocations_;
1229
1230 ARMBuffer m_buffer;
1231
1232 #ifdef JS_DISASM_ARM
1233 DisassemblerSpew spew_;
1234 #endif
1235
1236 public:
1237 // For the alignment fill use NOP: 0x0320f000 or (Always | InstNOP::NopInst).
1238 // For the nopFill use a branch to the next instruction: 0xeaffffff.
Assembler()1239 Assembler()
1240 : m_buffer(1, 1, 8, GetPoolMaxOffset(), 8, 0xe320f000, 0xeaffffff,
1241 GetNopFill()),
1242 isFinished(false),
1243 dtmActive(false),
1244 dtmCond(Always) {
1245 #ifdef JS_DISASM_ARM
1246 initDisassembler();
1247 #endif
1248 }
1249
~Assembler()1250 ~Assembler() {
1251 #ifdef JS_DISASM_ARM
1252 finishDisassembler();
1253 #endif
1254 }
1255
1256 // We need to wait until an AutoJitContextAlloc is created by the
1257 // MacroAssembler, before allocating any space.
initWithAllocator()1258 void initWithAllocator() { m_buffer.initWithAllocator(); }
1259
setUnlimitedBuffer()1260 void setUnlimitedBuffer() { m_buffer.setUnlimited(); }
1261
1262 static Condition InvertCondition(Condition cond);
1263 static Condition UnsignedCondition(Condition cond);
1264 static Condition ConditionWithoutEqual(Condition cond);
1265
1266 static DoubleCondition InvertCondition(DoubleCondition cond);
1267
writeDataRelocation(BufferOffset offset,ImmGCPtr ptr)1268 void writeDataRelocation(BufferOffset offset, ImmGCPtr ptr) {
1269 // Raw GC pointer relocations and Value relocations both end up in
1270 // Assembler::TraceDataRelocations.
1271 if (ptr.value) {
1272 if (gc::IsInsideNursery(ptr.value)) {
1273 embedsNurseryPointers_ = true;
1274 }
1275 dataRelocations_.writeUnsigned(offset.getOffset());
1276 }
1277 }
1278
1279 enum RelocBranchStyle { B_MOVWT, B_LDR_BX, B_LDR, B_MOVW_ADD };
1280
1281 enum RelocStyle { L_MOVWT, L_LDR };
1282
1283 public:
1284 // Given the start of a Control Flow sequence, grab the value that is
1285 // finally branched to given the start of a function that loads an address
1286 // into a register get the address that ends up in the register.
1287 template <class Iter>
1288 static const uint32_t* GetCF32Target(Iter* iter);
1289
1290 static uintptr_t GetPointer(uint8_t*);
1291 static const uint32_t* GetPtr32Target(InstructionIterator iter,
1292 Register* dest = nullptr,
1293 RelocStyle* rs = nullptr);
1294
1295 bool oom() const;
1296
setPrinter(Sprinter * sp)1297 void setPrinter(Sprinter* sp) {
1298 #ifdef JS_DISASM_ARM
1299 spew_.setPrinter(sp);
1300 #endif
1301 }
1302
getStackPointer()1303 Register getStackPointer() const { return StackPointer; }
1304
1305 private:
1306 bool isFinished;
1307
1308 protected:
refLabel(const Label * label)1309 LabelDoc refLabel(const Label* label) {
1310 #ifdef JS_DISASM_ARM
1311 return spew_.refLabel(label);
1312 #else
1313 return LabelDoc();
1314 #endif
1315 }
1316
1317 public:
1318 void finish();
1319 bool appendRawCode(const uint8_t* code, size_t numBytes);
1320 bool reserve(size_t size);
1321 bool swapBuffer(wasm::Bytes& bytes);
1322 void copyJumpRelocationTable(uint8_t* dest);
1323 void copyDataRelocationTable(uint8_t* dest);
1324
1325 // Size of the instruction stream, in bytes, after pools are flushed.
1326 size_t size() const;
1327 // Size of the jump relocation table, in bytes.
1328 size_t jumpRelocationTableBytes() const;
1329 size_t dataRelocationTableBytes() const;
1330
1331 // Size of the data table, in bytes.
1332 size_t bytesNeeded() const;
1333
1334 // Write a single instruction into the instruction stream. Very hot,
1335 // inlined for performance
writeInst(uint32_t x)1336 MOZ_ALWAYS_INLINE BufferOffset writeInst(uint32_t x) {
1337 MOZ_ASSERT(hasCreator());
1338 BufferOffset offs = m_buffer.putInt(x);
1339 #ifdef JS_DISASM_ARM
1340 spew(m_buffer.getInstOrNull(offs));
1341 #endif
1342 return offs;
1343 }
1344
1345 // As above, but also mark the instruction as a branch. Very hot, inlined
1346 // for performance
1347 MOZ_ALWAYS_INLINE BufferOffset
writeBranchInst(uint32_t x,const LabelDoc & documentation)1348 writeBranchInst(uint32_t x, const LabelDoc& documentation) {
1349 BufferOffset offs = m_buffer.putInt(x);
1350 #ifdef JS_DISASM_ARM
1351 spewBranch(m_buffer.getInstOrNull(offs), documentation);
1352 #endif
1353 return offs;
1354 }
1355
1356 // Write a placeholder NOP for a branch into the instruction stream
1357 // (in order to adjust assembler addresses and mark it as a branch), it will
1358 // be overwritten subsequently.
1359 BufferOffset allocBranchInst();
1360
1361 // A static variant for the cases where we don't want to have an assembler
1362 // object.
1363 static void WriteInstStatic(uint32_t x, uint32_t* dest);
1364
1365 public:
1366 void writeCodePointer(CodeLabel* label);
1367
1368 void haltingAlign(int alignment);
1369 void nopAlign(int alignment);
1370 BufferOffset as_nop();
1371 BufferOffset as_alu(Register dest, Register src1, Operand2 op2, ALUOp op,
1372 SBit s = LeaveCC, Condition c = Always);
1373 BufferOffset as_mov(Register dest, Operand2 op2, SBit s = LeaveCC,
1374 Condition c = Always);
1375 BufferOffset as_mvn(Register dest, Operand2 op2, SBit s = LeaveCC,
1376 Condition c = Always);
1377
1378 static void as_alu_patch(Register dest, Register src1, Operand2 op2, ALUOp op,
1379 SBit s, Condition c, uint32_t* pos);
1380 static void as_mov_patch(Register dest, Operand2 op2, SBit s, Condition c,
1381 uint32_t* pos);
1382
1383 // Logical operations:
1384 BufferOffset as_and(Register dest, Register src1, Operand2 op2,
1385 SBit s = LeaveCC, Condition c = Always);
1386 BufferOffset as_bic(Register dest, Register src1, Operand2 op2,
1387 SBit s = LeaveCC, Condition c = Always);
1388 BufferOffset as_eor(Register dest, Register src1, Operand2 op2,
1389 SBit s = LeaveCC, Condition c = Always);
1390 BufferOffset as_orr(Register dest, Register src1, Operand2 op2,
1391 SBit s = LeaveCC, Condition c = Always);
1392 // Reverse byte operations:
1393 BufferOffset as_rev(Register dest, Register src, Condition c = Always);
1394 BufferOffset as_rev16(Register dest, Register src, Condition c = Always);
1395 BufferOffset as_revsh(Register dest, Register src, Condition c = Always);
1396 // Mathematical operations:
1397 BufferOffset as_adc(Register dest, Register src1, Operand2 op2,
1398 SBit s = LeaveCC, Condition c = Always);
1399 BufferOffset as_add(Register dest, Register src1, Operand2 op2,
1400 SBit s = LeaveCC, Condition c = Always);
1401 BufferOffset as_sbc(Register dest, Register src1, Operand2 op2,
1402 SBit s = LeaveCC, Condition c = Always);
1403 BufferOffset as_sub(Register dest, Register src1, Operand2 op2,
1404 SBit s = LeaveCC, Condition c = Always);
1405 BufferOffset as_rsb(Register dest, Register src1, Operand2 op2,
1406 SBit s = LeaveCC, Condition c = Always);
1407 BufferOffset as_rsc(Register dest, Register src1, Operand2 op2,
1408 SBit s = LeaveCC, Condition c = Always);
1409 // Test operations:
1410 BufferOffset as_cmn(Register src1, Operand2 op2, Condition c = Always);
1411 BufferOffset as_cmp(Register src1, Operand2 op2, Condition c = Always);
1412 BufferOffset as_teq(Register src1, Operand2 op2, Condition c = Always);
1413 BufferOffset as_tst(Register src1, Operand2 op2, Condition c = Always);
1414
1415 // Sign extension operations:
1416 BufferOffset as_sxtb(Register dest, Register src, int rotate,
1417 Condition c = Always);
1418 BufferOffset as_sxth(Register dest, Register src, int rotate,
1419 Condition c = Always);
1420 BufferOffset as_uxtb(Register dest, Register src, int rotate,
1421 Condition c = Always);
1422 BufferOffset as_uxth(Register dest, Register src, int rotate,
1423 Condition c = Always);
1424
1425 // Not quite ALU worthy, but useful none the less: These also have the issue
1426 // of these being formatted completly differently from the standard ALU
1427 // operations.
1428 BufferOffset as_movw(Register dest, Imm16 imm, Condition c = Always);
1429 BufferOffset as_movt(Register dest, Imm16 imm, Condition c = Always);
1430
1431 static void as_movw_patch(Register dest, Imm16 imm, Condition c,
1432 Instruction* pos);
1433 static void as_movt_patch(Register dest, Imm16 imm, Condition c,
1434 Instruction* pos);
1435
1436 BufferOffset as_genmul(Register d1, Register d2, Register rm, Register rn,
1437 MULOp op, SBit s, Condition c = Always);
1438 BufferOffset as_mul(Register dest, Register src1, Register src2,
1439 SBit s = LeaveCC, Condition c = Always);
1440 BufferOffset as_mla(Register dest, Register acc, Register src1, Register src2,
1441 SBit s = LeaveCC, Condition c = Always);
1442 BufferOffset as_umaal(Register dest1, Register dest2, Register src1,
1443 Register src2, Condition c = Always);
1444 BufferOffset as_mls(Register dest, Register acc, Register src1, Register src2,
1445 Condition c = Always);
1446 BufferOffset as_umull(Register dest1, Register dest2, Register src1,
1447 Register src2, SBit s = LeaveCC, Condition c = Always);
1448 BufferOffset as_umlal(Register dest1, Register dest2, Register src1,
1449 Register src2, SBit s = LeaveCC, Condition c = Always);
1450 BufferOffset as_smull(Register dest1, Register dest2, Register src1,
1451 Register src2, SBit s = LeaveCC, Condition c = Always);
1452 BufferOffset as_smlal(Register dest1, Register dest2, Register src1,
1453 Register src2, SBit s = LeaveCC, Condition c = Always);
1454
1455 BufferOffset as_sdiv(Register dest, Register num, Register div,
1456 Condition c = Always);
1457 BufferOffset as_udiv(Register dest, Register num, Register div,
1458 Condition c = Always);
1459 BufferOffset as_clz(Register dest, Register src, Condition c = Always);
1460
1461 // Data transfer instructions: ldr, str, ldrb, strb.
1462 // Using an int to differentiate between 8 bits and 32 bits is overkill.
1463 BufferOffset as_dtr(LoadStore ls, int size, Index mode, Register rt,
1464 DTRAddr addr, Condition c = Always);
1465
1466 static void as_dtr_patch(LoadStore ls, int size, Index mode, Register rt,
1467 DTRAddr addr, Condition c, uint32_t* dest);
1468
1469 // Handles all of the other integral data transferring functions:
1470 // ldrsb, ldrsh, ldrd, etc. The size is given in bits.
1471 BufferOffset as_extdtr(LoadStore ls, int size, bool IsSigned, Index mode,
1472 Register rt, EDtrAddr addr, Condition c = Always);
1473
1474 BufferOffset as_dtm(LoadStore ls, Register rn, uint32_t mask, DTMMode mode,
1475 DTMWriteBack wb, Condition c = Always);
1476
1477 // Overwrite a pool entry with new data.
1478 static void WritePoolEntry(Instruction* addr, Condition c, uint32_t data);
1479
1480 // Load a 32 bit immediate from a pool into a register.
1481 BufferOffset as_Imm32Pool(Register dest, uint32_t value,
1482 Condition c = Always);
1483
1484 // Load a 64 bit floating point immediate from a pool into a register.
1485 BufferOffset as_FImm64Pool(VFPRegister dest, double value,
1486 Condition c = Always);
1487 // Load a 32 bit floating point immediate from a pool into a register.
1488 BufferOffset as_FImm32Pool(VFPRegister dest, float value,
1489 Condition c = Always);
1490
1491 // Atomic instructions: ldrexd, ldrex, ldrexh, ldrexb, strexd, strex, strexh,
1492 // strexb.
1493 //
1494 // The doubleword, halfword, and byte versions are available from ARMv6K
1495 // forward.
1496 //
1497 // The word versions are available from ARMv6 forward and can be used to
1498 // implement the halfword and byte versions on older systems.
1499
1500 // LDREXD rt, rt2, [rn]. Constraint: rt even register, rt2=rt+1.
1501 BufferOffset as_ldrexd(Register rt, Register rt2, Register rn,
1502 Condition c = Always);
1503
1504 // LDREX rt, [rn]
1505 BufferOffset as_ldrex(Register rt, Register rn, Condition c = Always);
1506 BufferOffset as_ldrexh(Register rt, Register rn, Condition c = Always);
1507 BufferOffset as_ldrexb(Register rt, Register rn, Condition c = Always);
1508
1509 // STREXD rd, rt, rt2, [rn]. Constraint: rt even register, rt2=rt+1.
1510 BufferOffset as_strexd(Register rd, Register rt, Register rt2, Register rn,
1511 Condition c = Always);
1512
1513 // STREX rd, rt, [rn]. Constraint: rd != rn, rd != rt.
1514 BufferOffset as_strex(Register rd, Register rt, Register rn,
1515 Condition c = Always);
1516 BufferOffset as_strexh(Register rd, Register rt, Register rn,
1517 Condition c = Always);
1518 BufferOffset as_strexb(Register rd, Register rt, Register rn,
1519 Condition c = Always);
1520
1521 // CLREX
1522 BufferOffset as_clrex();
1523
1524 // Memory synchronization.
1525 // These are available from ARMv7 forward.
1526 BufferOffset as_dmb(BarrierOption option = BarrierSY);
1527 BufferOffset as_dsb(BarrierOption option = BarrierSY);
1528 BufferOffset as_isb();
1529
1530 // Memory synchronization for architectures before ARMv7.
1531 BufferOffset as_dsb_trap();
1532 BufferOffset as_dmb_trap();
1533 BufferOffset as_isb_trap();
1534
1535 // Speculation barrier
1536 BufferOffset as_csdb();
1537
1538 // Control flow stuff:
1539
1540 // bx can *only* branch to a register never to an immediate.
1541 BufferOffset as_bx(Register r, Condition c = Always);
1542
1543 // Branch can branch to an immediate *or* to a register. Branches to
1544 // immediates are pc relative, branches to registers are absolute.
1545 BufferOffset as_b(BOffImm off, Condition c, Label* documentation = nullptr);
1546
1547 BufferOffset as_b(Label* l, Condition c = Always);
1548 BufferOffset as_b(BOffImm off, Condition c, BufferOffset inst);
1549
1550 // blx can go to either an immediate or a register. When blx'ing to a
1551 // register, we change processor mode depending on the low bit of the
1552 // register when blx'ing to an immediate, we *always* change processor
1553 // state.
1554 BufferOffset as_blx(Label* l);
1555
1556 BufferOffset as_blx(Register r, Condition c = Always);
1557 BufferOffset as_bl(BOffImm off, Condition c, Label* documentation = nullptr);
1558 // bl can only branch+link to an immediate, never to a register it never
1559 // changes processor state.
1560 BufferOffset as_bl();
1561 // bl #imm can have a condition code, blx #imm cannot.
1562 // blx reg can be conditional.
1563 BufferOffset as_bl(Label* l, Condition c);
1564 BufferOffset as_bl(BOffImm off, Condition c, BufferOffset inst);
1565
1566 BufferOffset as_mrs(Register r, Condition c = Always);
1567 BufferOffset as_msr(Register r, Condition c = Always);
1568
1569 // VFP instructions!
1570 private:
1571 enum vfp_size { IsDouble = 1 << 8, IsSingle = 0 << 8 };
1572
1573 BufferOffset writeVFPInst(vfp_size sz, uint32_t blob);
1574
1575 static void WriteVFPInstStatic(vfp_size sz, uint32_t blob, uint32_t* dest);
1576
1577 // Unityped variants: all registers hold the same (ieee754 single/double)
1578 // notably not included are vcvt; vmov vd, #imm; vmov rt, vn.
1579 BufferOffset as_vfp_float(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1580 VFPOp op, Condition c = Always);
1581
1582 public:
1583 BufferOffset as_vadd(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1584 Condition c = Always);
1585 BufferOffset as_vdiv(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1586 Condition c = Always);
1587 BufferOffset as_vmul(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1588 Condition c = Always);
1589 BufferOffset as_vnmul(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1590 Condition c = Always);
1591 BufferOffset as_vnmla(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1592 Condition c = Always);
1593 BufferOffset as_vnmls(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1594 Condition c = Always);
1595 BufferOffset as_vneg(VFPRegister vd, VFPRegister vm, Condition c = Always);
1596 BufferOffset as_vsqrt(VFPRegister vd, VFPRegister vm, Condition c = Always);
1597 BufferOffset as_vabs(VFPRegister vd, VFPRegister vm, Condition c = Always);
1598 BufferOffset as_vsub(VFPRegister vd, VFPRegister vn, VFPRegister vm,
1599 Condition c = Always);
1600 BufferOffset as_vcmp(VFPRegister vd, VFPRegister vm, Condition c = Always);
1601 BufferOffset as_vcmpz(VFPRegister vd, Condition c = Always);
1602
1603 // Specifically, a move between two same sized-registers.
1604 BufferOffset as_vmov(VFPRegister vd, VFPRegister vsrc, Condition c = Always);
1605
1606 // Transfer between Core and VFP.
1607 enum FloatToCore_ { FloatToCore = 1 << 20, CoreToFloat = 0 << 20 };
1608
1609 private:
1610 enum VFPXferSize { WordTransfer = 0x02000010, DoubleTransfer = 0x00400010 };
1611
1612 public:
1613 // Unlike the next function, moving between the core registers and vfp
1614 // registers can't be *that* properly typed. Namely, since I don't want to
1615 // munge the type VFPRegister to also include core registers. Thus, the core
1616 // and vfp registers are passed in based on their type, and src/dest is
1617 // determined by the float2core.
1618
1619 BufferOffset as_vxfer(Register vt1, Register vt2, VFPRegister vm,
1620 FloatToCore_ f2c, Condition c = Always, int idx = 0);
1621
1622 // Our encoding actually allows just the src and the dest (and their types)
1623 // to uniquely specify the encoding that we are going to use.
1624 BufferOffset as_vcvt(VFPRegister vd, VFPRegister vm, bool useFPSCR = false,
1625 Condition c = Always);
1626
1627 // Hard coded to a 32 bit fixed width result for now.
1628 BufferOffset as_vcvtFixed(VFPRegister vd, bool isSigned, uint32_t fixedPoint,
1629 bool toFixed, Condition c = Always);
1630
1631 // Transfer between VFP and memory.
1632 BufferOffset as_vdtr(LoadStore ls, VFPRegister vd, VFPAddr addr,
1633 Condition c = Always /* vfp doesn't have a wb option*/);
1634
1635 static void as_vdtr_patch(LoadStore ls, VFPRegister vd, VFPAddr addr,
1636 Condition c /* vfp doesn't have a wb option */,
1637 uint32_t* dest);
1638
1639 // VFP's ldm/stm work differently from the standard arm ones. You can only
1640 // transfer a range.
1641
1642 BufferOffset as_vdtm(LoadStore st, Register rn, VFPRegister vd, int length,
1643 /* also has update conditions */ Condition c = Always);
1644
1645 // vldr/vstr variants that handle unaligned accesses. These encode as NEON
1646 // single-element instructions and can only be used if NEON is available.
1647 // Here, vd must be tagged as a float or double register.
1648 BufferOffset as_vldr_unaligned(VFPRegister vd, Register rn);
1649 BufferOffset as_vstr_unaligned(VFPRegister vd, Register rn);
1650
1651 BufferOffset as_vimm(VFPRegister vd, VFPImm imm, Condition c = Always);
1652
1653 BufferOffset as_vmrs(Register r, Condition c = Always);
1654 BufferOffset as_vmsr(Register r, Condition c = Always);
1655
1656 // Label operations.
1657 bool nextLink(BufferOffset b, BufferOffset* next);
1658 void bind(Label* label, BufferOffset boff = BufferOffset());
currentOffset()1659 uint32_t currentOffset() { return nextOffset().getOffset(); }
1660 void retarget(Label* label, Label* target);
1661 // I'm going to pretend this doesn't exist for now.
1662 void retarget(Label* label, void* target, RelocationKind reloc);
1663
1664 static void Bind(uint8_t* rawCode, const CodeLabel& label);
1665
1666 void as_bkpt();
1667 BufferOffset as_illegal_trap();
1668
1669 public:
1670 static void TraceJumpRelocations(JSTracer* trc, JitCode* code,
1671 CompactBufferReader& reader);
1672 static void TraceDataRelocations(JSTracer* trc, JitCode* code,
1673 CompactBufferReader& reader);
1674
assertNoGCThings()1675 void assertNoGCThings() const {
1676 #ifdef DEBUG
1677 MOZ_ASSERT(dataRelocations_.length() == 0);
1678 for (auto& j : jumps_) {
1679 MOZ_ASSERT(j.kind() == RelocationKind::HARDCODED);
1680 }
1681 #endif
1682 }
1683
SupportsFloatingPoint()1684 static bool SupportsFloatingPoint() { return HasVFP(); }
SupportsUnalignedAccesses()1685 static bool SupportsUnalignedAccesses() { return HasARMv7(); }
1686 // Note, returning false here is technically wrong, but one has to go via the
1687 // as_vldr_unaligned and as_vstr_unaligned instructions to get proper behavior
1688 // and those are NEON-specific and have to be asked for specifically.
SupportsFastUnalignedFPAccesses()1689 static bool SupportsFastUnalignedFPAccesses() { return false; }
1690
HasRoundInstruction(RoundingMode mode)1691 static bool HasRoundInstruction(RoundingMode mode) { return false; }
1692
1693 protected:
addPendingJump(BufferOffset src,ImmPtr target,RelocationKind kind)1694 void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind) {
1695 enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind));
1696 if (kind == RelocationKind::JITCODE) {
1697 jumpRelocations_.writeUnsigned(src.getOffset());
1698 }
1699 }
1700
1701 public:
1702 // The buffer is about to be linked, make sure any constant pools or excess
1703 // bookkeeping has been flushed to the instruction stream.
flush()1704 void flush() {
1705 MOZ_ASSERT(!isFinished);
1706 m_buffer.flushPool();
1707 return;
1708 }
1709
comment(const char * msg)1710 void comment(const char* msg) {
1711 #ifdef JS_DISASM_ARM
1712 spew_.spew("; %s", msg);
1713 #endif
1714 }
1715
1716 // Copy the assembly code to the given buffer, and perform any pending
1717 // relocations relying on the target address.
1718 void executableCopy(uint8_t* buffer);
1719
1720 // Actual assembly emitting functions.
1721
1722 // Since I can't think of a reasonable default for the mode, I'm going to
1723 // leave it as a required argument.
1724 void startDataTransferM(LoadStore ls, Register rm, DTMMode mode,
1725 DTMWriteBack update = NoWriteBack,
1726 Condition c = Always) {
1727 MOZ_ASSERT(!dtmActive);
1728 dtmUpdate = update;
1729 dtmBase = rm;
1730 dtmLoadStore = ls;
1731 dtmLastReg = -1;
1732 dtmRegBitField = 0;
1733 dtmActive = 1;
1734 dtmCond = c;
1735 dtmMode = mode;
1736 }
1737
transferReg(Register rn)1738 void transferReg(Register rn) {
1739 MOZ_ASSERT(dtmActive);
1740 MOZ_ASSERT(rn.code() > dtmLastReg);
1741 dtmRegBitField |= 1 << rn.code();
1742 if (dtmLoadStore == IsLoad && rn.code() == 13 && dtmBase.code() == 13) {
1743 MOZ_CRASH("ARM Spec says this is invalid");
1744 }
1745 }
finishDataTransfer()1746 void finishDataTransfer() {
1747 dtmActive = false;
1748 as_dtm(dtmLoadStore, dtmBase, dtmRegBitField, dtmMode, dtmUpdate, dtmCond);
1749 }
1750
1751 void startFloatTransferM(LoadStore ls, Register rm, DTMMode mode,
1752 DTMWriteBack update = NoWriteBack,
1753 Condition c = Always) {
1754 MOZ_ASSERT(!dtmActive);
1755 dtmActive = true;
1756 dtmUpdate = update;
1757 dtmLoadStore = ls;
1758 dtmBase = rm;
1759 dtmCond = c;
1760 dtmLastReg = -1;
1761 dtmMode = mode;
1762 dtmDelta = 0;
1763 }
transferFloatReg(VFPRegister rn)1764 void transferFloatReg(VFPRegister rn) {
1765 if (dtmLastReg == -1) {
1766 vdtmFirstReg = rn.code();
1767 } else {
1768 if (dtmDelta == 0) {
1769 dtmDelta = rn.code() - dtmLastReg;
1770 MOZ_ASSERT(dtmDelta == 1 || dtmDelta == -1);
1771 }
1772 MOZ_ASSERT(dtmLastReg >= 0);
1773 MOZ_ASSERT(rn.code() == unsigned(dtmLastReg) + dtmDelta);
1774 }
1775
1776 dtmLastReg = rn.code();
1777 }
finishFloatTransfer()1778 void finishFloatTransfer() {
1779 MOZ_ASSERT(dtmActive);
1780 dtmActive = false;
1781 MOZ_ASSERT(dtmLastReg != -1);
1782 dtmDelta = dtmDelta ? dtmDelta : 1;
1783 // The operand for the vstr/vldr instruction is the lowest register in the
1784 // range.
1785 int low = std::min(dtmLastReg, vdtmFirstReg);
1786 int high = std::max(dtmLastReg, vdtmFirstReg);
1787 // Fencepost problem.
1788 int len = high - low + 1;
1789 // vdtm can only transfer 16 registers at once. If we need to transfer
1790 // more, then either hoops are necessary, or we need to be updating the
1791 // register.
1792 MOZ_ASSERT_IF(len > 16, dtmUpdate == WriteBack);
1793
1794 int adjustLow = dtmLoadStore == IsStore ? 0 : 1;
1795 int adjustHigh = dtmLoadStore == IsStore ? -1 : 0;
1796 while (len > 0) {
1797 // Limit the instruction to 16 registers.
1798 int curLen = std::min(len, 16);
1799 // If it is a store, we want to start at the high end and move down
1800 // (e.g. vpush d16-d31; vpush d0-d15).
1801 int curStart = (dtmLoadStore == IsStore) ? high - curLen + 1 : low;
1802 as_vdtm(dtmLoadStore, dtmBase,
1803 VFPRegister(FloatRegister::FromCode(curStart)), curLen, dtmCond);
1804 // Update the bounds.
1805 low += adjustLow * curLen;
1806 high += adjustHigh * curLen;
1807 // Update the length parameter.
1808 len -= curLen;
1809 }
1810 }
1811
1812 private:
1813 int dtmRegBitField;
1814 int vdtmFirstReg;
1815 int dtmLastReg;
1816 int dtmDelta;
1817 Register dtmBase;
1818 DTMWriteBack dtmUpdate;
1819 DTMMode dtmMode;
1820 LoadStore dtmLoadStore;
1821 bool dtmActive;
1822 Condition dtmCond;
1823
1824 public:
1825 enum {
1826 PadForAlign8 = (int)0x00,
1827 PadForAlign16 = (int)0x0000,
1828 PadForAlign32 = (int)0xe12fff7f // 'bkpt 0xffff'
1829 };
1830
1831 // API for speaking with the IonAssemblerBufferWithConstantPools generate an
1832 // initial placeholder instruction that we want to later fix up.
1833 static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
1834
1835 // Take the stub value that was written in before, and write in an actual
1836 // load using the index we'd computed previously as well as the address of
1837 // the pool start.
1838 static void PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
1839
1840 // We're not tracking short-range branches for ARM for now.
PatchShortRangeBranchToVeneer(ARMBuffer *,unsigned rangeIdx,BufferOffset deadline,BufferOffset veneer)1841 static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx,
1842 BufferOffset deadline,
1843 BufferOffset veneer) {
1844 MOZ_CRASH();
1845 }
1846 // END API
1847
1848 // Move our entire pool into the instruction stream. This is to force an
1849 // opportunistic dump of the pool, prefferably when it is more convenient to
1850 // do a dump.
1851 void flushBuffer();
1852 void enterNoPool(size_t maxInst);
1853 void leaveNoPool();
1854 void enterNoNops();
1855 void leaveNoNops();
1856
1857 static void WritePoolHeader(uint8_t* start, Pool* p, bool isNatural);
1858 static void WritePoolGuard(BufferOffset branch, Instruction* inst,
1859 BufferOffset dest);
1860
1861 static uint32_t PatchWrite_NearCallSize();
NopSize()1862 static uint32_t NopSize() { return 4; }
1863 static void PatchWrite_NearCall(CodeLocationLabel start,
1864 CodeLocationLabel toCall);
1865 static void PatchDataWithValueCheck(CodeLocationLabel label,
1866 PatchedImmPtr newValue,
1867 PatchedImmPtr expectedValue);
1868 static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
1869 ImmPtr expectedValue);
1870 static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
1871
AlignDoubleArg(uint32_t offset)1872 static uint32_t AlignDoubleArg(uint32_t offset) { return (offset + 1) & ~1; }
1873 static uint8_t* NextInstruction(uint8_t* instruction,
1874 uint32_t* count = nullptr);
1875
1876 // Toggle a jmp or cmp emitted by toggledJump().
1877 static void ToggleToJmp(CodeLocationLabel inst_);
1878 static void ToggleToCmp(CodeLocationLabel inst_);
1879
1880 static uint8_t* BailoutTableStart(uint8_t* code);
1881
1882 static size_t ToggledCallSize(uint8_t* code);
1883 static void ToggleCall(CodeLocationLabel inst_, bool enabled);
1884
1885 void processCodeLabels(uint8_t* rawCode);
1886
verifyHeapAccessDisassembly(uint32_t begin,uint32_t end,const Disassembler::HeapAccess & heapAccess)1887 void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
1888 const Disassembler::HeapAccess& heapAccess) {
1889 // Implement this if we implement a disassembler.
1890 }
1891 }; // Assembler
1892
1893 // An Instruction is a structure for both encoding and decoding any and all ARM
1894 // instructions. Many classes have not been implemented thus far.
1895 class Instruction {
1896 uint32_t data;
1897
1898 protected:
1899 // This is not for defaulting to always, this is for instructions that
1900 // cannot be made conditional, and have the usually invalid 4b1111 cond
1901 // field.
1902 explicit Instruction(uint32_t data_, bool fake = false)
1903 : data(data_ | 0xf0000000) {
1904 MOZ_ASSERT(fake || ((data_ & 0xf0000000) == 0));
1905 }
1906 // Standard constructor.
Instruction(uint32_t data_,Assembler::Condition c)1907 Instruction(uint32_t data_, Assembler::Condition c)
1908 : data(data_ | (uint32_t)c) {
1909 MOZ_ASSERT((data_ & 0xf0000000) == 0);
1910 }
1911 // You should never create an instruction directly. You should create a more
1912 // specific instruction which will eventually call one of these constructors
1913 // for you.
1914 public:
encode()1915 uint32_t encode() const { return data; }
1916 // Check if this instruction is really a particular case.
1917 template <class C>
is()1918 bool is() const {
1919 return C::IsTHIS(*this);
1920 }
1921
1922 // Safely get a more specific variant of this pointer.
1923 template <class C>
as()1924 C* as() const {
1925 return C::AsTHIS(*this);
1926 }
1927
1928 const Instruction& operator=(Instruction src) {
1929 data = src.data;
1930 return *this;
1931 }
1932 // Since almost all instructions have condition codes, the condition code
1933 // extractor resides in the base class.
extractCond()1934 Assembler::Condition extractCond() const {
1935 MOZ_ASSERT(data >> 28 != 0xf,
1936 "The instruction does not have condition code");
1937 return (Assembler::Condition)(data & 0xf0000000);
1938 }
1939
1940 // Sometimes, an api wants a uint32_t (or a pointer to it) rather than an
1941 // instruction. raw() just coerces this into a pointer to a uint32_t.
raw()1942 const uint32_t* raw() const { return &data; }
size()1943 uint32_t size() const { return 4; }
1944 }; // Instruction
1945
1946 // Make sure that it is the right size.
1947 static_assert(sizeof(Instruction) == 4);
1948
advanceRaw(ptrdiff_t instructions)1949 inline void InstructionIterator::advanceRaw(ptrdiff_t instructions) {
1950 inst_ = inst_ + instructions;
1951 }
1952
1953 // Data Transfer Instructions.
1954 class InstDTR : public Instruction {
1955 public:
1956 enum IsByte_ { IsByte = 0x00400000, IsWord = 0x00000000 };
1957 static const int IsDTR = 0x04000000;
1958 static const int IsDTRMask = 0x0c000000;
1959
1960 // TODO: Replace the initialization with something that is safer.
InstDTR(LoadStore ls,IsByte_ ib,Index mode,Register rt,DTRAddr addr,Assembler::Condition c)1961 InstDTR(LoadStore ls, IsByte_ ib, Index mode, Register rt, DTRAddr addr,
1962 Assembler::Condition c)
1963 : Instruction(ls | ib | mode | RT(rt) | addr.encode() | IsDTR, c) {}
1964
1965 static bool IsTHIS(const Instruction& i);
1966 static InstDTR* AsTHIS(const Instruction& i);
1967 };
1968 static_assert(sizeof(InstDTR) == sizeof(Instruction));
1969
1970 class InstLDR : public InstDTR {
1971 public:
InstLDR(Index mode,Register rt,DTRAddr addr,Assembler::Condition c)1972 InstLDR(Index mode, Register rt, DTRAddr addr, Assembler::Condition c)
1973 : InstDTR(IsLoad, IsWord, mode, rt, addr, c) {}
1974
1975 static bool IsTHIS(const Instruction& i);
1976 static InstLDR* AsTHIS(const Instruction& i);
1977
signedOffset()1978 int32_t signedOffset() const {
1979 int32_t offset = encode() & 0xfff;
1980 if (IsUp_(encode() & IsUp) != IsUp) {
1981 return -offset;
1982 }
1983 return offset;
1984 }
dest()1985 uint32_t* dest() const {
1986 int32_t offset = signedOffset();
1987 // When patching the load in PatchConstantPoolLoad, we ensure that the
1988 // offset is a multiple of 4, offset by 8 bytes from the actual
1989 // location. Indeed, when the base register is PC, ARM's 3 stages
1990 // pipeline design makes it that PC is off by 8 bytes (= 2 *
1991 // sizeof(uint32*)) when we actually executed it.
1992 MOZ_ASSERT(offset % 4 == 0);
1993 offset >>= 2;
1994 return (uint32_t*)raw() + offset + 2;
1995 }
1996 };
1997 static_assert(sizeof(InstDTR) == sizeof(InstLDR));
1998
1999 class InstNOP : public Instruction {
2000 public:
2001 static const uint32_t NopInst = 0x0320f000;
2002
InstNOP()2003 InstNOP() : Instruction(NopInst, Assembler::Always) {}
2004
2005 static bool IsTHIS(const Instruction& i);
2006 static InstNOP* AsTHIS(Instruction& i);
2007 };
2008
2009 // Branching to a register, or calling a register
2010 class InstBranchReg : public Instruction {
2011 protected:
2012 // Don't use BranchTag yourself, use a derived instruction.
2013 enum BranchTag { IsBX = 0x012fff10, IsBLX = 0x012fff30 };
2014
2015 static const uint32_t IsBRegMask = 0x0ffffff0;
2016
InstBranchReg(BranchTag tag,Register rm,Assembler::Condition c)2017 InstBranchReg(BranchTag tag, Register rm, Assembler::Condition c)
2018 : Instruction(tag | rm.code(), c) {}
2019
2020 public:
2021 static bool IsTHIS(const Instruction& i);
2022 static InstBranchReg* AsTHIS(const Instruction& i);
2023
2024 // Get the register that is being branched to
2025 void extractDest(Register* dest);
2026 // Make sure we are branching to a pre-known register
2027 bool checkDest(Register dest);
2028 };
2029 static_assert(sizeof(InstBranchReg) == sizeof(Instruction));
2030
2031 // Branching to an immediate offset, or calling an immediate offset
2032 class InstBranchImm : public Instruction {
2033 protected:
2034 enum BranchTag { IsB = 0x0a000000, IsBL = 0x0b000000 };
2035
2036 static const uint32_t IsBImmMask = 0x0f000000;
2037
InstBranchImm(BranchTag tag,BOffImm off,Assembler::Condition c)2038 InstBranchImm(BranchTag tag, BOffImm off, Assembler::Condition c)
2039 : Instruction(tag | off.encode(), c) {}
2040
2041 public:
2042 static bool IsTHIS(const Instruction& i);
2043 static InstBranchImm* AsTHIS(const Instruction& i);
2044
2045 void extractImm(BOffImm* dest);
2046 };
2047 static_assert(sizeof(InstBranchImm) == sizeof(Instruction));
2048
2049 // Very specific branching instructions.
2050 class InstBXReg : public InstBranchReg {
2051 public:
2052 static bool IsTHIS(const Instruction& i);
2053 static InstBXReg* AsTHIS(const Instruction& i);
2054 };
2055
2056 class InstBLXReg : public InstBranchReg {
2057 public:
InstBLXReg(Register reg,Assembler::Condition c)2058 InstBLXReg(Register reg, Assembler::Condition c)
2059 : InstBranchReg(IsBLX, reg, c) {}
2060
2061 static bool IsTHIS(const Instruction& i);
2062 static InstBLXReg* AsTHIS(const Instruction& i);
2063 };
2064
2065 class InstBImm : public InstBranchImm {
2066 public:
InstBImm(BOffImm off,Assembler::Condition c)2067 InstBImm(BOffImm off, Assembler::Condition c) : InstBranchImm(IsB, off, c) {}
2068
2069 static bool IsTHIS(const Instruction& i);
2070 static InstBImm* AsTHIS(const Instruction& i);
2071 };
2072
2073 class InstBLImm : public InstBranchImm {
2074 public:
InstBLImm(BOffImm off,Assembler::Condition c)2075 InstBLImm(BOffImm off, Assembler::Condition c)
2076 : InstBranchImm(IsBL, off, c) {}
2077
2078 static bool IsTHIS(const Instruction& i);
2079 static InstBLImm* AsTHIS(const Instruction& i);
2080 };
2081
2082 // Both movw and movt. The layout of both the immediate and the destination
2083 // register is the same so the code is being shared.
2084 class InstMovWT : public Instruction {
2085 protected:
2086 enum WT { IsW = 0x03000000, IsT = 0x03400000 };
2087 static const uint32_t IsWTMask = 0x0ff00000;
2088
InstMovWT(Register rd,Imm16 imm,WT wt,Assembler::Condition c)2089 InstMovWT(Register rd, Imm16 imm, WT wt, Assembler::Condition c)
2090 : Instruction(RD(rd) | imm.encode() | wt, c) {}
2091
2092 public:
2093 void extractImm(Imm16* dest);
2094 void extractDest(Register* dest);
2095 bool checkImm(Imm16 dest);
2096 bool checkDest(Register dest);
2097
2098 static bool IsTHIS(Instruction& i);
2099 static InstMovWT* AsTHIS(Instruction& i);
2100 };
2101 static_assert(sizeof(InstMovWT) == sizeof(Instruction));
2102
2103 class InstMovW : public InstMovWT {
2104 public:
InstMovW(Register rd,Imm16 imm,Assembler::Condition c)2105 InstMovW(Register rd, Imm16 imm, Assembler::Condition c)
2106 : InstMovWT(rd, imm, IsW, c) {}
2107
2108 static bool IsTHIS(const Instruction& i);
2109 static InstMovW* AsTHIS(const Instruction& i);
2110 };
2111
2112 class InstMovT : public InstMovWT {
2113 public:
InstMovT(Register rd,Imm16 imm,Assembler::Condition c)2114 InstMovT(Register rd, Imm16 imm, Assembler::Condition c)
2115 : InstMovWT(rd, imm, IsT, c) {}
2116
2117 static bool IsTHIS(const Instruction& i);
2118 static InstMovT* AsTHIS(const Instruction& i);
2119 };
2120
2121 class InstALU : public Instruction {
2122 static const int32_t ALUMask = 0xc << 24;
2123
2124 public:
InstALU(Register rd,Register rn,Operand2 op2,ALUOp op,SBit s,Assembler::Condition c)2125 InstALU(Register rd, Register rn, Operand2 op2, ALUOp op, SBit s,
2126 Assembler::Condition c)
2127 : Instruction(maybeRD(rd) | maybeRN(rn) | op2.encode() | op | s, c) {}
2128
2129 static bool IsTHIS(const Instruction& i);
2130 static InstALU* AsTHIS(const Instruction& i);
2131
2132 void extractOp(ALUOp* ret);
2133 bool checkOp(ALUOp op);
2134 void extractDest(Register* ret);
2135 bool checkDest(Register rd);
2136 void extractOp1(Register* ret);
2137 bool checkOp1(Register rn);
2138 Operand2 extractOp2();
2139 };
2140
2141 class InstCMP : public InstALU {
2142 public:
2143 static bool IsTHIS(const Instruction& i);
2144 static InstCMP* AsTHIS(const Instruction& i);
2145 };
2146
2147 class InstMOV : public InstALU {
2148 public:
2149 static bool IsTHIS(const Instruction& i);
2150 static InstMOV* AsTHIS(const Instruction& i);
2151 };
2152
2153 // Compile-time iterator over instructions, with a safe interface that
2154 // references not-necessarily-linear Instructions by linear BufferOffset.
2155 class BufferInstructionIterator
2156 : public ARMBuffer::AssemblerBufferInstIterator {
2157 public:
BufferInstructionIterator(BufferOffset bo,ARMBuffer * buffer)2158 BufferInstructionIterator(BufferOffset bo, ARMBuffer* buffer)
2159 : ARMBuffer::AssemblerBufferInstIterator(bo, buffer) {}
2160
2161 // Advances the buffer to the next intentionally-inserted instruction.
next()2162 Instruction* next() {
2163 advance(cur()->size());
2164 maybeSkipAutomaticInstructions();
2165 return cur();
2166 }
2167
2168 // Advances the BufferOffset past any automatically-inserted instructions.
2169 Instruction* maybeSkipAutomaticInstructions();
2170 };
2171
2172 static const uint32_t NumIntArgRegs = 4;
2173
2174 // There are 16 *float* registers available for arguments
2175 // If doubles are used, only half the number of registers are available.
2176 static const uint32_t NumFloatArgRegs = 16;
2177
GetIntArgReg(uint32_t usedIntArgs,uint32_t usedFloatArgs,Register * out)2178 static inline bool GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs,
2179 Register* out) {
2180 if (usedIntArgs >= NumIntArgRegs) {
2181 return false;
2182 }
2183
2184 *out = Register::FromCode(usedIntArgs);
2185 return true;
2186 }
2187
2188 // Get a register in which we plan to put a quantity that will be used as an
2189 // integer argument. This differs from GetIntArgReg in that if we have no more
2190 // actual argument registers to use we will fall back on using whatever
2191 // CallTempReg* don't overlap the argument registers, and only fail once those
2192 // run out too.
GetTempRegForIntArg(uint32_t usedIntArgs,uint32_t usedFloatArgs,Register * out)2193 static inline bool GetTempRegForIntArg(uint32_t usedIntArgs,
2194 uint32_t usedFloatArgs, Register* out) {
2195 if (GetIntArgReg(usedIntArgs, usedFloatArgs, out)) {
2196 return true;
2197 }
2198
2199 // Unfortunately, we have to assume things about the point at which
2200 // GetIntArgReg returns false, because we need to know how many registers it
2201 // can allocate.
2202 usedIntArgs -= NumIntArgRegs;
2203 if (usedIntArgs >= NumCallTempNonArgRegs) {
2204 return false;
2205 }
2206
2207 *out = CallTempNonArgRegs[usedIntArgs];
2208 return true;
2209 }
2210
2211 #if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
2212
GetFloat32ArgReg(uint32_t usedIntArgs,uint32_t usedFloatArgs,FloatRegister * out)2213 static inline bool GetFloat32ArgReg(uint32_t usedIntArgs,
2214 uint32_t usedFloatArgs,
2215 FloatRegister* out) {
2216 MOZ_ASSERT(UseHardFpABI());
2217 if (usedFloatArgs >= NumFloatArgRegs) {
2218 return false;
2219 }
2220 *out = VFPRegister(usedFloatArgs, VFPRegister::Single);
2221 return true;
2222 }
GetDoubleArgReg(uint32_t usedIntArgs,uint32_t usedFloatArgs,FloatRegister * out)2223 static inline bool GetDoubleArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs,
2224 FloatRegister* out) {
2225 MOZ_ASSERT(UseHardFpABI());
2226 MOZ_ASSERT((usedFloatArgs % 2) == 0);
2227 if (usedFloatArgs >= NumFloatArgRegs) {
2228 return false;
2229 }
2230 *out = VFPRegister(usedFloatArgs >> 1, VFPRegister::Double);
2231 return true;
2232 }
2233
2234 #endif
2235
2236 class DoubleEncoder {
2237 struct DoubleEntry {
2238 uint32_t dblTop;
2239 datastore::Imm8VFPImmData data;
2240 };
2241
2242 static const DoubleEntry table[256];
2243
2244 public:
lookup(uint32_t top,datastore::Imm8VFPImmData * ret)2245 bool lookup(uint32_t top, datastore::Imm8VFPImmData* ret) const {
2246 for (int i = 0; i < 256; i++) {
2247 if (table[i].dblTop == top) {
2248 *ret = table[i].data;
2249 return true;
2250 }
2251 }
2252 return false;
2253 }
2254 };
2255
2256 // Forbids nop filling for testing purposes. Not nestable.
2257 class AutoForbidNops {
2258 protected:
2259 Assembler* masm_;
2260
2261 public:
AutoForbidNops(Assembler * masm)2262 explicit AutoForbidNops(Assembler* masm) : masm_(masm) {
2263 masm_->enterNoNops();
2264 }
~AutoForbidNops()2265 ~AutoForbidNops() { masm_->leaveNoNops(); }
2266 };
2267
2268 class AutoForbidPoolsAndNops : public AutoForbidNops {
2269 public:
2270 // The maxInst argument is the maximum number of word sized instructions
2271 // that will be allocated within this context. It is used to determine if
2272 // the pool needs to be dumped before entering this content. The debug code
2273 // checks that no more than maxInst instructions are actually allocated.
2274 //
2275 // Allocation of pool entries is not supported within this content so the
2276 // code can not use large integers or float constants etc.
AutoForbidPoolsAndNops(Assembler * masm,size_t maxInst)2277 AutoForbidPoolsAndNops(Assembler* masm, size_t maxInst)
2278 : AutoForbidNops(masm) {
2279 masm_->enterNoPool(maxInst);
2280 }
2281
~AutoForbidPoolsAndNops()2282 ~AutoForbidPoolsAndNops() { masm_->leaveNoPool(); }
2283 };
2284
2285 } // namespace jit
2286 } // namespace js
2287
2288 #endif /* jit_arm_Assembler_arm_h */
2289