1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 #include "jit/arm/MacroAssembler-arm.h"
8 
9 #include "mozilla/Casting.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/MathAlgorithms.h"
12 
13 #include "jit/arm/Simulator-arm.h"
14 #include "jit/Bailouts.h"
15 #include "jit/BaselineFrame.h"
16 #include "jit/JitFrames.h"
17 #include "jit/MacroAssembler.h"
18 #include "jit/MoveEmitter.h"
19 
20 #include "jit/MacroAssembler-inl.h"
21 
22 using namespace js;
23 using namespace jit;
24 
25 using mozilla::Abs;
26 using mozilla::BitwiseCast;
27 
28 bool
isValueDTRDCandidate(ValueOperand & val)29 isValueDTRDCandidate(ValueOperand& val)
30 {
31     // In order to be used for a DTRD memory function, the two target registers
32     // need to be a) Adjacent, with the tag larger than the payload, and b)
33     // Aligned to a multiple of two.
34     if ((val.typeReg().code() != (val.payloadReg().code() + 1)))
35         return false;
36     if ((val.payloadReg().code() & 1) != 0)
37         return false;
38     return true;
39 }
40 
41 void
convertBoolToInt32(Register source,Register dest)42 MacroAssemblerARM::convertBoolToInt32(Register source, Register dest)
43 {
44     // Note that C++ bool is only 1 byte, so zero extend it to clear the
45     // higher-order bits.
46     ma_and(Imm32(0xff), source, dest);
47 }
48 
49 void
convertInt32ToDouble(Register src,FloatRegister dest_)50 MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_)
51 {
52     // Direct conversions aren't possible.
53     VFPRegister dest = VFPRegister(dest_);
54     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
55     as_vcvt(dest, dest.sintOverlay());
56 }
57 
58 void
convertInt32ToDouble(const Address & src,FloatRegister dest)59 MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest)
60 {
61     ScratchDoubleScope scratch(asMasm());
62     ma_vldr(src, scratch);
63     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
64 }
65 
66 void
convertInt32ToDouble(const BaseIndex & src,FloatRegister dest)67 MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest)
68 {
69     Register base = src.base;
70     uint32_t scale = Imm32::ShiftOf(src.scale).value;
71 
72     ScratchRegisterScope scratch(asMasm());
73 
74     if (src.offset != 0) {
75         ma_mov(base, scratch);
76         base = scratch;
77         ma_add(Imm32(src.offset), base);
78     }
79     ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch);
80     convertInt32ToDouble(scratch, dest);
81 }
82 
83 void
convertUInt32ToDouble(Register src,FloatRegister dest_)84 MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
85 {
86     // Direct conversions aren't possible.
87     VFPRegister dest = VFPRegister(dest_);
88     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
89     as_vcvt(dest, dest.uintOverlay());
90 }
91 
92 static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
93 
94 void
convertUInt64ToDouble(Register64 src,Register temp,FloatRegister dest)95 MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
96 {
97     convertUInt32ToDouble(src.high, dest);
98     movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
99     loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
100     mulDouble(ScratchDoubleReg, dest);
101     convertUInt32ToDouble(src.low, ScratchDoubleReg);
102     addDouble(ScratchDoubleReg, dest);
103 }
104 
105 void
convertUInt32ToFloat32(Register src,FloatRegister dest_)106 MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
107 {
108     // Direct conversions aren't possible.
109     VFPRegister dest = VFPRegister(dest_);
110     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
111     as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay());
112 }
113 
convertDoubleToFloat32(FloatRegister src,FloatRegister dest,Condition c)114 void MacroAssemblerARM::convertDoubleToFloat32(FloatRegister src, FloatRegister dest,
115                                                Condition c)
116 {
117     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c);
118 }
119 
120 // There are two options for implementing emitTruncateDouble:
121 //
122 // 1. Convert the floating point value to an integer, if it did not fit, then it
123 // was clamped to INT_MIN/INT_MAX, and we can test it. NOTE: if the value
124 // really was supposed to be INT_MAX / INT_MIN then it will be wrong.
125 //
126 // 2. Convert the floating point value to an integer, if it did not fit, then it
127 // set one or two bits in the fpcsr. Check those.
128 void
branchTruncateDouble(FloatRegister src,Register dest,Label * fail)129 MacroAssemblerARM::branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
130 {
131     ScratchDoubleScope scratch(asMasm());
132     FloatRegister scratchSIntReg = scratch.sintOverlay();
133 
134     ma_vcvt_F64_I32(src, scratchSIntReg);
135     ma_vxfer(scratchSIntReg, dest);
136     ma_cmp(dest, Imm32(0x7fffffff));
137     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
138     ma_b(fail, Assembler::Equal);
139 }
140 
141 // Checks whether a double is representable as a 32-bit integer. If so, the
142 // integer is written to the output register. Otherwise, a bailout is taken to
143 // the given snapshot. This function overwrites the scratch float register.
144 void
convertDoubleToInt32(FloatRegister src,Register dest,Label * fail,bool negativeZeroCheck)145 MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest,
146                                         Label* fail, bool negativeZeroCheck)
147 {
148     // Convert the floating point value to an integer, if it did not fit, then
149     // when we convert it *back* to a float, it will have a different value,
150     // which we can test.
151     ScratchDoubleScope scratchDouble(asMasm());
152     FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
153 
154     ma_vcvt_F64_I32(src, scratchSIntReg);
155     // Move the value into the dest register.
156     ma_vxfer(scratchSIntReg, dest);
157     ma_vcvt_I32_F64(scratchSIntReg, scratchDouble);
158     ma_vcmp(src, scratchDouble);
159     as_vmrs(pc);
160     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
161 
162     if (negativeZeroCheck) {
163         ma_cmp(dest, Imm32(0));
164         // Test and bail for -0.0, when integer result is 0. Move the top word
165         // of the double into the output reg, if it is non-zero, then the
166         // original value was -0.0.
167         as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 1);
168         ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
169         ma_b(fail, Assembler::Equal);
170     }
171 }
172 
173 // Checks whether a float32 is representable as a 32-bit integer. If so, the
174 // integer is written to the output register. Otherwise, a bailout is taken to
175 // the given snapshot. This function overwrites the scratch float register.
176 void
convertFloat32ToInt32(FloatRegister src,Register dest,Label * fail,bool negativeZeroCheck)177 MacroAssemblerARM::convertFloat32ToInt32(FloatRegister src, Register dest,
178                                          Label* fail, bool negativeZeroCheck)
179 {
180     // Converting the floating point value to an integer and then converting it
181     // back to a float32 would not work, as float to int32 conversions are
182     // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
183     // and then back to float(INT32_MAX + 1)).  If this ever happens, we just
184     // bail out.
185     ScratchFloat32Scope scratchFloat(asMasm());
186 
187     FloatRegister ScratchSIntReg = scratchFloat.sintOverlay();
188     ma_vcvt_F32_I32(src, ScratchSIntReg);
189 
190     // Store the result
191     ma_vxfer(ScratchSIntReg, dest);
192 
193     ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat);
194     ma_vcmp(src, scratchFloat);
195     as_vmrs(pc);
196     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
197 
198     // Bail out in the clamped cases.
199     ma_cmp(dest, Imm32(0x7fffffff));
200     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
201     ma_b(fail, Assembler::Equal);
202 
203     if (negativeZeroCheck) {
204         ma_cmp(dest, Imm32(0));
205         // Test and bail for -0.0, when integer result is 0. Move the float into
206         // the output reg, and if it is non-zero then the original value was
207         // -0.0
208         as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, Assembler::Equal, 0);
209         ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
210         ma_b(fail, Assembler::Equal);
211     }
212 }
213 
214 void
convertFloat32ToDouble(FloatRegister src,FloatRegister dest)215 MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, FloatRegister dest)
216 {
217     MOZ_ASSERT(dest.isDouble());
218     MOZ_ASSERT(src.isSingle());
219     as_vcvt(VFPRegister(dest), VFPRegister(src).singleOverlay());
220 }
221 
222 void
branchTruncateFloat32(FloatRegister src,Register dest,Label * fail)223 MacroAssemblerARM::branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
224 {
225     ScratchFloat32Scope scratch(asMasm());
226     ma_vcvt_F32_I32(src, scratch.sintOverlay());
227     ma_vxfer(scratch, dest);
228     ma_cmp(dest, Imm32(0x7fffffff));
229     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
230     ma_b(fail, Assembler::Equal);
231 }
232 
233 void
convertInt32ToFloat32(Register src,FloatRegister dest)234 MacroAssemblerARM::convertInt32ToFloat32(Register src, FloatRegister dest)
235 {
236     // Direct conversions aren't possible.
237     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
238     as_vcvt(dest.singleOverlay(), dest.sintOverlay());
239 }
240 
241 void
convertInt32ToFloat32(const Address & src,FloatRegister dest)242 MacroAssemblerARM::convertInt32ToFloat32(const Address& src, FloatRegister dest)
243 {
244     ScratchFloat32Scope scratch(asMasm());
245     ma_vldr(src, scratch);
246     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
247 }
248 
249 void
addDouble(FloatRegister src,FloatRegister dest)250 MacroAssemblerARM::addDouble(FloatRegister src, FloatRegister dest)
251 {
252     ma_vadd(dest, src, dest);
253 }
254 
255 void
subDouble(FloatRegister src,FloatRegister dest)256 MacroAssemblerARM::subDouble(FloatRegister src, FloatRegister dest)
257 {
258     ma_vsub(dest, src, dest);
259 }
260 
261 void
mulDouble(FloatRegister src,FloatRegister dest)262 MacroAssemblerARM::mulDouble(FloatRegister src, FloatRegister dest)
263 {
264     ma_vmul(dest, src, dest);
265 }
266 
267 void
divDouble(FloatRegister src,FloatRegister dest)268 MacroAssemblerARM::divDouble(FloatRegister src, FloatRegister dest)
269 {
270     ma_vdiv(dest, src, dest);
271 }
272 
273 void
negateDouble(FloatRegister reg)274 MacroAssemblerARM::negateDouble(FloatRegister reg)
275 {
276     ma_vneg(reg, reg);
277 }
278 
279 void
inc64(AbsoluteAddress dest)280 MacroAssemblerARM::inc64(AbsoluteAddress dest)
281 {
282     ScratchRegisterScope scratch(asMasm());
283 
284     ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex);
285 
286     ma_mov(Imm32((int32_t)dest.addr), scratch);
287     ma_ldrd(EDtrAddr(scratch, EDtrOffImm(0)), r0, r1);
288 
289     ma_add(Imm32(1), r0, SetCC);
290     ma_adc(Imm32(0), r1, LeaveCC);
291 
292     ma_strd(r0, r1, EDtrAddr(scratch, EDtrOffImm(0)));
293     ma_ldrd(EDtrAddr(sp, EDtrOffImm(8)), r0, r1, PostIndex);
294 }
295 
296 bool
alu_dbl(Register src1,Imm32 imm,Register dest,ALUOp op,SBit s,Condition c)297 MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
298                            SBit s, Condition c)
299 {
300     if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op))
301         return false;
302 
303     ALUOp interop = getDestVariant(op);
304     Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value);
305     if (both.fst.invalid)
306         return false;
307 
308     // For the most part, there is no good reason to set the condition codes for
309     // the first instruction. We can do better things if the second instruction
310     // doesn't have a dest, such as check for overflow by doing first operation
311     // don't do second operation if first operation overflowed. This preserves
312     // the overflow condition code. Unfortunately, it is horribly brittle.
313     as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c);
314     as_alu(dest, dest, Operand2(both.snd), op, s, c);
315     return true;
316 }
317 
318 void
ma_alu(Register src1,Imm32 imm,Register dest,ALUOp op,SBit s,Condition c)319 MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
320                           ALUOp op, SBit s, Condition c)
321 {
322     // As it turns out, if you ask for a compare-like instruction you *probably*
323     // want it to set condition codes.
324     if (dest == InvalidReg)
325         MOZ_ASSERT(s == SetCC);
326 
327     // The operator gives us the ability to determine how this can be used.
328     Imm8 imm8 = Imm8(imm.value);
329     // One instruction: If we can encode it using an imm8m, then do so.
330     if (!imm8.invalid) {
331         as_alu(dest, src1, imm8, op, s, c);
332         return;
333     }
334 
335     // One instruction, negated:
336     Imm32 negImm = imm;
337     Register negDest;
338     ALUOp negOp = ALUNeg(op, dest, &negImm, &negDest);
339     Imm8 negImm8 = Imm8(negImm.value);
340     // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. For bonus
341     // points, dest can be replaced (nearly always invalid => ScratchRegister)
342     // This is useful if we wish to negate tst. tst has an invalid (aka not
343     // used) dest, but its negation is bic *requires* a dest. We can accomodate,
344     // but it will need to clobber *something*, and the scratch register isn't
345     // being used, so...
346     if (negOp != OpInvalid && !negImm8.invalid) {
347         as_alu(negDest, src1, negImm8, negOp, s, c);
348         return;
349     }
350 
351     if (HasMOVWT()) {
352         // If the operation is a move-a-like then we can try to use movw to move
353         // the bits into the destination. Otherwise, we'll need to fall back on
354         // a multi-instruction format :(
355         // movw/movt does not set condition codes, so don't hold your breath.
356         if (s == LeaveCC && (op == OpMov || op == OpMvn)) {
357             // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument,
358             // so we can set the register this way. movt leaves the bottom 16
359             // bits in tact, so it is unsuitable to move a constant that
360             if (op == OpMov && ((imm.value & ~ 0xffff) == 0)) {
361                 MOZ_ASSERT(src1 == InvalidReg);
362                 as_movw(dest, Imm16((uint16_t)imm.value), c);
363                 return;
364             }
365 
366             // If they asked for a mvn rfoo, imm, where ~imm fits into 16 bits
367             // then do it.
368             if (op == OpMvn && (((~imm.value) & ~ 0xffff) == 0)) {
369                 MOZ_ASSERT(src1 == InvalidReg);
370                 as_movw(dest, Imm16((uint16_t)~imm.value), c);
371                 return;
372             }
373 
374             // TODO: constant dedup may enable us to add dest, r0, 23 *if* we
375             // are attempting to load a constant that looks similar to one that
376             // already exists. If it can't be done with a single movw then we
377             // *need* to use two instructions since this must be some sort of a
378             // move operation, we can just use a movw/movt pair and get the
379             // whole thing done in two moves. This does not work for ops like
380             // add, since we'd need to do: movw tmp; movt tmp; add dest, tmp,
381             // src1.
382             if (op == OpMvn)
383                 imm.value = ~imm.value;
384             as_movw(dest, Imm16(imm.value & 0xffff), c);
385             as_movt(dest, Imm16((imm.value >> 16) & 0xffff), c);
386             return;
387         }
388         // If we weren't doing a movalike, a 16 bit immediate will require 2
389         // instructions. With the same amount of space and (less)time, we can do
390         // two 8 bit operations, reusing the dest register. e.g.
391         //  movw tmp, 0xffff; add dest, src, tmp ror 4
392         // vs.
393         //  add dest, src, 0xff0; add dest, dest, 0xf000000f
394         //
395         // It turns out that there are some immediates that we miss with the
396         // second approach. A sample value is: add dest, src, 0x1fffe this can
397         // be done by movw tmp, 0xffff; add dest, src, tmp lsl 1 since imm8m's
398         // only get even offsets, we cannot encode this. I'll try to encode as
399         // two imm8's first, since they are faster. Both operations should take
400         // 1 cycle, where as add dest, tmp ror 4 takes two cycles to execute.
401     }
402 
403     // Either a) this isn't ARMv7 b) this isn't a move start by attempting to
404     // generate a two instruction form. Some things cannot be made into two-inst
405     // forms correctly. Namely, adds dest, src, 0xffff. Since we want the
406     // condition codes (and don't know which ones will be checked), we need to
407     // assume that the overflow flag will be checked and add{,s} dest, src,
408     // 0xff00; add{,s} dest, dest, 0xff is not guaranteed to set the overflow
409     // flag the same as the (theoretical) one instruction variant.
410     if (alu_dbl(src1, imm, dest, op, s, c))
411         return;
412 
413     // And try with its negative.
414     if (negOp != OpInvalid &&
415         alu_dbl(src1, negImm, negDest, negOp, s, c))
416         return;
417 
418     // Often this code is called with dest as the ScratchRegister.  The register
419     // is logically owned by the caller after this call.
420     const Register& scratch = ScratchRegister;
421     MOZ_ASSERT(src1 != scratch);
422 #ifdef DEBUG
423     if (dest != scratch) {
424         // If the destination register is not the scratch register, double check
425         // that the current function does not erase the content of the scratch
426         // register.
427         ScratchRegisterScope assertScratch(asMasm());
428     }
429 #endif
430 
431     // Well, damn. We can use two 16 bit mov's, then do the op or we can do a
432     // single load from a pool then op.
433     if (HasMOVWT()) {
434         // Try to load the immediate into a scratch register then use that
435         as_movw(scratch, Imm16(imm.value & 0xffff), c);
436         if ((imm.value >> 16) != 0)
437             as_movt(scratch, Imm16((imm.value >> 16) & 0xffff), c);
438     } else {
439         // Going to have to use a load. If the operation is a move, then just
440         // move it into the destination register
441         if (op == OpMov) {
442             as_Imm32Pool(dest, imm.value, c);
443             return;
444         } else {
445             // If this isn't just going into a register, then stick it in a
446             // temp, and then proceed.
447             as_Imm32Pool(scratch, imm.value, c);
448         }
449     }
450     as_alu(dest, src1, O2Reg(scratch), op, s, c);
451 }
452 
453 void
ma_alu(Register src1,Operand op2,Register dest,ALUOp op,SBit s,Assembler::Condition c)454 MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
455             SBit s, Assembler::Condition c)
456 {
457     MOZ_ASSERT(op2.getTag() == Operand::OP2);
458     as_alu(dest, src1, op2.toOp2(), op, s, c);
459 }
460 
461 void
ma_alu(Register src1,Operand2 op2,Register dest,ALUOp op,SBit s,Condition c)462 MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op, SBit s, Condition c)
463 {
464     as_alu(dest, src1, op2, op, s, c);
465 }
466 
467 void
ma_nop()468 MacroAssemblerARM::ma_nop()
469 {
470     as_nop();
471 }
472 
473 void
ma_movPatchable(Imm32 imm_,Register dest,Assembler::Condition c,RelocStyle rs)474 MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest, Assembler::Condition c,
475                                    RelocStyle rs)
476 {
477     int32_t imm = imm_.value;
478     switch(rs) {
479       case L_MOVWT:
480         as_movw(dest, Imm16(imm & 0xffff), c);
481         as_movt(dest, Imm16(imm >> 16 & 0xffff), c);
482         break;
483       case L_LDR:
484         as_Imm32Pool(dest, imm, c);
485         break;
486     }
487 }
488 
489 void
ma_movPatchable(ImmPtr imm,Register dest,Assembler::Condition c,RelocStyle rs)490 MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest, Assembler::Condition c,
491                                    RelocStyle rs)
492 {
493     ma_movPatchable(Imm32(int32_t(imm.value)), dest, c, rs);
494 }
495 
496 /* static */ void
ma_mov_patch(Imm32 imm_,Register dest,Assembler::Condition c,RelocStyle rs,Instruction * i)497 MacroAssemblerARM::ma_mov_patch(Imm32 imm_, Register dest, Assembler::Condition c,
498                                 RelocStyle rs, Instruction* i)
499 {
500     MOZ_ASSERT(i);
501     int32_t imm = imm_.value;
502 
503     // Make sure the current instruction is not an artificial guard inserted
504     // by the assembler buffer.
505     i = i->skipPool();
506 
507     switch(rs) {
508       case L_MOVWT:
509         Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, i);
510         i = i->next();
511         Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, i);
512         break;
513       case L_LDR:
514         Assembler::WritePoolEntry(i, c, imm);
515         break;
516     }
517 }
518 
519 /* static */ void
ma_mov_patch(ImmPtr imm,Register dest,Assembler::Condition c,RelocStyle rs,Instruction * i)520 MacroAssemblerARM::ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition c,
521                                 RelocStyle rs, Instruction* i)
522 {
523     ma_mov_patch(Imm32(int32_t(imm.value)), dest, c, rs, i);
524 }
525 
526 void
ma_mov(Register src,Register dest,SBit s,Assembler::Condition c)527 MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s, Assembler::Condition c)
528 {
529     if (s == SetCC || dest != src)
530         as_mov(dest, O2Reg(src), s, c);
531 }
532 
533 void
ma_mov(Imm32 imm,Register dest,SBit s,Assembler::Condition c)534 MacroAssemblerARM::ma_mov(Imm32 imm, Register dest,
535                           SBit s, Assembler::Condition c)
536 {
537     ma_alu(InvalidReg, imm, dest, OpMov, s, c);
538 }
539 
540 void
ma_mov(ImmWord imm,Register dest,SBit s,Assembler::Condition c)541 MacroAssemblerARM::ma_mov(ImmWord imm, Register dest,
542                           SBit s, Assembler::Condition c)
543 {
544     ma_alu(InvalidReg, Imm32(imm.value), dest, OpMov, s, c);
545 }
546 
547 void
ma_mov(ImmGCPtr ptr,Register dest)548 MacroAssemblerARM::ma_mov(ImmGCPtr ptr, Register dest)
549 {
550     // As opposed to x86/x64 version, the data relocation has to be executed
551     // before to recover the pointer, and not after.
552     writeDataRelocation(ptr);
553     RelocStyle rs;
554     if (HasMOVWT())
555         rs = L_MOVWT;
556     else
557         rs = L_LDR;
558 
559     ma_movPatchable(Imm32(uintptr_t(ptr.value)), dest, Always, rs);
560 }
561 
562 // Shifts (just a move with a shifting op2)
563 void
ma_lsl(Imm32 shift,Register src,Register dst)564 MacroAssemblerARM::ma_lsl(Imm32 shift, Register src, Register dst)
565 {
566     as_mov(dst, lsl(src, shift.value));
567 }
568 
569 void
ma_lsr(Imm32 shift,Register src,Register dst)570 MacroAssemblerARM::ma_lsr(Imm32 shift, Register src, Register dst)
571 {
572     as_mov(dst, lsr(src, shift.value));
573 }
574 
575 void
ma_asr(Imm32 shift,Register src,Register dst)576 MacroAssemblerARM::ma_asr(Imm32 shift, Register src, Register dst)
577 {
578     as_mov(dst, asr(src, shift.value));
579 }
580 
581 void
ma_ror(Imm32 shift,Register src,Register dst)582 MacroAssemblerARM::ma_ror(Imm32 shift, Register src, Register dst)
583 {
584     as_mov(dst, ror(src, shift.value));
585 }
586 
587 void
ma_rol(Imm32 shift,Register src,Register dst)588 MacroAssemblerARM::ma_rol(Imm32 shift, Register src, Register dst)
589 {
590     as_mov(dst, rol(src, shift.value));
591 }
592 
593 // Shifts (just a move with a shifting op2)
594 void
ma_lsl(Register shift,Register src,Register dst)595 MacroAssemblerARM::ma_lsl(Register shift, Register src, Register dst)
596 {
597     as_mov(dst, lsl(src, shift));
598 }
599 
600 void
ma_lsr(Register shift,Register src,Register dst)601 MacroAssemblerARM::ma_lsr(Register shift, Register src, Register dst)
602 {
603     as_mov(dst, lsr(src, shift));
604 }
605 
606 void
ma_asr(Register shift,Register src,Register dst)607 MacroAssemblerARM::ma_asr(Register shift, Register src, Register dst)
608 {
609     as_mov(dst, asr(src, shift));
610 }
611 
612 void
ma_ror(Register shift,Register src,Register dst)613 MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst)
614 {
615     as_mov(dst, ror(src, shift));
616 }
617 
618 void
ma_rol(Register shift,Register src,Register dst)619 MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst)
620 {
621     ScratchRegisterScope scratch(asMasm());
622     ma_rsb(shift, Imm32(32), scratch);
623     as_mov(dst, ror(src, scratch));
624 }
625 
626 // Move not (dest <- ~src)
627 void
ma_mvn(Imm32 imm,Register dest,SBit s,Assembler::Condition c)628 MacroAssemblerARM::ma_mvn(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
629 {
630     ma_alu(InvalidReg, imm, dest, OpMvn, s, c);
631 }
632 
633 void
ma_mvn(Register src1,Register dest,SBit s,Assembler::Condition c)634 MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, Assembler::Condition c)
635 {
636     as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c);
637 }
638 
639 // Negate (dest <- -src), src is a register, rather than a general op2.
640 void
ma_neg(Register src1,Register dest,SBit s,Assembler::Condition c)641 MacroAssemblerARM::ma_neg(Register src1, Register dest, SBit s, Assembler::Condition c)
642 {
643     as_rsb(dest, src1, Imm8(0), s, c);
644 }
645 
646 // And.
647 void
ma_and(Register src,Register dest,SBit s,Assembler::Condition c)648 MacroAssemblerARM::ma_and(Register src, Register dest, SBit s, Assembler::Condition c)
649 {
650     ma_and(dest, src, dest);
651 }
652 
653 void
ma_and(Register src1,Register src2,Register dest,SBit s,Assembler::Condition c)654 MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest,
655                           SBit s, Assembler::Condition c)
656 {
657     as_and(dest, src1, O2Reg(src2), s, c);
658 }
659 
660 void
ma_and(Imm32 imm,Register dest,SBit s,Assembler::Condition c)661 MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
662 {
663     ma_alu(dest, imm, dest, OpAnd, s, c);
664 }
665 
666 void
ma_and(Imm32 imm,Register src1,Register dest,SBit s,Assembler::Condition c)667 MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest,
668                           SBit s, Assembler::Condition c)
669 {
670     ma_alu(src1, imm, dest, OpAnd, s, c);
671 }
672 
673 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2).
674 void
ma_bic(Imm32 imm,Register dest,SBit s,Assembler::Condition c)675 MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
676 {
677     ma_alu(dest, imm, dest, OpBic, s, c);
678 }
679 
680 // Exclusive or.
681 void
ma_eor(Register src,Register dest,SBit s,Assembler::Condition c)682 MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c)
683 {
684     ma_eor(dest, src, dest, s, c);
685 }
686 
687 void
ma_eor(Register src1,Register src2,Register dest,SBit s,Assembler::Condition c)688 MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest,
689                           SBit s, Assembler::Condition c)
690 {
691     as_eor(dest, src1, O2Reg(src2), s, c);
692 }
693 
694 void
ma_eor(Imm32 imm,Register dest,SBit s,Assembler::Condition c)695 MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
696 {
697     ma_alu(dest, imm, dest, OpEor, s, c);
698 }
699 
700 void
ma_eor(Imm32 imm,Register src1,Register dest,SBit s,Assembler::Condition c)701 MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest,
702        SBit s, Assembler::Condition c)
703 {
704     ma_alu(src1, imm, dest, OpEor, s, c);
705 }
706 
707 // Or.
708 void
ma_orr(Register src,Register dest,SBit s,Assembler::Condition c)709 MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c)
710 {
711     ma_orr(dest, src, dest, s, c);
712 }
713 
714 void
ma_orr(Register src1,Register src2,Register dest,SBit s,Assembler::Condition c)715 MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest,
716                           SBit s, Assembler::Condition c)
717 {
718     as_orr(dest, src1, O2Reg(src2), s, c);
719 }
720 
721 void
ma_orr(Imm32 imm,Register dest,SBit s,Assembler::Condition c)722 MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
723 {
724     ma_alu(dest, imm, dest, OpOrr, s, c);
725 }
726 
727 void
ma_orr(Imm32 imm,Register src1,Register dest,SBit s,Assembler::Condition c)728 MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest,
729                           SBit s, Assembler::Condition c)
730 {
731     ma_alu(src1, imm, dest, OpOrr, s, c);
732 }
733 
734 // Arithmetic-based ops.
735 // Add with carry.
736 void
ma_adc(Imm32 imm,Register dest,SBit s,Condition c)737 MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c)
738 {
739     ma_alu(dest, imm, dest, OpAdc, s, c);
740 }
741 
742 void
ma_adc(Register src,Register dest,SBit s,Condition c)743 MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c)
744 {
745     as_alu(dest, dest, O2Reg(src), OpAdc, s, c);
746 }
747 
748 void
ma_adc(Register src1,Register src2,Register dest,SBit s,Condition c)749 MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c)
750 {
751     as_alu(dest, src1, O2Reg(src2), OpAdc, s, c);
752 }
753 
754 // Add.
755 void
ma_add(Imm32 imm,Register dest,SBit s,Condition c)756 MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SBit s, Condition c)
757 {
758     ma_alu(dest, imm, dest, OpAdd, s, c);
759 }
760 
761 void
ma_add(Register src1,Register dest,SBit s,Condition c)762 MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c)
763 {
764     ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c);
765 }
766 
767 void
ma_add(Register src1,Register src2,Register dest,SBit s,Condition c)768 MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest, SBit s, Condition c)
769 {
770     as_alu(dest, src1, O2Reg(src2), OpAdd, s, c);
771 }
772 
773 void
ma_add(Register src1,Operand op,Register dest,SBit s,Condition c)774 MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c)
775 {
776     ma_alu(src1, op, dest, OpAdd, s, c);
777 }
778 
779 void
ma_add(Register src1,Imm32 op,Register dest,SBit s,Condition c)780 MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c)
781 {
782     ma_alu(src1, op, dest, OpAdd, s, c);
783 }
784 
785 // Subtract with carry.
786 void
ma_sbc(Imm32 imm,Register dest,SBit s,Condition c)787 MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c)
788 {
789     ma_alu(dest, imm, dest, OpSbc, s, c);
790 }
791 
792 void
ma_sbc(Register src1,Register dest,SBit s,Condition c)793 MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c)
794 {
795     as_alu(dest, dest, O2Reg(src1), OpSbc, s, c);
796 }
797 
798 void
ma_sbc(Register src1,Register src2,Register dest,SBit s,Condition c)799 MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c)
800 {
801     as_alu(dest, src1, O2Reg(src2), OpSbc, s, c);
802 }
803 
804 // Subtract.
805 void
ma_sub(Imm32 imm,Register dest,SBit s,Condition c)806 MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c)
807 {
808     ma_alu(dest, imm, dest, OpSub, s, c);
809 }
810 
811 void
ma_sub(Register src1,Register dest,SBit s,Condition c)812 MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c)
813 {
814     ma_alu(dest, Operand(src1), dest, OpSub, s, c);
815 }
816 
817 void
ma_sub(Register src1,Register src2,Register dest,SBit s,Condition c)818 MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest, SBit s, Condition c)
819 {
820     ma_alu(src1, Operand(src2), dest, OpSub, s, c);
821 }
822 
823 void
ma_sub(Register src1,Operand op,Register dest,SBit s,Condition c)824 MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c)
825 {
826     ma_alu(src1, op, dest, OpSub, s, c);
827 }
828 
829 void
ma_sub(Register src1,Imm32 op,Register dest,SBit s,Condition c)830 MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c)
831 {
832     ma_alu(src1, op, dest, OpSub, s, c);
833 }
834 
835 // Reverse subtract.
836 void
ma_rsb(Imm32 imm,Register dest,SBit s,Condition c)837 MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c)
838 {
839     ma_alu(dest, imm, dest, OpRsb, s, c);
840 }
841 
842 void
ma_rsb(Register src1,Register dest,SBit s,Condition c)843 MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c)
844 {
845     as_alu(dest, dest, O2Reg(src1), OpAdd, s, c);
846 }
847 
848 void
ma_rsb(Register src1,Register src2,Register dest,SBit s,Condition c)849 MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c)
850 {
851     as_alu(dest, src1, O2Reg(src2), OpRsb, s, c);
852 }
853 
854 void
ma_rsb(Register src1,Imm32 op2,Register dest,SBit s,Condition c)855 MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c)
856 {
857     ma_alu(src1, op2, dest, OpRsb, s, c);
858 }
859 
860 // Reverse subtract with carry.
861 void
ma_rsc(Imm32 imm,Register dest,SBit s,Condition c)862 MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c)
863 {
864     ma_alu(dest, imm, dest, OpRsc, s, c);
865 }
866 
867 void
ma_rsc(Register src1,Register dest,SBit s,Condition c)868 MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c)
869 {
870     as_alu(dest, dest, O2Reg(src1), OpRsc, s, c);
871 }
872 
873 void
ma_rsc(Register src1,Register src2,Register dest,SBit s,Condition c)874 MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c)
875 {
876     as_alu(dest, src1, O2Reg(src2), OpRsc, s, c);
877 }
878 
879 // Compares/tests.
880 // Compare negative (sets condition codes as src1 + src2 would).
881 void
ma_cmn(Register src1,Imm32 imm,Condition c)882 MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c)
883 {
884     ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c);
885 }
886 
887 void
ma_cmn(Register src1,Register src2,Condition c)888 MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c)
889 {
890     as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c);
891 }
892 
893 void
ma_cmn(Register src1,Operand op,Condition c)894 MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c)
895 {
896     MOZ_CRASH("Feature NYI");
897 }
898 
899 // Compare (src - src2).
900 void
ma_cmp(Register src1,Imm32 imm,Condition c)901 MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, Condition c)
902 {
903     ma_alu(src1, imm, InvalidReg, OpCmp, SetCC, c);
904 }
905 
906 void
ma_cmp(Register src1,ImmWord ptr,Condition c)907 MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, Condition c)
908 {
909     ma_cmp(src1, Imm32(ptr.value), c);
910 }
911 
912 void
ma_cmp(Register src1,ImmGCPtr ptr,Condition c)913 MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, Condition c)
914 {
915     ScratchRegisterScope scratch(asMasm());
916     ma_mov(ptr, scratch);
917     ma_cmp(src1, scratch, c);
918 }
919 
920 void
ma_cmp(Register src1,Operand op,Condition c)921 MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c)
922 {
923     switch (op.getTag()) {
924       case Operand::OP2:
925         as_cmp(src1, op.toOp2(), c);
926         break;
927       case Operand::MEM: {
928         ScratchRegisterScope scratch(asMasm());
929         ma_ldr(op.toAddress(), scratch);
930         as_cmp(src1, O2Reg(scratch), c);
931         break;
932       }
933       default:
934         MOZ_CRASH("trying to compare FP and integer registers");
935     }
936 }
937 
938 void
ma_cmp(Register src1,Register src2,Condition c)939 MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c)
940 {
941     as_cmp(src1, O2Reg(src2), c);
942 }
943 
944 // Test for equality, (src1 ^ src2).
945 void
ma_teq(Register src1,Imm32 imm,Condition c)946 MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c)
947 {
948     ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c);
949 }
950 
951 void
ma_teq(Register src1,Register src2,Condition c)952 MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c)
953 {
954     as_tst(src1, O2Reg(src2), c);
955 }
956 
957 void
ma_teq(Register src1,Operand op,Condition c)958 MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c)
959 {
960     as_teq(src1, op.toOp2(), c);
961 }
962 
963 // Test (src1 & src2).
964 void
ma_tst(Register src1,Imm32 imm,Condition c)965 MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c)
966 {
967     ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c);
968 }
969 
970 void
ma_tst(Register src1,Register src2,Condition c)971 MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c)
972 {
973     as_tst(src1, O2Reg(src2), c);
974 }
975 
976 void
ma_tst(Register src1,Operand op,Condition c)977 MacroAssemblerARM::ma_tst(Register src1, Operand op, Condition c)
978 {
979     as_tst(src1, op.toOp2(), c);
980 }
981 
982 void
ma_mul(Register src1,Register src2,Register dest)983 MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest)
984 {
985     as_mul(dest, src1, src2);
986 }
987 
988 void
ma_mul(Register src1,Imm32 imm,Register dest)989 MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest)
990 {
991     ScratchRegisterScope scratch(asMasm());
992     ma_mov(imm, scratch);
993     as_mul(dest, src1, scratch);
994 }
995 
996 Assembler::Condition
ma_check_mul(Register src1,Register src2,Register dest,Condition cond)997 MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, Condition cond)
998 {
999     ScratchRegisterScope scratch(asMasm());
1000 
1001     // TODO: this operation is illegal on armv6 and earlier if src2 ==
1002     // ScratchRegister or src2 == dest.
1003     if (cond == Equal || cond == NotEqual) {
1004         as_smull(scratch, dest, src1, src2, SetCC);
1005         return cond;
1006     }
1007 
1008     if (cond == Overflow) {
1009         as_smull(scratch, dest, src1, src2);
1010         as_cmp(scratch, asr(dest, 31));
1011         return NotEqual;
1012     }
1013 
1014     MOZ_CRASH("Condition NYI");
1015 }
1016 
1017 Assembler::Condition
ma_check_mul(Register src1,Imm32 imm,Register dest,Condition cond)1018 MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, Condition cond)
1019 {
1020     ScratchRegisterScope scratch(asMasm());
1021 
1022     ma_mov(imm, scratch);
1023     if (cond == Equal || cond == NotEqual) {
1024         as_smull(scratch, dest, scratch, src1, SetCC);
1025         return cond;
1026     }
1027 
1028     if (cond == Overflow) {
1029         as_smull(scratch, dest, scratch, src1);
1030         as_cmp(scratch, asr(dest, 31));
1031         return NotEqual;
1032     }
1033 
1034     MOZ_CRASH("Condition NYI");
1035 }
1036 
1037 void
ma_mod_mask(Register src,Register dest,Register hold,Register tmp,int32_t shift)1038 MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Register tmp,
1039                                int32_t shift)
1040 {
1041     // We wish to compute x % (1<<y) - 1 for a known constant, y.
1042     //
1043     // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as
1044     // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
1045     //
1046     // 2. Since both addition and multiplication commute with modulus:
1047     //   x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
1048     //    (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)...
1049     //
1050     // 3. Since b == C + 1, b % C == 1, and b^n % C == 1 the whole thing
1051     // simplifies to: c_0 + c_1 + c_2 ... c_n % C
1052     //
1053     // Each c_n can easily be computed by a shift/bitextract, and the modulus
1054     // can be maintained by simply subtracting by C whenever the number gets
1055     // over C.
1056     int32_t mask = (1 << shift) - 1;
1057     Label head;
1058 
1059     // Register 'hold' holds -1 if the value was negative, 1 otherwise. The
1060     // ScratchRegister holds the remaining bits that have not been processed lr
1061     // serves as a temporary location to store extracted bits into as well as
1062     // holding the trial subtraction as a temp value dest is the accumulator
1063     // (and holds the final result)
1064     //
1065     // Move the whole value into tmp, setting the codition codes so we can muck
1066     // with them later.
1067     //
1068     // Note that we cannot use ScratchRegister in place of tmp here, as ma_and
1069     // below on certain architectures move the mask into ScratchRegister before
1070     // performing the bitwise and.
1071     as_mov(tmp, O2Reg(src), SetCC);
1072     // Zero out the dest.
1073     ma_mov(Imm32(0), dest);
1074     // Set the hold appropriately.
1075     ma_mov(Imm32(1), hold);
1076     ma_mov(Imm32(-1), hold, LeaveCC, Signed);
1077     ma_rsb(Imm32(0), tmp, SetCC, Signed);
1078 
1079     // Begin the main loop.
1080     bind(&head);
1081     {
1082         AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
1083 
1084         // Extract the bottom bits into lr.
1085         ma_and(Imm32(mask), tmp, scratch2);
1086         // Add those bits to the accumulator.
1087         ma_add(scratch2, dest, dest);
1088         // Do a trial subtraction, this is the same operation as cmp, but we store
1089         // the dest.
1090         ma_sub(dest, Imm32(mask), scratch2, SetCC);
1091         // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus.
1092         ma_mov(scratch2, dest, LeaveCC, NotSigned);
1093         // Get rid of the bits that we extracted before, and set the condition codes.
1094         as_mov(tmp, lsr(tmp, shift), SetCC);
1095         // If the shift produced zero, finish, otherwise, continue in the loop.
1096         ma_b(&head, NonZero);
1097     }
1098 
1099     // Check the hold to see if we need to negate the result. Hold can only be
1100     // 1 or -1, so this will never set the 0 flag.
1101     ma_cmp(hold, Imm32(0));
1102     // If the hold was non-zero, negate the result to be in line with what JS
1103     // wants this will set the condition codes if we try to negate.
1104     ma_rsb(Imm32(0), dest, SetCC, Signed);
1105     // Since the Zero flag is not set by the compare, we can *only* set the Zero
1106     // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of
1107     // the computation was -0.0).
1108 }
1109 
1110 void
ma_smod(Register num,Register div,Register dest)1111 MacroAssemblerARM::ma_smod(Register num, Register div, Register dest)
1112 {
1113     ScratchRegisterScope scratch(asMasm());
1114     as_sdiv(scratch, num, div);
1115     as_mls(dest, num, scratch, div);
1116 }
1117 
1118 void
ma_umod(Register num,Register div,Register dest)1119 MacroAssemblerARM::ma_umod(Register num, Register div, Register dest)
1120 {
1121     ScratchRegisterScope scratch(asMasm());
1122     as_udiv(scratch, num, div);
1123     as_mls(dest, num, scratch, div);
1124 }
1125 
1126 // Division
1127 void
ma_sdiv(Register num,Register div,Register dest,Condition cond)1128 MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, Condition cond)
1129 {
1130     as_sdiv(dest, num, div, cond);
1131 }
1132 
1133 void
ma_udiv(Register num,Register div,Register dest,Condition cond)1134 MacroAssemblerARM::ma_udiv(Register num, Register div, Register dest, Condition cond)
1135 {
1136     as_udiv(dest, num, div, cond);
1137 }
1138 
1139 // Miscellaneous instructions.
1140 void
ma_clz(Register src,Register dest,Condition cond)1141 MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond)
1142 {
1143     as_clz(dest, src, cond);
1144 }
1145 
1146 // Memory.
1147 // Shortcut for when we know we're transferring 32 bits of data.
1148 void
ma_dtr(LoadStore ls,Register rn,Imm32 offset,Register rt,Index mode,Assembler::Condition cc)1149 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt,
1150                           Index mode, Assembler::Condition cc)
1151 {
1152     ma_dataTransferN(ls, 32, true, rn, offset, rt, mode, cc);
1153 }
1154 
1155 void
ma_dtr(LoadStore ls,Register rn,Register rm,Register rt,Index mode,Assembler::Condition cc)1156 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Register rm, Register rt,
1157                           Index mode, Assembler::Condition cc)
1158 {
1159     MOZ_CRASH("Feature NYI");
1160 }
1161 
1162 void
ma_str(Register rt,DTRAddr addr,Index mode,Condition cc)1163 MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, Index mode, Condition cc)
1164 {
1165     as_dtr(IsStore, 32, mode, rt, addr, cc);
1166 }
1167 
1168 void
ma_dtr(LoadStore ls,Register rt,const Address & addr,Index mode,Condition cc)1169 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc)
1170 {
1171     ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc);
1172 }
1173 
1174 void
ma_str(Register rt,const Address & addr,Index mode,Condition cc)1175 MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc)
1176 {
1177     ma_dtr(IsStore, rt, addr, mode, cc);
1178 }
1179 
1180 void
ma_strd(Register rt,DebugOnly<Register> rt2,EDtrAddr addr,Index mode,Condition cc)1181 MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode, Condition cc)
1182 {
1183     MOZ_ASSERT((rt.code() & 1) == 0);
1184     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
1185     as_extdtr(IsStore, 64, true, mode, rt, addr, cc);
1186 }
1187 
1188 void
ma_ldr(DTRAddr addr,Register rt,Index mode,Condition cc)1189 MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc)
1190 {
1191     as_dtr(IsLoad, 32, mode, rt, addr, cc);
1192 }
1193 
1194 void
ma_ldr(const Address & addr,Register rt,Index mode,Condition cc)1195 MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc)
1196 {
1197     ma_dtr(IsLoad, rt, addr, mode, cc);
1198 }
1199 
1200 void
ma_ldrb(DTRAddr addr,Register rt,Index mode,Condition cc)1201 MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, Index mode, Condition cc)
1202 {
1203     as_dtr(IsLoad, 8, mode, rt, addr, cc);
1204 }
1205 
1206 void
ma_ldrsh(EDtrAddr addr,Register rt,Index mode,Condition cc)1207 MacroAssemblerARM::ma_ldrsh(EDtrAddr addr, Register rt, Index mode, Condition cc)
1208 {
1209     as_extdtr(IsLoad, 16, true, mode, rt, addr, cc);
1210 }
1211 
1212 void
ma_ldrh(EDtrAddr addr,Register rt,Index mode,Condition cc)1213 MacroAssemblerARM::ma_ldrh(EDtrAddr addr, Register rt, Index mode, Condition cc)
1214 {
1215     as_extdtr(IsLoad, 16, false, mode, rt, addr, cc);
1216 }
1217 
1218 void
ma_ldrsb(EDtrAddr addr,Register rt,Index mode,Condition cc)1219 MacroAssemblerARM::ma_ldrsb(EDtrAddr addr, Register rt, Index mode, Condition cc)
1220 {
1221     as_extdtr(IsLoad, 8, true, mode, rt, addr, cc);
1222 }
1223 
1224 void
ma_ldrd(EDtrAddr addr,Register rt,DebugOnly<Register> rt2,Index mode,Condition cc)1225 MacroAssemblerARM::ma_ldrd(EDtrAddr addr, Register rt, DebugOnly<Register> rt2,
1226                            Index mode, Condition cc)
1227 {
1228     MOZ_ASSERT((rt.code() & 1) == 0);
1229     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
1230     as_extdtr(IsLoad, 64, true, mode, rt, addr, cc);
1231 }
1232 
1233 void
ma_strh(Register rt,EDtrAddr addr,Index mode,Condition cc)1234 MacroAssemblerARM::ma_strh(Register rt, EDtrAddr addr, Index mode, Condition cc)
1235 {
1236     as_extdtr(IsStore, 16, false, mode, rt, addr, cc);
1237 }
1238 
1239 void
ma_strb(Register rt,DTRAddr addr,Index mode,Condition cc)1240 MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc)
1241 {
1242     as_dtr(IsStore, 8, mode, rt, addr, cc);
1243 }
1244 
1245 // Specialty for moving N bits of data, where n == 8,16,32,64.
1246 BufferOffset
ma_dataTransferN(LoadStore ls,int size,bool IsSigned,Register rn,Register rm,Register rt,Index mode,Assembler::Condition cc,unsigned shiftAmount)1247 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
1248                           Register rn, Register rm, Register rt,
1249                           Index mode, Assembler::Condition cc, unsigned shiftAmount)
1250 {
1251     if (size == 32 || (size == 8 && !IsSigned))
1252         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc);
1253 
1254     ScratchRegisterScope scratch(asMasm());
1255 
1256     if (shiftAmount != 0) {
1257         MOZ_ASSERT(rn != scratch);
1258         MOZ_ASSERT(rt != scratch);
1259         ma_lsl(Imm32(shiftAmount), rm, scratch);
1260         rm = scratch;
1261     }
1262 
1263     return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc);
1264 }
1265 
1266 BufferOffset
ma_dataTransferN(LoadStore ls,int size,bool IsSigned,Register rn,Imm32 offset,Register rt,Index mode,Assembler::Condition cc)1267 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
1268                                     Register rn, Imm32 offset, Register rt,
1269                                     Index mode, Assembler::Condition cc)
1270 {
1271     int off = offset.value;
1272 
1273     // We can encode this as a standard ldr.
1274     if (size == 32 || (size == 8 && !IsSigned) ) {
1275         if (off < 4096 && off > -4096) {
1276             // This encodes as a single instruction, Emulating mode's behavior
1277             // in a multi-instruction sequence is not necessary.
1278             return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc);
1279         }
1280 
1281         // We cannot encode this offset in a a single ldr. For mode == index,
1282         // try to encode it as |add scratch, base, imm; ldr dest, [scratch, +offset]|.
1283         // This does not wark for mode == PreIndex or mode == PostIndex.
1284         // PreIndex is simple, just do the add into the base register first,
1285         // then do a PreIndex'ed load. PostIndexed loads can be tricky.
1286         // Normally, doing the load with an index of 0, then doing an add would
1287         // work, but if the destination is the PC, you don't get to execute the
1288         // instruction after the branch, which will lead to the base register
1289         // not being updated correctly. Explicitly handle this case, without
1290         // doing anything fancy, then handle all of the other cases.
1291 
1292         // mode == Offset
1293         //  add   scratch, base, offset_hi
1294         //  ldr   dest, [scratch, +offset_lo]
1295         //
1296         // mode == PreIndex
1297         //  add   base, base, offset_hi
1298         //  ldr   dest, [base, +offset_lo]!
1299         //
1300         // mode == PostIndex, dest == pc
1301         //  ldr   scratch, [base]
1302         //  add   base, base, offset_hi
1303         //  add   base, base, offset_lo
1304         //  mov   dest, scratch
1305         // PostIndex with the pc as the destination needs to be handled
1306         // specially, since in the code below, the write into 'dest' is going to
1307         // alter the control flow, so the following instruction would never get
1308         // emitted.
1309         //
1310         // mode == PostIndex, dest != pc
1311         //  ldr   dest, [base], offset_lo
1312         //  add   base, base, offset_hi
1313 
1314         if (rt == pc && mode == PostIndex && ls == IsLoad) {
1315             ScratchRegisterScope scratch(asMasm());
1316             ma_mov(rn, scratch);
1317             ma_alu(rn, offset, rn, OpAdd);
1318             return as_dtr(IsLoad, size, Offset, pc, DTRAddr(scratch, DtrOffImm(0)), cc);
1319         }
1320 
1321         // Often this code is called with rt as the ScratchRegister.
1322         // The register is logically owned by the caller, so we cannot ask
1323         // for exclusive ownership here. If full checking is desired,
1324         // this function should take an explicit scratch register argument.
1325         const Register& scratch = ScratchRegister;
1326         MOZ_ASSERT(rn != scratch);
1327 
1328         int bottom = off & 0xfff;
1329         int neg_bottom = 0x1000 - bottom;
1330         // For a regular offset, base == ScratchRegister does what we want.
1331         // Modify the scratch register, leaving the actual base unscathed.
1332         Register base = scratch;
1333         // For the preindex case, we want to just re-use rn as the base
1334         // register, so when the base register is updated *before* the load, rn
1335         // is updated.
1336         if (mode == PreIndex)
1337             base = rn;
1338         MOZ_ASSERT(mode != PostIndex);
1339         // At this point, both off - bottom and off + neg_bottom will be
1340         // reasonable-ish quantities.
1341         //
1342         // Note a neg_bottom of 0x1000 can not be encoded as an immediate
1343         // negative offset in the instruction and this occurs when bottom is
1344         // zero, so this case is guarded against below.
1345         if (off < 0) {
1346             Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off
1347             if (!sub_off.invalid) {
1348                 // - sub_off = off - bottom
1349                 as_sub(scratch, rn, sub_off, LeaveCC, cc);
1350                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)), cc);
1351             }
1352 
1353             // sub_off = -neg_bottom - off
1354             sub_off = Imm8(-(off + neg_bottom));
1355             if (!sub_off.invalid && bottom != 0) {
1356                 // Guarded against by: bottom != 0
1357                 MOZ_ASSERT(neg_bottom < 0x1000);
1358                 // - sub_off = neg_bottom + off
1359                 as_sub(scratch, rn, sub_off, LeaveCC, cc);
1360                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
1361             }
1362         } else {
1363             // sub_off = off - bottom
1364             Operand2 sub_off = Imm8(off - bottom);
1365             if (!sub_off.invalid) {
1366                 //  sub_off = off - bottom
1367                 as_add(scratch, rn, sub_off, LeaveCC, cc);
1368                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)), cc);
1369             }
1370 
1371             // sub_off = neg_bottom + off
1372             sub_off = Imm8(off + neg_bottom);
1373             if (!sub_off.invalid && bottom != 0) {
1374                 // Guarded against by: bottom != 0
1375                 MOZ_ASSERT(neg_bottom < 0x1000);
1376                 // sub_off = neg_bottom + off
1377                 as_add(scratch, rn, sub_off, LeaveCC,  cc);
1378                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
1379             }
1380         }
1381 
1382         ma_mov(offset, scratch);
1383         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0)));
1384     } else {
1385         ScratchRegisterScope scratch(asMasm());
1386 
1387         // Should attempt to use the extended load/store instructions.
1388         if (off < 256 && off > -256)
1389             return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffImm(off)), cc);
1390 
1391         // We cannot encode this offset in a single extldr. Try to encode it as
1392         // an add scratch, base, imm; extldr dest, [scratch, +offset].
1393         int bottom = off & 0xff;
1394         int neg_bottom = 0x100 - bottom;
1395         // At this point, both off - bottom and off + neg_bottom will be
1396         // reasonable-ish quantities.
1397         //
1398         // Note a neg_bottom of 0x100 can not be encoded as an immediate
1399         // negative offset in the instruction and this occurs when bottom is
1400         // zero, so this case is guarded against below.
1401         if (off < 0) {
1402             // sub_off = bottom - off
1403             Operand2 sub_off = Imm8(-(off - bottom));
1404             if (!sub_off.invalid) {
1405                 // - sub_off = off - bottom
1406                 as_sub(scratch, rn, sub_off, LeaveCC, cc);
1407                 return as_extdtr(ls, size, IsSigned, Offset, rt,
1408                                  EDtrAddr(scratch, EDtrOffImm(bottom)),
1409                                  cc);
1410             }
1411             // sub_off = -neg_bottom - off
1412             sub_off = Imm8(-(off + neg_bottom));
1413             if (!sub_off.invalid && bottom != 0) {
1414                 // Guarded against by: bottom != 0
1415                 MOZ_ASSERT(neg_bottom < 0x100);
1416                 // - sub_off = neg_bottom + off
1417                 as_sub(scratch, rn, sub_off, LeaveCC, cc);
1418                 return as_extdtr(ls, size, IsSigned, Offset, rt,
1419                                  EDtrAddr(scratch, EDtrOffImm(-neg_bottom)),
1420                                  cc);
1421             }
1422         } else {
1423             // sub_off = off - bottom
1424             Operand2 sub_off = Imm8(off - bottom);
1425             if (!sub_off.invalid) {
1426                 // sub_off = off - bottom
1427                 as_add(scratch, rn, sub_off, LeaveCC, cc);
1428                 return as_extdtr(ls, size, IsSigned, Offset, rt,
1429                                  EDtrAddr(scratch, EDtrOffImm(bottom)),
1430                                  cc);
1431             }
1432             // sub_off = neg_bottom + off
1433             sub_off = Imm8(off + neg_bottom);
1434             if (!sub_off.invalid && bottom != 0) {
1435                 // Guarded against by: bottom != 0
1436                 MOZ_ASSERT(neg_bottom < 0x100);
1437                 // sub_off = neg_bottom + off
1438                 as_add(scratch, rn, sub_off, LeaveCC,  cc);
1439                 return as_extdtr(ls, size, IsSigned, Offset, rt,
1440                                  EDtrAddr(scratch, EDtrOffImm(-neg_bottom)),
1441                                  cc);
1442             }
1443         }
1444         ma_mov(offset, scratch);
1445         return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(scratch)), cc);
1446     }
1447 }
1448 
1449 void
ma_pop(Register r)1450 MacroAssemblerARM::ma_pop(Register r)
1451 {
1452     ma_dtr(IsLoad, sp, Imm32(4), r, PostIndex);
1453 }
1454 
1455 void
ma_push(Register r)1456 MacroAssemblerARM::ma_push(Register r)
1457 {
1458     // Pushing sp is not well defined: use two instructions.
1459     if (r == sp) {
1460         ScratchRegisterScope scratch(asMasm());
1461         ma_mov(sp, scratch);
1462         ma_dtr(IsStore, sp, Imm32(-4), scratch, PreIndex);
1463         return;
1464     }
1465 
1466     ma_dtr(IsStore, sp, Imm32(-4), r, PreIndex);
1467 }
1468 
1469 void
ma_vpop(VFPRegister r)1470 MacroAssemblerARM::ma_vpop(VFPRegister r)
1471 {
1472     startFloatTransferM(IsLoad, sp, IA, WriteBack);
1473     transferFloatReg(r);
1474     finishFloatTransfer();
1475 }
1476 
1477 void
ma_vpush(VFPRegister r)1478 MacroAssemblerARM::ma_vpush(VFPRegister r)
1479 {
1480     startFloatTransferM(IsStore, sp, DB, WriteBack);
1481     transferFloatReg(r);
1482     finishFloatTransfer();
1483 }
1484 
1485 // Barriers
1486 void
ma_dmb(BarrierOption option)1487 MacroAssemblerARM::ma_dmb(BarrierOption option)
1488 {
1489     if (HasDMBDSBISB())
1490         as_dmb(option);
1491     else
1492         as_dmb_trap();
1493 }
1494 
1495 void
ma_dsb(BarrierOption option)1496 MacroAssemblerARM::ma_dsb(BarrierOption option)
1497 {
1498     if (HasDMBDSBISB())
1499         as_dsb(option);
1500     else
1501         as_dsb_trap();
1502 }
1503 
1504 // Branches when done from within arm-specific code.
1505 BufferOffset
ma_b(Label * dest,Assembler::Condition c)1506 MacroAssemblerARM::ma_b(Label* dest, Assembler::Condition c)
1507 {
1508     return as_b(dest, c);
1509 }
1510 
1511 void
ma_bx(Register dest,Assembler::Condition c)1512 MacroAssemblerARM::ma_bx(Register dest, Assembler::Condition c)
1513 {
1514     as_bx(dest, c);
1515 }
1516 
1517 void
ma_b(void * target,Assembler::Condition c)1518 MacroAssemblerARM::ma_b(void* target, Assembler::Condition c)
1519 {
1520     // An immediate pool is used for easier patching.
1521     as_Imm32Pool(pc, uint32_t(target), c);
1522 }
1523 
1524 // This is almost NEVER necessary: we'll basically never be calling a label,
1525 // except possibly in the crazy bailout-table case.
1526 void
ma_bl(Label * dest,Assembler::Condition c)1527 MacroAssemblerARM::ma_bl(Label* dest, Assembler::Condition c)
1528 {
1529     as_bl(dest, c);
1530 }
1531 
1532 void
ma_blx(Register reg,Assembler::Condition c)1533 MacroAssemblerARM::ma_blx(Register reg, Assembler::Condition c)
1534 {
1535     as_blx(reg, c);
1536 }
1537 
1538 // VFP/ALU
1539 void
ma_vadd(FloatRegister src1,FloatRegister src2,FloatRegister dst)1540 MacroAssemblerARM::ma_vadd(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1541 {
1542     as_vadd(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
1543 }
1544 
1545 void
ma_vadd_f32(FloatRegister src1,FloatRegister src2,FloatRegister dst)1546 MacroAssemblerARM::ma_vadd_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1547 {
1548     as_vadd(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
1549             VFPRegister(src2).singleOverlay());
1550 }
1551 
1552 void
ma_vsub(FloatRegister src1,FloatRegister src2,FloatRegister dst)1553 MacroAssemblerARM::ma_vsub(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1554 {
1555     as_vsub(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
1556 }
1557 
1558 void
ma_vsub_f32(FloatRegister src1,FloatRegister src2,FloatRegister dst)1559 MacroAssemblerARM::ma_vsub_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1560 {
1561     as_vsub(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
1562             VFPRegister(src2).singleOverlay());
1563 }
1564 
1565 void
ma_vmul(FloatRegister src1,FloatRegister src2,FloatRegister dst)1566 MacroAssemblerARM::ma_vmul(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1567 {
1568     as_vmul(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
1569 }
1570 
1571 void
ma_vmul_f32(FloatRegister src1,FloatRegister src2,FloatRegister dst)1572 MacroAssemblerARM::ma_vmul_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1573 {
1574     as_vmul(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
1575             VFPRegister(src2).singleOverlay());
1576 }
1577 
1578 void
ma_vdiv(FloatRegister src1,FloatRegister src2,FloatRegister dst)1579 MacroAssemblerARM::ma_vdiv(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1580 {
1581     as_vdiv(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
1582 }
1583 
1584 void
ma_vdiv_f32(FloatRegister src1,FloatRegister src2,FloatRegister dst)1585 MacroAssemblerARM::ma_vdiv_f32(FloatRegister src1, FloatRegister src2, FloatRegister dst)
1586 {
1587     as_vdiv(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
1588             VFPRegister(src2).singleOverlay());
1589 }
1590 
1591 void
ma_vmov(FloatRegister src,FloatRegister dest,Condition cc)1592 MacroAssemblerARM::ma_vmov(FloatRegister src, FloatRegister dest, Condition cc)
1593 {
1594     as_vmov(dest, src, cc);
1595 }
1596 
1597 void
ma_vmov_f32(FloatRegister src,FloatRegister dest,Condition cc)1598 MacroAssemblerARM::ma_vmov_f32(FloatRegister src, FloatRegister dest, Condition cc)
1599 {
1600     as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
1601 }
1602 
1603 void
ma_vneg(FloatRegister src,FloatRegister dest,Condition cc)1604 MacroAssemblerARM::ma_vneg(FloatRegister src, FloatRegister dest, Condition cc)
1605 {
1606     as_vneg(dest, src, cc);
1607 }
1608 
1609 void
ma_vneg_f32(FloatRegister src,FloatRegister dest,Condition cc)1610 MacroAssemblerARM::ma_vneg_f32(FloatRegister src, FloatRegister dest, Condition cc)
1611 {
1612     as_vneg(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
1613 }
1614 
1615 void
ma_vabs(FloatRegister src,FloatRegister dest,Condition cc)1616 MacroAssemblerARM::ma_vabs(FloatRegister src, FloatRegister dest, Condition cc)
1617 {
1618     as_vabs(dest, src, cc);
1619 }
1620 
1621 void
ma_vabs_f32(FloatRegister src,FloatRegister dest,Condition cc)1622 MacroAssemblerARM::ma_vabs_f32(FloatRegister src, FloatRegister dest, Condition cc)
1623 {
1624     as_vabs(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
1625 }
1626 
1627 void
ma_vsqrt(FloatRegister src,FloatRegister dest,Condition cc)1628 MacroAssemblerARM::ma_vsqrt(FloatRegister src, FloatRegister dest, Condition cc)
1629 {
1630     as_vsqrt(dest, src, cc);
1631 }
1632 
1633 void
ma_vsqrt_f32(FloatRegister src,FloatRegister dest,Condition cc)1634 MacroAssemblerARM::ma_vsqrt_f32(FloatRegister src, FloatRegister dest, Condition cc)
1635 {
1636     as_vsqrt(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
1637 }
1638 
1639 static inline uint32_t
DoubleHighWord(const double value)1640 DoubleHighWord(const double value)
1641 {
1642     return static_cast<uint32_t>(BitwiseCast<uint64_t>(value) >> 32);
1643 }
1644 
1645 static inline uint32_t
DoubleLowWord(const double value)1646 DoubleLowWord(const double value)
1647 {
1648     return BitwiseCast<uint64_t>(value) & uint32_t(0xffffffff);
1649 }
1650 
1651 void
ma_vimm(double value,FloatRegister dest,Condition cc)1652 MacroAssemblerARM::ma_vimm(double value, FloatRegister dest, Condition cc)
1653 {
1654     if (HasVFPv3()) {
1655         if (DoubleLowWord(value) == 0) {
1656             if (DoubleHighWord(value) == 0) {
1657                 // To zero a register, load 1.0, then execute dN <- dN - dN
1658                 as_vimm(dest, VFPImm::One, cc);
1659                 as_vsub(dest, dest, dest, cc);
1660                 return;
1661             }
1662 
1663             VFPImm enc(DoubleHighWord(value));
1664             if (enc.isValid()) {
1665                 as_vimm(dest, enc, cc);
1666                 return;
1667             }
1668 
1669         }
1670     }
1671     // Fall back to putting the value in a pool.
1672     as_FImm64Pool(dest, value, cc);
1673 }
1674 
1675 static inline uint32_t
Float32Word(const float value)1676 Float32Word(const float value)
1677 {
1678     return BitwiseCast<uint32_t>(value);
1679 }
1680 
1681 void
ma_vimm_f32(float value,FloatRegister dest,Condition cc)1682 MacroAssemblerARM::ma_vimm_f32(float value, FloatRegister dest, Condition cc)
1683 {
1684     VFPRegister vd = VFPRegister(dest).singleOverlay();
1685     if (HasVFPv3()) {
1686         if (Float32Word(value) == 0) {
1687             // To zero a register, load 1.0, then execute sN <- sN - sN.
1688             as_vimm(vd, VFPImm::One, cc);
1689             as_vsub(vd, vd, vd, cc);
1690             return;
1691         }
1692 
1693         // Note that the vimm immediate float32 instruction encoding differs
1694         // from the vimm immediate double encoding, but this difference matches
1695         // the difference in the floating point formats, so it is possible to
1696         // convert the float32 to a double and then use the double encoding
1697         // paths. It is still necessary to firstly check that the double low
1698         // word is zero because some float32 numbers set these bits and this can
1699         // not be ignored.
1700         double doubleValue = value;
1701         if (DoubleLowWord(value) == 0) {
1702             VFPImm enc(DoubleHighWord(doubleValue));
1703             if (enc.isValid()) {
1704                 as_vimm(vd, enc, cc);
1705                 return;
1706             }
1707         }
1708     }
1709     // Fall back to putting the value in a pool.
1710     as_FImm32Pool(vd, value, cc);
1711 }
1712 
1713 void
ma_vcmp(FloatRegister src1,FloatRegister src2,Condition cc)1714 MacroAssemblerARM::ma_vcmp(FloatRegister src1, FloatRegister src2, Condition cc)
1715 {
1716     as_vcmp(VFPRegister(src1), VFPRegister(src2), cc);
1717 }
1718 
1719 void
ma_vcmp_f32(FloatRegister src1,FloatRegister src2,Condition cc)1720 MacroAssemblerARM::ma_vcmp_f32(FloatRegister src1, FloatRegister src2, Condition cc)
1721 {
1722     as_vcmp(VFPRegister(src1).singleOverlay(), VFPRegister(src2).singleOverlay(), cc);
1723 }
1724 
1725 void
ma_vcmpz(FloatRegister src1,Condition cc)1726 MacroAssemblerARM::ma_vcmpz(FloatRegister src1, Condition cc)
1727 {
1728     as_vcmpz(VFPRegister(src1), cc);
1729 }
1730 
1731 void
ma_vcmpz_f32(FloatRegister src1,Condition cc)1732 MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc)
1733 {
1734     as_vcmpz(VFPRegister(src1).singleOverlay(), cc);
1735 }
1736 
1737 void
ma_vcvt_F64_I32(FloatRegister src,FloatRegister dest,Condition cc)1738 MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest, Condition cc)
1739 {
1740     MOZ_ASSERT(src.isDouble());
1741     MOZ_ASSERT(dest.isSInt());
1742     as_vcvt(dest, src, false, cc);
1743 }
1744 
1745 void
ma_vcvt_F64_U32(FloatRegister src,FloatRegister dest,Condition cc)1746 MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest, Condition cc)
1747 {
1748     MOZ_ASSERT(src.isDouble());
1749     MOZ_ASSERT(dest.isUInt());
1750     as_vcvt(dest, src, false, cc);
1751 }
1752 
1753 void
ma_vcvt_I32_F64(FloatRegister src,FloatRegister dest,Condition cc)1754 MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister src, FloatRegister dest, Condition cc)
1755 {
1756     MOZ_ASSERT(src.isSInt());
1757     MOZ_ASSERT(dest.isDouble());
1758     as_vcvt(dest, src, false, cc);
1759 }
1760 
1761 void
ma_vcvt_U32_F64(FloatRegister src,FloatRegister dest,Condition cc)1762 MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister src, FloatRegister dest, Condition cc)
1763 {
1764     MOZ_ASSERT(src.isUInt());
1765     MOZ_ASSERT(dest.isDouble());
1766     as_vcvt(dest, src, false, cc);
1767 }
1768 
1769 void
ma_vcvt_F32_I32(FloatRegister src,FloatRegister dest,Condition cc)1770 MacroAssemblerARM::ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, Condition cc)
1771 {
1772     MOZ_ASSERT(src.isSingle());
1773     MOZ_ASSERT(dest.isSInt());
1774     as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
1775 }
1776 
1777 void
ma_vcvt_F32_U32(FloatRegister src,FloatRegister dest,Condition cc)1778 MacroAssemblerARM::ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, Condition cc)
1779 {
1780     MOZ_ASSERT(src.isSingle());
1781     MOZ_ASSERT(dest.isUInt());
1782     as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
1783 }
1784 
1785 void
ma_vcvt_I32_F32(FloatRegister src,FloatRegister dest,Condition cc)1786 MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, Condition cc)
1787 {
1788     MOZ_ASSERT(src.isSInt());
1789     MOZ_ASSERT(dest.isSingle());
1790     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).sintOverlay(), false, cc);
1791 }
1792 
1793 void
ma_vcvt_U32_F32(FloatRegister src,FloatRegister dest,Condition cc)1794 MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, Condition cc)
1795 {
1796     MOZ_ASSERT(src.isUInt());
1797     MOZ_ASSERT(dest.isSingle());
1798     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).uintOverlay(), false, cc);
1799 }
1800 
1801 void
ma_vxfer(FloatRegister src,Register dest,Condition cc)1802 MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest, Condition cc)
1803 {
1804     as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, cc);
1805 }
1806 
1807 void
ma_vxfer(FloatRegister src,Register dest1,Register dest2,Condition cc)1808 MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest1, Register dest2, Condition cc)
1809 {
1810     as_vxfer(dest1, dest2, VFPRegister(src), FloatToCore, cc);
1811 }
1812 
1813 void
ma_vxfer(Register src,FloatRegister dest,Condition cc)1814 MacroAssemblerARM::ma_vxfer(Register src, FloatRegister dest, Condition cc)
1815 {
1816     as_vxfer(src, InvalidReg, VFPRegister(dest).singleOverlay(), CoreToFloat, cc);
1817 }
1818 
1819 void
ma_vxfer(Register src1,Register src2,FloatRegister dest,Condition cc)1820 MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc)
1821 {
1822     as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc);
1823 }
1824 
1825 BufferOffset
ma_vdtr(LoadStore ls,const Address & addr,VFPRegister rt,Condition cc)1826 MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, Condition cc)
1827 {
1828     int off = addr.offset;
1829     MOZ_ASSERT((off & 3) == 0);
1830     Register base = addr.base;
1831     if (off > -1024 && off < 1024)
1832         return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc);
1833 
1834     ScratchRegisterScope scratch(asMasm());
1835 
1836     // We cannot encode this offset in a a single ldr. Try to encode it as an
1837     // add scratch, base, imm; ldr dest, [scratch, +offset].
1838     int bottom = off & (0xff << 2);
1839     int neg_bottom = (0x100 << 2) - bottom;
1840     // At this point, both off - bottom and off + neg_bottom will be
1841     // reasonable-ish quantities.
1842     //
1843     // Note a neg_bottom of 0x400 can not be encoded as an immediate negative
1844     // offset in the instruction and this occurs when bottom is zero, so this
1845     // case is guarded against below.
1846     if (off < 0) {
1847         // sub_off = bottom - off
1848         Operand2 sub_off = Imm8(-(off - bottom));
1849         if (!sub_off.invalid) {
1850             // - sub_off = off - bottom
1851             as_sub(scratch, base, sub_off, LeaveCC, cc);
1852             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc);
1853         }
1854         // sub_off = -neg_bottom - off
1855         sub_off = Imm8(-(off + neg_bottom));
1856         if (!sub_off.invalid && bottom != 0) {
1857             // Guarded against by: bottom != 0
1858             MOZ_ASSERT(neg_bottom < 0x400);
1859             // - sub_off = neg_bottom + off
1860             as_sub(scratch, base, sub_off, LeaveCC, cc);
1861             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
1862         }
1863     } else {
1864         // sub_off = off - bottom
1865         Operand2 sub_off = Imm8(off - bottom);
1866         if (!sub_off.invalid) {
1867             // sub_off = off - bottom
1868             as_add(scratch, base, sub_off, LeaveCC, cc);
1869             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc);
1870         }
1871         // sub_off = neg_bottom + off
1872         sub_off = Imm8(off + neg_bottom);
1873         if (!sub_off.invalid && bottom != 0) {
1874             // Guarded against by: bottom != 0
1875             MOZ_ASSERT(neg_bottom < 0x400);
1876             // sub_off = neg_bottom + off
1877             as_add(scratch, base, sub_off, LeaveCC, cc);
1878             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
1879         }
1880     }
1881     ma_add(base, Imm32(off), scratch, LeaveCC, cc);
1882     return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc);
1883 }
1884 
1885 BufferOffset
ma_vldr(VFPAddr addr,VFPRegister dest,Condition cc)1886 MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc)
1887 {
1888     return as_vdtr(IsLoad, dest, addr, cc);
1889 }
1890 
1891 BufferOffset
ma_vldr(const Address & addr,VFPRegister dest,Condition cc)1892 MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc)
1893 {
1894     return ma_vdtr(IsLoad, addr, dest, cc);
1895 }
1896 
1897 BufferOffset
ma_vldr(VFPRegister src,Register base,Register index,int32_t shift,Condition cc)1898 MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc)
1899 {
1900     ScratchRegisterScope scratch(asMasm());
1901     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
1902     return ma_vldr(Address(scratch, 0), src, cc);
1903 }
1904 
1905 BufferOffset
ma_vstr(VFPRegister src,VFPAddr addr,Condition cc)1906 MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc)
1907 {
1908     return as_vdtr(IsStore, src, addr, cc);
1909 }
1910 
1911 BufferOffset
ma_vstr(VFPRegister src,const Address & addr,Condition cc)1912 MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc)
1913 {
1914     return ma_vdtr(IsStore, addr, src, cc);
1915 }
1916 
1917 BufferOffset
ma_vstr(VFPRegister src,Register base,Register index,int32_t shift,int32_t offset,Condition cc)1918 MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
1919                            int32_t offset, Condition cc)
1920 {
1921     ScratchRegisterScope scratch(asMasm());
1922     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
1923     return ma_vstr(src, Address(scratch, offset), cc);
1924 }
1925 
1926 bool
buildOOLFakeExitFrame(void * fakeReturnAddr)1927 MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
1928 {
1929     DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
1930     uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS);
1931 
1932     asMasm().Push(Imm32(descriptor)); // descriptor_
1933     asMasm().Push(ImmPtr(fakeReturnAddr));
1934 
1935     return true;
1936 }
1937 
1938 void
alignFrameForICArguments(AfterICSaveLive & aic)1939 MacroAssembler::alignFrameForICArguments(AfterICSaveLive& aic)
1940 {
1941     // Exists for MIPS compatibility.
1942 }
1943 
1944 void
restoreFrameAlignmentForICArguments(AfterICSaveLive & aic)1945 MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive& aic)
1946 {
1947     // Exists for MIPS compatibility.
1948 }
1949 
1950 void
add32(Register src,Register dest)1951 MacroAssemblerARMCompat::add32(Register src, Register dest)
1952 {
1953     ma_add(src, dest, SetCC);
1954 }
1955 
1956 void
add32(Imm32 imm,Register dest)1957 MacroAssemblerARMCompat::add32(Imm32 imm, Register dest)
1958 {
1959     ma_add(imm, dest, SetCC);
1960 }
1961 
1962 void
add32(Imm32 imm,const Address & dest)1963 MacroAssemblerARMCompat::add32(Imm32 imm, const Address& dest)
1964 {
1965     ScratchRegisterScope scratch(asMasm());
1966     load32(dest, scratch);
1967     ma_add(imm, scratch, SetCC);
1968     store32(scratch, dest);
1969 }
1970 
1971 void
addPtr(Register src,Register dest)1972 MacroAssemblerARMCompat::addPtr(Register src, Register dest)
1973 {
1974     ma_add(src, dest);
1975 }
1976 
1977 void
addPtr(const Address & src,Register dest)1978 MacroAssemblerARMCompat::addPtr(const Address& src, Register dest)
1979 {
1980     ScratchRegisterScope scratch(asMasm());
1981     load32(src, scratch);
1982     ma_add(scratch, dest, SetCC);
1983 }
1984 
1985 void
move32(Imm32 imm,Register dest)1986 MacroAssemblerARMCompat::move32(Imm32 imm, Register dest)
1987 {
1988     ma_mov(imm, dest);
1989 }
1990 
1991 void
move32(Register src,Register dest)1992 MacroAssemblerARMCompat::move32(Register src, Register dest)
1993 {
1994     ma_mov(src, dest);
1995 }
1996 
1997 void
movePtr(Register src,Register dest)1998 MacroAssemblerARMCompat::movePtr(Register src, Register dest)
1999 {
2000     ma_mov(src, dest);
2001 }
2002 
2003 void
movePtr(ImmWord imm,Register dest)2004 MacroAssemblerARMCompat::movePtr(ImmWord imm, Register dest)
2005 {
2006     ma_mov(Imm32(imm.value), dest);
2007 }
2008 
2009 void
movePtr(ImmGCPtr imm,Register dest)2010 MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest)
2011 {
2012     ma_mov(imm, dest);
2013 }
2014 
2015 void
movePtr(ImmPtr imm,Register dest)2016 MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest)
2017 {
2018     movePtr(ImmWord(uintptr_t(imm.value)), dest);
2019 }
2020 
2021 void
movePtr(wasm::SymbolicAddress imm,Register dest)2022 MacroAssemblerARMCompat::movePtr(wasm::SymbolicAddress imm, Register dest)
2023 {
2024     RelocStyle rs;
2025     if (HasMOVWT())
2026         rs = L_MOVWT;
2027     else
2028         rs = L_LDR;
2029 
2030     append(AsmJSAbsoluteLink(CodeOffset(currentOffset()), imm));
2031     ma_movPatchable(Imm32(-1), dest, Always, rs);
2032 }
2033 
2034 void
load8ZeroExtend(const Address & address,Register dest)2035 MacroAssemblerARMCompat::load8ZeroExtend(const Address& address, Register dest)
2036 {
2037     ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest);
2038 }
2039 
2040 void
load8ZeroExtend(const BaseIndex & src,Register dest)2041 MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest)
2042 {
2043     Register base = src.base;
2044     uint32_t scale = Imm32::ShiftOf(src.scale).value;
2045 
2046     if (src.offset == 0) {
2047         ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
2048     } else {
2049         ScratchRegisterScope scratch(asMasm());
2050         ma_add(base, Imm32(src.offset), scratch);
2051         ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
2052     }
2053 }
2054 
2055 void
load8SignExtend(const Address & address,Register dest)2056 MacroAssemblerARMCompat::load8SignExtend(const Address& address, Register dest)
2057 {
2058     ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest);
2059 }
2060 
2061 void
load8SignExtend(const BaseIndex & src,Register dest)2062 MacroAssemblerARMCompat::load8SignExtend(const BaseIndex& src, Register dest)
2063 {
2064     Register index = src.index;
2065 
2066     ScratchRegisterScope scratch(asMasm());
2067 
2068     // ARMv7 does not have LSL on an index register with an extended load.
2069     if (src.scale != TimesOne) {
2070         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
2071         index = scratch;
2072     }
2073 
2074     if (src.offset != 0) {
2075         if (index != scratch) {
2076             ma_mov(index, scratch);
2077             index = scratch;
2078         }
2079         ma_add(Imm32(src.offset), index);
2080     }
2081     ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest);
2082 }
2083 
2084 void
load16ZeroExtend(const Address & address,Register dest)2085 MacroAssemblerARMCompat::load16ZeroExtend(const Address& address, Register dest)
2086 {
2087     ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest);
2088 }
2089 
2090 void
load16ZeroExtend(const BaseIndex & src,Register dest)2091 MacroAssemblerARMCompat::load16ZeroExtend(const BaseIndex& src, Register dest)
2092 {
2093     Register index = src.index;
2094 
2095     ScratchRegisterScope scratch(asMasm());
2096 
2097     // ARMv7 does not have LSL on an index register with an extended load.
2098     if (src.scale != TimesOne) {
2099         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
2100         index = scratch;
2101     }
2102 
2103     if (src.offset != 0) {
2104         if (index != scratch) {
2105             ma_mov(index, scratch);
2106             index = scratch;
2107         }
2108         ma_add(Imm32(src.offset), index);
2109     }
2110     ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
2111 }
2112 
2113 void
load16SignExtend(const Address & address,Register dest)2114 MacroAssemblerARMCompat::load16SignExtend(const Address& address, Register dest)
2115 {
2116     ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest);
2117 }
2118 
2119 void
load16SignExtend(const BaseIndex & src,Register dest)2120 MacroAssemblerARMCompat::load16SignExtend(const BaseIndex& src, Register dest)
2121 {
2122     Register index = src.index;
2123 
2124     ScratchRegisterScope scratch(asMasm());
2125 
2126     // We don't have LSL on index register yet.
2127     if (src.scale != TimesOne) {
2128         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
2129         index = scratch;
2130     }
2131 
2132     if (src.offset != 0) {
2133         if (index != scratch) {
2134             ma_mov(index, scratch);
2135             index = scratch;
2136         }
2137         ma_add(Imm32(src.offset), index);
2138     }
2139     ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
2140 }
2141 
2142 void
load32(const Address & address,Register dest)2143 MacroAssemblerARMCompat::load32(const Address& address, Register dest)
2144 {
2145     loadPtr(address, dest);
2146 }
2147 
2148 void
load32(const BaseIndex & address,Register dest)2149 MacroAssemblerARMCompat::load32(const BaseIndex& address, Register dest)
2150 {
2151     loadPtr(address, dest);
2152 }
2153 
2154 void
load32(AbsoluteAddress address,Register dest)2155 MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest)
2156 {
2157     loadPtr(address, dest);
2158 }
2159 
2160 void
loadPtr(const Address & address,Register dest)2161 MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest)
2162 {
2163     ma_ldr(address, dest);
2164 }
2165 
2166 void
loadPtr(const BaseIndex & src,Register dest)2167 MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, Register dest)
2168 {
2169     Register base = src.base;
2170     uint32_t scale = Imm32::ShiftOf(src.scale).value;
2171 
2172     if (src.offset != 0) {
2173         ScratchRegisterScope scratch(asMasm());
2174         ma_mov(base, scratch);
2175         ma_add(Imm32(src.offset), scratch);
2176         ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
2177         return;
2178     }
2179 
2180     ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
2181 }
2182 
2183 void
loadPtr(AbsoluteAddress address,Register dest)2184 MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest)
2185 {
2186     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
2187     movePtr(ImmWord(uintptr_t(address.addr)), dest);
2188     loadPtr(Address(dest, 0), dest);
2189 }
2190 
2191 void
loadPtr(wasm::SymbolicAddress address,Register dest)2192 MacroAssemblerARMCompat::loadPtr(wasm::SymbolicAddress address, Register dest)
2193 {
2194     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
2195     movePtr(address, dest);
2196     loadPtr(Address(dest, 0), dest);
2197 }
2198 
2199 void
loadPrivate(const Address & address,Register dest)2200 MacroAssemblerARMCompat::loadPrivate(const Address& address, Register dest)
2201 {
2202     ma_ldr(ToPayload(address), dest);
2203 }
2204 
2205 void
loadDouble(const Address & address,FloatRegister dest)2206 MacroAssemblerARMCompat::loadDouble(const Address& address, FloatRegister dest)
2207 {
2208     ma_vldr(address, dest);
2209 }
2210 
2211 void
loadDouble(const BaseIndex & src,FloatRegister dest)2212 MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, FloatRegister dest)
2213 {
2214     // VFP instructions don't even support register Base + register Index modes,
2215     // so just add the index, then handle the offset like normal.
2216     Register base = src.base;
2217     Register index = src.index;
2218     uint32_t scale = Imm32::ShiftOf(src.scale).value;
2219     int32_t offset = src.offset;
2220 
2221     ScratchRegisterScope scratch(asMasm());
2222     as_add(scratch, base, lsl(index, scale));
2223     ma_vldr(Address(scratch, offset), dest);
2224 }
2225 
2226 void
loadFloatAsDouble(const Address & address,FloatRegister dest)2227 MacroAssemblerARMCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
2228 {
2229     VFPRegister rt = dest;
2230     ma_vldr(address, rt.singleOverlay());
2231     as_vcvt(rt, rt.singleOverlay());
2232 }
2233 
2234 void
loadFloatAsDouble(const BaseIndex & src,FloatRegister dest)2235 MacroAssemblerARMCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
2236 {
2237     // VFP instructions don't even support register Base + register Index modes,
2238     // so just add the index, then handle the offset like normal.
2239     Register base = src.base;
2240     Register index = src.index;
2241     uint32_t scale = Imm32::ShiftOf(src.scale).value;
2242     int32_t offset = src.offset;
2243     VFPRegister rt = dest;
2244 
2245     ScratchRegisterScope scratch(asMasm());
2246     as_add(scratch, base, lsl(index, scale));
2247     ma_vldr(Address(scratch, offset), rt.singleOverlay());
2248     as_vcvt(rt, rt.singleOverlay());
2249 }
2250 
2251 void
loadFloat32(const Address & address,FloatRegister dest)2252 MacroAssemblerARMCompat::loadFloat32(const Address& address, FloatRegister dest)
2253 {
2254     ma_vldr(address, VFPRegister(dest).singleOverlay());
2255 }
2256 
2257 void
loadFloat32(const BaseIndex & src,FloatRegister dest)2258 MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, FloatRegister dest)
2259 {
2260     // VFP instructions don't even support register Base + register Index modes,
2261     // so just add the index, then handle the offset like normal.
2262     Register base = src.base;
2263     Register index = src.index;
2264     uint32_t scale = Imm32::ShiftOf(src.scale).value;
2265     int32_t offset = src.offset;
2266 
2267     ScratchRegisterScope scratch(asMasm());
2268     as_add(scratch, base, lsl(index, scale));
2269     ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay());
2270 }
2271 
2272 void
store8(Imm32 imm,const Address & address)2273 MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address)
2274 {
2275     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2276     ma_mov(imm, scratch2);
2277     store8(scratch2, address);
2278 }
2279 
2280 void
store8(Register src,const Address & address)2281 MacroAssemblerARMCompat::store8(Register src, const Address& address)
2282 {
2283     ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src);
2284 }
2285 
2286 void
store8(Imm32 imm,const BaseIndex & dest)2287 MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest)
2288 {
2289     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2290     ma_mov(imm, scratch2);
2291     store8(scratch2, dest);
2292 }
2293 
2294 void
store8(Register src,const BaseIndex & dest)2295 MacroAssemblerARMCompat::store8(Register src, const BaseIndex& dest)
2296 {
2297     Register base = dest.base;
2298     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
2299 
2300     ScratchRegisterScope scratch(asMasm());
2301 
2302     if (dest.offset != 0) {
2303         ma_add(base, Imm32(dest.offset), scratch);
2304         base = scratch;
2305     }
2306     ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
2307 }
2308 
2309 void
store16(Imm32 imm,const Address & address)2310 MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address)
2311 {
2312     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2313     ma_mov(imm, scratch2);
2314     store16(scratch2, address);
2315 }
2316 
2317 void
store16(Register src,const Address & address)2318 MacroAssemblerARMCompat::store16(Register src, const Address& address)
2319 {
2320     ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src);
2321 }
2322 
2323 void
store16(Imm32 imm,const BaseIndex & dest)2324 MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest)
2325 {
2326     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2327     ma_mov(imm, scratch2);
2328     store16(scratch2, dest);
2329 }
2330 
2331 void
store16(Register src,const BaseIndex & address)2332 MacroAssemblerARMCompat::store16(Register src, const BaseIndex& address)
2333 {
2334     Register index = address.index;
2335 
2336     ScratchRegisterScope scratch(asMasm());
2337 
2338     // We don't have LSL on index register yet.
2339     if (address.scale != TimesOne) {
2340         ma_lsl(Imm32::ShiftOf(address.scale), index, scratch);
2341         index = scratch;
2342     }
2343 
2344     if (address.offset != 0) {
2345         ma_add(index, Imm32(address.offset), scratch);
2346         index = scratch;
2347     }
2348     ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index)));
2349 }
2350 
2351 void
store32(Register src,AbsoluteAddress address)2352 MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address)
2353 {
2354     storePtr(src, address);
2355 }
2356 
2357 void
store32(Register src,const Address & address)2358 MacroAssemblerARMCompat::store32(Register src, const Address& address)
2359 {
2360     storePtr(src, address);
2361 }
2362 
2363 void
store32(Imm32 src,const Address & address)2364 MacroAssemblerARMCompat::store32(Imm32 src, const Address& address)
2365 {
2366     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2367     move32(src, scratch2);
2368     storePtr(scratch2, address);
2369 }
2370 
2371 void
store32(Imm32 imm,const BaseIndex & dest)2372 MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest)
2373 {
2374     ScratchRegisterScope scratch(asMasm());
2375     ma_mov(imm, scratch);
2376     store32(scratch, dest);
2377 }
2378 
2379 void
store32(Register src,const BaseIndex & dest)2380 MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest)
2381 {
2382     Register base = dest.base;
2383     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
2384 
2385     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2386 
2387     if (dest.offset != 0) {
2388         ma_add(base, Imm32(dest.offset), scratch2);
2389         base = scratch2;
2390     }
2391     ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
2392 }
2393 
2394 void
store32_NoSecondScratch(Imm32 src,const Address & address)2395 MacroAssemblerARMCompat::store32_NoSecondScratch(Imm32 src, const Address& address)
2396 {
2397     // move32() needs to use the ScratchRegister internally, but there is no additional
2398     // scratch register available since this function forbids use of the second one.
2399     move32(src, ScratchRegister);
2400     storePtr(ScratchRegister, address);
2401 }
2402 
2403 template <typename T>
2404 void
storePtr(ImmWord imm,T address)2405 MacroAssemblerARMCompat::storePtr(ImmWord imm, T address)
2406 {
2407     ScratchRegisterScope scratch(asMasm());
2408     movePtr(imm, scratch);
2409     storePtr(scratch, address);
2410 }
2411 
2412 template void MacroAssemblerARMCompat::storePtr<Address>(ImmWord imm, Address address);
2413 template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmWord imm, BaseIndex address);
2414 
2415 template <typename T>
2416 void
storePtr(ImmPtr imm,T address)2417 MacroAssemblerARMCompat::storePtr(ImmPtr imm, T address)
2418 {
2419     storePtr(ImmWord(uintptr_t(imm.value)), address);
2420 }
2421 
2422 template void MacroAssemblerARMCompat::storePtr<Address>(ImmPtr imm, Address address);
2423 template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmPtr imm, BaseIndex address);
2424 
2425 template <typename T>
2426 void
storePtr(ImmGCPtr imm,T address)2427 MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, T address)
2428 {
2429     ScratchRegisterScope scratch(asMasm());
2430     movePtr(imm, scratch);
2431     storePtr(scratch, address);
2432 }
2433 
2434 template void MacroAssemblerARMCompat::storePtr<Address>(ImmGCPtr imm, Address address);
2435 template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
2436 
2437 void
storePtr(Register src,const Address & address)2438 MacroAssemblerARMCompat::storePtr(Register src, const Address& address)
2439 {
2440     ma_str(src, address);
2441 }
2442 
2443 void
storePtr(Register src,const BaseIndex & address)2444 MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex& address)
2445 {
2446     store32(src, address);
2447 }
2448 
2449 void
storePtr(Register src,AbsoluteAddress dest)2450 MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest)
2451 {
2452     ScratchRegisterScope scratch(asMasm());
2453     movePtr(ImmWord(uintptr_t(dest.addr)), scratch);
2454     storePtr(src, Address(scratch, 0));
2455 }
2456 
2457 // Note: this function clobbers the input register.
2458 void
clampDoubleToUint8(FloatRegister input,Register output)2459 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
2460 {
2461     if (HasVFPv3()) {
2462         Label notSplit;
2463         {
2464             ScratchDoubleScope scratchDouble(*this);
2465             MOZ_ASSERT(input != scratchDouble);
2466             ma_vimm(0.5, scratchDouble);
2467 
2468             ma_vadd(input, scratchDouble, scratchDouble);
2469             // Convert the double into an unsigned fixed point value with 24 bits of
2470             // precision. The resulting number will look like 0xII.DDDDDD
2471             as_vcvtFixed(scratchDouble, false, 24, true);
2472         }
2473 
2474         // Move the fixed point value into an integer register.
2475         {
2476             ScratchFloat32Scope scratchFloat(*this);
2477             as_vxfer(output, InvalidReg, scratchFloat.uintOverlay(), FloatToCore);
2478         }
2479 
2480         // See if this value *might* have been an exact integer after adding
2481         // 0.5. This tests the 1/2 through 1/16,777,216th places, but 0.5 needs
2482         // to be tested out to the 1/140,737,488,355,328th place.
2483         ma_tst(output, Imm32(0x00ffffff));
2484         // Convert to a uint8 by shifting out all of the fraction bits.
2485         ma_lsr(Imm32(24), output, output);
2486         // If any of the bottom 24 bits were non-zero, then we're good, since
2487         // this number can't be exactly XX.0
2488         ma_b(&notSplit, NonZero);
2489         {
2490             ScratchRegisterScope scratch(*this);
2491             as_vxfer(scratch, InvalidReg, input, FloatToCore);
2492             ma_cmp(scratch, Imm32(0));
2493         }
2494         // If the lower 32 bits of the double were 0, then this was an exact number,
2495         // and it should be even.
2496         ma_bic(Imm32(1), output, LeaveCC, Zero);
2497         bind(&notSplit);
2498     } else {
2499         ScratchDoubleScope scratchDouble(*this);
2500         MOZ_ASSERT(input != scratchDouble);
2501         ma_vimm(0.5, scratchDouble);
2502 
2503         Label outOfRange;
2504         ma_vcmpz(input);
2505         // Do the add, in place so we can reference it later.
2506         ma_vadd(input, scratchDouble, input);
2507         // Do the conversion to an integer.
2508         as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input));
2509         // Copy the converted value out.
2510         as_vxfer(output, InvalidReg, scratchDouble, FloatToCore);
2511         as_vmrs(pc);
2512         ma_mov(Imm32(0), output, LeaveCC, Overflow);  // NaN => 0
2513         ma_b(&outOfRange, Overflow);  // NaN
2514         ma_cmp(output, Imm32(0xff));
2515         ma_mov(Imm32(0xff), output, LeaveCC, Above);
2516         ma_b(&outOfRange, Above);
2517         // Convert it back to see if we got the same value back.
2518         as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay());
2519         // Do the check.
2520         as_vcmp(scratchDouble, input);
2521         as_vmrs(pc);
2522         ma_bic(Imm32(1), output, LeaveCC, Zero);
2523         bind(&outOfRange);
2524     }
2525 }
2526 
2527 void
cmp32(Register lhs,Imm32 rhs)2528 MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs)
2529 {
2530     MOZ_ASSERT(lhs != ScratchRegister);
2531     ma_cmp(lhs, rhs);
2532 }
2533 
2534 void
cmp32(const Operand & lhs,Register rhs)2535 MacroAssemblerARMCompat::cmp32(const Operand& lhs, Register rhs)
2536 {
2537     ma_cmp(lhs.toReg(), rhs);
2538 }
2539 
2540 void
cmp32(const Operand & lhs,Imm32 rhs)2541 MacroAssemblerARMCompat::cmp32(const Operand& lhs, Imm32 rhs)
2542 {
2543     MOZ_ASSERT(lhs.toReg() != ScratchRegister);
2544     ma_cmp(lhs.toReg(), rhs);
2545 }
2546 
2547 void
cmp32(Register lhs,Register rhs)2548 MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs)
2549 {
2550     ma_cmp(lhs, rhs);
2551 }
2552 
2553 void
cmpPtr(Register lhs,ImmWord rhs)2554 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs)
2555 {
2556     MOZ_ASSERT(lhs != ScratchRegister);
2557     ma_cmp(lhs, Imm32(rhs.value));
2558 }
2559 
2560 void
cmpPtr(Register lhs,ImmPtr rhs)2561 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs)
2562 {
2563     return cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
2564 }
2565 
2566 void
cmpPtr(Register lhs,Register rhs)2567 MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs)
2568 {
2569     ma_cmp(lhs, rhs);
2570 }
2571 
2572 void
cmpPtr(Register lhs,ImmGCPtr rhs)2573 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs)
2574 {
2575     ma_cmp(lhs, rhs);
2576 }
2577 
2578 void
cmpPtr(Register lhs,Imm32 rhs)2579 MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs)
2580 {
2581     ma_cmp(lhs, rhs);
2582 }
2583 
2584 void
cmpPtr(const Address & lhs,Register rhs)2585 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs)
2586 {
2587     ScratchRegisterScope scratch(asMasm());
2588     loadPtr(lhs, scratch);
2589     cmpPtr(scratch, rhs);
2590 }
2591 
2592 void
cmpPtr(const Address & lhs,ImmWord rhs)2593 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs)
2594 {
2595     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2596     loadPtr(lhs, scratch2);
2597     ma_cmp(scratch2, Imm32(rhs.value));
2598 }
2599 
2600 void
cmpPtr(const Address & lhs,ImmPtr rhs)2601 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs)
2602 {
2603     cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
2604 }
2605 
2606 void
cmpPtr(const Address & lhs,ImmGCPtr rhs)2607 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs)
2608 {
2609     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2610     loadPtr(lhs, scratch2);
2611     ma_cmp(scratch2, rhs);
2612 }
2613 
2614 void
cmpPtr(const Address & lhs,Imm32 rhs)2615 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs)
2616 {
2617     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
2618     loadPtr(lhs, scratch2);
2619     ma_cmp(scratch2, rhs);
2620 }
2621 
2622 void
setStackArg(Register reg,uint32_t arg)2623 MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg)
2624 {
2625     ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg);
2626 
2627 }
2628 
2629 void
subPtr(Imm32 imm,const Register dest)2630 MacroAssemblerARMCompat::subPtr(Imm32 imm, const Register dest)
2631 {
2632     ma_sub(imm, dest);
2633 }
2634 
2635 void
subPtr(const Address & addr,const Register dest)2636 MacroAssemblerARMCompat::subPtr(const Address& addr, const Register dest)
2637 {
2638     ScratchRegisterScope scratch(asMasm());
2639     loadPtr(addr, scratch);
2640     ma_sub(scratch, dest);
2641 }
2642 
2643 void
subPtr(Register src,Register dest)2644 MacroAssemblerARMCompat::subPtr(Register src, Register dest)
2645 {
2646     ma_sub(src, dest);
2647 }
2648 
2649 void
subPtr(Register src,const Address & dest)2650 MacroAssemblerARMCompat::subPtr(Register src, const Address& dest)
2651 {
2652     ScratchRegisterScope scratch(asMasm());
2653     loadPtr(dest, scratch);
2654     ma_sub(src, scratch);
2655     storePtr(scratch, dest);
2656 }
2657 
2658 void
addPtr(Imm32 imm,const Register dest)2659 MacroAssemblerARMCompat::addPtr(Imm32 imm, const Register dest)
2660 {
2661     ma_add(imm, dest);
2662 }
2663 
2664 void
addPtr(Imm32 imm,const Address & dest)2665 MacroAssemblerARMCompat::addPtr(Imm32 imm, const Address& dest)
2666 {
2667     ScratchRegisterScope scratch(asMasm());
2668     loadPtr(dest, scratch);
2669     addPtr(imm, scratch);
2670     storePtr(scratch, dest);
2671 }
2672 
2673 void
compareDouble(FloatRegister lhs,FloatRegister rhs)2674 MacroAssemblerARMCompat::compareDouble(FloatRegister lhs, FloatRegister rhs)
2675 {
2676     // Compare the doubles, setting vector status flags.
2677     if (rhs.isMissing())
2678         ma_vcmpz(lhs);
2679     else
2680         ma_vcmp(lhs, rhs);
2681 
2682     // Move vector status bits to normal status flags.
2683     as_vmrs(pc);
2684 }
2685 
2686 void
branchDouble(DoubleCondition cond,FloatRegister lhs,FloatRegister rhs,Label * label)2687 MacroAssemblerARMCompat::branchDouble(DoubleCondition cond, FloatRegister lhs,
2688                                       FloatRegister rhs, Label* label)
2689 {
2690     compareDouble(lhs, rhs);
2691 
2692     if (cond == DoubleNotEqual) {
2693         // Force the unordered cases not to jump.
2694         Label unordered;
2695         ma_b(&unordered, VFP_Unordered);
2696         ma_b(label, VFP_NotEqualOrUnordered);
2697         bind(&unordered);
2698         return;
2699     }
2700 
2701     if (cond == DoubleEqualOrUnordered) {
2702         ma_b(label, VFP_Unordered);
2703         ma_b(label, VFP_Equal);
2704         return;
2705     }
2706 
2707     ma_b(label, ConditionFromDoubleCondition(cond));
2708 }
2709 
2710 void
compareFloat(FloatRegister lhs,FloatRegister rhs)2711 MacroAssemblerARMCompat::compareFloat(FloatRegister lhs, FloatRegister rhs)
2712 {
2713     // Compare the doubles, setting vector status flags.
2714     if (rhs.isMissing())
2715         as_vcmpz(VFPRegister(lhs).singleOverlay());
2716     else
2717         as_vcmp(VFPRegister(lhs).singleOverlay(), VFPRegister(rhs).singleOverlay());
2718 
2719     // Move vector status bits to normal status flags.
2720     as_vmrs(pc);
2721 }
2722 
2723 void
branchFloat(DoubleCondition cond,FloatRegister lhs,FloatRegister rhs,Label * label)2724 MacroAssemblerARMCompat::branchFloat(DoubleCondition cond, FloatRegister lhs,
2725                                      FloatRegister rhs, Label* label)
2726 {
2727     compareFloat(lhs, rhs);
2728 
2729     if (cond == DoubleNotEqual) {
2730         // Force the unordered cases not to jump.
2731         Label unordered;
2732         ma_b(&unordered, VFP_Unordered);
2733         ma_b(label, VFP_NotEqualOrUnordered);
2734         bind(&unordered);
2735         return;
2736     }
2737 
2738     if (cond == DoubleEqualOrUnordered) {
2739         ma_b(label, VFP_Unordered);
2740         ma_b(label, VFP_Equal);
2741         return;
2742     }
2743 
2744     ma_b(label, ConditionFromDoubleCondition(cond));
2745 }
2746 
2747 Assembler::Condition
testInt32(Assembler::Condition cond,const ValueOperand & value)2748 MacroAssemblerARMCompat::testInt32(Assembler::Condition cond, const ValueOperand& value)
2749 {
2750     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2751     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_INT32));
2752     return cond;
2753 }
2754 
2755 Assembler::Condition
testBoolean(Assembler::Condition cond,const ValueOperand & value)2756 MacroAssemblerARMCompat::testBoolean(Assembler::Condition cond, const ValueOperand& value)
2757 {
2758     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2759     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN));
2760     return cond;
2761 }
2762 
2763 Assembler::Condition
testDouble(Assembler::Condition cond,const ValueOperand & value)2764 MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const ValueOperand& value)
2765 {
2766     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2767     Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
2768     ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR));
2769     return actual;
2770 }
2771 
2772 Assembler::Condition
testNull(Assembler::Condition cond,const ValueOperand & value)2773 MacroAssemblerARMCompat::testNull(Assembler::Condition cond, const ValueOperand& value)
2774 {
2775     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2776     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL));
2777     return cond;
2778 }
2779 
2780 Assembler::Condition
testUndefined(Assembler::Condition cond,const ValueOperand & value)2781 MacroAssemblerARMCompat::testUndefined(Assembler::Condition cond, const ValueOperand& value)
2782 {
2783     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2784     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED));
2785     return cond;
2786 }
2787 
2788 Assembler::Condition
testString(Assembler::Condition cond,const ValueOperand & value)2789 MacroAssemblerARMCompat::testString(Assembler::Condition cond, const ValueOperand& value)
2790 {
2791     return testString(cond, value.typeReg());
2792 }
2793 
2794 Assembler::Condition
testSymbol(Assembler::Condition cond,const ValueOperand & value)2795 MacroAssemblerARMCompat::testSymbol(Assembler::Condition cond, const ValueOperand& value)
2796 {
2797     return testSymbol(cond, value.typeReg());
2798 }
2799 
2800 Assembler::Condition
testObject(Assembler::Condition cond,const ValueOperand & value)2801 MacroAssemblerARMCompat::testObject(Assembler::Condition cond, const ValueOperand& value)
2802 {
2803     return testObject(cond, value.typeReg());
2804 }
2805 
2806 Assembler::Condition
testNumber(Assembler::Condition cond,const ValueOperand & value)2807 MacroAssemblerARMCompat::testNumber(Assembler::Condition cond, const ValueOperand& value)
2808 {
2809     return testNumber(cond, value.typeReg());
2810 }
2811 
2812 Assembler::Condition
testMagic(Assembler::Condition cond,const ValueOperand & value)2813 MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const ValueOperand& value)
2814 {
2815     return testMagic(cond, value.typeReg());
2816 }
2817 
2818 Assembler::Condition
testPrimitive(Assembler::Condition cond,const ValueOperand & value)2819 MacroAssemblerARMCompat::testPrimitive(Assembler::Condition cond, const ValueOperand& value)
2820 {
2821     return testPrimitive(cond, value.typeReg());
2822 }
2823 
2824 // Register-based tests.
2825 Assembler::Condition
testInt32(Assembler::Condition cond,Register tag)2826 MacroAssemblerARMCompat::testInt32(Assembler::Condition cond, Register tag)
2827 {
2828     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2829     ma_cmp(tag, ImmTag(JSVAL_TAG_INT32));
2830     return cond;
2831 }
2832 
2833 Assembler::Condition
testBoolean(Assembler::Condition cond,Register tag)2834 MacroAssemblerARMCompat::testBoolean(Assembler::Condition cond, Register tag)
2835 {
2836     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2837     ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN));
2838     return cond;
2839 }
2840 
2841 Assembler::Condition
testNull(Assembler::Condition cond,Register tag)2842 MacroAssemblerARMCompat::testNull(Assembler::Condition cond, Register tag)
2843 {
2844     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2845     ma_cmp(tag, ImmTag(JSVAL_TAG_NULL));
2846     return cond;
2847 }
2848 
2849 Assembler::Condition
testUndefined(Assembler::Condition cond,Register tag)2850 MacroAssemblerARMCompat::testUndefined(Assembler::Condition cond, Register tag)
2851 {
2852     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2853     ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED));
2854     return cond;
2855 }
2856 
2857 Assembler::Condition
testString(Assembler::Condition cond,Register tag)2858 MacroAssemblerARMCompat::testString(Assembler::Condition cond, Register tag)
2859 {
2860     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2861     ma_cmp(tag, ImmTag(JSVAL_TAG_STRING));
2862     return cond;
2863 }
2864 
2865 Assembler::Condition
testSymbol(Assembler::Condition cond,Register tag)2866 MacroAssemblerARMCompat::testSymbol(Assembler::Condition cond, Register tag)
2867 {
2868     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2869     ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL));
2870     return cond;
2871 }
2872 
2873 Assembler::Condition
testObject(Assembler::Condition cond,Register tag)2874 MacroAssemblerARMCompat::testObject(Assembler::Condition cond, Register tag)
2875 {
2876     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2877     ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT));
2878     return cond;
2879 }
2880 
2881 Assembler::Condition
testMagic(Assembler::Condition cond,Register tag)2882 MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, Register tag)
2883 {
2884     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2885     ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC));
2886     return cond;
2887 }
2888 
2889 Assembler::Condition
testPrimitive(Assembler::Condition cond,Register tag)2890 MacroAssemblerARMCompat::testPrimitive(Assembler::Condition cond, Register tag)
2891 {
2892     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2893     ma_cmp(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET));
2894     return cond == Equal ? Below : AboveOrEqual;
2895 }
2896 
2897 Assembler::Condition
testGCThing(Assembler::Condition cond,const Address & address)2898 MacroAssemblerARMCompat::testGCThing(Assembler::Condition cond, const Address& address)
2899 {
2900     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2901     ScratchRegisterScope scratch(asMasm());
2902     extractTag(address, scratch);
2903     ma_cmp(scratch, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
2904     return cond == Equal ? AboveOrEqual : Below;
2905 }
2906 
2907 Assembler::Condition
testMagic(Assembler::Condition cond,const Address & address)2908 MacroAssemblerARMCompat::testMagic(Assembler::Condition cond, const Address& address)
2909 {
2910     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2911     ScratchRegisterScope scratch(asMasm());
2912     extractTag(address, scratch);
2913     ma_cmp(scratch, ImmTag(JSVAL_TAG_MAGIC));
2914     return cond;
2915 }
2916 
2917 Assembler::Condition
testInt32(Assembler::Condition cond,const Address & address)2918 MacroAssemblerARMCompat::testInt32(Assembler::Condition cond, const Address& address)
2919 {
2920     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2921     ScratchRegisterScope scratch(asMasm());
2922     extractTag(address, scratch);
2923     ma_cmp(scratch, ImmTag(JSVAL_TAG_INT32));
2924     return cond;
2925 }
2926 
2927 Assembler::Condition
testDouble(Condition cond,const Address & address)2928 MacroAssemblerARMCompat::testDouble(Condition cond, const Address& address)
2929 {
2930     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2931     ScratchRegisterScope scratch(asMasm());
2932     extractTag(address, scratch);
2933     return testDouble(cond, scratch);
2934 }
2935 
2936 Assembler::Condition
testBoolean(Condition cond,const Address & address)2937 MacroAssemblerARMCompat::testBoolean(Condition cond, const Address& address)
2938 {
2939     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2940     ScratchRegisterScope scratch(asMasm());
2941     extractTag(address, scratch);
2942     return testBoolean(cond, scratch);
2943 }
2944 
2945 Assembler::Condition
testNull(Condition cond,const Address & address)2946 MacroAssemblerARMCompat::testNull(Condition cond, const Address& address)
2947 {
2948     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2949     ScratchRegisterScope scratch(asMasm());
2950     extractTag(address, scratch);
2951     return testNull(cond, scratch);
2952 }
2953 
2954 Assembler::Condition
testUndefined(Condition cond,const Address & address)2955 MacroAssemblerARMCompat::testUndefined(Condition cond, const Address& address)
2956 {
2957     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2958     ScratchRegisterScope scratch(asMasm());
2959     extractTag(address, scratch);
2960     return testUndefined(cond, scratch);
2961 }
2962 
2963 Assembler::Condition
testString(Condition cond,const Address & address)2964 MacroAssemblerARMCompat::testString(Condition cond, const Address& address)
2965 {
2966     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2967     ScratchRegisterScope scratch(asMasm());
2968     extractTag(address, scratch);
2969     return testString(cond, scratch);
2970 }
2971 
2972 Assembler::Condition
testSymbol(Condition cond,const Address & address)2973 MacroAssemblerARMCompat::testSymbol(Condition cond, const Address& address)
2974 {
2975     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2976     ScratchRegisterScope scratch(asMasm());
2977     extractTag(address, scratch);
2978     return testSymbol(cond, scratch);
2979 }
2980 
2981 Assembler::Condition
testObject(Condition cond,const Address & address)2982 MacroAssemblerARMCompat::testObject(Condition cond, const Address& address)
2983 {
2984     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2985     ScratchRegisterScope scratch(asMasm());
2986     extractTag(address, scratch);
2987     return testObject(cond, scratch);
2988 }
2989 
2990 Assembler::Condition
testNumber(Condition cond,const Address & address)2991 MacroAssemblerARMCompat::testNumber(Condition cond, const Address& address)
2992 {
2993     MOZ_ASSERT(cond == Equal || cond == NotEqual);
2994     ScratchRegisterScope scratch(asMasm());
2995     extractTag(address, scratch);
2996     return testNumber(cond, scratch);
2997 }
2998 
2999 Assembler::Condition
testDouble(Condition cond,Register tag)3000 MacroAssemblerARMCompat::testDouble(Condition cond, Register tag)
3001 {
3002     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
3003     Condition actual = (cond == Equal) ? Below : AboveOrEqual;
3004     ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR));
3005     return actual;
3006 }
3007 
3008 Assembler::Condition
testNumber(Condition cond,Register tag)3009 MacroAssemblerARMCompat::testNumber(Condition cond, Register tag)
3010 {
3011     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3012     ma_cmp(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
3013     return cond == Equal ? BelowOrEqual : Above;
3014 }
3015 
3016 Assembler::Condition
testUndefined(Condition cond,const BaseIndex & src)3017 MacroAssemblerARMCompat::testUndefined(Condition cond, const BaseIndex& src)
3018 {
3019     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3020     ScratchRegisterScope scratch(asMasm());
3021     extractTag(src, scratch);
3022     ma_cmp(scratch, ImmTag(JSVAL_TAG_UNDEFINED));
3023     return cond;
3024 }
3025 
3026 Assembler::Condition
testNull(Condition cond,const BaseIndex & src)3027 MacroAssemblerARMCompat::testNull(Condition cond, const BaseIndex& src)
3028 {
3029     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3030     ScratchRegisterScope scratch(asMasm());
3031     extractTag(src, scratch);
3032     ma_cmp(scratch, ImmTag(JSVAL_TAG_NULL));
3033     return cond;
3034 }
3035 
3036 Assembler::Condition
testBoolean(Condition cond,const BaseIndex & src)3037 MacroAssemblerARMCompat::testBoolean(Condition cond, const BaseIndex& src)
3038 {
3039     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3040     ScratchRegisterScope scratch(asMasm());
3041     extractTag(src, scratch);
3042     ma_cmp(scratch, ImmTag(JSVAL_TAG_BOOLEAN));
3043     return cond;
3044 }
3045 
3046 Assembler::Condition
testString(Condition cond,const BaseIndex & src)3047 MacroAssemblerARMCompat::testString(Condition cond, const BaseIndex& src)
3048 {
3049     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3050     ScratchRegisterScope scratch(asMasm());
3051     extractTag(src, scratch);
3052     ma_cmp(scratch, ImmTag(JSVAL_TAG_STRING));
3053     return cond;
3054 }
3055 
3056 Assembler::Condition
testSymbol(Condition cond,const BaseIndex & src)3057 MacroAssemblerARMCompat::testSymbol(Condition cond, const BaseIndex& src)
3058 {
3059     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3060     ScratchRegisterScope scratch(asMasm());
3061     extractTag(src, scratch);
3062     ma_cmp(scratch, ImmTag(JSVAL_TAG_SYMBOL));
3063     return cond;
3064 }
3065 
3066 Assembler::Condition
testInt32(Condition cond,const BaseIndex & src)3067 MacroAssemblerARMCompat::testInt32(Condition cond, const BaseIndex& src)
3068 {
3069     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3070     ScratchRegisterScope scratch(asMasm());
3071     extractTag(src, scratch);
3072     ma_cmp(scratch, ImmTag(JSVAL_TAG_INT32));
3073     return cond;
3074 }
3075 
3076 Assembler::Condition
testObject(Condition cond,const BaseIndex & src)3077 MacroAssemblerARMCompat::testObject(Condition cond, const BaseIndex& src)
3078 {
3079     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3080     ScratchRegisterScope scratch(asMasm());
3081     extractTag(src, scratch);
3082     ma_cmp(scratch, ImmTag(JSVAL_TAG_OBJECT));
3083     return cond;
3084 }
3085 
3086 Assembler::Condition
testDouble(Condition cond,const BaseIndex & src)3087 MacroAssemblerARMCompat::testDouble(Condition cond, const BaseIndex& src)
3088 {
3089     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3090     Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
3091     ScratchRegisterScope scratch(asMasm());
3092     extractTag(src, scratch);
3093     ma_cmp(scratch, ImmTag(JSVAL_TAG_CLEAR));
3094     return actual;
3095 }
3096 
3097 Assembler::Condition
testMagic(Condition cond,const BaseIndex & address)3098 MacroAssemblerARMCompat::testMagic(Condition cond, const BaseIndex& address)
3099 {
3100     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3101     ScratchRegisterScope scratch(asMasm());
3102     extractTag(address, scratch);
3103     ma_cmp(scratch, ImmTag(JSVAL_TAG_MAGIC));
3104     return cond;
3105 }
3106 
3107 Assembler::Condition
testGCThing(Condition cond,const BaseIndex & address)3108 MacroAssemblerARMCompat::testGCThing(Condition cond, const BaseIndex& address)
3109 {
3110     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3111     ScratchRegisterScope scratch(asMasm());
3112     extractTag(address, scratch);
3113     ma_cmp(scratch, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
3114     return cond == Equal ? AboveOrEqual : Below;
3115 }
3116 
3117 void
branchTestValue(Condition cond,const ValueOperand & value,const Value & v,Label * label)3118 MacroAssemblerARMCompat::branchTestValue(Condition cond, const ValueOperand& value,
3119                                          const Value& v, Label* label)
3120 {
3121     // If cond == NotEqual, branch when a.payload != b.payload || a.tag !=
3122     // b.tag. If the payloads are equal, compare the tags. If the payloads are
3123     // not equal, short circuit true (NotEqual).
3124     //
3125     // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag.
3126     // If the payloads are equal, compare the tags. If the payloads are not
3127     // equal, short circuit false (NotEqual).
3128     jsval_layout jv = JSVAL_TO_IMPL(v);
3129     if (v.isMarkable())
3130         ma_cmp(value.payloadReg(), ImmGCPtr(reinterpret_cast<gc::Cell*>(v.toGCThing())));
3131     else
3132         ma_cmp(value.payloadReg(), Imm32(jv.s.payload.i32));
3133     ma_cmp(value.typeReg(), Imm32(jv.s.tag), Equal);
3134     ma_b(label, cond);
3135 }
3136 
3137 void
branchTestValue(Condition cond,const Address & valaddr,const ValueOperand & value,Label * label)3138 MacroAssemblerARMCompat::branchTestValue(Condition cond, const Address& valaddr,
3139                                          const ValueOperand& value, Label* label)
3140 {
3141     MOZ_ASSERT(cond == Equal || cond == NotEqual);
3142     ScratchRegisterScope scratch(asMasm());
3143 
3144     // Check payload before tag, since payload is more likely to differ.
3145     if (cond == NotEqual) {
3146         ma_ldr(ToPayload(valaddr), scratch);
3147         branchPtr(NotEqual, scratch, value.payloadReg(), label);
3148 
3149         ma_ldr(ToType(valaddr), scratch);
3150         branchPtr(NotEqual, scratch, value.typeReg(), label);
3151     } else {
3152         Label fallthrough;
3153 
3154         ma_ldr(ToPayload(valaddr), scratch);
3155         branchPtr(NotEqual, scratch, value.payloadReg(), &fallthrough);
3156 
3157         ma_ldr(ToType(valaddr), scratch);
3158         branchPtr(Equal, scratch, value.typeReg(), label);
3159 
3160         bind(&fallthrough);
3161     }
3162 }
3163 
3164 // Unboxing code.
3165 void
unboxNonDouble(const ValueOperand & operand,Register dest)3166 MacroAssemblerARMCompat::unboxNonDouble(const ValueOperand& operand, Register dest)
3167 {
3168     if (operand.payloadReg() != dest)
3169         ma_mov(operand.payloadReg(), dest);
3170 }
3171 
3172 void
unboxNonDouble(const Address & src,Register dest)3173 MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest)
3174 {
3175     ma_ldr(ToPayload(src), dest);
3176 }
3177 
3178 void
unboxNonDouble(const BaseIndex & src,Register dest)3179 MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src, Register dest)
3180 {
3181     ScratchRegisterScope scratch(asMasm());
3182     ma_alu(src.base, lsl(src.index, src.scale), scratch, OpAdd);
3183     ma_ldr(Address(scratch, src.offset), dest);
3184 }
3185 
3186 void
unboxDouble(const ValueOperand & operand,FloatRegister dest)3187 MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand, FloatRegister dest)
3188 {
3189     MOZ_ASSERT(dest.isDouble());
3190     as_vxfer(operand.payloadReg(), operand.typeReg(),
3191              VFPRegister(dest), CoreToFloat);
3192 }
3193 
3194 void
unboxDouble(const Address & src,FloatRegister dest)3195 MacroAssemblerARMCompat::unboxDouble(const Address& src, FloatRegister dest)
3196 {
3197     MOZ_ASSERT(dest.isDouble());
3198     ma_vldr(src, dest);
3199 }
3200 
3201 void
unboxValue(const ValueOperand & src,AnyRegister dest)3202 MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
3203 {
3204     if (dest.isFloat()) {
3205         Label notInt32, end;
3206         branchTestInt32(Assembler::NotEqual, src, &notInt32);
3207         convertInt32ToDouble(src.payloadReg(), dest.fpu());
3208         ma_b(&end);
3209         bind(&notInt32);
3210         unboxDouble(src, dest.fpu());
3211         bind(&end);
3212     } else if (src.payloadReg() != dest.gpr()) {
3213         as_mov(dest.gpr(), O2Reg(src.payloadReg()));
3214     }
3215 }
3216 
3217 void
unboxPrivate(const ValueOperand & src,Register dest)3218 MacroAssemblerARMCompat::unboxPrivate(const ValueOperand& src, Register dest)
3219 {
3220     ma_mov(src.payloadReg(), dest);
3221 }
3222 
3223 void
boxDouble(FloatRegister src,const ValueOperand & dest)3224 MacroAssemblerARMCompat::boxDouble(FloatRegister src, const ValueOperand& dest)
3225 {
3226     as_vxfer(dest.payloadReg(), dest.typeReg(), VFPRegister(src), FloatToCore);
3227 }
3228 
3229 void
boxNonDouble(JSValueType type,Register src,const ValueOperand & dest)3230 MacroAssemblerARMCompat::boxNonDouble(JSValueType type, Register src, const ValueOperand& dest) {
3231     if (src != dest.payloadReg())
3232         ma_mov(src, dest.payloadReg());
3233     ma_mov(ImmType(type), dest.typeReg());
3234 }
3235 
3236 void
boolValueToDouble(const ValueOperand & operand,FloatRegister dest)3237 MacroAssemblerARMCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest)
3238 {
3239     VFPRegister d = VFPRegister(dest);
3240     ma_vimm(1.0, dest);
3241     ma_cmp(operand.payloadReg(), Imm32(0));
3242     // If the source is 0, then subtract the dest from itself, producing 0.
3243     as_vsub(d, d, d, Equal);
3244 }
3245 
3246 void
int32ValueToDouble(const ValueOperand & operand,FloatRegister dest)3247 MacroAssemblerARMCompat::int32ValueToDouble(const ValueOperand& operand, FloatRegister dest)
3248 {
3249     VFPRegister vfpdest = VFPRegister(dest);
3250     ScratchFloat32Scope scratch(asMasm());
3251 
3252     // Transfer the integral value to a floating point register.
3253     as_vxfer(operand.payloadReg(), InvalidReg, scratch.sintOverlay(), CoreToFloat);
3254     // Convert the value to a double.
3255     as_vcvt(vfpdest, scratch.sintOverlay());
3256 }
3257 
3258 void
boolValueToFloat32(const ValueOperand & operand,FloatRegister dest)3259 MacroAssemblerARMCompat::boolValueToFloat32(const ValueOperand& operand, FloatRegister dest)
3260 {
3261     VFPRegister d = VFPRegister(dest).singleOverlay();
3262     ma_vimm_f32(1.0, dest);
3263     ma_cmp(operand.payloadReg(), Imm32(0));
3264     // If the source is 0, then subtract the dest from itself, producing 0.
3265     as_vsub(d, d, d, Equal);
3266 }
3267 
3268 void
int32ValueToFloat32(const ValueOperand & operand,FloatRegister dest)3269 MacroAssemblerARMCompat::int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest)
3270 {
3271     // Transfer the integral value to a floating point register.
3272     VFPRegister vfpdest = VFPRegister(dest).singleOverlay();
3273     as_vxfer(operand.payloadReg(), InvalidReg,
3274              vfpdest.sintOverlay(), CoreToFloat);
3275     // Convert the value to a float.
3276     as_vcvt(vfpdest, vfpdest.sintOverlay());
3277 }
3278 
3279 void
loadConstantFloat32(float f,FloatRegister dest)3280 MacroAssemblerARMCompat::loadConstantFloat32(float f, FloatRegister dest)
3281 {
3282     ma_vimm_f32(f, dest);
3283 }
3284 
3285 void
loadInt32OrDouble(const Address & src,FloatRegister dest)3286 MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
3287 {
3288     Label notInt32, end;
3289 
3290     // If it's an int, convert to a double.
3291     {
3292         ScratchRegisterScope scratch(asMasm());
3293 
3294         ma_ldr(ToType(src), scratch);
3295         branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
3296         ma_ldr(ToPayload(src), scratch);
3297         convertInt32ToDouble(scratch, dest);
3298         ma_b(&end);
3299     }
3300 
3301     // Not an int, just load as double.
3302     bind(&notInt32);
3303     ma_vldr(src, dest);
3304     bind(&end);
3305 }
3306 
3307 void
loadInt32OrDouble(Register base,Register index,FloatRegister dest,int32_t shift)3308 MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index,
3309                                            FloatRegister dest, int32_t shift)
3310 {
3311     Label notInt32, end;
3312 
3313     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
3314 
3315     ScratchRegisterScope scratch(asMasm());
3316 
3317     // If it's an int, convert it to double.
3318     ma_alu(base, lsl(index, shift), scratch, OpAdd);
3319 
3320     // Since we only have one scratch register, we need to stomp over it with
3321     // the tag.
3322     ma_ldr(Address(scratch, NUNBOX32_TYPE_OFFSET), scratch);
3323     branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
3324 
3325     // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided
3326     ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch);
3327     convertInt32ToDouble(scratch, dest);
3328     ma_b(&end);
3329 
3330     // Not an int, just load as double.
3331     bind(&notInt32);
3332     // First, recompute the offset that had been stored in the scratch register
3333     // since the scratch register was overwritten loading in the type.
3334     ma_alu(base, lsl(index, shift), scratch, OpAdd);
3335     ma_vldr(Address(scratch, 0), dest);
3336     bind(&end);
3337 }
3338 
3339 void
loadConstantDouble(double dp,FloatRegister dest)3340 MacroAssemblerARMCompat::loadConstantDouble(double dp, FloatRegister dest)
3341 {
3342     as_FImm64Pool(dest, dp);
3343 }
3344 
3345 // Treat the value as a boolean, and set condition codes accordingly.
3346 Assembler::Condition
testInt32Truthy(bool truthy,const ValueOperand & operand)3347 MacroAssemblerARMCompat::testInt32Truthy(bool truthy, const ValueOperand& operand)
3348 {
3349     ma_tst(operand.payloadReg(), operand.payloadReg());
3350     return truthy ? NonZero : Zero;
3351 }
3352 
3353 Assembler::Condition
testBooleanTruthy(bool truthy,const ValueOperand & operand)3354 MacroAssemblerARMCompat::testBooleanTruthy(bool truthy, const ValueOperand& operand)
3355 {
3356     ma_tst(operand.payloadReg(), operand.payloadReg());
3357     return truthy ? NonZero : Zero;
3358 }
3359 
3360 Assembler::Condition
testDoubleTruthy(bool truthy,FloatRegister reg)3361 MacroAssemblerARMCompat::testDoubleTruthy(bool truthy, FloatRegister reg)
3362 {
3363     as_vcmpz(VFPRegister(reg));
3364     as_vmrs(pc);
3365     as_cmp(r0, O2Reg(r0), Overflow);
3366     return truthy ? NonZero : Zero;
3367 }
3368 
3369 Register
extractObject(const Address & address,Register scratch)3370 MacroAssemblerARMCompat::extractObject(const Address& address, Register scratch)
3371 {
3372     ma_ldr(ToPayload(address), scratch);
3373     return scratch;
3374 }
3375 
3376 Register
extractTag(const Address & address,Register scratch)3377 MacroAssemblerARMCompat::extractTag(const Address& address, Register scratch)
3378 {
3379     ma_ldr(ToType(address), scratch);
3380     return scratch;
3381 }
3382 
3383 Register
extractTag(const BaseIndex & address,Register scratch)3384 MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch)
3385 {
3386     ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC);
3387     return extractTag(Address(scratch, address.offset), scratch);
3388 }
3389 
3390 template <typename T>
3391 void
storeUnboxedValue(ConstantOrRegister value,MIRType valueType,const T & dest,MIRType slotType)3392 MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
3393                                            const T& dest, MIRType slotType)
3394 {
3395     if (valueType == MIRType_Double) {
3396         storeDouble(value.reg().typedReg().fpu(), dest);
3397         return;
3398     }
3399 
3400     // Store the type tag if needed.
3401     if (valueType != slotType)
3402         storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
3403 
3404     // Store the payload.
3405     if (value.constant())
3406         storePayload(value.value(), dest);
3407     else
3408         storePayload(value.reg().typedReg().gpr(), dest);
3409 }
3410 
3411 template void
3412 MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
3413                                            const Address& dest, MIRType slotType);
3414 
3415 template void
3416 MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
3417                                            const BaseIndex& dest, MIRType slotType);
3418 
3419 
3420 void
branchTest64(Condition cond,Register64 lhs,Register64 rhs,Register temp,Label * label)3421 MacroAssemblerARMCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
3422                                       Register temp, Label* label)
3423 {
3424     if (cond == Assembler::Zero) {
3425         MOZ_ASSERT(lhs.low == rhs.low);
3426         MOZ_ASSERT(lhs.high == rhs.high);
3427         mov(lhs.low, ScratchRegister);
3428         asMasm().or32(lhs.high, ScratchRegister);
3429         branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
3430     } else {
3431         MOZ_CRASH("Unsupported condition");
3432     }
3433 }
3434 
3435 void
moveValue(const Value & val,Register type,Register data)3436 MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data)
3437 {
3438     jsval_layout jv = JSVAL_TO_IMPL(val);
3439     ma_mov(Imm32(jv.s.tag), type);
3440     if (val.isMarkable())
3441         ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), data);
3442     else
3443         ma_mov(Imm32(jv.s.payload.i32), data);
3444 }
3445 
3446 void
moveValue(const Value & val,const ValueOperand & dest)3447 MacroAssemblerARMCompat::moveValue(const Value& val, const ValueOperand& dest)
3448 {
3449     moveValue(val, dest.typeReg(), dest.payloadReg());
3450 }
3451 
3452 /////////////////////////////////////////////////////////////////
3453 // X86/X64-common (ARM too now) interface.
3454 /////////////////////////////////////////////////////////////////
3455 void
storeValue(ValueOperand val,const Address & dst)3456 MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst)
3457 {
3458     ma_str(val.payloadReg(), ToPayload(dst));
3459     ma_str(val.typeReg(), ToType(dst));
3460 }
3461 
3462 void
storeValue(ValueOperand val,const BaseIndex & dest)3463 MacroAssemblerARMCompat::storeValue(ValueOperand val, const BaseIndex& dest)
3464 {
3465     ScratchRegisterScope scratch(asMasm());
3466 
3467     if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) {
3468         Register tmpIdx;
3469         if (dest.offset == 0) {
3470             if (dest.scale == TimesOne) {
3471                 tmpIdx = dest.index;
3472             } else {
3473                 ma_lsl(Imm32(dest.scale), dest.index, scratch);
3474                 tmpIdx = scratch;
3475             }
3476             ma_strd(val.payloadReg(), val.typeReg(), EDtrAddr(dest.base, EDtrOffReg(tmpIdx)));
3477         } else {
3478             ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
3479             ma_strd(val.payloadReg(), val.typeReg(),
3480                     EDtrAddr(scratch, EDtrOffImm(dest.offset)));
3481         }
3482     } else {
3483         ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
3484         storeValue(val, Address(scratch, dest.offset));
3485     }
3486 }
3487 
3488 void
loadValue(const BaseIndex & addr,ValueOperand val)3489 MacroAssemblerARMCompat::loadValue(const BaseIndex& addr, ValueOperand val)
3490 {
3491     ScratchRegisterScope scratch(asMasm());
3492 
3493     if (isValueDTRDCandidate(val) && Abs(addr.offset) <= 255) {
3494         Register tmpIdx;
3495         if (addr.offset == 0) {
3496             if (addr.scale == TimesOne) {
3497                 tmpIdx = addr.index;
3498             } else {
3499                 ma_lsl(Imm32(addr.scale), addr.index, scratch);
3500                 tmpIdx = scratch;
3501             }
3502             ma_ldrd(EDtrAddr(addr.base, EDtrOffReg(tmpIdx)), val.payloadReg(), val.typeReg());
3503         } else {
3504             ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd);
3505             ma_ldrd(EDtrAddr(scratch, EDtrOffImm(addr.offset)),
3506                     val.payloadReg(), val.typeReg());
3507         }
3508     } else {
3509         ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd);
3510         loadValue(Address(scratch, addr.offset), val);
3511     }
3512 }
3513 
3514 void
loadValue(Address src,ValueOperand val)3515 MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val)
3516 {
3517     Address payload = ToPayload(src);
3518     Address type = ToType(src);
3519 
3520     // TODO: copy this code into a generic function that acts on all sequences
3521     // of memory accesses
3522     if (isValueDTRDCandidate(val)) {
3523         // If the value we want is in two consecutive registers starting with an
3524         // even register, they can be combined as a single ldrd.
3525         int offset = src.offset;
3526         if (offset < 256 && offset > -256) {
3527             ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), val.payloadReg(), val.typeReg());
3528             return;
3529         }
3530     }
3531     // If the value is lower than the type, then we may be able to use an ldm
3532     // instruction.
3533 
3534     if (val.payloadReg().code() < val.typeReg().code()) {
3535         if (src.offset <= 4 && src.offset >= -8 && (src.offset & 3) == 0) {
3536             // Turns out each of the 4 value -8, -4, 0, 4 corresponds exactly
3537             // with one of LDM{DB, DA, IA, IB}
3538             DTMMode mode;
3539             switch (src.offset) {
3540               case -8: mode = DB; break;
3541               case -4: mode = DA; break;
3542               case  0: mode = IA; break;
3543               case  4: mode = IB; break;
3544               default: MOZ_CRASH("Bogus Offset for LoadValue as DTM");
3545             }
3546             startDataTransferM(IsLoad, src.base, mode);
3547             transferReg(val.payloadReg());
3548             transferReg(val.typeReg());
3549             finishDataTransfer();
3550             return;
3551         }
3552     }
3553     // Ensure that loading the payload does not erase the pointer to the Value
3554     // in memory.
3555     if (type.base != val.payloadReg()) {
3556         ma_ldr(payload, val.payloadReg());
3557         ma_ldr(type, val.typeReg());
3558     } else {
3559         ma_ldr(type, val.typeReg());
3560         ma_ldr(payload, val.payloadReg());
3561     }
3562 }
3563 
3564 void
tagValue(JSValueType type,Register payload,ValueOperand dest)3565 MacroAssemblerARMCompat::tagValue(JSValueType type, Register payload, ValueOperand dest)
3566 {
3567     MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
3568     if (payload != dest.payloadReg())
3569         ma_mov(payload, dest.payloadReg());
3570     ma_mov(ImmType(type), dest.typeReg());
3571 }
3572 
3573 void
pushValue(ValueOperand val)3574 MacroAssemblerARMCompat::pushValue(ValueOperand val)
3575 {
3576     ma_push(val.typeReg());
3577     ma_push(val.payloadReg());
3578 }
3579 
3580 void
pushValue(const Address & addr)3581 MacroAssemblerARMCompat::pushValue(const Address& addr)
3582 {
3583     ScratchRegisterScope scratch(asMasm());
3584     ma_ldr(ToType(addr), scratch);
3585     ma_push(scratch);
3586     ma_ldr(ToPayloadAfterStackPush(addr), scratch);
3587     ma_push(scratch);
3588 }
3589 
3590 void
popValue(ValueOperand val)3591 MacroAssemblerARMCompat::popValue(ValueOperand val)
3592 {
3593     ma_pop(val.payloadReg());
3594     ma_pop(val.typeReg());
3595 }
3596 
3597 void
storePayload(const Value & val,const Address & dest)3598 MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
3599 {
3600     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
3601 
3602     jsval_layout jv = JSVAL_TO_IMPL(val);
3603     if (val.isMarkable())
3604         ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch2);
3605     else
3606         ma_mov(Imm32(jv.s.payload.i32), scratch2);
3607     ma_str(scratch2, ToPayload(dest));
3608 }
3609 
3610 void
storePayload(Register src,const Address & dest)3611 MacroAssemblerARMCompat::storePayload(Register src, const Address& dest)
3612 {
3613     ma_str(src, ToPayload(dest));
3614 }
3615 
3616 void
storePayload(const Value & val,const BaseIndex & dest)3617 MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest)
3618 {
3619     unsigned shift = ScaleToShift(dest.scale);
3620 
3621     ScratchRegisterScope scratch(asMasm());
3622 
3623     jsval_layout jv = JSVAL_TO_IMPL(val);
3624     if (val.isMarkable())
3625         ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch);
3626     else
3627         ma_mov(Imm32(jv.s.payload.i32), scratch);
3628 
3629     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
3630     // << shift + imm] cannot be encoded into a single instruction, and cannot
3631     // be integrated into the as_dtr call.
3632     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
3633 
3634     // If an offset is used, modify the base so that a [base + index << shift]
3635     // instruction format can be used.
3636     if (dest.offset != 0)
3637         ma_add(dest.base, Imm32(dest.offset), dest.base);
3638 
3639     as_dtr(IsStore, 32, Offset, scratch,
3640            DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
3641 
3642     // Restore the original value of the base, if necessary.
3643     if (dest.offset != 0)
3644         ma_sub(dest.base, Imm32(dest.offset), dest.base);
3645 }
3646 
3647 void
storePayload(Register src,const BaseIndex & dest)3648 MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex& dest)
3649 {
3650     unsigned shift = ScaleToShift(dest.scale);
3651     MOZ_ASSERT(shift < 32);
3652 
3653     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
3654     // << shift + imm] cannot be encoded into a single instruction, and cannot
3655     // be integrated into the as_dtr call.
3656     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
3657 
3658     // Save/restore the base if the BaseIndex has an offset, as above.
3659     if (dest.offset != 0)
3660         ma_add(dest.base, Imm32(dest.offset), dest.base);
3661 
3662     // Technically, shift > -32 can be handle by changing LSL to ASR, but should
3663     // never come up, and this is one less code path to get wrong.
3664     as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
3665 
3666     if (dest.offset != 0)
3667         ma_sub(dest.base, Imm32(dest.offset), dest.base);
3668 }
3669 
3670 void
storeTypeTag(ImmTag tag,const Address & dest)3671 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest)
3672 {
3673     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
3674     ma_mov(tag, scratch2);
3675     ma_str(scratch2, ToType(dest));
3676 }
3677 
3678 void
storeTypeTag(ImmTag tag,const BaseIndex & dest)3679 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest)
3680 {
3681     Register base = dest.base;
3682     Register index = dest.index;
3683     unsigned shift = ScaleToShift(dest.scale);
3684     MOZ_ASSERT(base != ScratchRegister);
3685     MOZ_ASSERT(index != ScratchRegister);
3686 
3687     // A value needs to be store a value int base + index << shift + 4.
3688     // ARM cannot handle this in a single operand, so a temp register is
3689     // required. However, the scratch register is presently in use to hold the
3690     // immediate that is being stored into said memory location. Work around
3691     // this by modifying the base so the valid [base + index << shift] format
3692     // can be used, then restore it.
3693     ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
3694 
3695     ScratchRegisterScope scratch(asMasm());
3696     ma_mov(tag, scratch);
3697     ma_str(scratch, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
3698     ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
3699 }
3700 
3701 void
ma_call(ImmPtr dest)3702 MacroAssemblerARM::ma_call(ImmPtr dest)
3703 {
3704     RelocStyle rs;
3705     if (HasMOVWT())
3706         rs = L_MOVWT;
3707     else
3708         rs = L_LDR;
3709 
3710     ma_movPatchable(dest, CallReg, Always, rs);
3711     as_blx(CallReg);
3712 }
3713 
3714 void
breakpoint()3715 MacroAssemblerARMCompat::breakpoint()
3716 {
3717     as_bkpt();
3718 }
3719 
3720 void
simulatorStop(const char * msg)3721 MacroAssemblerARMCompat::simulatorStop(const char* msg)
3722 {
3723 #ifdef JS_SIMULATOR_ARM
3724     MOZ_ASSERT(sizeof(char*) == 4);
3725     writeInst(0xefffffff);
3726     writeInst((int)msg);
3727 #endif
3728 }
3729 
3730 void
ensureDouble(const ValueOperand & source,FloatRegister dest,Label * failure)3731 MacroAssemblerARMCompat::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
3732 {
3733     Label isDouble, done;
3734     branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
3735     branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
3736 
3737     convertInt32ToDouble(source.payloadReg(), dest);
3738     jump(&done);
3739 
3740     bind(&isDouble);
3741     unboxDouble(source, dest);
3742 
3743     bind(&done);
3744 }
3745 
3746 void
breakpoint(Condition cc)3747 MacroAssemblerARMCompat::breakpoint(Condition cc)
3748 {
3749     ma_ldr(DTRAddr(r12, DtrRegImmShift(r12, LSL, 0, IsDown)), r12, Offset, cc);
3750 }
3751 
3752 void
checkStackAlignment()3753 MacroAssemblerARMCompat::checkStackAlignment()
3754 {
3755     asMasm().assertStackAlignment(ABIStackAlignment);
3756 }
3757 
3758 void
handleFailureWithHandlerTail(void * handler)3759 MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
3760 {
3761     // Reserve space for exception information.
3762     int size = (sizeof(ResumeFromException) + 7) & ~7;
3763 
3764     ma_sub(Imm32(size), sp);
3765     ma_mov(sp, r0);
3766 
3767     // Call the handler.
3768     asMasm().setupUnalignedABICall(r1);
3769     asMasm().passABIArg(r0);
3770     asMasm().callWithABI(handler);
3771 
3772     Label entryFrame;
3773     Label catch_;
3774     Label finally;
3775     Label return_;
3776     Label bailout;
3777 
3778     ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0);
3779     branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
3780     branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
3781     branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
3782     branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
3783     branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
3784 
3785     breakpoint(); // Invalid kind.
3786 
3787     // No exception handler. Load the error value, load the new stack pointer
3788     // and return from the entry frame.
3789     bind(&entryFrame);
3790     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
3791     ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
3792 
3793     // We're going to be returning by the ion calling convention, which returns
3794     // by ??? (for now, I think ldr pc, [sp]!)
3795     as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
3796 
3797     // If we found a catch handler, this must be a baseline frame. Restore state
3798     // and jump to the catch block.
3799     bind(&catch_);
3800     ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
3801     ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
3802     ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
3803     jump(r0);
3804 
3805     // If we found a finally block, this must be a baseline frame. Push two
3806     // values expected by JSOP_RETSUB: BooleanValue(true) and the exception.
3807     bind(&finally);
3808     ValueOperand exception = ValueOperand(r1, r2);
3809     loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception);
3810 
3811     ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
3812     ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
3813     ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
3814 
3815     pushValue(BooleanValue(true));
3816     pushValue(exception);
3817     jump(r0);
3818 
3819     // Only used in debug mode. Return BaselineFrame->returnValue() to the
3820     // caller.
3821     bind(&return_);
3822     ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
3823     ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
3824     loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand);
3825     ma_mov(r11, sp);
3826     pop(r11);
3827 
3828     // If profiling is enabled, then update the lastProfilingFrame to refer to caller
3829     // frame before returning.
3830     {
3831         Label skipProfilingInstrumentation;
3832         // Test if profiler enabled.
3833         AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->spsProfiler().addressOfEnabled());
3834         branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation);
3835         profilerExitFrame();
3836         bind(&skipProfilingInstrumentation);
3837     }
3838 
3839     ret();
3840 
3841     // If we are bailing out to baseline to handle an exception, jump to the
3842     // bailout tail stub.
3843     bind(&bailout);
3844     ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2);
3845     ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
3846     ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1);
3847     jump(r1);
3848 }
3849 
3850 Assembler::Condition
testStringTruthy(bool truthy,const ValueOperand & value)3851 MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand& value)
3852 {
3853     Register string = value.payloadReg();
3854     ScratchRegisterScope scratch(asMasm());
3855     ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch);
3856     ma_cmp(scratch, Imm32(0));
3857     return truthy ? Assembler::NotEqual : Assembler::Equal;
3858 }
3859 
3860 void
floor(FloatRegister input,Register output,Label * bail)3861 MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail)
3862 {
3863     Label handleZero;
3864     Label handleNeg;
3865     Label fin;
3866 
3867     ScratchDoubleScope scratchDouble(asMasm());
3868 
3869     compareDouble(input, NoVFPRegister);
3870     ma_b(&handleZero, Assembler::Equal);
3871     ma_b(&handleNeg, Assembler::Signed);
3872     // NaN is always a bail condition, just bail directly.
3873     ma_b(bail, Assembler::Overflow);
3874 
3875     // The argument is a positive number, truncation is the path to glory. Since
3876     // it is known to be > 0.0, explicitly convert to a larger range, then a
3877     // value that rounds to INT_MAX is explicitly different from an argument
3878     // that clamps to INT_MAX.
3879     ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
3880     ma_vxfer(scratchDouble.uintOverlay(), output);
3881     ma_mov(output, output, SetCC);
3882     ma_b(bail, Signed);
3883     ma_b(&fin);
3884 
3885     bind(&handleZero);
3886     // Move the top word of the double into the output reg, if it is non-zero,
3887     // then the original value was -0.0.
3888     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
3889     ma_cmp(output, Imm32(0));
3890     ma_b(bail, NonZero);
3891     ma_b(&fin);
3892 
3893     bind(&handleNeg);
3894     // Negative case, negate, then start dancing.
3895     ma_vneg(input, input);
3896     ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
3897     ma_vxfer(scratchDouble.uintOverlay(), output);
3898     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
3899     compareDouble(scratchDouble, input);
3900     ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
3901     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
3902     // result will still be a negative number.
3903     ma_rsb(output, Imm32(0), output, SetCC);
3904     // Flip the negated input back to its original value.
3905     ma_vneg(input, input);
3906     // If the result looks non-negative, then this value didn't actually fit
3907     // into the int range, and special handling is required. Zero is also caught
3908     // by this case, but floor of a negative number should never be zero.
3909     ma_b(bail, NotSigned);
3910 
3911     bind(&fin);
3912 }
3913 
3914 void
floorf(FloatRegister input,Register output,Label * bail)3915 MacroAssemblerARMCompat::floorf(FloatRegister input, Register output, Label* bail)
3916 {
3917     Label handleZero;
3918     Label handleNeg;
3919     Label fin;
3920     compareFloat(input, NoVFPRegister);
3921     ma_b(&handleZero, Assembler::Equal);
3922     ma_b(&handleNeg, Assembler::Signed);
3923     // NaN is always a bail condition, just bail directly.
3924     ma_b(bail, Assembler::Overflow);
3925 
3926     // The argument is a positive number, truncation is the path to glory; Since
3927     // it is known to be > 0.0, explicitly convert to a larger range, then a
3928     // value that rounds to INT_MAX is explicitly different from an argument
3929     // that clamps to INT_MAX.
3930     {
3931         ScratchFloat32Scope scratch(asMasm());
3932         ma_vcvt_F32_U32(input, scratch.uintOverlay());
3933         ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
3934     }
3935     ma_mov(output, output, SetCC);
3936     ma_b(bail, Signed);
3937     ma_b(&fin);
3938 
3939     bind(&handleZero);
3940     // Move the top word of the double into the output reg, if it is non-zero,
3941     // then the original value was -0.0.
3942     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
3943     ma_cmp(output, Imm32(0));
3944     ma_b(bail, NonZero);
3945     ma_b(&fin);
3946 
3947     bind(&handleNeg);
3948     // Negative case, negate, then start dancing.
3949     {
3950         ScratchFloat32Scope scratch(asMasm());
3951         ma_vneg_f32(input, input);
3952         ma_vcvt_F32_U32(input, scratch.uintOverlay());
3953         ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
3954         ma_vcvt_U32_F32(scratch.uintOverlay(), scratch);
3955         compareFloat(scratch, input);
3956         ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
3957     }
3958     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
3959     // result will still be a negative number.
3960     ma_rsb(output, Imm32(0), output, SetCC);
3961     // Flip the negated input back to its original value.
3962     ma_vneg_f32(input, input);
3963     // If the result looks non-negative, then this value didn't actually fit
3964     // into the int range, and special handling is required. Zero is also caught
3965     // by this case, but floor of a negative number should never be zero.
3966     ma_b(bail, NotSigned);
3967 
3968     bind(&fin);
3969 }
3970 
3971 void
ceil(FloatRegister input,Register output,Label * bail)3972 MacroAssemblerARMCompat::ceil(FloatRegister input, Register output, Label* bail)
3973 {
3974     Label handleZero;
3975     Label handlePos;
3976     Label fin;
3977 
3978     compareDouble(input, NoVFPRegister);
3979     // NaN is always a bail condition, just bail directly.
3980     ma_b(bail, Assembler::Overflow);
3981     ma_b(&handleZero, Assembler::Equal);
3982     ma_b(&handlePos, Assembler::NotSigned);
3983 
3984     ScratchDoubleScope scratchDouble(asMasm());
3985 
3986     // We are in the ]-Inf; 0[ range
3987     // If we are in the ]-1; 0[ range => bailout
3988     ma_vimm(-1.0, scratchDouble);
3989     compareDouble(input, scratchDouble);
3990     ma_b(bail, Assembler::GreaterThan);
3991 
3992     // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be
3993     // computed with direct truncation here (x > 0).
3994     ma_vneg(input, scratchDouble);
3995     FloatRegister ScratchUIntReg = scratchDouble.uintOverlay();
3996     ma_vcvt_F64_U32(scratchDouble, ScratchUIntReg);
3997     ma_vxfer(ScratchUIntReg, output);
3998     ma_neg(output, output, SetCC);
3999     ma_b(bail, NotSigned);
4000     ma_b(&fin);
4001 
4002     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
4003     // then it was -0 and we need to bail out.
4004     bind(&handleZero);
4005     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
4006     ma_cmp(output, Imm32(0));
4007     ma_b(bail, NonZero);
4008     ma_b(&fin);
4009 
4010     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
4011     // non integer values, maybe bail if overflow.
4012     bind(&handlePos);
4013     ma_vcvt_F64_U32(input, ScratchUIntReg);
4014     ma_vxfer(ScratchUIntReg, output);
4015     ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble);
4016     compareDouble(scratchDouble, input);
4017     ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
4018     // Bail out if the add overflowed or the result is non positive.
4019     ma_mov(output, output, SetCC);
4020     ma_b(bail, Signed);
4021     ma_b(bail, Zero);
4022 
4023     bind(&fin);
4024 }
4025 
4026 void
ceilf(FloatRegister input,Register output,Label * bail)4027 MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output, Label* bail)
4028 {
4029     Label handleZero;
4030     Label handlePos;
4031     Label fin;
4032 
4033     compareFloat(input, NoVFPRegister);
4034     // NaN is always a bail condition, just bail directly.
4035     ma_b(bail, Assembler::Overflow);
4036     ma_b(&handleZero, Assembler::Equal);
4037     ma_b(&handlePos, Assembler::NotSigned);
4038 
4039     // We are in the ]-Inf; 0[ range
4040     // If we are in the ]-1; 0[ range => bailout
4041     {
4042         ScratchFloat32Scope scratch(asMasm());
4043         ma_vimm_f32(-1.f, scratch);
4044         compareFloat(input, scratch);
4045         ma_b(bail, Assembler::GreaterThan);
4046     }
4047 
4048     // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be
4049     // computed with direct truncation here (x > 0).
4050     {
4051         ScratchDoubleScope scratchDouble(asMasm());
4052         FloatRegister scratchFloat = scratchDouble.asSingle();
4053         FloatRegister scratchUInt = scratchDouble.uintOverlay();
4054 
4055         ma_vneg_f32(input, scratchFloat);
4056         ma_vcvt_F32_U32(scratchFloat, scratchUInt);
4057         ma_vxfer(scratchUInt, output);
4058         ma_neg(output, output, SetCC);
4059         ma_b(bail, NotSigned);
4060         ma_b(&fin);
4061     }
4062 
4063     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
4064     // then it was -0 and we need to bail out.
4065     bind(&handleZero);
4066     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
4067     ma_cmp(output, Imm32(0));
4068     ma_b(bail, NonZero);
4069     ma_b(&fin);
4070 
4071     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
4072     // non integer values, maybe bail if overflow.
4073     bind(&handlePos);
4074     {
4075         ScratchDoubleScope scratchDouble(asMasm());
4076         FloatRegister scratchFloat = scratchDouble.asSingle();
4077         FloatRegister scratchUInt = scratchDouble.uintOverlay();
4078 
4079         ma_vcvt_F32_U32(input, scratchUInt);
4080         ma_vxfer(scratchUInt, output);
4081         ma_vcvt_U32_F32(scratchUInt, scratchFloat);
4082         compareFloat(scratchFloat, input);
4083         ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
4084 
4085         // Bail on overflow or non-positive result.
4086         ma_mov(output, output, SetCC);
4087         ma_b(bail, Signed);
4088         ma_b(bail, Zero);
4089     }
4090 
4091     bind(&fin);
4092 }
4093 
4094 CodeOffset
toggledJump(Label * label)4095 MacroAssemblerARMCompat::toggledJump(Label* label)
4096 {
4097     // Emit a B that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp().
4098     BufferOffset b = ma_b(label, Always);
4099     CodeOffset ret(b.getOffset());
4100     return ret;
4101 }
4102 
4103 CodeOffset
toggledCall(JitCode * target,bool enabled)4104 MacroAssemblerARMCompat::toggledCall(JitCode* target, bool enabled)
4105 {
4106     BufferOffset bo = nextOffset();
4107     addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE);
4108     ScratchRegisterScope scratch(asMasm());
4109     ma_movPatchable(ImmPtr(target->raw()), scratch, Always, HasMOVWT() ? L_MOVWT : L_LDR);
4110     if (enabled)
4111         ma_blx(scratch);
4112     else
4113         ma_nop();
4114     return CodeOffset(bo.getOffset());
4115 }
4116 
4117 void
round(FloatRegister input,Register output,Label * bail,FloatRegister tmp)4118 MacroAssemblerARMCompat::round(FloatRegister input, Register output, Label* bail, FloatRegister tmp)
4119 {
4120     Label handleZero;
4121     Label handleNeg;
4122     Label fin;
4123 
4124     ScratchDoubleScope scratchDouble(asMasm());
4125 
4126     // Do a compare based on the original value, then do most other things based
4127     // on the shifted value.
4128     ma_vcmpz(input);
4129     // Since we already know the sign bit, flip all numbers to be positive,
4130     // stored in tmp.
4131     ma_vabs(input, tmp);
4132     as_vmrs(pc);
4133     ma_b(&handleZero, Assembler::Equal);
4134     ma_b(&handleNeg, Assembler::Signed);
4135     // NaN is always a bail condition, just bail directly.
4136     ma_b(bail, Assembler::Overflow);
4137 
4138     // The argument is a positive number, truncation is the path to glory; Since
4139     // it is known to be > 0.0, explicitly convert to a larger range, then a
4140     // value that rounds to INT_MAX is explicitly different from an argument
4141     // that clamps to INT_MAX.
4142 
4143     // Add the biggest number less than 0.5 (not 0.5, because adding that to
4144     // the biggest number less than 0.5 would undesirably round up to 1), and
4145     // store the result into tmp.
4146     ma_vimm(GetBiggestNumberLessThan(0.5), scratchDouble);
4147     ma_vadd(scratchDouble, tmp, tmp);
4148 
4149     ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
4150     ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
4151     ma_mov(output, output, SetCC);
4152     ma_b(bail, Signed);
4153     ma_b(&fin);
4154 
4155     bind(&handleZero);
4156     // Move the top word of the double into the output reg, if it is non-zero,
4157     // then the original value was -0.0
4158     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
4159     ma_cmp(output, Imm32(0));
4160     ma_b(bail, NonZero);
4161     ma_b(&fin);
4162 
4163     bind(&handleNeg);
4164     // Negative case, negate, then start dancing. This number may be positive,
4165     // since we added 0.5.
4166 
4167     // Add 0.5 to negative numbers, store the result into tmp
4168     ma_vimm(0.5, scratchDouble);
4169     ma_vadd(scratchDouble, tmp, tmp);
4170 
4171     ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
4172     ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
4173 
4174     // -output is now a correctly rounded value, unless the original value was
4175     // exactly halfway between two integers, at which point, it has been rounded
4176     // away from zero, when it should be rounded towards \infty.
4177     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
4178     compareDouble(scratchDouble, tmp);
4179     ma_sub(output, Imm32(1), output, LeaveCC, Equal);
4180     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
4181     // result will still be a negative number.
4182     ma_rsb(output, Imm32(0), output, SetCC);
4183 
4184     // If the result looks non-negative, then this value didn't actually fit
4185     // into the int range, and special handling is required, or it was zero,
4186     // which means the result is actually -0.0 which also requires special
4187     // handling.
4188     ma_b(bail, NotSigned);
4189 
4190     bind(&fin);
4191 }
4192 
4193 void
roundf(FloatRegister input,Register output,Label * bail,FloatRegister tmp)4194 MacroAssemblerARMCompat::roundf(FloatRegister input, Register output, Label* bail, FloatRegister tmp)
4195 {
4196     Label handleZero;
4197     Label handleNeg;
4198     Label fin;
4199 
4200     ScratchFloat32Scope scratchFloat(asMasm());
4201 
4202     // Do a compare based on the original value, then do most other things based
4203     // on the shifted value.
4204     compareFloat(input, NoVFPRegister);
4205     ma_b(&handleZero, Assembler::Equal);
4206     ma_b(&handleNeg, Assembler::Signed);
4207 
4208     // NaN is always a bail condition, just bail directly.
4209     ma_b(bail, Assembler::Overflow);
4210 
4211     // The argument is a positive number, truncation is the path to glory; Since
4212     // it is known to be > 0.0, explicitly convert to a larger range, then a
4213     // value that rounds to INT_MAX is explicitly different from an argument
4214     // that clamps to INT_MAX.
4215 
4216     // Add the biggest number less than 0.5f (not 0.5f, because adding that to
4217     // the biggest number less than 0.5f would undesirably round up to 1), and
4218     // store the result into tmp.
4219     ma_vimm_f32(GetBiggestNumberLessThan(0.5f), scratchFloat);
4220     ma_vadd_f32(scratchFloat, input, tmp);
4221 
4222     // Note: it doesn't matter whether x + .5 === x or not here, as it doesn't
4223     // affect the semantics of the float to unsigned conversion (in particular,
4224     // we are not applying any fixup after the operation).
4225     ma_vcvt_F32_U32(tmp, scratchFloat.uintOverlay());
4226     ma_vxfer(VFPRegister(scratchFloat).uintOverlay(), output);
4227     ma_mov(output, output, SetCC);
4228     ma_b(bail, Signed);
4229     ma_b(&fin);
4230 
4231     bind(&handleZero);
4232 
4233     // Move the whole float32 into the output reg, if it is non-zero, then the
4234     // original value was -0.0.
4235     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0);
4236     ma_cmp(output, Imm32(0));
4237     ma_b(bail, NonZero);
4238     ma_b(&fin);
4239 
4240     bind(&handleNeg);
4241 
4242     // Add 0.5 to negative numbers, storing the result into tmp.
4243     ma_vneg_f32(input, tmp);
4244     ma_vimm_f32(0.5f, scratchFloat);
4245     ma_vadd_f32(tmp, scratchFloat, scratchFloat);
4246 
4247     // Adding 0.5 to a float input has chances to yield the wrong result, if
4248     // the input is too large. In this case, skip the -1 adjustment made below.
4249     compareFloat(scratchFloat, tmp);
4250 
4251     // Negative case, negate, then start dancing. This number may be positive,
4252     // since we added 0.5.
4253     // /!\ The conditional jump afterwards depends on these two instructions
4254     //     *not* setting the status flags. They need to not change after the
4255     //     comparison above.
4256     ma_vcvt_F32_U32(scratchFloat, tmp.uintOverlay());
4257     ma_vxfer(VFPRegister(tmp).uintOverlay(), output);
4258 
4259     Label flipSign;
4260     ma_b(&flipSign, Equal);
4261 
4262     // -output is now a correctly rounded value, unless the original value was
4263     // exactly halfway between two integers, at which point, it has been rounded
4264     // away from zero, when it should be rounded towards \infty.
4265     ma_vcvt_U32_F32(tmp.uintOverlay(), tmp);
4266     compareFloat(tmp, scratchFloat);
4267     ma_sub(output, Imm32(1), output, LeaveCC, Equal);
4268 
4269     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
4270     // result will still be a negative number.
4271     bind(&flipSign);
4272     ma_rsb(output, Imm32(0), output, SetCC);
4273 
4274     // If the result looks non-negative, then this value didn't actually fit
4275     // into the int range, and special handling is required, or it was zero,
4276     // which means the result is actually -0.0 which also requires special
4277     // handling.
4278     ma_b(bail, NotSigned);
4279 
4280     bind(&fin);
4281 }
4282 
4283 CodeOffsetJump
jumpWithPatch(RepatchLabel * label,Condition cond,Label * documentation)4284 MacroAssemblerARMCompat::jumpWithPatch(RepatchLabel* label, Condition cond, Label* documentation)
4285 {
4286     ARMBuffer::PoolEntry pe;
4287     BufferOffset bo = as_BranchPool(0xdeadbeef, label, &pe, cond, documentation);
4288     // Fill in a new CodeOffset with both the load and the pool entry that the
4289     // instruction loads from.
4290     CodeOffsetJump ret(bo.getOffset(), pe.index());
4291     return ret;
4292 }
4293 
4294 void
branchPtrInNurseryRange(Condition cond,Register ptr,Register temp,Label * label)4295 MacroAssemblerARMCompat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
4296                                                  Label* label)
4297 {
4298     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
4299 
4300     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
4301     MOZ_ASSERT(ptr != temp);
4302     MOZ_ASSERT(ptr != scratch2);
4303 
4304     const Nursery& nursery = GetJitContext()->runtime->gcNursery();
4305     uintptr_t startChunk = nursery.start() >> Nursery::ChunkShift;
4306 
4307     ma_mov(Imm32(startChunk), scratch2);
4308     as_rsb(scratch2, scratch2, lsr(ptr, Nursery::ChunkShift));
4309     branch32(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual,
4310               scratch2, Imm32(nursery.numChunks()), label);
4311 }
4312 
4313 void
branchValueIsNurseryObject(Condition cond,ValueOperand value,Register temp,Label * label)4314 MacroAssemblerARMCompat::branchValueIsNurseryObject(Condition cond, ValueOperand value,
4315                                                     Register temp, Label* label)
4316 {
4317     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
4318 
4319     Label done;
4320 
4321     branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
4322     branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
4323 
4324     bind(&done);
4325 }
4326 
4327 namespace js {
4328 namespace jit {
4329 
4330 template<>
4331 Register
computePointer(const BaseIndex & src,Register r)4332 MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex& src, Register r)
4333 {
4334     Register base = src.base;
4335     Register index = src.index;
4336     uint32_t scale = Imm32::ShiftOf(src.scale).value;
4337     int32_t offset = src.offset;
4338     as_add(r, base, lsl(index, scale));
4339     if (offset != 0)
4340         ma_add(r, Imm32(offset), r);
4341     return r;
4342 }
4343 
4344 template<>
4345 Register
computePointer(const Address & src,Register r)4346 MacroAssemblerARMCompat::computePointer<Address>(const Address& src, Register r)
4347 {
4348     if (src.offset == 0)
4349         return src.base;
4350     ma_add(src.base, Imm32(src.offset), r);
4351     return r;
4352 }
4353 
4354 } // namespace jit
4355 } // namespace js
4356 
4357 template<typename T>
4358 void
compareExchange(int nbytes,bool signExtend,const T & mem,Register oldval,Register newval,Register output)4359 MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend, const T& mem,
4360                                          Register oldval, Register newval, Register output)
4361 {
4362     // If LDREXB/H and STREXB/H are not available we use the
4363     // word-width operations with read-modify-add.  That does not
4364     // abstract well, so fork.
4365     //
4366     // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
4367     if (nbytes < 4 && !HasLDSTREXBHD())
4368         compareExchangeARMv6(nbytes, signExtend, mem, oldval, newval, output);
4369     else
4370         compareExchangeARMv7(nbytes, signExtend, mem, oldval, newval, output);
4371 }
4372 
4373 // General algorithm:
4374 //
4375 //     ...    ptr, <addr>         ; compute address of item
4376 //     dmb
4377 // L0  ldrex* output, [ptr]
4378 //     sxt*   output, output, 0   ; sign-extend if applicable
4379 //     *xt*   tmp, oldval, 0      ; sign-extend or zero-extend if applicable
4380 //     cmp    output, tmp
4381 //     bne    L1                  ; failed - values are different
4382 //     strex* tmp, newval, [ptr]
4383 //     cmp    tmp, 1
4384 //     beq    L0                  ; failed - location is dirty, retry
4385 // L1  dmb
4386 //
4387 // Discussion here:  http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html.
4388 // However note that that discussion uses 'isb' as the trailing fence.
4389 // I've not quite figured out why, and I've gone with dmb here which
4390 // is safe.  Also see the LLVM source, which uses 'dmb ish' generally.
4391 // (Apple's Swift CPU apparently handles ish in a non-default, faster
4392 // way.)
4393 
4394 template<typename T>
4395 void
compareExchangeARMv7(int nbytes,bool signExtend,const T & mem,Register oldval,Register newval,Register output)4396 MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T& mem,
4397                                               Register oldval, Register newval, Register output)
4398 {
4399     Label again;
4400     Label done;
4401     ma_dmb(BarrierST);
4402 
4403     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
4404     Register ptr = computePointer(mem, scratch2);
4405 
4406     ScratchRegisterScope scratch(asMasm());
4407 
4408     bind(&again);
4409     switch (nbytes) {
4410       case 1:
4411         as_ldrexb(output, ptr);
4412         if (signExtend) {
4413             as_sxtb(output, output, 0);
4414             as_sxtb(scratch, oldval, 0);
4415         } else {
4416             as_uxtb(scratch, oldval, 0);
4417         }
4418         break;
4419       case 2:
4420         as_ldrexh(output, ptr);
4421         if (signExtend) {
4422             as_sxth(output, output, 0);
4423             as_sxth(scratch, oldval, 0);
4424         } else {
4425             as_uxth(scratch, oldval, 0);
4426         }
4427         break;
4428       case 4:
4429         MOZ_ASSERT(!signExtend);
4430         as_ldrex(output, ptr);
4431         break;
4432     }
4433     if (nbytes < 4)
4434         as_cmp(output, O2Reg(scratch));
4435     else
4436         as_cmp(output, O2Reg(oldval));
4437     as_b(&done, NotEqual);
4438     switch (nbytes) {
4439       case 1:
4440         as_strexb(scratch, newval, ptr);
4441         break;
4442       case 2:
4443         as_strexh(scratch, newval, ptr);
4444         break;
4445       case 4:
4446         as_strex(scratch, newval, ptr);
4447         break;
4448     }
4449     as_cmp(scratch, Imm8(1));
4450     as_b(&again, Equal);
4451     bind(&done);
4452     ma_dmb();
4453 }
4454 
4455 template<typename T>
4456 void
compareExchangeARMv6(int nbytes,bool signExtend,const T & mem,Register oldval,Register newval,Register output)4457 MacroAssemblerARMCompat::compareExchangeARMv6(int nbytes, bool signExtend, const T& mem,
4458                                               Register oldval, Register newval, Register output)
4459 {
4460     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
4461     MOZ_ASSERT(nbytes == 1 || nbytes == 2);
4462     MOZ_CRASH("NYI");
4463 }
4464 
4465 template void
4466 js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
4467                                                   const Address& address, Register oldval,
4468                                                   Register newval, Register output);
4469 template void
4470 js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
4471                                                   const BaseIndex& address, Register oldval,
4472                                                   Register newval, Register output);
4473 
4474 template<typename T>
4475 void
atomicExchange(int nbytes,bool signExtend,const T & mem,Register value,Register output)4476 MacroAssemblerARMCompat::atomicExchange(int nbytes, bool signExtend, const T& mem,
4477                                         Register value, Register output)
4478 {
4479     // If LDREXB/H and STREXB/H are not available we use the
4480     // word-width operations with read-modify-add.  That does not
4481     // abstract well, so fork.
4482     //
4483     // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
4484     if (nbytes < 4 && !HasLDSTREXBHD())
4485         atomicExchangeARMv6(nbytes, signExtend, mem, value, output);
4486     else
4487         atomicExchangeARMv7(nbytes, signExtend, mem, value, output);
4488 }
4489 
4490 template<typename T>
4491 void
atomicExchangeARMv7(int nbytes,bool signExtend,const T & mem,Register value,Register output)4492 MacroAssemblerARMCompat::atomicExchangeARMv7(int nbytes, bool signExtend, const T& mem,
4493                                              Register value, Register output)
4494 {
4495     Label again;
4496     Label done;
4497     ma_dmb(BarrierST);
4498 
4499     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
4500     Register ptr = computePointer(mem, scratch2);
4501 
4502     ScratchRegisterScope scratch(asMasm());
4503 
4504     bind(&again);
4505     switch (nbytes) {
4506       case 1:
4507         as_ldrexb(output, ptr);
4508         if (signExtend)
4509             as_sxtb(output, output, 0);
4510         as_strexb(scratch, value, ptr);
4511         break;
4512       case 2:
4513         as_ldrexh(output, ptr);
4514         if (signExtend)
4515             as_sxth(output, output, 0);
4516         as_strexh(scratch, value, ptr);
4517         break;
4518       case 4:
4519         MOZ_ASSERT(!signExtend);
4520         as_ldrex(output, ptr);
4521         as_strex(scratch, value, ptr);
4522         break;
4523       default:
4524         MOZ_CRASH();
4525     }
4526     as_cmp(scratch, Imm8(1));
4527     as_b(&again, Equal);
4528     bind(&done);
4529     ma_dmb();
4530 }
4531 
4532 template<typename T>
4533 void
atomicExchangeARMv6(int nbytes,bool signExtend,const T & mem,Register value,Register output)4534 MacroAssemblerARMCompat::atomicExchangeARMv6(int nbytes, bool signExtend, const T& mem,
4535                                              Register value, Register output)
4536 {
4537     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
4538     MOZ_ASSERT(nbytes == 1 || nbytes == 2);
4539     MOZ_CRASH("NYI");
4540 }
4541 
4542 template void
4543 js::jit::MacroAssemblerARMCompat::atomicExchange(int nbytes, bool signExtend,
4544                                                  const Address& address, Register value,
4545                                                  Register output);
4546 template void
4547 js::jit::MacroAssemblerARMCompat::atomicExchange(int nbytes, bool signExtend,
4548                                                  const BaseIndex& address, Register value,
4549                                                  Register output);
4550 
4551 template<typename T>
4552 void
atomicFetchOp(int nbytes,bool signExtend,AtomicOp op,const Imm32 & value,const T & mem,Register flagTemp,Register output)4553 MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Imm32& value,
4554                                        const T& mem, Register flagTemp, Register output)
4555 {
4556     // The Imm32 case is not needed yet because lowering always forces
4557     // the value into a register at present (bug 1077317).
4558     //
4559     // This would be useful for immediates small enough to fit into
4560     // add/sub/and/or/xor.
4561     MOZ_CRASH("Feature NYI");
4562 }
4563 
4564 // General algorithm:
4565 //
4566 //     ...    ptr, <addr>         ; compute address of item
4567 //     dmb
4568 // L0  ldrex* output, [ptr]
4569 //     sxt*   output, output, 0   ; sign-extend if applicable
4570 //     OP     tmp, output, value  ; compute value to store
4571 //     strex* tmp2, tmp, [ptr]    ; tmp2 required by strex
4572 //     cmp    tmp2, 1
4573 //     beq    L0                  ; failed - location is dirty, retry
4574 //     dmb                        ; ordering barrier required
4575 //
4576 // Also see notes above at compareExchange re the barrier strategy.
4577 //
4578 // Observe that the value being operated into the memory element need
4579 // not be sign-extended because no OP will make use of bits to the
4580 // left of the bits indicated by the width of the element, and neither
4581 // output nor the bits stored are affected by OP.
4582 
4583 template<typename T>
4584 void
atomicFetchOp(int nbytes,bool signExtend,AtomicOp op,const Register & value,const T & mem,Register flagTemp,Register output)4585 MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
4586                                        const Register& value, const T& mem, Register flagTemp,
4587                                        Register output)
4588 {
4589     // Fork for non-word operations on ARMv6.
4590     //
4591     // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
4592     if (nbytes < 4 && !HasLDSTREXBHD())
4593         atomicFetchOpARMv6(nbytes, signExtend, op, value, mem, flagTemp, output);
4594     else
4595         atomicFetchOpARMv7(nbytes, signExtend, op, value, mem, flagTemp, output);
4596 }
4597 
4598 template<typename T>
4599 void
atomicFetchOpARMv7(int nbytes,bool signExtend,AtomicOp op,const Register & value,const T & mem,Register flagTemp,Register output)4600 MacroAssemblerARMCompat::atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op,
4601                                             const Register& value, const T& mem, Register flagTemp,
4602                                             Register output)
4603 {
4604     MOZ_ASSERT(flagTemp != InvalidReg);
4605 
4606     Label again;
4607 
4608     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
4609     Register ptr = computePointer(mem, scratch2);
4610 
4611     ma_dmb();
4612 
4613     ScratchRegisterScope scratch(asMasm());
4614 
4615     bind(&again);
4616     switch (nbytes) {
4617       case 1:
4618         as_ldrexb(output, ptr);
4619         if (signExtend)
4620             as_sxtb(output, output, 0);
4621         break;
4622       case 2:
4623         as_ldrexh(output, ptr);
4624         if (signExtend)
4625             as_sxth(output, output, 0);
4626         break;
4627       case 4:
4628         MOZ_ASSERT(!signExtend);
4629         as_ldrex(output, ptr);
4630         break;
4631     }
4632     switch (op) {
4633       case AtomicFetchAddOp:
4634         as_add(scratch, output, O2Reg(value));
4635         break;
4636       case AtomicFetchSubOp:
4637         as_sub(scratch, output, O2Reg(value));
4638         break;
4639       case AtomicFetchAndOp:
4640         as_and(scratch, output, O2Reg(value));
4641         break;
4642       case AtomicFetchOrOp:
4643         as_orr(scratch, output, O2Reg(value));
4644         break;
4645       case AtomicFetchXorOp:
4646         as_eor(scratch, output, O2Reg(value));
4647         break;
4648     }
4649     // Rd must differ from the two other arguments to strex.
4650     switch (nbytes) {
4651       case 1:
4652         as_strexb(flagTemp, scratch, ptr);
4653         break;
4654       case 2:
4655         as_strexh(flagTemp, scratch, ptr);
4656         break;
4657       case 4:
4658         as_strex(flagTemp, scratch, ptr);
4659         break;
4660     }
4661     as_cmp(flagTemp, Imm8(1));
4662     as_b(&again, Equal);
4663     ma_dmb();
4664 }
4665 
4666 template<typename T>
4667 void
atomicFetchOpARMv6(int nbytes,bool signExtend,AtomicOp op,const Register & value,const T & mem,Register flagTemp,Register output)4668 MacroAssemblerARMCompat::atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op,
4669                                             const Register& value, const T& mem, Register flagTemp,
4670                                             Register output)
4671 {
4672     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
4673     MOZ_ASSERT(nbytes == 1 || nbytes == 2);
4674     MOZ_CRASH("NYI");
4675 }
4676 
4677 template<typename T>
4678 void
atomicEffectOp(int nbytes,AtomicOp op,const Register & value,const T & mem,Register flagTemp)4679 MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Register& value,
4680                                         const T& mem, Register flagTemp)
4681 {
4682     // Fork for non-word operations on ARMv6.
4683     //
4684     // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
4685     if (nbytes < 4 && !HasLDSTREXBHD())
4686         atomicEffectOpARMv6(nbytes, op, value, mem, flagTemp);
4687     else
4688         atomicEffectOpARMv7(nbytes, op, value, mem, flagTemp);
4689 }
4690 
4691 template<typename T>
4692 void
atomicEffectOp(int nbytes,AtomicOp op,const Imm32 & value,const T & mem,Register flagTemp)4693 MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Imm32& value,
4694                                         const T& mem, Register flagTemp)
4695 {
4696     // The Imm32 case is not needed yet because lowering always forces
4697     // the value into a register at present (bug 1077317).
4698     //
4699     // This would be useful for immediates small enough to fit into
4700     // add/sub/and/or/xor.
4701     MOZ_CRASH("NYI");
4702 }
4703 
4704 // Uses both scratch registers, one for the address and one for a temp,
4705 // but needs two temps for strex:
4706 //
4707 //     ...    ptr, <addr>         ; compute address of item
4708 //     dmb
4709 // L0  ldrex* temp, [ptr]
4710 //     OP     temp, temp, value   ; compute value to store
4711 //     strex* temp2, temp, [ptr]
4712 //     cmp    temp2, 1
4713 //     beq    L0                  ; failed - location is dirty, retry
4714 //     dmb                        ; ordering barrier required
4715 
4716 template<typename T>
4717 void
atomicEffectOpARMv7(int nbytes,AtomicOp op,const Register & value,const T & mem,Register flagTemp)4718 MacroAssemblerARMCompat::atomicEffectOpARMv7(int nbytes, AtomicOp op, const Register& value,
4719                                              const T& mem, Register flagTemp)
4720 {
4721     MOZ_ASSERT(flagTemp != InvalidReg);
4722 
4723     Label again;
4724 
4725     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
4726     Register ptr = computePointer(mem, scratch2);
4727 
4728     ma_dmb();
4729 
4730     ScratchRegisterScope scratch(asMasm());
4731 
4732     bind(&again);
4733     switch (nbytes) {
4734       case 1:
4735         as_ldrexb(scratch, ptr);
4736         break;
4737       case 2:
4738         as_ldrexh(scratch, ptr);
4739         break;
4740       case 4:
4741         as_ldrex(scratch, ptr);
4742         break;
4743     }
4744     switch (op) {
4745       case AtomicFetchAddOp:
4746         as_add(scratch, scratch, O2Reg(value));
4747         break;
4748       case AtomicFetchSubOp:
4749         as_sub(scratch, scratch, O2Reg(value));
4750         break;
4751       case AtomicFetchAndOp:
4752         as_and(scratch, scratch, O2Reg(value));
4753         break;
4754       case AtomicFetchOrOp:
4755         as_orr(scratch, scratch, O2Reg(value));
4756         break;
4757       case AtomicFetchXorOp:
4758         as_eor(scratch, scratch, O2Reg(value));
4759         break;
4760     }
4761     // Rd must differ from the two other arguments to strex.
4762     switch (nbytes) {
4763       case 1:
4764         as_strexb(flagTemp, scratch, ptr);
4765         break;
4766       case 2:
4767         as_strexh(flagTemp, scratch, ptr);
4768         break;
4769       case 4:
4770         as_strex(flagTemp, scratch, ptr);
4771         break;
4772     }
4773     as_cmp(flagTemp, Imm8(1));
4774     as_b(&again, Equal);
4775     ma_dmb();
4776 }
4777 
4778 template<typename T>
4779 void
atomicEffectOpARMv6(int nbytes,AtomicOp op,const Register & value,const T & mem,Register flagTemp)4780 MacroAssemblerARMCompat::atomicEffectOpARMv6(int nbytes, AtomicOp op, const Register& value,
4781                                              const T& mem, Register flagTemp)
4782 {
4783     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
4784     MOZ_ASSERT(nbytes == 1 || nbytes == 2);
4785     MOZ_CRASH("NYI");
4786 }
4787 
4788 template void
4789 js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
4790                                                 const Imm32& value, const Address& mem,
4791                                                 Register flagTemp, Register output);
4792 template void
4793 js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
4794                                                 const Imm32& value, const BaseIndex& mem,
4795                                                 Register flagTemp, Register output);
4796 template void
4797 js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
4798                                                 const Register& value, const Address& mem,
4799                                                 Register flagTemp, Register output);
4800 template void
4801 js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
4802                                                 const Register& value, const BaseIndex& mem,
4803                                                 Register flagTemp, Register output);
4804 
4805 template void
4806 js::jit::MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Imm32& value,
4807                                                  const Address& mem, Register flagTemp);
4808 template void
4809 js::jit::MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Imm32& value,
4810                                                  const BaseIndex& mem, Register flagTemp);
4811 template void
4812 js::jit::MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Register& value,
4813                                                  const Address& mem, Register flagTemp);
4814 template void
4815 js::jit::MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Register& value,
4816                                                  const BaseIndex& mem, Register flagTemp);
4817 
4818 template<typename T>
4819 void
compareExchangeToTypedIntArray(Scalar::Type arrayType,const T & mem,Register oldval,Register newval,Register temp,AnyRegister output)4820 MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
4821                                                         Register oldval, Register newval,
4822                                                         Register temp, AnyRegister output)
4823 {
4824     switch (arrayType) {
4825       case Scalar::Int8:
4826         compareExchange8SignExtend(mem, oldval, newval, output.gpr());
4827         break;
4828       case Scalar::Uint8:
4829         compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
4830         break;
4831       case Scalar::Int16:
4832         compareExchange16SignExtend(mem, oldval, newval, output.gpr());
4833         break;
4834       case Scalar::Uint16:
4835         compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
4836         break;
4837       case Scalar::Int32:
4838         compareExchange32(mem, oldval, newval, output.gpr());
4839         break;
4840       case Scalar::Uint32:
4841         // At the moment, the code in MCallOptimize.cpp requires the output
4842         // type to be double for uint32 arrays.  See bug 1077305.
4843         MOZ_ASSERT(output.isFloat());
4844         compareExchange32(mem, oldval, newval, temp);
4845         convertUInt32ToDouble(temp, output.fpu());
4846         break;
4847       default:
4848         MOZ_CRASH("Invalid typed array type");
4849     }
4850 }
4851 
4852 template void
4853 MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
4854                                                         Register oldval, Register newval, Register temp,
4855                                                         AnyRegister output);
4856 template void
4857 MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
4858                                                         Register oldval, Register newval, Register temp,
4859                                                         AnyRegister output);
4860 
4861 template<typename T>
4862 void
atomicExchangeToTypedIntArray(Scalar::Type arrayType,const T & mem,Register value,Register temp,AnyRegister output)4863 MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
4864                                                        Register value, Register temp, AnyRegister output)
4865 {
4866     switch (arrayType) {
4867       case Scalar::Int8:
4868         atomicExchange8SignExtend(mem, value, output.gpr());
4869         break;
4870       case Scalar::Uint8:
4871         atomicExchange8ZeroExtend(mem, value, output.gpr());
4872         break;
4873       case Scalar::Int16:
4874         atomicExchange16SignExtend(mem, value, output.gpr());
4875         break;
4876       case Scalar::Uint16:
4877         atomicExchange16ZeroExtend(mem, value, output.gpr());
4878         break;
4879       case Scalar::Int32:
4880         atomicExchange32(mem, value, output.gpr());
4881         break;
4882       case Scalar::Uint32:
4883         // At the moment, the code in MCallOptimize.cpp requires the output
4884         // type to be double for uint32 arrays.  See bug 1077305.
4885         MOZ_ASSERT(output.isFloat());
4886         atomicExchange32(mem, value, temp);
4887         convertUInt32ToDouble(temp, output.fpu());
4888         break;
4889       default:
4890         MOZ_CRASH("Invalid typed array type");
4891     }
4892 }
4893 
4894 template void
4895 MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
4896                                                        Register value, Register temp, AnyRegister output);
4897 template void
4898 MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
4899                                                        Register value, Register temp, AnyRegister output);
4900 
4901 void
profilerEnterFrame(Register framePtr,Register scratch)4902 MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr, Register scratch)
4903 {
4904     AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
4905     loadPtr(activation, scratch);
4906     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
4907     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
4908 }
4909 
4910 void
profilerExitFrame()4911 MacroAssemblerARMCompat::profilerExitFrame()
4912 {
4913     branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
4914 }
4915 
4916 MacroAssembler&
asMasm()4917 MacroAssemblerARM::asMasm()
4918 {
4919     return *static_cast<MacroAssembler*>(this);
4920 }
4921 
4922 const MacroAssembler&
asMasm() const4923 MacroAssemblerARM::asMasm() const
4924 {
4925     return *static_cast<const MacroAssembler*>(this);
4926 }
4927 
4928 MacroAssembler&
asMasm()4929 MacroAssemblerARMCompat::asMasm()
4930 {
4931     return *static_cast<MacroAssembler*>(this);
4932 }
4933 
4934 const MacroAssembler&
asMasm() const4935 MacroAssemblerARMCompat::asMasm() const
4936 {
4937     return *static_cast<const MacroAssembler*>(this);
4938 }
4939 
4940 //{{{ check_macroassembler_style
4941 // ===============================================================
4942 // Stack manipulation functions.
4943 
4944 void
PushRegsInMask(LiveRegisterSet set)4945 MacroAssembler::PushRegsInMask(LiveRegisterSet set)
4946 {
4947     int32_t diffF = set.fpus().getPushSizeInBytes();
4948     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
4949 
4950     if (set.gprs().size() > 1) {
4951         adjustFrame(diffG);
4952         startDataTransferM(IsStore, StackPointer, DB, WriteBack);
4953         for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
4954             diffG -= sizeof(intptr_t);
4955             transferReg(*iter);
4956         }
4957         finishDataTransfer();
4958     } else {
4959         reserveStack(diffG);
4960         for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
4961             diffG -= sizeof(intptr_t);
4962             storePtr(*iter, Address(StackPointer, diffG));
4963         }
4964     }
4965     MOZ_ASSERT(diffG == 0);
4966 
4967     adjustFrame(diffF);
4968     diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
4969     MOZ_ASSERT(diffF == 0);
4970 }
4971 
4972 void
PopRegsInMaskIgnore(LiveRegisterSet set,LiveRegisterSet ignore)4973 MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
4974 {
4975     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
4976     int32_t diffF = set.fpus().getPushSizeInBytes();
4977     const int32_t reservedG = diffG;
4978     const int32_t reservedF = diffF;
4979 
4980     // ARM can load multiple registers at once, but only if we want back all
4981     // the registers we previously saved to the stack.
4982     if (ignore.emptyFloat()) {
4983         diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA);
4984         adjustFrame(-reservedF);
4985     } else {
4986         LiveFloatRegisterSet fpset(set.fpus().reduceSetForPush());
4987         LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush());
4988         for (FloatRegisterBackwardIterator iter(fpset); iter.more(); iter++) {
4989             diffF -= (*iter).size();
4990             if (!fpignore.has(*iter))
4991                 loadDouble(Address(StackPointer, diffF), *iter);
4992         }
4993         freeStack(reservedF);
4994     }
4995     MOZ_ASSERT(diffF == 0);
4996 
4997     if (set.gprs().size() > 1 && ignore.emptyGeneral()) {
4998         startDataTransferM(IsLoad, StackPointer, IA, WriteBack);
4999         for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
5000             diffG -= sizeof(intptr_t);
5001             transferReg(*iter);
5002         }
5003         finishDataTransfer();
5004         adjustFrame(-reservedG);
5005     } else {
5006         for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
5007             diffG -= sizeof(intptr_t);
5008             if (!ignore.has(*iter))
5009                 loadPtr(Address(StackPointer, diffG), *iter);
5010         }
5011         freeStack(reservedG);
5012     }
5013     MOZ_ASSERT(diffG == 0);
5014 }
5015 
5016 void
Push(Register reg)5017 MacroAssembler::Push(Register reg)
5018 {
5019     ma_push(reg);
5020     adjustFrame(sizeof(intptr_t));
5021 }
5022 
5023 void
Push(const Imm32 imm)5024 MacroAssembler::Push(const Imm32 imm)
5025 {
5026     push(imm);
5027     adjustFrame(sizeof(intptr_t));
5028 }
5029 
5030 void
Push(const ImmWord imm)5031 MacroAssembler::Push(const ImmWord imm)
5032 {
5033     push(imm);
5034     adjustFrame(sizeof(intptr_t));
5035 }
5036 
5037 void
Push(const ImmPtr imm)5038 MacroAssembler::Push(const ImmPtr imm)
5039 {
5040     Push(ImmWord(uintptr_t(imm.value)));
5041 }
5042 
5043 void
Push(const ImmGCPtr ptr)5044 MacroAssembler::Push(const ImmGCPtr ptr)
5045 {
5046     push(ptr);
5047     adjustFrame(sizeof(intptr_t));
5048 }
5049 
5050 void
Push(FloatRegister reg)5051 MacroAssembler::Push(FloatRegister reg)
5052 {
5053     VFPRegister r = VFPRegister(reg);
5054     ma_vpush(VFPRegister(reg));
5055     adjustFrame(r.size());
5056 }
5057 
5058 void
Pop(Register reg)5059 MacroAssembler::Pop(Register reg)
5060 {
5061     ma_pop(reg);
5062     adjustFrame(-sizeof(intptr_t));
5063 }
5064 
5065 void
Pop(const ValueOperand & val)5066 MacroAssembler::Pop(const ValueOperand& val)
5067 {
5068     popValue(val);
5069     adjustFrame(-sizeof(Value));
5070 }
5071 
5072 void
reserveStack(uint32_t amount)5073 MacroAssembler::reserveStack(uint32_t amount)
5074 {
5075     if (amount)
5076         ma_sub(Imm32(amount), sp);
5077     adjustFrame(amount);
5078 }
5079 
5080 // ===============================================================
5081 // Simple call functions.
5082 
5083 CodeOffset
call(Register reg)5084 MacroAssembler::call(Register reg)
5085 {
5086     as_blx(reg);
5087     return CodeOffset(currentOffset());
5088 }
5089 
5090 CodeOffset
call(Label * label)5091 MacroAssembler::call(Label* label)
5092 {
5093     // For now, assume that it'll be nearby.
5094     as_bl(label, Always);
5095     return CodeOffset(currentOffset());
5096 }
5097 
5098 void
call(ImmWord imm)5099 MacroAssembler::call(ImmWord imm)
5100 {
5101     call(ImmPtr((void*)imm.value));
5102 }
5103 
5104 void
call(ImmPtr imm)5105 MacroAssembler::call(ImmPtr imm)
5106 {
5107     BufferOffset bo = m_buffer.nextOffset();
5108     addPendingJump(bo, imm, Relocation::HARDCODED);
5109     ma_call(imm);
5110 }
5111 
5112 void
call(wasm::SymbolicAddress imm)5113 MacroAssembler::call(wasm::SymbolicAddress imm)
5114 {
5115     movePtr(imm, CallReg);
5116     call(CallReg);
5117 }
5118 
5119 void
call(JitCode * c)5120 MacroAssembler::call(JitCode* c)
5121 {
5122     BufferOffset bo = m_buffer.nextOffset();
5123     addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
5124     RelocStyle rs;
5125     if (HasMOVWT())
5126         rs = L_MOVWT;
5127     else
5128         rs = L_LDR;
5129 
5130     ScratchRegisterScope scratch(*this);
5131     ma_movPatchable(ImmPtr(c->raw()), scratch, Always, rs);
5132     callJitNoProfiler(scratch);
5133 }
5134 
5135 CodeOffset
callWithPatch()5136 MacroAssembler::callWithPatch()
5137 {
5138     // For now, assume that it'll be nearby.
5139     as_bl(BOffImm(), Always, /* documentation */ nullptr);
5140     return CodeOffset(currentOffset());
5141 }
5142 void
patchCall(uint32_t callerOffset,uint32_t calleeOffset)5143 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
5144 {
5145     BufferOffset inst(callerOffset - 4);
5146     as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst);
5147 }
5148 
5149 void
pushReturnAddress()5150 MacroAssembler::pushReturnAddress()
5151 {
5152     push(lr);
5153 }
5154 
5155 // ===============================================================
5156 // ABI function calls.
5157 
5158 void
setupUnalignedABICall(Register scratch)5159 MacroAssembler::setupUnalignedABICall(Register scratch)
5160 {
5161     setupABICall();
5162     dynamicAlignment_ = true;
5163 
5164     ma_mov(sp, scratch);
5165     // Force sp to be aligned.
5166     ma_and(Imm32(~(ABIStackAlignment - 1)), sp, sp);
5167     ma_push(scratch);
5168 }
5169 
5170 void
callWithABIPre(uint32_t * stackAdjust,bool callFromAsmJS)5171 MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
5172 {
5173     MOZ_ASSERT(inCall_);
5174     uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
5175 
5176     if (dynamicAlignment_) {
5177         // sizeof(intptr_t) accounts for the saved stack pointer pushed by
5178         // setupUnalignedABICall.
5179         stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
5180                                              ABIStackAlignment);
5181     } else {
5182         uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
5183         stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
5184                                              ABIStackAlignment);
5185     }
5186 
5187     *stackAdjust = stackForCall;
5188     reserveStack(stackForCall);
5189 
5190     // Position all arguments.
5191     {
5192         enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
5193         if (!enoughMemory_)
5194             return;
5195 
5196         MoveEmitter emitter(*this);
5197         emitter.emit(moveResolver_);
5198         emitter.finish();
5199     }
5200 
5201     assertStackAlignment(ABIStackAlignment);
5202 
5203     // Save the lr register if we need to preserve it.
5204     if (secondScratchReg_ != lr)
5205         ma_mov(lr, secondScratchReg_);
5206 }
5207 
5208 void
callWithABIPost(uint32_t stackAdjust,MoveOp::Type result)5209 MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
5210 {
5211     if (secondScratchReg_ != lr)
5212         ma_mov(secondScratchReg_, lr);
5213 
5214     switch (result) {
5215       case MoveOp::DOUBLE:
5216         if (!UseHardFpABI()) {
5217             // Move double from r0/r1 to ReturnFloatReg.
5218             ma_vxfer(r0, r1, ReturnDoubleReg);
5219             break;
5220         }
5221       case MoveOp::FLOAT32:
5222         if (!UseHardFpABI()) {
5223             // Move float32 from r0 to ReturnFloatReg.
5224             ma_vxfer(r0, ReturnFloat32Reg.singleOverlay());
5225             break;
5226         }
5227       case MoveOp::GENERAL:
5228         break;
5229 
5230       default:
5231         MOZ_CRASH("unexpected callWithABI result");
5232     }
5233 
5234     freeStack(stackAdjust);
5235 
5236     if (dynamicAlignment_) {
5237         // While the x86 supports pop esp, on ARM that isn't well defined, so
5238         // just do it manually.
5239         as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
5240     }
5241 
5242 #ifdef DEBUG
5243     MOZ_ASSERT(inCall_);
5244     inCall_ = false;
5245 #endif
5246 }
5247 
5248 void
callWithABINoProfiler(Register fun,MoveOp::Type result)5249 MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
5250 {
5251     // Load the callee in r12, as above.
5252     ma_mov(fun, r12);
5253     uint32_t stackAdjust;
5254     callWithABIPre(&stackAdjust);
5255     call(r12);
5256     callWithABIPost(stackAdjust, result);
5257 }
5258 
5259 void
callWithABINoProfiler(const Address & fun,MoveOp::Type result)5260 MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
5261 {
5262     // Load the callee in r12, no instruction between the ldr and call should
5263     // clobber it. Note that we can't use fun.base because it may be one of the
5264     // IntArg registers clobbered before the call.
5265     ma_ldr(fun, r12);
5266     uint32_t stackAdjust;
5267     callWithABIPre(&stackAdjust);
5268     call(r12);
5269     callWithABIPost(stackAdjust, result);
5270 }
5271 
5272 // ===============================================================
5273 // Jit Frames.
5274 
5275 uint32_t
pushFakeReturnAddress(Register scratch)5276 MacroAssembler::pushFakeReturnAddress(Register scratch)
5277 {
5278     // On ARM any references to the pc, adds an additional 8 to it, which
5279     // correspond to 2 instructions of 4 bytes.  Thus we use an additional nop
5280     // to pad until we reach the pushed pc.
5281     //
5282     // Note: In practice this should not be necessary, as this fake return
5283     // address is never used for resuming any execution. Thus theoriticaly we
5284     // could just do a Push(pc), and ignore the nop as well as the pool.
5285     enterNoPool(2);
5286     DebugOnly<uint32_t> offsetBeforePush = currentOffset();
5287     Push(pc); // actually pushes $pc + 8.
5288     ma_nop();
5289     uint32_t pseudoReturnOffset = currentOffset();
5290     leaveNoPool();
5291 
5292     MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8);
5293     return pseudoReturnOffset;
5294 }
5295 
5296 //}}} check_macroassembler_style
5297