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(¬Split, 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(¬Split);
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, ¬Int32);
3207 convertInt32ToDouble(src.payloadReg(), dest.fpu());
3208 ma_b(&end);
3209 bind(¬Int32);
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, ¬Int32);
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(¬Int32);
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, ¬Int32);
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(¬Int32);
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