1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "jit/mips32/MacroAssembler-mips32.h"
8 
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/EndianUtils.h"
11 #include "mozilla/MathAlgorithms.h"
12 
13 #include "jit/Bailouts.h"
14 #include "jit/BaselineFrame.h"
15 #include "jit/JitFrames.h"
16 #include "jit/JitRuntime.h"
17 #include "jit/MacroAssembler.h"
18 #include "jit/mips32/Simulator-mips32.h"
19 #include "jit/MoveEmitter.h"
20 #include "jit/SharedICRegisters.h"
21 #include "util/Memory.h"
22 #include "vm/JitActivation.h"  // js::jit::JitActivation
23 #include "vm/JSContext.h"
24 
25 #include "jit/MacroAssembler-inl.h"
26 
27 using namespace js;
28 using namespace jit;
29 
30 using mozilla::Abs;
31 
32 static const int32_t PAYLOAD_OFFSET = NUNBOX32_PAYLOAD_OFFSET;
33 static const int32_t TAG_OFFSET = NUNBOX32_TYPE_OFFSET;
34 
35 static_assert(sizeof(intptr_t) == 4, "Not 64-bit clean.");
36 
convertBoolToInt32(Register src,Register dest)37 void MacroAssemblerMIPSCompat::convertBoolToInt32(Register src, Register dest) {
38   // Note that C++ bool is only 1 byte, so zero extend it to clear the
39   // higher-order bits.
40   ma_and(dest, src, Imm32(0xff));
41 }
42 
convertInt32ToDouble(Register src,FloatRegister dest)43 void MacroAssemblerMIPSCompat::convertInt32ToDouble(Register src,
44                                                     FloatRegister dest) {
45   as_mtc1(src, dest);
46   as_cvtdw(dest, dest);
47 }
48 
convertInt32ToDouble(const Address & src,FloatRegister dest)49 void MacroAssemblerMIPSCompat::convertInt32ToDouble(const Address& src,
50                                                     FloatRegister dest) {
51   ma_ls(dest, src);
52   as_cvtdw(dest, dest);
53 }
54 
convertInt32ToDouble(const BaseIndex & src,FloatRegister dest)55 void MacroAssemblerMIPSCompat::convertInt32ToDouble(const BaseIndex& src,
56                                                     FloatRegister dest) {
57   computeScaledAddress(src, ScratchRegister);
58   convertInt32ToDouble(Address(ScratchRegister, src.offset), dest);
59 }
60 
convertUInt32ToDouble(Register src,FloatRegister dest)61 void MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src,
62                                                      FloatRegister dest) {
63   Label positive, done;
64   ma_b(src, src, &positive, NotSigned, ShortJump);
65 
66   const uint32_t kExponentShift =
67       mozilla::FloatingPoint<double>::kExponentShift - 32;
68   const uint32_t kExponent =
69       (31 + mozilla::FloatingPoint<double>::kExponentBias);
70 
71   ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
72   ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
73   ma_or(SecondScratchReg, ScratchRegister);
74   ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
75   moveToDoubleHi(SecondScratchReg, dest);
76   moveToDoubleLo(ScratchRegister, dest);
77 
78   ma_b(&done, ShortJump);
79 
80   bind(&positive);
81   convertInt32ToDouble(src, dest);
82 
83   bind(&done);
84 }
85 
convertUInt32ToFloat32(Register src,FloatRegister dest)86 void MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src,
87                                                       FloatRegister dest) {
88   Label positive, done;
89   ma_b(src, src, &positive, NotSigned, ShortJump);
90 
91   const uint32_t kExponentShift =
92       mozilla::FloatingPoint<double>::kExponentShift - 32;
93   const uint32_t kExponent =
94       (31 + mozilla::FloatingPoint<double>::kExponentBias);
95 
96   ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
97   ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
98   ma_or(SecondScratchReg, ScratchRegister);
99   ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
100   FloatRegister destDouble = dest.asDouble();
101   moveToDoubleHi(SecondScratchReg, destDouble);
102   moveToDoubleLo(ScratchRegister, destDouble);
103 
104   convertDoubleToFloat32(destDouble, dest);
105 
106   ma_b(&done, ShortJump);
107 
108   bind(&positive);
109   convertInt32ToFloat32(src, dest);
110 
111   bind(&done);
112 }
113 
convertDoubleToFloat32(FloatRegister src,FloatRegister dest)114 void MacroAssemblerMIPSCompat::convertDoubleToFloat32(FloatRegister src,
115                                                       FloatRegister dest) {
116   as_cvtsd(dest, src);
117 }
118 
convertDoubleToPtr(FloatRegister src,Register dest,Label * fail,bool negativeZeroCheck)119 void MacroAssemblerMIPSCompat::convertDoubleToPtr(FloatRegister src,
120                                                   Register dest, Label* fail,
121                                                   bool negativeZeroCheck) {
122   convertDoubleToInt32(src, dest, fail, negativeZeroCheck);
123 }
124 
125 const int CauseBitPos = int(Assembler::CauseI);
126 const int CauseBitCount = 1 + int(Assembler::CauseV) - int(Assembler::CauseI);
127 const int CauseIOrVMask = ((1 << int(Assembler::CauseI)) |
128                            (1 << int(Assembler::CauseV))) >>
129                           int(Assembler::CauseI);
130 
131 // Checks whether a double is representable as a 32-bit integer. If so, the
132 // integer is written to the output register. Otherwise, a bailout is taken to
133 // the given snapshot. This function overwrites the scratch float register.
convertDoubleToInt32(FloatRegister src,Register dest,Label * fail,bool negativeZeroCheck)134 void MacroAssemblerMIPSCompat::convertDoubleToInt32(FloatRegister src,
135                                                     Register dest, Label* fail,
136                                                     bool negativeZeroCheck) {
137   if (negativeZeroCheck) {
138     moveFromDoubleHi(src, dest);
139     moveFromDoubleLo(src, SecondScratchReg);
140     ma_xor(dest, Imm32(INT32_MIN));
141     ma_or(dest, SecondScratchReg);
142     ma_b(dest, Imm32(0), fail, Assembler::Equal);
143   }
144 
145   // Truncate double to int ; if result is inexact or invalid fail.
146   as_truncwd(ScratchFloat32Reg, src);
147   as_cfc1(ScratchRegister, Assembler::FCSR);
148   moveFromFloat32(ScratchFloat32Reg, dest);
149   ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
150   // Here adding the masking andi instruction just for a precaution.
151   // For the instruction of trunc.*.*, the Floating Point Exceptions can be
152   // only Inexact, Invalid Operation, Unimplemented Operation.
153   // Leaving it maybe is also ok.
154   as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
155   ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
156 }
157 
158 // Checks whether a float32 is representable as a 32-bit integer. If so, the
159 // integer is written to the output register. Otherwise, a bailout is taken to
160 // the given snapshot. This function overwrites the scratch float register.
convertFloat32ToInt32(FloatRegister src,Register dest,Label * fail,bool negativeZeroCheck)161 void MacroAssemblerMIPSCompat::convertFloat32ToInt32(FloatRegister src,
162                                                      Register dest, Label* fail,
163                                                      bool negativeZeroCheck) {
164   if (negativeZeroCheck) {
165     moveFromFloat32(src, dest);
166     ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
167   }
168 
169   as_truncws(ScratchFloat32Reg, src);
170   as_cfc1(ScratchRegister, Assembler::FCSR);
171   moveFromFloat32(ScratchFloat32Reg, dest);
172   ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
173   as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
174   ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
175 }
176 
convertFloat32ToDouble(FloatRegister src,FloatRegister dest)177 void MacroAssemblerMIPSCompat::convertFloat32ToDouble(FloatRegister src,
178                                                       FloatRegister dest) {
179   as_cvtds(dest, src);
180 }
181 
convertInt32ToFloat32(Register src,FloatRegister dest)182 void MacroAssemblerMIPSCompat::convertInt32ToFloat32(Register src,
183                                                      FloatRegister dest) {
184   as_mtc1(src, dest);
185   as_cvtsw(dest, dest);
186 }
187 
convertInt32ToFloat32(const Address & src,FloatRegister dest)188 void MacroAssemblerMIPSCompat::convertInt32ToFloat32(const Address& src,
189                                                      FloatRegister dest) {
190   ma_ls(dest, src);
191   as_cvtsw(dest, dest);
192 }
193 
ma_li(Register dest,CodeLabel * label)194 void MacroAssemblerMIPS::ma_li(Register dest, CodeLabel* label) {
195   BufferOffset bo = m_buffer.nextOffset();
196   ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
197   label->patchAt()->bind(bo.getOffset());
198   label->setLinkMode(CodeLabel::MoveImmediate);
199 }
200 
ma_li(Register dest,ImmWord imm)201 void MacroAssemblerMIPS::ma_li(Register dest, ImmWord imm) {
202   ma_li(dest, Imm32(uint32_t(imm.value)));
203 }
204 
ma_liPatchable(Register dest,ImmPtr imm)205 void MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm) {
206   ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
207 }
208 
ma_liPatchable(Register dest,ImmWord imm)209 void MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmWord imm) {
210   ma_liPatchable(dest, Imm32(int32_t(imm.value)));
211 }
212 
213 // Arithmetic-based ops.
214 
215 // Add.
ma_add32TestOverflow(Register rd,Register rs,Register rt,Label * overflow)216 void MacroAssemblerMIPS::ma_add32TestOverflow(Register rd, Register rs,
217                                               Register rt, Label* overflow) {
218   MOZ_ASSERT_IF(rs == rd, rs != rt);
219   MOZ_ASSERT(rs != ScratchRegister);
220   MOZ_ASSERT(rt != ScratchRegister);
221   MOZ_ASSERT(rd != rt);
222   MOZ_ASSERT(rd != ScratchRegister);
223   MOZ_ASSERT(rd != SecondScratchReg);
224 
225   if (rs == rt) {
226     as_addu(rd, rs, rs);
227     as_xor(SecondScratchReg, rs, rd);
228     ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
229     return;
230   }
231 
232   // If different sign, no overflow
233   as_xor(ScratchRegister, rs, rt);
234 
235   as_addu(rd, rs, rt);
236   as_nor(ScratchRegister, ScratchRegister, zero);
237   // If different sign, then overflow
238   as_xor(SecondScratchReg, rt, rd);
239   as_and(SecondScratchReg, SecondScratchReg, ScratchRegister);
240   ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
241 }
242 
ma_add32TestOverflow(Register rd,Register rs,Imm32 imm,Label * overflow)243 void MacroAssemblerMIPS::ma_add32TestOverflow(Register rd, Register rs,
244                                               Imm32 imm, Label* overflow) {
245   MOZ_ASSERT(rs != ScratchRegister);
246   MOZ_ASSERT(rs != SecondScratchReg);
247   MOZ_ASSERT(rd != ScratchRegister);
248   MOZ_ASSERT(rd != SecondScratchReg);
249 
250   Register rs_copy = rs;
251 
252   if (imm.value > 0) {
253     as_nor(ScratchRegister, rs, zero);
254   } else if (rs == rd) {
255     ma_move(ScratchRegister, rs);
256     rs_copy = ScratchRegister;
257   }
258 
259   if (Imm16::IsInSignedRange(imm.value)) {
260     as_addiu(rd, rs, imm.value);
261   } else {
262     ma_li(SecondScratchReg, imm);
263     as_addu(rd, rs, SecondScratchReg);
264   }
265 
266   if (imm.value > 0) {
267     as_and(ScratchRegister, ScratchRegister, rd);
268   } else {
269     as_nor(SecondScratchReg, rd, zero);
270     as_and(ScratchRegister, rs_copy, SecondScratchReg);
271   }
272 
273   ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan);
274 }
275 
276 // Subtract.
ma_sub32TestOverflow(Register rd,Register rs,Register rt,Label * overflow)277 void MacroAssemblerMIPS::ma_sub32TestOverflow(Register rd, Register rs,
278                                               Register rt, Label* overflow) {
279   // The rs == rt case should probably be folded at MIR stage.
280   // Happens for Number_isInteger*. Not worth specializing here.
281   MOZ_ASSERT_IF(rs == rd, rs != rt);
282   MOZ_ASSERT(rs != SecondScratchReg);
283   MOZ_ASSERT(rt != SecondScratchReg);
284   MOZ_ASSERT(rd != rt);
285   MOZ_ASSERT(rd != ScratchRegister);
286   MOZ_ASSERT(rd != SecondScratchReg);
287 
288   Register rs_copy = rs;
289 
290   if (rs == rd) {
291     ma_move(SecondScratchReg, rs);
292     rs_copy = SecondScratchReg;
293   }
294 
295   as_subu(rd, rs, rt);
296   // If same sign, no overflow
297   as_xor(ScratchRegister, rs_copy, rt);
298   // If different sign, then overflow
299   as_xor(SecondScratchReg, rs_copy, rd);
300   as_and(SecondScratchReg, SecondScratchReg, ScratchRegister);
301   ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
302 }
303 
304 // Memory.
305 
ma_load(Register dest,Address address,LoadStoreSize size,LoadStoreExtension extension)306 void MacroAssemblerMIPS::ma_load(Register dest, Address address,
307                                  LoadStoreSize size,
308                                  LoadStoreExtension extension) {
309   int16_t encodedOffset;
310   Register base;
311 
312   if (isLoongson() && ZeroExtend != extension &&
313       !Imm16::IsInSignedRange(address.offset)) {
314     ma_li(ScratchRegister, Imm32(address.offset));
315     base = address.base;
316 
317     switch (size) {
318       case SizeByte:
319         as_gslbx(dest, base, ScratchRegister, 0);
320         break;
321       case SizeHalfWord:
322         as_gslhx(dest, base, ScratchRegister, 0);
323         break;
324       case SizeWord:
325         as_gslwx(dest, base, ScratchRegister, 0);
326         break;
327       case SizeDouble:
328         as_gsldx(dest, base, ScratchRegister, 0);
329         break;
330       default:
331         MOZ_CRASH("Invalid argument for ma_load");
332     }
333     return;
334   }
335 
336   if (!Imm16::IsInSignedRange(address.offset)) {
337     ma_li(ScratchRegister, Imm32(address.offset));
338     as_addu(ScratchRegister, address.base, ScratchRegister);
339     base = ScratchRegister;
340     encodedOffset = Imm16(0).encode();
341   } else {
342     encodedOffset = Imm16(address.offset).encode();
343     base = address.base;
344   }
345 
346   switch (size) {
347     case SizeByte:
348       if (ZeroExtend == extension) {
349         as_lbu(dest, base, encodedOffset);
350       } else {
351         as_lb(dest, base, encodedOffset);
352       }
353       break;
354     case SizeHalfWord:
355       if (ZeroExtend == extension) {
356         as_lhu(dest, base, encodedOffset);
357       } else {
358         as_lh(dest, base, encodedOffset);
359       }
360       break;
361     case SizeWord:
362       as_lw(dest, base, encodedOffset);
363       break;
364     default:
365       MOZ_CRASH("Invalid argument for ma_load");
366   }
367 }
368 
ma_store(Register data,Address address,LoadStoreSize size,LoadStoreExtension extension)369 void MacroAssemblerMIPS::ma_store(Register data, Address address,
370                                   LoadStoreSize size,
371                                   LoadStoreExtension extension) {
372   int16_t encodedOffset;
373   Register base;
374 
375   if (isLoongson() && !Imm16::IsInSignedRange(address.offset)) {
376     ma_li(ScratchRegister, Imm32(address.offset));
377     base = address.base;
378 
379     switch (size) {
380       case SizeByte:
381         as_gssbx(data, base, ScratchRegister, 0);
382         break;
383       case SizeHalfWord:
384         as_gsshx(data, base, ScratchRegister, 0);
385         break;
386       case SizeWord:
387         as_gsswx(data, base, ScratchRegister, 0);
388         break;
389       case SizeDouble:
390         as_gssdx(data, base, ScratchRegister, 0);
391         break;
392       default:
393         MOZ_CRASH("Invalid argument for ma_store");
394     }
395     return;
396   }
397 
398   if (!Imm16::IsInSignedRange(address.offset)) {
399     ma_li(ScratchRegister, Imm32(address.offset));
400     as_addu(ScratchRegister, address.base, ScratchRegister);
401     base = ScratchRegister;
402     encodedOffset = Imm16(0).encode();
403   } else {
404     encodedOffset = Imm16(address.offset).encode();
405     base = address.base;
406   }
407 
408   switch (size) {
409     case SizeByte:
410       as_sb(data, base, encodedOffset);
411       break;
412     case SizeHalfWord:
413       as_sh(data, base, encodedOffset);
414       break;
415     case SizeWord:
416       as_sw(data, base, encodedOffset);
417       break;
418     default:
419       MOZ_CRASH("Invalid argument for ma_store");
420   }
421 }
422 
computeScaledAddress(const BaseIndex & address,Register dest)423 void MacroAssemblerMIPSCompat::computeScaledAddress(const BaseIndex& address,
424                                                     Register dest) {
425   int32_t shift = Imm32::ShiftOf(address.scale).value;
426   if (shift) {
427     ma_sll(ScratchRegister, address.index, Imm32(shift));
428     as_addu(dest, address.base, ScratchRegister);
429   } else {
430     as_addu(dest, address.base, address.index);
431   }
432 }
433 
434 // Shortcut for when we know we're transferring 32 bits of data.
ma_lw(Register data,Address address)435 void MacroAssemblerMIPS::ma_lw(Register data, Address address) {
436   ma_load(data, address, SizeWord);
437 }
438 
ma_sw(Register data,Address address)439 void MacroAssemblerMIPS::ma_sw(Register data, Address address) {
440   ma_store(data, address, SizeWord);
441 }
442 
ma_sw(Imm32 imm,Address address)443 void MacroAssemblerMIPS::ma_sw(Imm32 imm, Address address) {
444   MOZ_ASSERT(address.base != ScratchRegister);
445   ma_li(ScratchRegister, imm);
446 
447   if (Imm16::IsInSignedRange(address.offset)) {
448     as_sw(ScratchRegister, address.base, address.offset);
449   } else {
450     MOZ_ASSERT(address.base != SecondScratchReg);
451 
452     ma_li(SecondScratchReg, Imm32(address.offset));
453     as_addu(SecondScratchReg, address.base, SecondScratchReg);
454     as_sw(ScratchRegister, SecondScratchReg, 0);
455   }
456 }
457 
ma_sw(Register data,BaseIndex & address)458 void MacroAssemblerMIPS::ma_sw(Register data, BaseIndex& address) {
459   ma_store(data, address, SizeWord);
460 }
461 
ma_pop(Register r)462 void MacroAssemblerMIPS::ma_pop(Register r) {
463   as_lw(r, StackPointer, 0);
464   as_addiu(StackPointer, StackPointer, sizeof(intptr_t));
465 }
466 
ma_push(Register r)467 void MacroAssemblerMIPS::ma_push(Register r) {
468   if (r == sp) {
469     // Pushing sp requires one more instruction.
470     ma_move(ScratchRegister, sp);
471     r = ScratchRegister;
472   }
473 
474   as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
475   as_sw(r, StackPointer, 0);
476 }
477 
478 // Branches when done from within mips-specific code.
ma_b(Register lhs,Address addr,Label * label,Condition c,JumpKind jumpKind)479 void MacroAssemblerMIPS::ma_b(Register lhs, Address addr, Label* label,
480                               Condition c, JumpKind jumpKind) {
481   MOZ_ASSERT(lhs != ScratchRegister);
482   ma_lw(ScratchRegister, addr);
483   ma_b(lhs, ScratchRegister, label, c, jumpKind);
484 }
485 
ma_b(Address addr,Imm32 imm,Label * label,Condition c,JumpKind jumpKind)486 void MacroAssemblerMIPS::ma_b(Address addr, Imm32 imm, Label* label,
487                               Condition c, JumpKind jumpKind) {
488   ma_lw(SecondScratchReg, addr);
489   ma_b(SecondScratchReg, imm, label, c, jumpKind);
490 }
491 
ma_b(Address addr,ImmGCPtr imm,Label * label,Condition c,JumpKind jumpKind)492 void MacroAssemblerMIPS::ma_b(Address addr, ImmGCPtr imm, Label* label,
493                               Condition c, JumpKind jumpKind) {
494   ma_lw(SecondScratchReg, addr);
495   ma_b(SecondScratchReg, imm, label, c, jumpKind);
496 }
497 
ma_bal(Label * label,DelaySlotFill delaySlotFill)498 void MacroAssemblerMIPS::ma_bal(Label* label, DelaySlotFill delaySlotFill) {
499   spew("branch .Llabel %p\n", label);
500   if (label->bound()) {
501     // Generate the long jump for calls because return address has to be
502     // the address after the reserved block.
503     addLongJump(nextOffset(), BufferOffset(label->offset()));
504     ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
505     as_jalr(ScratchRegister);
506     if (delaySlotFill == FillDelaySlot) {
507       as_nop();
508     }
509     return;
510   }
511 
512   // Second word holds a pointer to the next branch in label's chain.
513   uint32_t nextInChain =
514       label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
515 
516   // Make the whole branch continous in the buffer.
517   m_buffer.ensureSpace(4 * sizeof(uint32_t));
518 
519   spew("bal .Llabel %p\n", label);
520   BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
521   writeInst(nextInChain);
522   if (!oom()) {
523     label->use(bo.getOffset());
524   }
525   // Leave space for long jump.
526   as_nop();
527   if (delaySlotFill == FillDelaySlot) {
528     as_nop();
529   }
530 }
531 
branchWithCode(InstImm code,Label * label,JumpKind jumpKind)532 void MacroAssemblerMIPS::branchWithCode(InstImm code, Label* label,
533                                         JumpKind jumpKind) {
534   spew("branch .Llabel %p", label);
535   MOZ_ASSERT(code.encode() !=
536              InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
537   InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
538 
539   if (label->bound()) {
540     int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();
541 
542     if (BOffImm16::IsInRange(offset)) {
543       jumpKind = ShortJump;
544     }
545 
546     if (jumpKind == ShortJump) {
547       MOZ_ASSERT(BOffImm16::IsInRange(offset));
548       code.setBOffImm16(BOffImm16(offset));
549 #ifdef JS_JITSPEW
550       decodeBranchInstAndSpew(code);
551 #endif
552       writeInst(code.encode());
553       as_nop();
554       return;
555     }
556 
557     if (code.encode() == inst_beq.encode()) {
558       // Handle long jump
559       addLongJump(nextOffset(), BufferOffset(label->offset()));
560       ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
561       as_jr(ScratchRegister);
562       as_nop();
563       return;
564     }
565 
566     // Handle long conditional branch
567     spew("invert branch .Llabel %p", label);
568     InstImm code_r = invertBranch(code, BOffImm16(5 * sizeof(uint32_t)));
569 #ifdef JS_JITSPEW
570     decodeBranchInstAndSpew(code_r);
571 #endif
572     writeInst(code_r.encode());
573 
574     // No need for a "nop" here because we can clobber scratch.
575     addLongJump(nextOffset(), BufferOffset(label->offset()));
576     ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
577     as_jr(ScratchRegister);
578     as_nop();
579     return;
580   }
581 
582   // Generate open jump and link it to a label.
583 
584   // Second word holds a pointer to the next branch in label's chain.
585   uint32_t nextInChain =
586       label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
587 
588   if (jumpKind == ShortJump) {
589     // Make the whole branch continous in the buffer.
590     m_buffer.ensureSpace(2 * sizeof(uint32_t));
591 
592     // Indicate that this is short jump with offset 4.
593     code.setBOffImm16(BOffImm16(4));
594 #ifdef JS_JITSPEW
595     decodeBranchInstAndSpew(code);
596 #endif
597     BufferOffset bo = writeInst(code.encode());
598     writeInst(nextInChain);
599     if (!oom()) {
600       label->use(bo.getOffset());
601     }
602     return;
603   }
604 
605   bool conditional = code.encode() != inst_beq.encode();
606 
607   // Make the whole branch continous in the buffer.
608   m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t));
609 
610 #ifdef JS_JITSPEW
611   decodeBranchInstAndSpew(code);
612 #endif
613   BufferOffset bo = writeInst(code.encode());
614   writeInst(nextInChain);
615   if (!oom()) {
616     label->use(bo.getOffset());
617   }
618   // Leave space for potential long jump.
619   as_nop();
620   as_nop();
621   if (conditional) {
622     as_nop();
623   }
624 }
625 
cmp64Set(Condition cond,Register64 lhs,Imm64 val,Register dest)626 void MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs,
627                                         Imm64 val, Register dest) {
628   if (val.value == 0) {
629     switch (cond) {
630       case Assembler::Equal:
631       case Assembler::BelowOrEqual:
632         as_or(dest, lhs.high, lhs.low);
633         as_sltiu(dest, dest, 1);
634         break;
635       case Assembler::NotEqual:
636       case Assembler::Above:
637         as_or(dest, lhs.high, lhs.low);
638         as_sltu(dest, zero, dest);
639         break;
640       case Assembler::LessThan:
641       case Assembler::GreaterThanOrEqual:
642         as_slt(dest, lhs.high, zero);
643         if (cond == Assembler::GreaterThanOrEqual) {
644           as_xori(dest, dest, 1);
645         }
646         break;
647       case Assembler::GreaterThan:
648       case Assembler::LessThanOrEqual:
649         as_or(SecondScratchReg, lhs.high, lhs.low);
650         as_sra(ScratchRegister, lhs.high, 31);
651         as_sltu(dest, ScratchRegister, SecondScratchReg);
652         if (cond == Assembler::LessThanOrEqual) {
653           as_xori(dest, dest, 1);
654         }
655         break;
656       case Assembler::Below:
657       case Assembler::AboveOrEqual:
658         as_ori(dest, zero, cond == Assembler::AboveOrEqual ? 1 : 0);
659         break;
660       default:
661         MOZ_CRASH("Condition code not supported");
662         break;
663     }
664     return;
665   }
666 
667   Condition c = ma_cmp64(cond, lhs, val, dest);
668 
669   switch (cond) {
670     // For Equal/NotEqual cond ma_cmp64 dest holds non boolean result.
671     case Assembler::Equal:
672       as_sltiu(dest, dest, 1);
673       break;
674     case Assembler::NotEqual:
675       as_sltu(dest, zero, dest);
676       break;
677     default:
678       if (c == Assembler::Zero) as_xori(dest, dest, 1);
679       break;
680   }
681 }
682 
cmp64Set(Condition cond,Register64 lhs,Register64 rhs,Register dest)683 void MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs,
684                                         Register64 rhs, Register dest) {
685   Condition c = ma_cmp64(cond, lhs, rhs, dest);
686 
687   switch (cond) {
688     // For Equal/NotEqual cond ma_cmp64 dest holds non boolean result.
689     case Assembler::Equal:
690       as_sltiu(dest, dest, 1);
691       break;
692     case Assembler::NotEqual:
693       as_sltu(dest, zero, dest);
694       break;
695     default:
696       if (c == Assembler::Zero) as_xori(dest, dest, 1);
697       break;
698   }
699 }
700 
ma_cmp64(Condition cond,Register64 lhs,Register64 rhs,Register dest)701 Assembler::Condition MacroAssemblerMIPSCompat::ma_cmp64(Condition cond,
702                                                         Register64 lhs,
703                                                         Register64 rhs,
704                                                         Register dest) {
705   switch (cond) {
706     case Assembler::Equal:
707     case Assembler::NotEqual:
708       as_xor(SecondScratchReg, lhs.high, rhs.high);
709       as_xor(ScratchRegister, lhs.low, rhs.low);
710       as_or(dest, SecondScratchReg, ScratchRegister);
711       return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero;
712       break;
713     case Assembler::LessThan:
714     case Assembler::GreaterThanOrEqual:
715       as_slt(SecondScratchReg, rhs.high, lhs.high);
716       as_sltu(ScratchRegister, lhs.low, rhs.low);
717       as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
718       as_slt(ScratchRegister, lhs.high, rhs.high);
719       as_or(dest, ScratchRegister, SecondScratchReg);
720       return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero
721                                                      : Assembler::NonZero;
722       break;
723     case Assembler::GreaterThan:
724     case Assembler::LessThanOrEqual:
725       as_slt(SecondScratchReg, lhs.high, rhs.high);
726       as_sltu(ScratchRegister, rhs.low, lhs.low);
727       as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
728       as_slt(ScratchRegister, rhs.high, lhs.high);
729       as_or(dest, ScratchRegister, SecondScratchReg);
730       return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero
731                                                   : Assembler::NonZero;
732       break;
733     case Assembler::Below:
734     case Assembler::AboveOrEqual:
735       as_sltu(SecondScratchReg, rhs.high, lhs.high);
736       as_sltu(ScratchRegister, lhs.low, rhs.low);
737       as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
738       as_sltu(ScratchRegister, lhs.high, rhs.high);
739       as_or(dest, ScratchRegister, SecondScratchReg);
740       return (cond == Assembler::AboveOrEqual) ? Assembler::Zero
741                                                : Assembler::NonZero;
742       break;
743     case Assembler::Above:
744     case Assembler::BelowOrEqual:
745       as_sltu(SecondScratchReg, lhs.high, rhs.high);
746       as_sltu(ScratchRegister, rhs.low, lhs.low);
747       as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
748       as_sltu(ScratchRegister, rhs.high, lhs.high);
749       as_or(dest, ScratchRegister, SecondScratchReg);
750       return (cond == Assembler::BelowOrEqual) ? Assembler::Zero
751                                                : Assembler::NonZero;
752       break;
753     default:
754       MOZ_CRASH("Condition code not supported");
755       break;
756   }
757 }
758 
ma_cmp64(Condition cond,Register64 lhs,Imm64 val,Register dest)759 Assembler::Condition MacroAssemblerMIPSCompat::ma_cmp64(Condition cond,
760                                                         Register64 lhs,
761                                                         Imm64 val,
762                                                         Register dest) {
763   MOZ_ASSERT(val.value != 0);
764 
765   switch (cond) {
766     case Assembler::Equal:
767     case Assembler::NotEqual:
768       ma_xor(SecondScratchReg, lhs.high, val.hi());
769       ma_xor(ScratchRegister, lhs.low, val.low());
770       as_or(dest, SecondScratchReg, ScratchRegister);
771       return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero;
772       break;
773     case Assembler::LessThan:
774     case Assembler::GreaterThanOrEqual:
775       ma_li(SecondScratchReg, val.hi());
776       as_slt(ScratchRegister, lhs.high, SecondScratchReg);
777       as_slt(SecondScratchReg, SecondScratchReg, lhs.high);
778       as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
779       ma_li(ScratchRegister, val.low());
780       as_sltu(ScratchRegister, lhs.low, ScratchRegister);
781       as_slt(dest, SecondScratchReg, ScratchRegister);
782       return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero
783                                                      : Assembler::NonZero;
784       break;
785     case Assembler::GreaterThan:
786     case Assembler::LessThanOrEqual:
787       ma_li(SecondScratchReg, val.hi());
788       as_slt(ScratchRegister, SecondScratchReg, lhs.high);
789       as_slt(SecondScratchReg, lhs.high, SecondScratchReg);
790       as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
791       ma_li(ScratchRegister, val.low());
792       as_sltu(ScratchRegister, ScratchRegister, lhs.low);
793       as_slt(dest, SecondScratchReg, ScratchRegister);
794       return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero
795                                                   : Assembler::NonZero;
796       break;
797     case Assembler::Below:
798     case Assembler::AboveOrEqual:
799       ma_li(SecondScratchReg, val.hi());
800       as_sltu(ScratchRegister, lhs.high, SecondScratchReg);
801       as_sltu(SecondScratchReg, SecondScratchReg, lhs.high);
802       as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
803       ma_li(ScratchRegister, val.low());
804       as_sltu(ScratchRegister, lhs.low, ScratchRegister);
805       as_slt(dest, SecondScratchReg, ScratchRegister);
806       return (cond == Assembler::AboveOrEqual) ? Assembler::Zero
807                                                : Assembler::NonZero;
808       break;
809     case Assembler::Above:
810     case Assembler::BelowOrEqual:
811       ma_li(SecondScratchReg, val.hi());
812       as_sltu(ScratchRegister, SecondScratchReg, lhs.high);
813       as_sltu(SecondScratchReg, lhs.high, SecondScratchReg);
814       as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
815       ma_li(ScratchRegister, val.low());
816       as_sltu(ScratchRegister, ScratchRegister, lhs.low);
817       as_slt(dest, SecondScratchReg, ScratchRegister);
818       return (cond == Assembler::BelowOrEqual) ? Assembler::Zero
819                                                : Assembler::NonZero;
820       break;
821     default:
822       MOZ_CRASH("Condition code not supported");
823       break;
824   }
825 }
826 
827 // fp instructions
ma_lid(FloatRegister dest,double value)828 void MacroAssemblerMIPS::ma_lid(FloatRegister dest, double value) {
829   struct DoubleStruct {
830     uint32_t lo;
831     uint32_t hi;
832   };
833   DoubleStruct intStruct = mozilla::BitwiseCast<DoubleStruct>(value);
834 #if MOZ_BIG_ENDIAN()
835   std::swap(intStruct.hi, intStruct.lo);
836 #endif
837 
838   // put hi part of 64 bit value into the odd register
839   if (intStruct.hi == 0) {
840     moveToDoubleHi(zero, dest);
841   } else {
842     ma_li(ScratchRegister, Imm32(intStruct.hi));
843     moveToDoubleHi(ScratchRegister, dest);
844   }
845 
846   // put low part of 64 bit value into the even register
847   if (intStruct.lo == 0) {
848     moveToDoubleLo(zero, dest);
849   } else {
850     ma_li(ScratchRegister, Imm32(intStruct.lo));
851     moveToDoubleLo(ScratchRegister, dest);
852   }
853 }
854 
ma_mv(FloatRegister src,ValueOperand dest)855 void MacroAssemblerMIPS::ma_mv(FloatRegister src, ValueOperand dest) {
856   moveFromDoubleLo(src, dest.payloadReg());
857   moveFromDoubleHi(src, dest.typeReg());
858 }
859 
ma_mv(ValueOperand src,FloatRegister dest)860 void MacroAssemblerMIPS::ma_mv(ValueOperand src, FloatRegister dest) {
861   moveToDoubleLo(src.payloadReg(), dest);
862   moveToDoubleHi(src.typeReg(), dest);
863 }
864 
ma_ls(FloatRegister ft,Address address)865 void MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address) {
866   if (Imm16::IsInSignedRange(address.offset)) {
867     as_lwc1(ft, address.base, address.offset);
868   } else {
869     MOZ_ASSERT(address.base != ScratchRegister);
870     ma_li(ScratchRegister, Imm32(address.offset));
871     if (isLoongson()) {
872       as_gslsx(ft, address.base, ScratchRegister, 0);
873     } else {
874       as_addu(ScratchRegister, address.base, ScratchRegister);
875       as_lwc1(ft, ScratchRegister, 0);
876     }
877   }
878 }
879 
ma_ld(FloatRegister ft,Address address)880 void MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address) {
881   if (Imm16::IsInSignedRange(address.offset)) {
882     as_ldc1(ft, address.base, address.offset);
883   } else {
884     MOZ_ASSERT(address.base != ScratchRegister);
885     ma_li(ScratchRegister, Imm32(address.offset));
886     if (isLoongson()) {
887       as_gsldx(ft, address.base, ScratchRegister, 0);
888     } else {
889       as_addu(ScratchRegister, address.base, ScratchRegister);
890       as_ldc1(ft, ScratchRegister, 0);
891     }
892   }
893 }
894 
ma_sd(FloatRegister ft,Address address)895 void MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address) {
896   if (Imm16::IsInSignedRange(address.offset)) {
897     as_sdc1(ft, address.base, address.offset);
898   } else {
899     MOZ_ASSERT(address.base != ScratchRegister);
900     ma_li(ScratchRegister, Imm32(address.offset));
901     if (isLoongson()) {
902       as_gssdx(ft, address.base, ScratchRegister, 0);
903     } else {
904       as_addu(ScratchRegister, address.base, ScratchRegister);
905       as_sdc1(ft, ScratchRegister, 0);
906     }
907   }
908 }
909 
ma_ss(FloatRegister ft,Address address)910 void MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address) {
911   if (Imm16::IsInSignedRange(address.offset)) {
912     as_swc1(ft, address.base, address.offset);
913   } else {
914     MOZ_ASSERT(address.base != ScratchRegister);
915     ma_li(ScratchRegister, Imm32(address.offset));
916     if (isLoongson()) {
917       as_gsssx(ft, address.base, ScratchRegister, 0);
918     } else {
919       as_addu(ScratchRegister, address.base, ScratchRegister);
920       as_swc1(ft, ScratchRegister, 0);
921     }
922   }
923 }
924 
ma_ldc1WordAligned(FloatRegister ft,Register base,int32_t off)925 void MacroAssemblerMIPS::ma_ldc1WordAligned(FloatRegister ft, Register base,
926                                             int32_t off) {
927   MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
928              Imm16::IsInSignedRange(off + TAG_OFFSET));
929 
930   as_lwc1(ft, base, off + PAYLOAD_OFFSET);
931   as_lwc1(getOddPair(ft), base, off + TAG_OFFSET);
932 }
933 
ma_sdc1WordAligned(FloatRegister ft,Register base,int32_t off)934 void MacroAssemblerMIPS::ma_sdc1WordAligned(FloatRegister ft, Register base,
935                                             int32_t off) {
936   MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
937              Imm16::IsInSignedRange(off + TAG_OFFSET));
938 
939   as_swc1(ft, base, off + PAYLOAD_OFFSET);
940   as_swc1(getOddPair(ft), base, off + TAG_OFFSET);
941 }
942 
ma_pop(FloatRegister f)943 void MacroAssemblerMIPS::ma_pop(FloatRegister f) {
944   if (f.isDouble()) {
945     ma_ldc1WordAligned(f, StackPointer, 0);
946   } else {
947     as_lwc1(f, StackPointer, 0);
948   }
949 
950   as_addiu(StackPointer, StackPointer, f.size());
951 }
952 
ma_push(FloatRegister f)953 void MacroAssemblerMIPS::ma_push(FloatRegister f) {
954   as_addiu(StackPointer, StackPointer, -f.size());
955 
956   if (f.isDouble()) {
957     ma_sdc1WordAligned(f, StackPointer, 0);
958   } else {
959     as_swc1(f, StackPointer, 0);
960   }
961 }
962 
buildOOLFakeExitFrame(void * fakeReturnAddr)963 bool MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) {
964   uint32_t descriptor = MakeFrameDescriptor(
965       asMasm().framePushed(), FrameType::IonJS, ExitFrameLayout::Size());
966 
967   asMasm().Push(Imm32(descriptor));  // descriptor_
968   asMasm().Push(ImmPtr(fakeReturnAddr));
969 
970   return true;
971 }
972 
move32(Imm32 imm,Register dest)973 void MacroAssemblerMIPSCompat::move32(Imm32 imm, Register dest) {
974   ma_li(dest, imm);
975 }
976 
move32(Register src,Register dest)977 void MacroAssemblerMIPSCompat::move32(Register src, Register dest) {
978   ma_move(dest, src);
979 }
980 
movePtr(Register src,Register dest)981 void MacroAssemblerMIPSCompat::movePtr(Register src, Register dest) {
982   ma_move(dest, src);
983 }
movePtr(ImmWord imm,Register dest)984 void MacroAssemblerMIPSCompat::movePtr(ImmWord imm, Register dest) {
985   ma_li(dest, imm);
986 }
987 
movePtr(ImmGCPtr imm,Register dest)988 void MacroAssemblerMIPSCompat::movePtr(ImmGCPtr imm, Register dest) {
989   ma_li(dest, imm);
990 }
991 
movePtr(ImmPtr imm,Register dest)992 void MacroAssemblerMIPSCompat::movePtr(ImmPtr imm, Register dest) {
993   movePtr(ImmWord(uintptr_t(imm.value)), dest);
994 }
movePtr(wasm::SymbolicAddress imm,Register dest)995 void MacroAssemblerMIPSCompat::movePtr(wasm::SymbolicAddress imm,
996                                        Register dest) {
997   append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm));
998   ma_liPatchable(dest, ImmWord(-1));
999 }
1000 
load8ZeroExtend(const Address & address,Register dest)1001 void MacroAssemblerMIPSCompat::load8ZeroExtend(const Address& address,
1002                                                Register dest) {
1003   ma_load(dest, address, SizeByte, ZeroExtend);
1004 }
1005 
load8ZeroExtend(const BaseIndex & src,Register dest)1006 void MacroAssemblerMIPSCompat::load8ZeroExtend(const BaseIndex& src,
1007                                                Register dest) {
1008   ma_load(dest, src, SizeByte, ZeroExtend);
1009 }
1010 
load8SignExtend(const Address & address,Register dest)1011 void MacroAssemblerMIPSCompat::load8SignExtend(const Address& address,
1012                                                Register dest) {
1013   ma_load(dest, address, SizeByte, SignExtend);
1014 }
1015 
load8SignExtend(const BaseIndex & src,Register dest)1016 void MacroAssemblerMIPSCompat::load8SignExtend(const BaseIndex& src,
1017                                                Register dest) {
1018   ma_load(dest, src, SizeByte, SignExtend);
1019 }
1020 
load16ZeroExtend(const Address & address,Register dest)1021 void MacroAssemblerMIPSCompat::load16ZeroExtend(const Address& address,
1022                                                 Register dest) {
1023   ma_load(dest, address, SizeHalfWord, ZeroExtend);
1024 }
1025 
load16ZeroExtend(const BaseIndex & src,Register dest)1026 void MacroAssemblerMIPSCompat::load16ZeroExtend(const BaseIndex& src,
1027                                                 Register dest) {
1028   ma_load(dest, src, SizeHalfWord, ZeroExtend);
1029 }
1030 
load16SignExtend(const Address & address,Register dest)1031 void MacroAssemblerMIPSCompat::load16SignExtend(const Address& address,
1032                                                 Register dest) {
1033   ma_load(dest, address, SizeHalfWord, SignExtend);
1034 }
1035 
load16SignExtend(const BaseIndex & src,Register dest)1036 void MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex& src,
1037                                                 Register dest) {
1038   ma_load(dest, src, SizeHalfWord, SignExtend);
1039 }
1040 
load32(const Address & address,Register dest)1041 void MacroAssemblerMIPSCompat::load32(const Address& address, Register dest) {
1042   ma_load(dest, address, SizeWord);
1043 }
1044 
load32(const BaseIndex & address,Register dest)1045 void MacroAssemblerMIPSCompat::load32(const BaseIndex& address, Register dest) {
1046   ma_load(dest, address, SizeWord);
1047 }
1048 
load32(AbsoluteAddress address,Register dest)1049 void MacroAssemblerMIPSCompat::load32(AbsoluteAddress address, Register dest) {
1050   movePtr(ImmPtr(address.addr), ScratchRegister);
1051   load32(Address(ScratchRegister, 0), dest);
1052 }
1053 
load32(wasm::SymbolicAddress address,Register dest)1054 void MacroAssemblerMIPSCompat::load32(wasm::SymbolicAddress address,
1055                                       Register dest) {
1056   movePtr(address, ScratchRegister);
1057   load32(Address(ScratchRegister, 0), dest);
1058 }
1059 
loadPtr(const Address & address,Register dest)1060 void MacroAssemblerMIPSCompat::loadPtr(const Address& address, Register dest) {
1061   ma_load(dest, address, SizeWord);
1062 }
1063 
loadPtr(const BaseIndex & src,Register dest)1064 void MacroAssemblerMIPSCompat::loadPtr(const BaseIndex& src, Register dest) {
1065   ma_load(dest, src, SizeWord);
1066 }
1067 
loadPtr(AbsoluteAddress address,Register dest)1068 void MacroAssemblerMIPSCompat::loadPtr(AbsoluteAddress address, Register dest) {
1069   movePtr(ImmPtr(address.addr), ScratchRegister);
1070   loadPtr(Address(ScratchRegister, 0), dest);
1071 }
1072 
loadPtr(wasm::SymbolicAddress address,Register dest)1073 void MacroAssemblerMIPSCompat::loadPtr(wasm::SymbolicAddress address,
1074                                        Register dest) {
1075   movePtr(address, ScratchRegister);
1076   loadPtr(Address(ScratchRegister, 0), dest);
1077 }
1078 
loadPrivate(const Address & address,Register dest)1079 void MacroAssemblerMIPSCompat::loadPrivate(const Address& address,
1080                                            Register dest) {
1081   ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET));
1082 }
1083 
loadUnalignedDouble(const wasm::MemoryAccessDesc & access,const BaseIndex & src,Register temp,FloatRegister dest)1084 void MacroAssemblerMIPSCompat::loadUnalignedDouble(
1085     const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
1086     FloatRegister dest) {
1087   MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
1088   computeScaledAddress(src, SecondScratchReg);
1089 
1090   BufferOffset load;
1091   if (Imm16::IsInSignedRange(src.offset) &&
1092       Imm16::IsInSignedRange(src.offset + 7)) {
1093     load = as_lwl(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET + 3);
1094     as_lwr(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET);
1095     append(access, load.getOffset());
1096     moveToDoubleLo(temp, dest);
1097     load = as_lwl(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET + 3);
1098     as_lwr(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET);
1099     append(access, load.getOffset());
1100     moveToDoubleHi(temp, dest);
1101   } else {
1102     ma_li(ScratchRegister, Imm32(src.offset));
1103     as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
1104     load = as_lwl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
1105     as_lwr(temp, ScratchRegister, INT64LOW_OFFSET);
1106     append(access, load.getOffset());
1107     moveToDoubleLo(temp, dest);
1108     load = as_lwl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
1109     as_lwr(temp, ScratchRegister, INT64HIGH_OFFSET);
1110     append(access, load.getOffset());
1111     moveToDoubleHi(temp, dest);
1112   }
1113 }
1114 
loadUnalignedFloat32(const wasm::MemoryAccessDesc & access,const BaseIndex & src,Register temp,FloatRegister dest)1115 void MacroAssemblerMIPSCompat::loadUnalignedFloat32(
1116     const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
1117     FloatRegister dest) {
1118   MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
1119   computeScaledAddress(src, SecondScratchReg);
1120   BufferOffset load;
1121   if (Imm16::IsInSignedRange(src.offset) &&
1122       Imm16::IsInSignedRange(src.offset + 3)) {
1123     load = as_lwl(temp, SecondScratchReg, src.offset + 3);
1124     as_lwr(temp, SecondScratchReg, src.offset);
1125   } else {
1126     ma_li(ScratchRegister, Imm32(src.offset));
1127     as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
1128     load = as_lwl(temp, ScratchRegister, 3);
1129     as_lwr(temp, ScratchRegister, 0);
1130   }
1131   append(access, load.getOffset());
1132   moveToFloat32(temp, dest);
1133 }
1134 
store8(Imm32 imm,const Address & address)1135 void MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address) {
1136   ma_li(SecondScratchReg, imm);
1137   ma_store(SecondScratchReg, address, SizeByte);
1138 }
1139 
store8(Register src,const Address & address)1140 void MacroAssemblerMIPSCompat::store8(Register src, const Address& address) {
1141   ma_store(src, address, SizeByte);
1142 }
1143 
store8(Imm32 imm,const BaseIndex & dest)1144 void MacroAssemblerMIPSCompat::store8(Imm32 imm, const BaseIndex& dest) {
1145   ma_store(imm, dest, SizeByte);
1146 }
1147 
store8(Register src,const BaseIndex & dest)1148 void MacroAssemblerMIPSCompat::store8(Register src, const BaseIndex& dest) {
1149   ma_store(src, dest, SizeByte);
1150 }
1151 
store16(Imm32 imm,const Address & address)1152 void MacroAssemblerMIPSCompat::store16(Imm32 imm, const Address& address) {
1153   ma_li(SecondScratchReg, imm);
1154   ma_store(SecondScratchReg, address, SizeHalfWord);
1155 }
1156 
store16(Register src,const Address & address)1157 void MacroAssemblerMIPSCompat::store16(Register src, const Address& address) {
1158   ma_store(src, address, SizeHalfWord);
1159 }
1160 
store16(Imm32 imm,const BaseIndex & dest)1161 void MacroAssemblerMIPSCompat::store16(Imm32 imm, const BaseIndex& dest) {
1162   ma_store(imm, dest, SizeHalfWord);
1163 }
1164 
store16(Register src,const BaseIndex & address)1165 void MacroAssemblerMIPSCompat::store16(Register src, const BaseIndex& address) {
1166   ma_store(src, address, SizeHalfWord);
1167 }
1168 
store32(Register src,AbsoluteAddress address)1169 void MacroAssemblerMIPSCompat::store32(Register src, AbsoluteAddress address) {
1170   movePtr(ImmPtr(address.addr), ScratchRegister);
1171   store32(src, Address(ScratchRegister, 0));
1172 }
1173 
store32(Register src,const Address & address)1174 void MacroAssemblerMIPSCompat::store32(Register src, const Address& address) {
1175   ma_store(src, address, SizeWord);
1176 }
1177 
store32(Imm32 src,const Address & address)1178 void MacroAssemblerMIPSCompat::store32(Imm32 src, const Address& address) {
1179   move32(src, SecondScratchReg);
1180   ma_store(SecondScratchReg, address, SizeWord);
1181 }
1182 
store32(Imm32 imm,const BaseIndex & dest)1183 void MacroAssemblerMIPSCompat::store32(Imm32 imm, const BaseIndex& dest) {
1184   ma_store(imm, dest, SizeWord);
1185 }
1186 
store32(Register src,const BaseIndex & dest)1187 void MacroAssemblerMIPSCompat::store32(Register src, const BaseIndex& dest) {
1188   ma_store(src, dest, SizeWord);
1189 }
1190 
1191 template <typename T>
storePtr(ImmWord imm,T address)1192 void MacroAssemblerMIPSCompat::storePtr(ImmWord imm, T address) {
1193   ma_li(SecondScratchReg, imm);
1194   ma_store(SecondScratchReg, address, SizeWord);
1195 }
1196 
1197 template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmWord imm,
1198                                                           Address address);
1199 template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmWord imm,
1200                                                             BaseIndex address);
1201 
1202 template <typename T>
storePtr(ImmPtr imm,T address)1203 void MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, T address) {
1204   storePtr(ImmWord(uintptr_t(imm.value)), address);
1205 }
1206 
1207 template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmPtr imm,
1208                                                           Address address);
1209 template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmPtr imm,
1210                                                             BaseIndex address);
1211 
1212 template <typename T>
storePtr(ImmGCPtr imm,T address)1213 void MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, T address) {
1214   movePtr(imm, SecondScratchReg);
1215   storePtr(SecondScratchReg, address);
1216 }
1217 
1218 template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmGCPtr imm,
1219                                                           Address address);
1220 template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmGCPtr imm,
1221                                                             BaseIndex address);
1222 
storePtr(Register src,const Address & address)1223 void MacroAssemblerMIPSCompat::storePtr(Register src, const Address& address) {
1224   ma_store(src, address, SizeWord);
1225 }
1226 
storePtr(Register src,const BaseIndex & address)1227 void MacroAssemblerMIPSCompat::storePtr(Register src,
1228                                         const BaseIndex& address) {
1229   ma_store(src, address, SizeWord);
1230 }
1231 
storePtr(Register src,AbsoluteAddress dest)1232 void MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest) {
1233   movePtr(ImmPtr(dest.addr), ScratchRegister);
1234   storePtr(src, Address(ScratchRegister, 0));
1235 }
1236 
storeUnalignedFloat32(const wasm::MemoryAccessDesc & access,FloatRegister src,Register temp,const BaseIndex & dest)1237 void MacroAssemblerMIPSCompat::storeUnalignedFloat32(
1238     const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
1239     const BaseIndex& dest) {
1240   MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
1241   computeScaledAddress(dest, SecondScratchReg);
1242   moveFromFloat32(src, temp);
1243 
1244   BufferOffset store;
1245   if (Imm16::IsInSignedRange(dest.offset) &&
1246       Imm16::IsInSignedRange(dest.offset + 3)) {
1247     store = as_swl(temp, SecondScratchReg, dest.offset + 3);
1248     as_swr(temp, SecondScratchReg, dest.offset);
1249   } else {
1250     ma_li(ScratchRegister, Imm32(dest.offset));
1251     as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
1252     store = as_swl(temp, ScratchRegister, 3);
1253     as_swr(temp, ScratchRegister, 0);
1254   }
1255   append(access, store.getOffset());
1256 }
1257 
storeUnalignedDouble(const wasm::MemoryAccessDesc & access,FloatRegister src,Register temp,const BaseIndex & dest)1258 void MacroAssemblerMIPSCompat::storeUnalignedDouble(
1259     const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
1260     const BaseIndex& dest) {
1261   MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
1262   computeScaledAddress(dest, SecondScratchReg);
1263 
1264   BufferOffset store;
1265   if (Imm16::IsInSignedRange(dest.offset) &&
1266       Imm16::IsInSignedRange(dest.offset + 7)) {
1267     moveFromDoubleHi(src, temp);
1268     store = as_swl(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET + 3);
1269     as_swr(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET);
1270     moveFromDoubleLo(src, temp);
1271     as_swl(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET + 3);
1272     as_swr(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET);
1273 
1274   } else {
1275     ma_li(ScratchRegister, Imm32(dest.offset));
1276     as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
1277     moveFromDoubleHi(src, temp);
1278     store = as_swl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
1279     as_swr(temp, ScratchRegister, INT64HIGH_OFFSET);
1280     moveFromDoubleLo(src, temp);
1281     as_swl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
1282     as_swr(temp, ScratchRegister, INT64LOW_OFFSET);
1283   }
1284   append(access, store.getOffset());
1285 }
1286 
clampDoubleToUint8(FloatRegister input,Register output)1287 void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) {
1288   as_roundwd(ScratchDoubleReg, input);
1289   ma_li(ScratchRegister, Imm32(255));
1290   as_mfc1(output, ScratchDoubleReg);
1291   zeroDouble(ScratchDoubleReg);
1292   as_sltiu(SecondScratchReg, output, 255);
1293   as_colt(DoubleFloat, ScratchDoubleReg, input);
1294   // if res > 255; res = 255;
1295   as_movz(output, ScratchRegister, SecondScratchReg);
1296   // if !(input > 0); res = 0;
1297   as_movf(output, zero);
1298 }
1299 
1300 // higher level tag testing code
ToPayload(Operand base)1301 Operand MacroAssemblerMIPSCompat::ToPayload(Operand base) {
1302   return Operand(Register::FromCode(base.base()), base.disp() + PAYLOAD_OFFSET);
1303 }
1304 
ToType(Operand base)1305 Operand MacroAssemblerMIPSCompat::ToType(Operand base) {
1306   return Operand(Register::FromCode(base.base()), base.disp() + TAG_OFFSET);
1307 }
1308 
testNullSet(Condition cond,const ValueOperand & value,Register dest)1309 void MacroAssemblerMIPSCompat::testNullSet(Condition cond,
1310                                            const ValueOperand& value,
1311                                            Register dest) {
1312   MOZ_ASSERT(cond == Equal || cond == NotEqual);
1313   ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_NULL), cond);
1314 }
1315 
testObjectSet(Condition cond,const ValueOperand & value,Register dest)1316 void MacroAssemblerMIPSCompat::testObjectSet(Condition cond,
1317                                              const ValueOperand& value,
1318                                              Register dest) {
1319   MOZ_ASSERT(cond == Equal || cond == NotEqual);
1320   ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_OBJECT), cond);
1321 }
1322 
testUndefinedSet(Condition cond,const ValueOperand & value,Register dest)1323 void MacroAssemblerMIPSCompat::testUndefinedSet(Condition cond,
1324                                                 const ValueOperand& value,
1325                                                 Register dest) {
1326   MOZ_ASSERT(cond == Equal || cond == NotEqual);
1327   ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), cond);
1328 }
1329 
1330 // unboxing code
unboxNonDouble(const ValueOperand & operand,Register dest,JSValueType)1331 void MacroAssemblerMIPSCompat::unboxNonDouble(const ValueOperand& operand,
1332                                               Register dest, JSValueType) {
1333   if (operand.payloadReg() != dest) {
1334     ma_move(dest, operand.payloadReg());
1335   }
1336 }
1337 
unboxNonDouble(const Address & src,Register dest,JSValueType)1338 void MacroAssemblerMIPSCompat::unboxNonDouble(const Address& src, Register dest,
1339                                               JSValueType) {
1340   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1341 }
1342 
unboxNonDouble(const BaseIndex & src,Register dest,JSValueType)1343 void MacroAssemblerMIPSCompat::unboxNonDouble(const BaseIndex& src,
1344                                               Register dest, JSValueType) {
1345   computeScaledAddress(src, SecondScratchReg);
1346   ma_lw(dest, Address(SecondScratchReg, src.offset + PAYLOAD_OFFSET));
1347 }
1348 
unboxInt32(const ValueOperand & operand,Register dest)1349 void MacroAssemblerMIPSCompat::unboxInt32(const ValueOperand& operand,
1350                                           Register dest) {
1351   ma_move(dest, operand.payloadReg());
1352 }
1353 
unboxInt32(const Address & src,Register dest)1354 void MacroAssemblerMIPSCompat::unboxInt32(const Address& src, Register dest) {
1355   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1356 }
1357 
unboxBoolean(const ValueOperand & operand,Register dest)1358 void MacroAssemblerMIPSCompat::unboxBoolean(const ValueOperand& operand,
1359                                             Register dest) {
1360   ma_move(dest, operand.payloadReg());
1361 }
1362 
unboxBoolean(const Address & src,Register dest)1363 void MacroAssemblerMIPSCompat::unboxBoolean(const Address& src, Register dest) {
1364   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1365 }
1366 
unboxDouble(const ValueOperand & operand,FloatRegister dest)1367 void MacroAssemblerMIPSCompat::unboxDouble(const ValueOperand& operand,
1368                                            FloatRegister dest) {
1369   moveToDoubleLo(operand.payloadReg(), dest);
1370   moveToDoubleHi(operand.typeReg(), dest);
1371 }
1372 
unboxDouble(const Address & src,FloatRegister dest)1373 void MacroAssemblerMIPSCompat::unboxDouble(const Address& src,
1374                                            FloatRegister dest) {
1375   ma_lw(ScratchRegister, Address(src.base, src.offset + PAYLOAD_OFFSET));
1376   moveToDoubleLo(ScratchRegister, dest);
1377   ma_lw(ScratchRegister, Address(src.base, src.offset + TAG_OFFSET));
1378   moveToDoubleHi(ScratchRegister, dest);
1379 }
1380 
unboxDouble(const BaseIndex & src,FloatRegister dest)1381 void MacroAssemblerMIPSCompat::unboxDouble(const BaseIndex& src,
1382                                            FloatRegister dest) {
1383   loadDouble(src, dest);
1384 }
1385 
unboxString(const ValueOperand & operand,Register dest)1386 void MacroAssemblerMIPSCompat::unboxString(const ValueOperand& operand,
1387                                            Register dest) {
1388   ma_move(dest, operand.payloadReg());
1389 }
1390 
unboxString(const Address & src,Register dest)1391 void MacroAssemblerMIPSCompat::unboxString(const Address& src, Register dest) {
1392   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1393 }
1394 
unboxBigInt(const ValueOperand & operand,Register dest)1395 void MacroAssemblerMIPSCompat::unboxBigInt(const ValueOperand& operand,
1396                                            Register dest) {
1397   ma_move(dest, operand.payloadReg());
1398 }
1399 
unboxBigInt(const Address & src,Register dest)1400 void MacroAssemblerMIPSCompat::unboxBigInt(const Address& src, Register dest) {
1401   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1402 }
1403 
unboxObject(const ValueOperand & src,Register dest)1404 void MacroAssemblerMIPSCompat::unboxObject(const ValueOperand& src,
1405                                            Register dest) {
1406   ma_move(dest, src.payloadReg());
1407 }
1408 
unboxObject(const Address & src,Register dest)1409 void MacroAssemblerMIPSCompat::unboxObject(const Address& src, Register dest) {
1410   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1411 }
1412 
unboxObjectOrNull(const Address & src,Register dest)1413 void MacroAssemblerMIPSCompat::unboxObjectOrNull(const Address& src,
1414                                                  Register dest) {
1415   ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
1416 }
1417 
unboxValue(const ValueOperand & src,AnyRegister dest,JSValueType)1418 void MacroAssemblerMIPSCompat::unboxValue(const ValueOperand& src,
1419                                           AnyRegister dest, JSValueType) {
1420   if (dest.isFloat()) {
1421     Label notInt32, end;
1422     asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
1423     convertInt32ToDouble(src.payloadReg(), dest.fpu());
1424     ma_b(&end, ShortJump);
1425     bind(&notInt32);
1426     unboxDouble(src, dest.fpu());
1427     bind(&end);
1428   } else if (src.payloadReg() != dest.gpr()) {
1429     ma_move(dest.gpr(), src.payloadReg());
1430   }
1431 }
1432 
boxDouble(FloatRegister src,const ValueOperand & dest,FloatRegister)1433 void MacroAssemblerMIPSCompat::boxDouble(FloatRegister src,
1434                                          const ValueOperand& dest,
1435                                          FloatRegister) {
1436   moveFromDoubleLo(src, dest.payloadReg());
1437   moveFromDoubleHi(src, dest.typeReg());
1438 }
1439 
boxNonDouble(JSValueType type,Register src,const ValueOperand & dest)1440 void MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, Register src,
1441                                             const ValueOperand& dest) {
1442   if (src != dest.payloadReg()) {
1443     ma_move(dest.payloadReg(), src);
1444   }
1445   ma_li(dest.typeReg(), ImmType(type));
1446 }
1447 
boolValueToDouble(const ValueOperand & operand,FloatRegister dest)1448 void MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand& operand,
1449                                                  FloatRegister dest) {
1450   convertBoolToInt32(operand.payloadReg(), ScratchRegister);
1451   convertInt32ToDouble(ScratchRegister, dest);
1452 }
1453 
int32ValueToDouble(const ValueOperand & operand,FloatRegister dest)1454 void MacroAssemblerMIPSCompat::int32ValueToDouble(const ValueOperand& operand,
1455                                                   FloatRegister dest) {
1456   convertInt32ToDouble(operand.payloadReg(), dest);
1457 }
1458 
boolValueToFloat32(const ValueOperand & operand,FloatRegister dest)1459 void MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand& operand,
1460                                                   FloatRegister dest) {
1461   convertBoolToInt32(operand.payloadReg(), ScratchRegister);
1462   convertInt32ToFloat32(ScratchRegister, dest);
1463 }
1464 
int32ValueToFloat32(const ValueOperand & operand,FloatRegister dest)1465 void MacroAssemblerMIPSCompat::int32ValueToFloat32(const ValueOperand& operand,
1466                                                    FloatRegister dest) {
1467   convertInt32ToFloat32(operand.payloadReg(), dest);
1468 }
1469 
loadConstantFloat32(float f,FloatRegister dest)1470 void MacroAssemblerMIPSCompat::loadConstantFloat32(float f,
1471                                                    FloatRegister dest) {
1472   ma_lis(dest, f);
1473 }
1474 
loadInt32OrDouble(const Address & src,FloatRegister dest)1475 void MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src,
1476                                                  FloatRegister dest) {
1477   Label notInt32, end;
1478   // If it's an int, convert it to double.
1479   ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET));
1480   asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
1481   ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET));
1482   convertInt32ToDouble(SecondScratchReg, dest);
1483   ma_b(&end, ShortJump);
1484 
1485   // Not an int, just load as double.
1486   bind(&notInt32);
1487   ma_ld(dest, src);
1488   bind(&end);
1489 }
1490 
loadInt32OrDouble(Register base,Register index,FloatRegister dest,int32_t shift)1491 void MacroAssemblerMIPSCompat::loadInt32OrDouble(Register base, Register index,
1492                                                  FloatRegister dest,
1493                                                  int32_t shift) {
1494   Label notInt32, end;
1495 
1496   // If it's an int, convert it to double.
1497 
1498   computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
1499                        SecondScratchReg);
1500   // Since we only have one scratch, we need to stomp over it with the tag.
1501   load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg);
1502   asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
1503 
1504   computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
1505                        SecondScratchReg);
1506   load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg);
1507   convertInt32ToDouble(SecondScratchReg, dest);
1508   ma_b(&end, ShortJump);
1509 
1510   // Not an int, just load as double.
1511   bind(&notInt32);
1512   // First, recompute the offset that had been stored in the scratch register
1513   // since the scratch register was overwritten loading in the type.
1514   computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
1515                        SecondScratchReg);
1516   loadDouble(Address(SecondScratchReg, 0), dest);
1517   bind(&end);
1518 }
1519 
loadConstantDouble(double dp,FloatRegister dest)1520 void MacroAssemblerMIPSCompat::loadConstantDouble(double dp,
1521                                                   FloatRegister dest) {
1522   ma_lid(dest, dp);
1523 }
1524 
extractObject(const Address & address,Register scratch)1525 Register MacroAssemblerMIPSCompat::extractObject(const Address& address,
1526                                                  Register scratch) {
1527   ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET));
1528   return scratch;
1529 }
1530 
extractTag(const Address & address,Register scratch)1531 Register MacroAssemblerMIPSCompat::extractTag(const Address& address,
1532                                               Register scratch) {
1533   ma_lw(scratch, Address(address.base, address.offset + TAG_OFFSET));
1534   return scratch;
1535 }
1536 
extractTag(const BaseIndex & address,Register scratch)1537 Register MacroAssemblerMIPSCompat::extractTag(const BaseIndex& address,
1538                                               Register scratch) {
1539   computeScaledAddress(address, scratch);
1540   return extractTag(Address(scratch, address.offset), scratch);
1541 }
1542 
getType(const Value & val)1543 uint32_t MacroAssemblerMIPSCompat::getType(const Value& val) {
1544   return val.toNunboxTag();
1545 }
1546 
moveData(const Value & val,Register data)1547 void MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) {
1548   if (val.isGCThing()) {
1549     ma_li(data, ImmGCPtr(val.toGCThing()));
1550   } else {
1551     ma_li(data, Imm32(val.toNunboxPayload()));
1552   }
1553 }
1554 
1555 /////////////////////////////////////////////////////////////////
1556 // X86/X64-common/ARM/MIPS interface.
1557 /////////////////////////////////////////////////////////////////
storeValue(ValueOperand val,Operand dst)1558 void MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst) {
1559   storeValue(val, Address(Register::FromCode(dst.base()), dst.disp()));
1560 }
1561 
storeValue(ValueOperand val,const BaseIndex & dest)1562 void MacroAssemblerMIPSCompat::storeValue(ValueOperand val,
1563                                           const BaseIndex& dest) {
1564   computeScaledAddress(dest, SecondScratchReg);
1565   storeValue(val, Address(SecondScratchReg, dest.offset));
1566 }
1567 
storeValue(JSValueType type,Register reg,BaseIndex dest)1568 void MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg,
1569                                           BaseIndex dest) {
1570   computeScaledAddress(dest, ScratchRegister);
1571 
1572   // Make sure that ma_sw doesn't clobber ScratchRegister
1573   int32_t offset = dest.offset;
1574   if (!Imm16::IsInSignedRange(offset)) {
1575     ma_li(SecondScratchReg, Imm32(offset));
1576     as_addu(ScratchRegister, ScratchRegister, SecondScratchReg);
1577     offset = 0;
1578   }
1579 
1580   storeValue(type, reg, Address(ScratchRegister, offset));
1581 }
1582 
storeValue(ValueOperand val,const Address & dest)1583 void MacroAssemblerMIPSCompat::storeValue(ValueOperand val,
1584                                           const Address& dest) {
1585   ma_sw(val.payloadReg(), Address(dest.base, dest.offset + PAYLOAD_OFFSET));
1586   ma_sw(val.typeReg(), Address(dest.base, dest.offset + TAG_OFFSET));
1587 }
1588 
storeValue(JSValueType type,Register reg,Address dest)1589 void MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg,
1590                                           Address dest) {
1591   MOZ_ASSERT(dest.base != SecondScratchReg);
1592 
1593   ma_sw(reg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
1594   ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type)));
1595   ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
1596 }
1597 
storeValue(const Value & val,Address dest)1598 void MacroAssemblerMIPSCompat::storeValue(const Value& val, Address dest) {
1599   MOZ_ASSERT(dest.base != SecondScratchReg);
1600 
1601   ma_li(SecondScratchReg, Imm32(getType(val)));
1602   ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
1603   moveData(val, SecondScratchReg);
1604   ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
1605 }
1606 
storeValue(const Value & val,BaseIndex dest)1607 void MacroAssemblerMIPSCompat::storeValue(const Value& val, BaseIndex dest) {
1608   computeScaledAddress(dest, ScratchRegister);
1609 
1610   // Make sure that ma_sw doesn't clobber ScratchRegister
1611   int32_t offset = dest.offset;
1612   if (!Imm16::IsInSignedRange(offset)) {
1613     ma_li(SecondScratchReg, Imm32(offset));
1614     as_addu(ScratchRegister, ScratchRegister, SecondScratchReg);
1615     offset = 0;
1616   }
1617   storeValue(val, Address(ScratchRegister, offset));
1618 }
1619 
loadValue(const BaseIndex & addr,ValueOperand val)1620 void MacroAssemblerMIPSCompat::loadValue(const BaseIndex& addr,
1621                                          ValueOperand val) {
1622   computeScaledAddress(addr, SecondScratchReg);
1623   loadValue(Address(SecondScratchReg, addr.offset), val);
1624 }
1625 
loadValue(Address src,ValueOperand val)1626 void MacroAssemblerMIPSCompat::loadValue(Address src, ValueOperand val) {
1627   // Ensure that loading the payload does not erase the pointer to the
1628   // Value in memory.
1629   if (src.base != val.payloadReg()) {
1630     ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET));
1631     ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET));
1632   } else {
1633     ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET));
1634     ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET));
1635   }
1636 }
1637 
tagValue(JSValueType type,Register payload,ValueOperand dest)1638 void MacroAssemblerMIPSCompat::tagValue(JSValueType type, Register payload,
1639                                         ValueOperand dest) {
1640   MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
1641   if (payload != dest.payloadReg()) {
1642     ma_move(dest.payloadReg(), payload);
1643   }
1644   ma_li(dest.typeReg(), ImmType(type));
1645 }
1646 
pushValue(ValueOperand val)1647 void MacroAssemblerMIPSCompat::pushValue(ValueOperand val) {
1648   // Allocate stack slots for type and payload. One for each.
1649   asMasm().subPtr(Imm32(sizeof(Value)), StackPointer);
1650   // Store type and payload.
1651   storeValue(val, Address(StackPointer, 0));
1652 }
1653 
pushValue(const Address & addr)1654 void MacroAssemblerMIPSCompat::pushValue(const Address& addr) {
1655   // Allocate stack slots for type and payload. One for each.
1656   ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value)));
1657   // If address is based on StackPointer its offset needs to be adjusted
1658   // to accommodate for previous stack allocation.
1659   int32_t offset =
1660       addr.base != StackPointer ? addr.offset : addr.offset + sizeof(Value);
1661   // Store type and payload.
1662   ma_lw(ScratchRegister, Address(addr.base, offset + TAG_OFFSET));
1663   ma_sw(ScratchRegister, Address(StackPointer, TAG_OFFSET));
1664   ma_lw(ScratchRegister, Address(addr.base, offset + PAYLOAD_OFFSET));
1665   ma_sw(ScratchRegister, Address(StackPointer, PAYLOAD_OFFSET));
1666 }
1667 
popValue(ValueOperand val)1668 void MacroAssemblerMIPSCompat::popValue(ValueOperand val) {
1669   // Load payload and type.
1670   as_lw(val.payloadReg(), StackPointer, PAYLOAD_OFFSET);
1671   as_lw(val.typeReg(), StackPointer, TAG_OFFSET);
1672   // Free stack.
1673   as_addiu(StackPointer, StackPointer, sizeof(Value));
1674 }
1675 
storePayload(const Value & val,Address dest)1676 void MacroAssemblerMIPSCompat::storePayload(const Value& val, Address dest) {
1677   moveData(val, SecondScratchReg);
1678   ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
1679 }
1680 
storePayload(Register src,Address dest)1681 void MacroAssemblerMIPSCompat::storePayload(Register src, Address dest) {
1682   ma_sw(src, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
1683   return;
1684 }
1685 
storePayload(const Value & val,const BaseIndex & dest)1686 void MacroAssemblerMIPSCompat::storePayload(const Value& val,
1687                                             const BaseIndex& dest) {
1688   MOZ_ASSERT(dest.offset == 0);
1689 
1690   computeScaledAddress(dest, SecondScratchReg);
1691 
1692   moveData(val, ScratchRegister);
1693 
1694   as_sw(ScratchRegister, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET);
1695 }
1696 
storePayload(Register src,const BaseIndex & dest)1697 void MacroAssemblerMIPSCompat::storePayload(Register src,
1698                                             const BaseIndex& dest) {
1699   MOZ_ASSERT(dest.offset == 0);
1700 
1701   computeScaledAddress(dest, SecondScratchReg);
1702   as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET);
1703 }
1704 
storeTypeTag(ImmTag tag,Address dest)1705 void MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest) {
1706   ma_li(SecondScratchReg, tag);
1707   ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
1708 }
1709 
storeTypeTag(ImmTag tag,const BaseIndex & dest)1710 void MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) {
1711   MOZ_ASSERT(dest.offset == 0);
1712 
1713   computeScaledAddress(dest, SecondScratchReg);
1714   ma_li(ScratchRegister, tag);
1715   as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET);
1716 }
1717 
breakpoint()1718 void MacroAssemblerMIPSCompat::breakpoint() { as_break(0); }
1719 
ensureDouble(const ValueOperand & source,FloatRegister dest,Label * failure)1720 void MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand& source,
1721                                             FloatRegister dest,
1722                                             Label* failure) {
1723   Label isDouble, done;
1724   asMasm().branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
1725   asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
1726 
1727   convertInt32ToDouble(source.payloadReg(), dest);
1728   jump(&done);
1729 
1730   bind(&isDouble);
1731   unboxDouble(source, dest);
1732 
1733   bind(&done);
1734 }
1735 
checkStackAlignment()1736 void MacroAssemblerMIPSCompat::checkStackAlignment() {
1737 #ifdef DEBUG
1738   Label aligned;
1739   as_andi(ScratchRegister, sp, ABIStackAlignment - 1);
1740   ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump);
1741   as_break(BREAK_STACK_UNALIGNED);
1742   bind(&aligned);
1743 #endif
1744 }
1745 
alignStackPointer()1746 void MacroAssemblerMIPSCompat::alignStackPointer() {
1747   movePtr(StackPointer, SecondScratchReg);
1748   asMasm().subPtr(Imm32(sizeof(intptr_t)), StackPointer);
1749   asMasm().andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer);
1750   storePtr(SecondScratchReg, Address(StackPointer, 0));
1751 }
1752 
restoreStackPointer()1753 void MacroAssemblerMIPSCompat::restoreStackPointer() {
1754   loadPtr(Address(StackPointer, 0), StackPointer);
1755 }
1756 
handleFailureWithHandlerTail(Label * profilerExitTail)1757 void MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(
1758     Label* profilerExitTail) {
1759   // Reserve space for exception information.
1760   int size = (sizeof(ResumeFromException) + ABIStackAlignment) &
1761              ~(ABIStackAlignment - 1);
1762   asMasm().subPtr(Imm32(size), StackPointer);
1763   ma_move(a0, StackPointer);  // Use a0 since it is a first function argument
1764 
1765   // Call the handler.
1766   using Fn = void (*)(ResumeFromException * rfe);
1767   asMasm().setupUnalignedABICall(a1);
1768   asMasm().passABIArg(a0);
1769   asMasm().callWithABI<Fn, HandleException>(
1770       MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
1771 
1772   Label entryFrame;
1773   Label catch_;
1774   Label finally;
1775   Label return_;
1776   Label bailout;
1777   Label wasm;
1778   Label wasmCatch;
1779 
1780   // Already clobbered a0, so use it...
1781   load32(Address(StackPointer, offsetof(ResumeFromException, kind)), a0);
1782   asMasm().branch32(Assembler::Equal, a0,
1783                     Imm32(ResumeFromException::RESUME_ENTRY_FRAME),
1784                     &entryFrame);
1785   asMasm().branch32(Assembler::Equal, a0,
1786                     Imm32(ResumeFromException::RESUME_CATCH), &catch_);
1787   asMasm().branch32(Assembler::Equal, a0,
1788                     Imm32(ResumeFromException::RESUME_FINALLY), &finally);
1789   asMasm().branch32(Assembler::Equal, a0,
1790                     Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
1791   asMasm().branch32(Assembler::Equal, a0,
1792                     Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
1793   asMasm().branch32(Assembler::Equal, a0,
1794                     Imm32(ResumeFromException::RESUME_WASM), &wasm);
1795   asMasm().branch32(Assembler::Equal, a0,
1796                     Imm32(ResumeFromException::RESUME_WASM_CATCH), &wasmCatch);
1797 
1798   breakpoint();  // Invalid kind.
1799 
1800   // No exception handler. Load the error value, load the new stack pointer
1801   // and return from the entry frame.
1802   bind(&entryFrame);
1803   asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
1804   loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)),
1805           StackPointer);
1806 
1807   // We're going to be returning by the ion calling convention
1808   ma_pop(ra);
1809   as_jr(ra);
1810   as_nop();
1811 
1812   // If we found a catch handler, this must be a baseline frame. Restore
1813   // state and jump to the catch block.
1814   bind(&catch_);
1815   loadPtr(Address(StackPointer, offsetof(ResumeFromException, target)), a0);
1816   loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)),
1817           BaselineFrameReg);
1818   loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)),
1819           StackPointer);
1820   jump(a0);
1821 
1822   // If we found a finally block, this must be a baseline frame. Push
1823   // two values expected by JSOp::Retsub: BooleanValue(true) and the
1824   // exception.
1825   bind(&finally);
1826   ValueOperand exception = ValueOperand(a1, a2);
1827   loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception);
1828 
1829   loadPtr(Address(sp, offsetof(ResumeFromException, target)), a0);
1830   loadPtr(Address(sp, offsetof(ResumeFromException, framePointer)),
1831           BaselineFrameReg);
1832   loadPtr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
1833 
1834   pushValue(BooleanValue(true));
1835   pushValue(exception);
1836   jump(a0);
1837 
1838   // Only used in debug mode. Return BaselineFrame->returnValue() to the
1839   // caller.
1840   bind(&return_);
1841   loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)),
1842           BaselineFrameReg);
1843   loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)),
1844           StackPointer);
1845   loadValue(
1846       Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()),
1847       JSReturnOperand);
1848   ma_move(StackPointer, BaselineFrameReg);
1849   pop(BaselineFrameReg);
1850 
1851   // If profiling is enabled, then update the lastProfilingFrame to refer to
1852   // caller frame before returning.
1853   {
1854     Label skipProfilingInstrumentation;
1855     // Test if profiler enabled.
1856     AbsoluteAddress addressOfEnabled(
1857         GetJitContext()->runtime->geckoProfiler().addressOfEnabled());
1858     asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
1859                       &skipProfilingInstrumentation);
1860     jump(profilerExitTail);
1861     bind(&skipProfilingInstrumentation);
1862   }
1863 
1864   ret();
1865 
1866   // If we are bailing out to baseline to handle an exception, jump to
1867   // the bailout tail stub. Load 1 (true) in ReturnReg to indicate success.
1868   bind(&bailout);
1869   loadPtr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), a2);
1870   ma_li(ReturnReg, Imm32(1));
1871   loadPtr(Address(sp, offsetof(ResumeFromException, target)), a1);
1872   jump(a1);
1873 
1874   // If we are throwing and the innermost frame was a wasm frame, reset SP and
1875   // FP; SP is pointing to the unwound return address to the wasm entry, so
1876   // we can just ret().
1877   bind(&wasm);
1878   loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)),
1879           FramePointer);
1880   loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)),
1881           StackPointer);
1882   ret();
1883 
1884   // Found a wasm catch handler, restore state and jump to it.
1885   bind(&wasmCatch);
1886   loadPtr(Address(sp, offsetof(ResumeFromException, target)), a1);
1887   loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)),
1888           FramePointer);
1889   loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)),
1890           StackPointer);
1891   jump(a1);
1892 }
1893 
toggledJump(Label * label)1894 CodeOffset MacroAssemblerMIPSCompat::toggledJump(Label* label) {
1895   CodeOffset ret(nextOffset().getOffset());
1896   ma_b(label);
1897   return ret;
1898 }
1899 
toggledCall(JitCode * target,bool enabled)1900 CodeOffset MacroAssemblerMIPSCompat::toggledCall(JitCode* target,
1901                                                  bool enabled) {
1902   BufferOffset bo = nextOffset();
1903   CodeOffset offset(bo.getOffset());
1904   addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE);
1905   ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
1906   if (enabled) {
1907     as_jalr(ScratchRegister);
1908     as_nop();
1909   } else {
1910     as_nop();
1911     as_nop();
1912   }
1913   MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() ==
1914                             ToggledCallSize(nullptr));
1915   return offset;
1916 }
1917 
profilerEnterFrame(Register framePtr,Register scratch)1918 void MacroAssemblerMIPSCompat::profilerEnterFrame(Register framePtr,
1919                                                   Register scratch) {
1920   asMasm().loadJSContext(scratch);
1921   loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
1922   storePtr(framePtr,
1923            Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
1924   storePtr(ImmPtr(nullptr),
1925            Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
1926 }
1927 
profilerExitFrame()1928 void MacroAssemblerMIPSCompat::profilerExitFrame() {
1929   jump(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
1930 }
1931 
subFromStackPtr(Imm32 imm32)1932 void MacroAssembler::subFromStackPtr(Imm32 imm32) {
1933   if (imm32.value) {
1934     asMasm().subPtr(imm32, StackPointer);
1935   }
1936 }
1937 
1938 //{{{ check_macroassembler_style
1939 // ===============================================================
1940 // Stack manipulation functions.
1941 
PushRegsInMaskSizeInBytes(LiveRegisterSet set)1942 size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) {
1943   return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes();
1944 }
1945 
PushRegsInMask(LiveRegisterSet set)1946 void MacroAssembler::PushRegsInMask(LiveRegisterSet set) {
1947   int32_t diffF = set.fpus().getPushSizeInBytes();
1948   int32_t diffG = set.gprs().size() * sizeof(intptr_t);
1949 
1950   reserveStack(diffG);
1951   for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
1952     diffG -= sizeof(intptr_t);
1953     storePtr(*iter, Address(StackPointer, diffG));
1954   }
1955   MOZ_ASSERT(diffG == 0);
1956 
1957 #ifdef ENABLE_WASM_SIMD
1958 #  error "Needs more careful logic if SIMD is enabled"
1959 #endif
1960 
1961   if (diffF > 0) {
1962     // Double values have to be aligned. We reserve extra space so that we can
1963     // start writing from the first aligned location.
1964     // We reserve a whole extra double so that the buffer has even size.
1965     ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
1966     reserveStack(diffF);
1967 
1968     diffF -= sizeof(double);
1969 
1970     for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
1971          iter.more(); ++iter) {
1972       as_sdc1(*iter, SecondScratchReg, -diffF);
1973       diffF -= sizeof(double);
1974     }
1975 
1976     MOZ_ASSERT(diffF == 0);
1977   }
1978 }
1979 
PopRegsInMaskIgnore(LiveRegisterSet set,LiveRegisterSet ignore)1980 void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set,
1981                                          LiveRegisterSet ignore) {
1982   int32_t diffG = set.gprs().size() * sizeof(intptr_t);
1983   int32_t diffF = set.fpus().getPushSizeInBytes();
1984   const int32_t reservedG = diffG;
1985   const int32_t reservedF = diffF;
1986 
1987 #ifdef ENABLE_WASM_SIMD
1988 #  error "Needs more careful logic if SIMD is enabled"
1989 #endif
1990 
1991   if (reservedF > 0) {
1992     // Read the buffer form the first aligned location.
1993     ma_addu(SecondScratchReg, sp, Imm32(reservedF));
1994     ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
1995 
1996     diffF -= sizeof(double);
1997 
1998     LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush());
1999     for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
2000          iter.more(); ++iter) {
2001       if (!ignore.has(*iter)) {
2002         as_ldc1(*iter, SecondScratchReg, -diffF);
2003       }
2004       diffF -= sizeof(double);
2005     }
2006     freeStack(reservedF);
2007     MOZ_ASSERT(diffF == 0);
2008   }
2009 
2010   for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
2011     diffG -= sizeof(intptr_t);
2012     if (!ignore.has(*iter)) {
2013       loadPtr(Address(StackPointer, diffG), *iter);
2014     }
2015   }
2016   freeStack(reservedG);
2017   MOZ_ASSERT(diffG == 0);
2018 }
2019 
storeRegsInMask(LiveRegisterSet set,Address dest,Register scratch)2020 void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest,
2021                                      Register scratch) {
2022   int32_t diffF = set.fpus().getPushSizeInBytes();
2023   int32_t diffG = set.gprs().size() * sizeof(intptr_t);
2024 
2025   MOZ_ASSERT(dest.offset >= diffG + diffF);
2026   MOZ_ASSERT(dest.base == StackPointer);
2027 
2028   for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
2029     diffG -= sizeof(intptr_t);
2030     dest.offset -= sizeof(intptr_t);
2031     storePtr(*iter, dest);
2032   }
2033   MOZ_ASSERT(diffG == 0);
2034 
2035 #ifdef ENABLE_WASM_SIMD
2036 #  error "Needs more careful logic if SIMD is enabled"
2037 #endif
2038 
2039   if (diffF > 0) {
2040     computeEffectiveAddress(dest, scratch);
2041     ma_and(scratch, scratch, Imm32(~(ABIStackAlignment - 1)));
2042 
2043     diffF -= sizeof(double);
2044 
2045     for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
2046          iter.more(); ++iter) {
2047       as_sdc1(*iter, scratch, -diffF);
2048       diffF -= sizeof(double);
2049     }
2050     MOZ_ASSERT(diffF == 0);
2051   }
2052 }
2053 // ===============================================================
2054 // ABI function calls.
2055 
setupUnalignedABICall(Register scratch)2056 void MacroAssembler::setupUnalignedABICall(Register scratch) {
2057   MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
2058   setupNativeABICall();
2059   dynamicAlignment_ = true;
2060 
2061   ma_move(scratch, StackPointer);
2062 
2063   // Force sp to be aligned
2064   asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
2065   ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
2066   storePtr(scratch, Address(StackPointer, 0));
2067 }
2068 
callWithABIPre(uint32_t * stackAdjust,bool callFromWasm)2069 void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) {
2070   MOZ_ASSERT(inCall_);
2071   uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
2072 
2073   // Reserve place for $ra.
2074   stackForCall += sizeof(intptr_t);
2075 
2076   if (dynamicAlignment_) {
2077     stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment);
2078   } else {
2079     uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0;
2080     stackForCall += ComputeByteAlignment(
2081         stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment);
2082   }
2083 
2084   *stackAdjust = stackForCall;
2085   reserveStack(stackForCall);
2086 
2087   // Save $ra because call is going to clobber it. Restore it in
2088   // callWithABIPost. NOTE: This is needed for calls from SharedIC.
2089   // Maybe we can do this differently.
2090   storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t)));
2091 
2092   // Position all arguments.
2093   {
2094     enoughMemory_ &= moveResolver_.resolve();
2095     if (!enoughMemory_) {
2096       return;
2097     }
2098 
2099     MoveEmitter emitter(*this);
2100     emitter.emit(moveResolver_);
2101     emitter.finish();
2102   }
2103 
2104   assertStackAlignment(ABIStackAlignment);
2105 }
2106 
callWithABIPost(uint32_t stackAdjust,MoveOp::Type result,bool callFromWasm)2107 void MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result,
2108                                      bool callFromWasm) {
2109   // Restore ra value (as stored in callWithABIPre()).
2110   loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra);
2111 
2112   if (dynamicAlignment_) {
2113     // Restore sp value from stack (as stored in setupUnalignedABICall()).
2114     loadPtr(Address(StackPointer, stackAdjust), StackPointer);
2115     // Use adjustFrame instead of freeStack because we already restored sp.
2116     adjustFrame(-stackAdjust);
2117   } else {
2118     freeStack(stackAdjust);
2119   }
2120 
2121 #ifdef DEBUG
2122   MOZ_ASSERT(inCall_);
2123   inCall_ = false;
2124 #endif
2125 }
2126 
callWithABINoProfiler(Register fun,MoveOp::Type result)2127 void MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result) {
2128   // Load the callee in t9, no instruction between the lw and call
2129   // should clobber it. Note that we can't use fun.base because it may
2130   // be one of the IntArg registers clobbered before the call.
2131   ma_move(t9, fun);
2132   uint32_t stackAdjust;
2133   callWithABIPre(&stackAdjust);
2134   call(t9);
2135   callWithABIPost(stackAdjust, result);
2136 }
2137 
callWithABINoProfiler(const Address & fun,MoveOp::Type result)2138 void MacroAssembler::callWithABINoProfiler(const Address& fun,
2139                                            MoveOp::Type result) {
2140   // Load the callee in t9, as above.
2141   loadPtr(Address(fun.base, fun.offset), t9);
2142   uint32_t stackAdjust;
2143   callWithABIPre(&stackAdjust);
2144   call(t9);
2145   callWithABIPost(stackAdjust, result);
2146 }
2147 // ===============================================================
2148 // Move instructions
2149 
moveValue(const TypedOrValueRegister & src,const ValueOperand & dest)2150 void MacroAssembler::moveValue(const TypedOrValueRegister& src,
2151                                const ValueOperand& dest) {
2152   if (src.hasValue()) {
2153     moveValue(src.valueReg(), dest);
2154     return;
2155   }
2156 
2157   MIRType type = src.type();
2158   AnyRegister reg = src.typedReg();
2159 
2160   if (!IsFloatingPointType(type)) {
2161     if (reg.gpr() != dest.payloadReg()) {
2162       move32(reg.gpr(), dest.payloadReg());
2163     }
2164     mov(ImmWord(MIRTypeToTag(type)), dest.typeReg());
2165     return;
2166   }
2167 
2168   ScratchDoubleScope scratch(*this);
2169   FloatRegister freg = reg.fpu();
2170   if (type == MIRType::Float32) {
2171     convertFloat32ToDouble(freg, scratch);
2172     freg = scratch;
2173   }
2174   boxDouble(freg, dest, scratch);
2175 }
2176 
moveValue(const ValueOperand & src,const ValueOperand & dest)2177 void MacroAssembler::moveValue(const ValueOperand& src,
2178                                const ValueOperand& dest) {
2179   Register s0 = src.typeReg();
2180   Register s1 = src.payloadReg();
2181   Register d0 = dest.typeReg();
2182   Register d1 = dest.payloadReg();
2183 
2184   // Either one or both of the source registers could be the same as a
2185   // destination register.
2186   if (s1 == d0) {
2187     if (s0 == d1) {
2188       // If both are, this is just a swap of two registers.
2189       ScratchRegisterScope scratch(*this);
2190       MOZ_ASSERT(d1 != scratch);
2191       MOZ_ASSERT(d0 != scratch);
2192       move32(d1, scratch);
2193       move32(d0, d1);
2194       move32(scratch, d0);
2195       return;
2196     }
2197     // If only one is, copy that source first.
2198     std::swap(s0, s1);
2199     std::swap(d0, d1);
2200   }
2201 
2202   if (s0 != d0) {
2203     move32(s0, d0);
2204   }
2205   if (s1 != d1) {
2206     move32(s1, d1);
2207   }
2208 }
2209 
moveValue(const Value & src,const ValueOperand & dest)2210 void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) {
2211   move32(Imm32(src.toNunboxTag()), dest.typeReg());
2212   if (src.isGCThing()) {
2213     movePtr(ImmGCPtr(src.toGCThing()), dest.payloadReg());
2214   } else {
2215     move32(Imm32(src.toNunboxPayload()), dest.payloadReg());
2216   }
2217 }
2218 
2219 // ===============================================================
2220 // Branch functions
2221 
branchValueIsNurseryCell(Condition cond,const Address & address,Register temp,Label * label)2222 void MacroAssembler::branchValueIsNurseryCell(Condition cond,
2223                                               const Address& address,
2224                                               Register temp, Label* label) {
2225   MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2226 
2227   Label done;
2228 
2229   branchTestGCThing(Assembler::NotEqual, address,
2230                     cond == Assembler::Equal ? &done : label);
2231 
2232   loadPtr(address, temp);
2233   branchPtrInNurseryChunk(cond, temp, InvalidReg, label);
2234 
2235   bind(&done);
2236 }
2237 
branchValueIsNurseryCell(Condition cond,ValueOperand value,Register temp,Label * label)2238 void MacroAssembler::branchValueIsNurseryCell(Condition cond,
2239                                               ValueOperand value, Register temp,
2240                                               Label* label) {
2241   MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
2242 
2243   Label done;
2244 
2245   branchTestGCThing(Assembler::NotEqual, value,
2246                     cond == Assembler::Equal ? &done : label);
2247   branchPtrInNurseryChunk(cond, value.payloadReg(), temp, label);
2248 
2249   bind(&done);
2250 }
2251 
branchTestValue(Condition cond,const ValueOperand & lhs,const Value & rhs,Label * label)2252 void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
2253                                      const Value& rhs, Label* label) {
2254   MOZ_ASSERT(cond == Equal || cond == NotEqual);
2255   ScratchRegisterScope scratch(*this);
2256   moveData(rhs, scratch);
2257 
2258   if (cond == Equal) {
2259     Label done;
2260     ma_b(lhs.payloadReg(), scratch, &done, NotEqual, ShortJump);
2261     { ma_b(lhs.typeReg(), Imm32(getType(rhs)), label, Equal); }
2262     bind(&done);
2263   } else {
2264     ma_b(lhs.payloadReg(), scratch, label, NotEqual);
2265 
2266     ma_b(lhs.typeReg(), Imm32(getType(rhs)), label, NotEqual);
2267   }
2268 }
2269 
2270 // ========================================================================
2271 // Memory access primitives.
2272 template <typename T>
storeUnboxedValue(const ConstantOrRegister & value,MIRType valueType,const T & dest,MIRType slotType)2273 void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
2274                                        MIRType valueType, const T& dest,
2275                                        MIRType slotType) {
2276   if (valueType == MIRType::Double) {
2277     storeDouble(value.reg().typedReg().fpu(), dest);
2278     return;
2279   }
2280 
2281   // Store the type tag if needed.
2282   if (valueType != slotType) {
2283     storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
2284   }
2285 
2286   // Store the payload.
2287   if (value.constant()) {
2288     storePayload(value.value(), dest);
2289   } else {
2290     storePayload(value.reg().typedReg().gpr(), dest);
2291   }
2292 }
2293 
2294 template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
2295                                                 MIRType valueType,
2296                                                 const Address& dest,
2297                                                 MIRType slotType);
2298 template void MacroAssembler::storeUnboxedValue(
2299     const ConstantOrRegister& value, MIRType valueType,
2300     const BaseObjectElementIndex& dest, MIRType slotType);
2301 
PushBoxed(FloatRegister reg)2302 void MacroAssembler::PushBoxed(FloatRegister reg) { Push(reg); }
2303 
wasmBoundsCheck32(Condition cond,Register index,Register boundsCheckLimit,Label * label)2304 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
2305                                        Register boundsCheckLimit,
2306                                        Label* label) {
2307   ma_b(index, boundsCheckLimit, label, cond);
2308 }
2309 
wasmBoundsCheck32(Condition cond,Register index,Address boundsCheckLimit,Label * label)2310 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
2311                                        Address boundsCheckLimit, Label* label) {
2312   SecondScratchRegisterScope scratch2(*this);
2313   load32(boundsCheckLimit, SecondScratchReg);
2314   ma_b(index, SecondScratchReg, label, cond);
2315 }
2316 
wasmTruncateDoubleToUInt32(FloatRegister input,Register output,bool isSaturating,Label * oolEntry)2317 void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input,
2318                                                 Register output,
2319                                                 bool isSaturating,
2320                                                 Label* oolEntry) {
2321   Label done;
2322 
2323   as_truncwd(ScratchFloat32Reg, input);
2324   ma_li(ScratchRegister, Imm32(INT32_MAX));
2325   moveFromFloat32(ScratchFloat32Reg, output);
2326 
2327   // For numbers in  -1.[ : ]INT32_MAX range do nothing more
2328   ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump);
2329 
2330   loadConstantDouble(double(INT32_MAX + 1ULL), ScratchDoubleReg);
2331   ma_li(ScratchRegister, Imm32(INT32_MIN));
2332   as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
2333   as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
2334   as_cfc1(SecondScratchReg, Assembler::FCSR);
2335   moveFromFloat32(ScratchFloat32Reg, output);
2336   ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1);
2337   ma_addu(output, ScratchRegister);
2338 
2339   ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
2340 
2341   bind(&done);
2342 }
2343 
wasmTruncateFloat32ToUInt32(FloatRegister input,Register output,bool isSaturating,Label * oolEntry)2344 void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input,
2345                                                  Register output,
2346                                                  bool isSaturating,
2347                                                  Label* oolEntry) {
2348   Label done;
2349 
2350   as_truncws(ScratchFloat32Reg, input);
2351   ma_li(ScratchRegister, Imm32(INT32_MAX));
2352   moveFromFloat32(ScratchFloat32Reg, output);
2353   // For numbers in  -1.[ : ]INT32_MAX range do nothing more
2354   ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump);
2355 
2356   loadConstantFloat32(float(INT32_MAX + 1ULL), ScratchFloat32Reg);
2357   ma_li(ScratchRegister, Imm32(INT32_MIN));
2358   as_subs(ScratchFloat32Reg, input, ScratchFloat32Reg);
2359   as_truncws(ScratchFloat32Reg, ScratchFloat32Reg);
2360   as_cfc1(SecondScratchReg, Assembler::FCSR);
2361   moveFromFloat32(ScratchFloat32Reg, output);
2362   ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1);
2363   ma_addu(output, ScratchRegister);
2364 
2365   // Guard against negative values that result in 0 due the precision loss.
2366   as_sltiu(ScratchRegister, output, 1);
2367   ma_or(SecondScratchReg, ScratchRegister);
2368 
2369   ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
2370 
2371   bind(&done);
2372 }
2373 
wasmLoadI64(const wasm::MemoryAccessDesc & access,Register memoryBase,Register ptr,Register ptrScratch,Register64 output)2374 void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access,
2375                                  Register memoryBase, Register ptr,
2376                                  Register ptrScratch, Register64 output) {
2377   wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
2378 }
2379 
wasmUnalignedLoadI64(const wasm::MemoryAccessDesc & access,Register memoryBase,Register ptr,Register ptrScratch,Register64 output,Register tmp)2380 void MacroAssembler::wasmUnalignedLoadI64(const wasm::MemoryAccessDesc& access,
2381                                           Register memoryBase, Register ptr,
2382                                           Register ptrScratch,
2383                                           Register64 output, Register tmp) {
2384   wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, tmp);
2385 }
2386 
wasmStoreI64(const wasm::MemoryAccessDesc & access,Register64 value,Register memoryBase,Register ptr,Register ptrScratch)2387 void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access,
2388                                   Register64 value, Register memoryBase,
2389                                   Register ptr, Register ptrScratch) {
2390   wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
2391 }
2392 
wasmUnalignedStoreI64(const wasm::MemoryAccessDesc & access,Register64 value,Register memoryBase,Register ptr,Register ptrScratch,Register tmp)2393 void MacroAssembler::wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access,
2394                                            Register64 value,
2395                                            Register memoryBase, Register ptr,
2396                                            Register ptrScratch, Register tmp) {
2397   wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, tmp);
2398 }
2399 
wasmLoadI64Impl(const wasm::MemoryAccessDesc & access,Register memoryBase,Register ptr,Register ptrScratch,Register64 output,Register tmp)2400 void MacroAssemblerMIPSCompat::wasmLoadI64Impl(
2401     const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
2402     Register ptrScratch, Register64 output, Register tmp) {
2403   uint32_t offset = access.offset();
2404   MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
2405 
2406   MOZ_ASSERT(!access.isZeroExtendSimd128Load());
2407   MOZ_ASSERT(!access.isSplatSimd128Load());
2408   MOZ_ASSERT(!access.isWidenSimd128Load());
2409 
2410   // Maybe add the offset.
2411   if (offset) {
2412     asMasm().movePtr(ptr, ptrScratch);
2413     asMasm().addPtr(Imm32(offset), ptrScratch);
2414     ptr = ptrScratch;
2415   }
2416 
2417   unsigned byteSize = access.byteSize();
2418   bool isSigned;
2419 
2420   switch (access.type()) {
2421     case Scalar::Int8:
2422       isSigned = true;
2423       break;
2424     case Scalar::Uint8:
2425       isSigned = false;
2426       break;
2427     case Scalar::Int16:
2428       isSigned = true;
2429       break;
2430     case Scalar::Uint16:
2431       isSigned = false;
2432       break;
2433     case Scalar::Int32:
2434       isSigned = true;
2435       break;
2436     case Scalar::Uint32:
2437       isSigned = false;
2438       break;
2439     case Scalar::Int64:
2440       isSigned = true;
2441       break;
2442     default:
2443       MOZ_CRASH("unexpected array type");
2444   }
2445 
2446   BaseIndex address(memoryBase, ptr, TimesOne);
2447   MOZ_ASSERT(INT64LOW_OFFSET == 0);
2448   if (IsUnaligned(access)) {
2449     MOZ_ASSERT(tmp != InvalidReg);
2450     if (byteSize <= 4) {
2451       asMasm().ma_load_unaligned(access, output.low, address, tmp,
2452                                  static_cast<LoadStoreSize>(8 * byteSize),
2453                                  isSigned ? SignExtend : ZeroExtend);
2454       if (!isSigned) {
2455         asMasm().move32(Imm32(0), output.high);
2456       } else {
2457         asMasm().ma_sra(output.high, output.low, Imm32(31));
2458       }
2459     } else {
2460       MOZ_ASSERT(output.low != ptr);
2461       asMasm().ma_load_unaligned(access, output.low, address, tmp, SizeWord,
2462                                  ZeroExtend);
2463       asMasm().ma_load_unaligned(
2464           access, output.high,
2465           BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), tmp, SizeWord,
2466           SignExtend);
2467     }
2468     return;
2469   }
2470 
2471   asMasm().memoryBarrierBefore(access.sync());
2472   if (byteSize <= 4) {
2473     asMasm().ma_load(output.low, address,
2474                      static_cast<LoadStoreSize>(8 * byteSize),
2475                      isSigned ? SignExtend : ZeroExtend);
2476     asMasm().append(access, asMasm().size() - 4);
2477     if (!isSigned) {
2478       asMasm().move32(Imm32(0), output.high);
2479     } else {
2480       asMasm().ma_sra(output.high, output.low, Imm32(31));
2481     }
2482   } else {
2483     MOZ_ASSERT(output.low != ptr);
2484     asMasm().ma_load(output.low, BaseIndex(HeapReg, ptr, TimesOne), SizeWord);
2485     asMasm().append(access, asMasm().size() - 4);
2486     asMasm().ma_load(output.high,
2487                      BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET),
2488                      SizeWord);
2489     asMasm().append(access, asMasm().size() - 4);
2490   }
2491   asMasm().memoryBarrierAfter(access.sync());
2492 }
2493 
wasmStoreI64Impl(const wasm::MemoryAccessDesc & access,Register64 value,Register memoryBase,Register ptr,Register ptrScratch,Register tmp)2494 void MacroAssemblerMIPSCompat::wasmStoreI64Impl(
2495     const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
2496     Register ptr, Register ptrScratch, Register tmp) {
2497   uint32_t offset = access.offset();
2498   MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
2499   MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
2500 
2501   // Maybe add the offset.
2502   if (offset) {
2503     asMasm().addPtr(Imm32(offset), ptrScratch);
2504     ptr = ptrScratch;
2505   }
2506 
2507   unsigned byteSize = access.byteSize();
2508   bool isSigned;
2509   switch (access.type()) {
2510     case Scalar::Int8:
2511       isSigned = true;
2512       break;
2513     case Scalar::Uint8:
2514       isSigned = false;
2515       break;
2516     case Scalar::Int16:
2517       isSigned = true;
2518       break;
2519     case Scalar::Uint16:
2520       isSigned = false;
2521       break;
2522     case Scalar::Int32:
2523       isSigned = true;
2524       break;
2525     case Scalar::Uint32:
2526       isSigned = false;
2527       break;
2528     case Scalar::Int64:
2529       isSigned = true;
2530       break;
2531     default:
2532       MOZ_CRASH("unexpected array type");
2533   }
2534 
2535   MOZ_ASSERT(INT64LOW_OFFSET == 0);
2536   BaseIndex address(memoryBase, ptr, TimesOne);
2537   if (IsUnaligned(access)) {
2538     MOZ_ASSERT(tmp != InvalidReg);
2539     if (byteSize <= 4) {
2540       asMasm().ma_store_unaligned(access, value.low, address, tmp,
2541                                   static_cast<LoadStoreSize>(8 * byteSize),
2542                                   isSigned ? SignExtend : ZeroExtend);
2543     } else {
2544       asMasm().ma_store_unaligned(
2545           access, value.high,
2546           BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), tmp, SizeWord,
2547           SignExtend);
2548       asMasm().ma_store_unaligned(access, value.low, address, tmp, SizeWord,
2549                                   ZeroExtend);
2550     }
2551     return;
2552   }
2553 
2554   asMasm().memoryBarrierBefore(access.sync());
2555   if (byteSize <= 4) {
2556     asMasm().ma_store(value.low, address,
2557                       static_cast<LoadStoreSize>(8 * byteSize));
2558     asMasm().append(access, asMasm().size() - 4);
2559   } else {
2560     asMasm().ma_store(value.high,
2561                       BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET),
2562                       SizeWord);
2563     asMasm().append(access, asMasm().size() - 4);
2564     asMasm().ma_store(value.low, address, SizeWord);
2565   }
2566   asMasm().memoryBarrierAfter(access.sync());
2567 }
2568 
EnterAtomic64Region(MacroAssembler & masm,const wasm::MemoryAccessDesc & access,Register addr,Register spinlock,Register scratch)2569 static void EnterAtomic64Region(MacroAssembler& masm,
2570                                 const wasm::MemoryAccessDesc& access,
2571                                 Register addr, Register spinlock,
2572                                 Register scratch) {
2573   masm.movePtr(wasm::SymbolicAddress::js_jit_gAtomic64Lock, spinlock);
2574 
2575   masm.append(access, masm.size());
2576   masm.as_lbu(
2577       zero, addr,
2578       7);  // Force memory trap on invalid access before we enter the spinlock.
2579 
2580   Label tryLock;
2581 
2582   masm.memoryBarrier(MembarFull);
2583 
2584   masm.bind(&tryLock);
2585 
2586   masm.as_ll(scratch, spinlock, 0);
2587   masm.ma_b(scratch, scratch, &tryLock, Assembler::NonZero, ShortJump);
2588   masm.ma_li(scratch, Imm32(1));
2589   masm.as_sc(scratch, spinlock, 0);
2590   masm.ma_b(scratch, scratch, &tryLock, Assembler::Zero, ShortJump);
2591 
2592   masm.memoryBarrier(MembarFull);
2593 }
2594 
ExitAtomic64Region(MacroAssembler & masm,Register spinlock)2595 static void ExitAtomic64Region(MacroAssembler& masm, Register spinlock) {
2596   masm.memoryBarrier(MembarFull);
2597   masm.as_sw(zero, spinlock, 0);
2598   masm.memoryBarrier(MembarFull);
2599 }
2600 
2601 template <typename T>
AtomicLoad64(MacroAssembler & masm,const wasm::MemoryAccessDesc & access,const T & mem,Register64 temp,Register64 output)2602 static void AtomicLoad64(MacroAssembler& masm,
2603                          const wasm::MemoryAccessDesc& access, const T& mem,
2604                          Register64 temp, Register64 output) {
2605   MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg);
2606 
2607   masm.computeEffectiveAddress(mem, SecondScratchReg);
2608 
2609   EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg,
2610                       /* spinlock= */ ScratchRegister,
2611                       /* scratch= */ output.low);
2612 
2613   masm.load64(Address(SecondScratchReg, 0), output);
2614 
2615   ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
2616 }
2617 
wasmAtomicLoad64(const wasm::MemoryAccessDesc & access,const Address & mem,Register64 temp,Register64 output)2618 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access,
2619                                       const Address& mem, Register64 temp,
2620                                       Register64 output) {
2621   AtomicLoad64(*this, access, mem, temp, output);
2622 }
2623 
wasmAtomicLoad64(const wasm::MemoryAccessDesc & access,const BaseIndex & mem,Register64 temp,Register64 output)2624 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access,
2625                                       const BaseIndex& mem, Register64 temp,
2626                                       Register64 output) {
2627   AtomicLoad64(*this, access, mem, temp, output);
2628 }
2629 
2630 template <typename T>
wasmAtomicStore64(const wasm::MemoryAccessDesc & access,const T & mem,Register temp,Register64 value)2631 void MacroAssemblerMIPSCompat::wasmAtomicStore64(
2632     const wasm::MemoryAccessDesc& access, const T& mem, Register temp,
2633     Register64 value) {
2634   computeEffectiveAddress(mem, SecondScratchReg);
2635 
2636   EnterAtomic64Region(asMasm(), access, /* addr= */ SecondScratchReg,
2637                       /* spinlock= */ ScratchRegister, /* scratch= */ temp);
2638 
2639   store64(value, Address(SecondScratchReg, 0));
2640 
2641   ExitAtomic64Region(asMasm(), /* spinlock= */ ScratchRegister);
2642 }
2643 
2644 template void MacroAssemblerMIPSCompat::wasmAtomicStore64(
2645     const wasm::MemoryAccessDesc& access, const Address& mem, Register temp,
2646     Register64 value);
2647 template void MacroAssemblerMIPSCompat::wasmAtomicStore64(
2648     const wasm::MemoryAccessDesc& access, const BaseIndex& mem, Register temp,
2649     Register64 value);
2650 
2651 template <typename T>
WasmCompareExchange64(MacroAssembler & masm,const wasm::MemoryAccessDesc & access,const T & mem,Register64 expect,Register64 replace,Register64 output)2652 static void WasmCompareExchange64(MacroAssembler& masm,
2653                                   const wasm::MemoryAccessDesc& access,
2654                                   const T& mem, Register64 expect,
2655                                   Register64 replace, Register64 output) {
2656   MOZ_ASSERT(output != expect);
2657   MOZ_ASSERT(output != replace);
2658 
2659   Label exit;
2660 
2661   masm.computeEffectiveAddress(mem, SecondScratchReg);
2662   Address addr(SecondScratchReg, 0);
2663 
2664   EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg,
2665                       /* spinlock= */ ScratchRegister,
2666                       /* scratch= */ output.low);
2667 
2668   masm.load64(addr, output);
2669 
2670   masm.ma_b(output.low, expect.low, &exit, Assembler::NotEqual, ShortJump);
2671   masm.ma_b(output.high, expect.high, &exit, Assembler::NotEqual, ShortJump);
2672   masm.store64(replace, addr);
2673   masm.bind(&exit);
2674   ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
2675 }
2676 
wasmCompareExchange64(const wasm::MemoryAccessDesc & access,const Address & mem,Register64 expect,Register64 replace,Register64 output)2677 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
2678                                            const Address& mem,
2679                                            Register64 expect,
2680                                            Register64 replace,
2681                                            Register64 output) {
2682   WasmCompareExchange64(*this, access, mem, expect, replace, output);
2683 }
2684 
wasmCompareExchange64(const wasm::MemoryAccessDesc & access,const BaseIndex & mem,Register64 expect,Register64 replace,Register64 output)2685 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
2686                                            const BaseIndex& mem,
2687                                            Register64 expect,
2688                                            Register64 replace,
2689                                            Register64 output) {
2690   WasmCompareExchange64(*this, access, mem, expect, replace, output);
2691 }
2692 
2693 template <typename T>
WasmAtomicExchange64(MacroAssembler & masm,const wasm::MemoryAccessDesc & access,const T & mem,Register64 src,Register64 output)2694 static void WasmAtomicExchange64(MacroAssembler& masm,
2695                                  const wasm::MemoryAccessDesc& access,
2696                                  const T& mem, Register64 src,
2697                                  Register64 output) {
2698   masm.computeEffectiveAddress(mem, SecondScratchReg);
2699   Address addr(SecondScratchReg, 0);
2700 
2701   EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg,
2702                       /* spinlock= */ ScratchRegister,
2703                       /* scratch= */ output.low);
2704 
2705   masm.load64(addr, output);
2706   masm.store64(src, addr);
2707 
2708   ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
2709 }
2710 
wasmAtomicExchange64(const wasm::MemoryAccessDesc & access,const Address & mem,Register64 src,Register64 output)2711 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
2712                                           const Address& mem, Register64 src,
2713                                           Register64 output) {
2714   WasmAtomicExchange64(*this, access, mem, src, output);
2715 }
2716 
wasmAtomicExchange64(const wasm::MemoryAccessDesc & access,const BaseIndex & mem,Register64 src,Register64 output)2717 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
2718                                           const BaseIndex& mem, Register64 src,
2719                                           Register64 output) {
2720   WasmAtomicExchange64(*this, access, mem, src, output);
2721 }
2722 
2723 template <typename T>
AtomicFetchOp64(MacroAssembler & masm,const wasm::MemoryAccessDesc & access,AtomicOp op,Register64 value,const T & mem,Register64 temp,Register64 output)2724 static void AtomicFetchOp64(MacroAssembler& masm,
2725                             const wasm::MemoryAccessDesc& access, AtomicOp op,
2726                             Register64 value, const T& mem, Register64 temp,
2727                             Register64 output) {
2728   masm.computeEffectiveAddress(mem, SecondScratchReg);
2729 
2730   EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg,
2731                       /* spinlock= */ ScratchRegister,
2732                       /* scratch= */ output.low);
2733 
2734   masm.load64(Address(SecondScratchReg, 0), output);
2735 
2736   switch (op) {
2737     case AtomicFetchAddOp:
2738       masm.as_addu(temp.low, output.low, value.low);
2739       masm.as_sltu(temp.high, temp.low, output.low);
2740       masm.as_addu(temp.high, temp.high, output.high);
2741       masm.as_addu(temp.high, temp.high, value.high);
2742       break;
2743     case AtomicFetchSubOp:
2744       masm.as_sltu(temp.high, output.low, value.low);
2745       masm.as_subu(temp.high, output.high, temp.high);
2746       masm.as_subu(temp.low, output.low, value.low);
2747       masm.as_subu(temp.high, temp.high, value.high);
2748       break;
2749     case AtomicFetchAndOp:
2750       masm.as_and(temp.low, output.low, value.low);
2751       masm.as_and(temp.high, output.high, value.high);
2752       break;
2753     case AtomicFetchOrOp:
2754       masm.as_or(temp.low, output.low, value.low);
2755       masm.as_or(temp.high, output.high, value.high);
2756       break;
2757     case AtomicFetchXorOp:
2758       masm.as_xor(temp.low, output.low, value.low);
2759       masm.as_xor(temp.high, output.high, value.high);
2760       break;
2761     default:
2762       MOZ_CRASH();
2763   }
2764 
2765   masm.store64(temp, Address(SecondScratchReg, 0));
2766 
2767   ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
2768 }
2769 
wasmAtomicFetchOp64(const wasm::MemoryAccessDesc & access,AtomicOp op,Register64 value,const Address & mem,Register64 temp,Register64 output)2770 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
2771                                          AtomicOp op, Register64 value,
2772                                          const Address& mem, Register64 temp,
2773                                          Register64 output) {
2774   AtomicFetchOp64(*this, access, op, value, mem, temp, output);
2775 }
2776 
wasmAtomicFetchOp64(const wasm::MemoryAccessDesc & access,AtomicOp op,Register64 value,const BaseIndex & mem,Register64 temp,Register64 output)2777 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
2778                                          AtomicOp op, Register64 value,
2779                                          const BaseIndex& mem, Register64 temp,
2780                                          Register64 output) {
2781   AtomicFetchOp64(*this, access, op, value, mem, temp, output);
2782 }
2783 
2784 // ========================================================================
2785 // Convert floating point.
2786 
2787 static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
2788 
convertUInt64ToDoubleNeedsTemp()2789 bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; }
2790 
convertUInt64ToDouble(Register64 src,FloatRegister dest,Register temp)2791 void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest,
2792                                            Register temp) {
2793   MOZ_ASSERT(temp == Register::Invalid());
2794   convertUInt32ToDouble(src.high, dest);
2795   loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
2796   mulDouble(ScratchDoubleReg, dest);
2797   convertUInt32ToDouble(src.low, ScratchDoubleReg);
2798   addDouble(ScratchDoubleReg, dest);
2799 }
2800 
convertInt64ToDouble(Register64 src,FloatRegister dest)2801 void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) {
2802   convertInt32ToDouble(src.high, dest);
2803   loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
2804   mulDouble(ScratchDoubleReg, dest);
2805   convertUInt32ToDouble(src.low, ScratchDoubleReg);
2806   addDouble(ScratchDoubleReg, dest);
2807 }
2808 
convertIntPtrToDouble(Register src,FloatRegister dest)2809 void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) {
2810   convertInt32ToDouble(src, dest);
2811 }
2812 
2813 //}}} check_macroassembler_style
2814