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/x86-shared/CodeGenerator-x86-shared.h"
8 
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MathAlgorithms.h"
11 
12 #include "jsmath.h"
13 
14 #include "jit/CodeGenerator.h"
15 #include "jit/InlineScriptTree.h"
16 #include "jit/JitRuntime.h"
17 #include "jit/RangeAnalysis.h"
18 #include "js/ScalarType.h"  // js::Scalar::Type
19 #include "util/DifferentialTesting.h"
20 #include "vm/TraceLogging.h"
21 
22 #include "jit/MacroAssembler-inl.h"
23 #include "jit/shared/CodeGenerator-shared-inl.h"
24 
25 using namespace js;
26 using namespace js::jit;
27 
28 using mozilla::Abs;
29 using mozilla::DebugOnly;
30 using mozilla::FloorLog2;
31 using mozilla::NegativeInfinity;
32 
33 using JS::GenericNaN;
34 
35 namespace js {
36 namespace jit {
37 
CodeGeneratorX86Shared(MIRGenerator * gen,LIRGraph * graph,MacroAssembler * masm)38 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator* gen,
39                                                LIRGraph* graph,
40                                                MacroAssembler* masm)
41     : CodeGeneratorShared(gen, graph, masm) {}
42 
43 #ifdef JS_PUNBOX64
ToOperandOrRegister64(const LInt64Allocation input)44 Operand CodeGeneratorX86Shared::ToOperandOrRegister64(
45     const LInt64Allocation input) {
46   return ToOperand(input.value());
47 }
48 #else
ToOperandOrRegister64(const LInt64Allocation input)49 Register64 CodeGeneratorX86Shared::ToOperandOrRegister64(
50     const LInt64Allocation input) {
51   return ToRegister64(input);
52 }
53 #endif
54 
accept(CodeGeneratorX86Shared * codegen)55 void OutOfLineBailout::accept(CodeGeneratorX86Shared* codegen) {
56   codegen->visitOutOfLineBailout(this);
57 }
58 
emitBranch(Assembler::Condition cond,MBasicBlock * mirTrue,MBasicBlock * mirFalse,Assembler::NaNCond ifNaN)59 void CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond,
60                                         MBasicBlock* mirTrue,
61                                         MBasicBlock* mirFalse,
62                                         Assembler::NaNCond ifNaN) {
63   if (ifNaN == Assembler::NaN_IsFalse) {
64     jumpToBlock(mirFalse, Assembler::Parity);
65   } else if (ifNaN == Assembler::NaN_IsTrue) {
66     jumpToBlock(mirTrue, Assembler::Parity);
67   }
68 
69   if (isNextBlock(mirFalse->lir())) {
70     jumpToBlock(mirTrue, cond);
71   } else {
72     jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
73     jumpToBlock(mirTrue);
74   }
75 }
76 
visitDouble(LDouble * ins)77 void CodeGenerator::visitDouble(LDouble* ins) {
78   const LDefinition* out = ins->getDef(0);
79   masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out));
80 }
81 
visitFloat32(LFloat32 * ins)82 void CodeGenerator::visitFloat32(LFloat32* ins) {
83   const LDefinition* out = ins->getDef(0);
84   masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
85 }
86 
visitTestIAndBranch(LTestIAndBranch * test)87 void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) {
88   Register input = ToRegister(test->input());
89   masm.test32(input, input);
90   emitBranch(Assembler::NonZero, test->ifTrue(), test->ifFalse());
91 }
92 
visitTestDAndBranch(LTestDAndBranch * test)93 void CodeGenerator::visitTestDAndBranch(LTestDAndBranch* test) {
94   const LAllocation* opd = test->input();
95 
96   // vucomisd flags:
97   //             Z  P  C
98   //            ---------
99   //      NaN    1  1  1
100   //        >    0  0  0
101   //        <    0  0  1
102   //        =    1  0  0
103   //
104   // NaN is falsey, so comparing against 0 and then using the Z flag is
105   // enough to determine which branch to take.
106   ScratchDoubleScope scratch(masm);
107   masm.zeroDouble(scratch);
108   masm.vucomisd(scratch, ToFloatRegister(opd));
109   emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
110 }
111 
visitTestFAndBranch(LTestFAndBranch * test)112 void CodeGenerator::visitTestFAndBranch(LTestFAndBranch* test) {
113   const LAllocation* opd = test->input();
114   // vucomiss flags are the same as doubles; see comment above
115   {
116     ScratchFloat32Scope scratch(masm);
117     masm.zeroFloat32(scratch);
118     masm.vucomiss(scratch, ToFloatRegister(opd));
119   }
120   emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
121 }
122 
visitBitAndAndBranch(LBitAndAndBranch * baab)123 void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) {
124   if (baab->right()->isConstant()) {
125     masm.test32(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
126   } else {
127     masm.test32(ToRegister(baab->left()), ToRegister(baab->right()));
128   }
129   emitBranch(baab->cond(), baab->ifTrue(), baab->ifFalse());
130 }
131 
emitCompare(MCompare::CompareType type,const LAllocation * left,const LAllocation * right)132 void CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type,
133                                          const LAllocation* left,
134                                          const LAllocation* right) {
135 #ifdef JS_CODEGEN_X64
136   if (type == MCompare::Compare_Object || type == MCompare::Compare_Symbol ||
137       type == MCompare::Compare_UIntPtr) {
138     if (right->isConstant()) {
139       MOZ_ASSERT(type == MCompare::Compare_UIntPtr);
140       masm.cmpPtr(ToRegister(left), Imm32(ToInt32(right)));
141     } else {
142       masm.cmpPtr(ToRegister(left), ToOperand(right));
143     }
144     return;
145   }
146 #endif
147 
148   if (right->isConstant()) {
149     masm.cmp32(ToRegister(left), Imm32(ToInt32(right)));
150   } else {
151     masm.cmp32(ToRegister(left), ToOperand(right));
152   }
153 }
154 
visitCompare(LCompare * comp)155 void CodeGenerator::visitCompare(LCompare* comp) {
156   MCompare* mir = comp->mir();
157   emitCompare(mir->compareType(), comp->left(), comp->right());
158   masm.emitSet(JSOpToCondition(mir->compareType(), comp->jsop()),
159                ToRegister(comp->output()));
160 }
161 
visitCompareAndBranch(LCompareAndBranch * comp)162 void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) {
163   MCompare* mir = comp->cmpMir();
164   emitCompare(mir->compareType(), comp->left(), comp->right());
165   Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
166   emitBranch(cond, comp->ifTrue(), comp->ifFalse());
167 }
168 
visitCompareD(LCompareD * comp)169 void CodeGenerator::visitCompareD(LCompareD* comp) {
170   FloatRegister lhs = ToFloatRegister(comp->left());
171   FloatRegister rhs = ToFloatRegister(comp->right());
172 
173   Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
174 
175   Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
176   if (comp->mir()->operandsAreNeverNaN()) {
177     nanCond = Assembler::NaN_HandledByCond;
178   }
179 
180   masm.compareDouble(cond, lhs, rhs);
181   masm.emitSet(Assembler::ConditionFromDoubleCondition(cond),
182                ToRegister(comp->output()), nanCond);
183 }
184 
visitCompareF(LCompareF * comp)185 void CodeGenerator::visitCompareF(LCompareF* comp) {
186   FloatRegister lhs = ToFloatRegister(comp->left());
187   FloatRegister rhs = ToFloatRegister(comp->right());
188 
189   Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
190 
191   Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
192   if (comp->mir()->operandsAreNeverNaN()) {
193     nanCond = Assembler::NaN_HandledByCond;
194   }
195 
196   masm.compareFloat(cond, lhs, rhs);
197   masm.emitSet(Assembler::ConditionFromDoubleCondition(cond),
198                ToRegister(comp->output()), nanCond);
199 }
200 
visitNotI(LNotI * ins)201 void CodeGenerator::visitNotI(LNotI* ins) {
202   masm.cmp32(ToRegister(ins->input()), Imm32(0));
203   masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
204 }
205 
visitNotD(LNotD * ins)206 void CodeGenerator::visitNotD(LNotD* ins) {
207   FloatRegister opd = ToFloatRegister(ins->input());
208 
209   // Not returns true if the input is a NaN. We don't have to worry about
210   // it if we know the input is never NaN though.
211   Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
212   if (ins->mir()->operandIsNeverNaN()) {
213     nanCond = Assembler::NaN_HandledByCond;
214   }
215 
216   ScratchDoubleScope scratch(masm);
217   masm.zeroDouble(scratch);
218   masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, scratch);
219   masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
220 }
221 
visitNotF(LNotF * ins)222 void CodeGenerator::visitNotF(LNotF* ins) {
223   FloatRegister opd = ToFloatRegister(ins->input());
224 
225   // Not returns true if the input is a NaN. We don't have to worry about
226   // it if we know the input is never NaN though.
227   Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
228   if (ins->mir()->operandIsNeverNaN()) {
229     nanCond = Assembler::NaN_HandledByCond;
230   }
231 
232   ScratchFloat32Scope scratch(masm);
233   masm.zeroFloat32(scratch);
234   masm.compareFloat(Assembler::DoubleEqualOrUnordered, opd, scratch);
235   masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
236 }
237 
visitCompareDAndBranch(LCompareDAndBranch * comp)238 void CodeGenerator::visitCompareDAndBranch(LCompareDAndBranch* comp) {
239   FloatRegister lhs = ToFloatRegister(comp->left());
240   FloatRegister rhs = ToFloatRegister(comp->right());
241 
242   Assembler::DoubleCondition cond =
243       JSOpToDoubleCondition(comp->cmpMir()->jsop());
244 
245   Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
246   if (comp->cmpMir()->operandsAreNeverNaN()) {
247     nanCond = Assembler::NaN_HandledByCond;
248   }
249 
250   masm.compareDouble(cond, lhs, rhs);
251   emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(),
252              comp->ifFalse(), nanCond);
253 }
254 
visitCompareFAndBranch(LCompareFAndBranch * comp)255 void CodeGenerator::visitCompareFAndBranch(LCompareFAndBranch* comp) {
256   FloatRegister lhs = ToFloatRegister(comp->left());
257   FloatRegister rhs = ToFloatRegister(comp->right());
258 
259   Assembler::DoubleCondition cond =
260       JSOpToDoubleCondition(comp->cmpMir()->jsop());
261 
262   Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
263   if (comp->cmpMir()->operandsAreNeverNaN()) {
264     nanCond = Assembler::NaN_HandledByCond;
265   }
266 
267   masm.compareFloat(cond, lhs, rhs);
268   emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(),
269              comp->ifFalse(), nanCond);
270 }
271 
visitWasmStackArg(LWasmStackArg * ins)272 void CodeGenerator::visitWasmStackArg(LWasmStackArg* ins) {
273   const MWasmStackArg* mir = ins->mir();
274   Address dst(StackPointer, mir->spOffset());
275   if (ins->arg()->isConstant()) {
276     masm.storePtr(ImmWord(ToInt32(ins->arg())), dst);
277   } else if (ins->arg()->isGeneralReg()) {
278     masm.storePtr(ToRegister(ins->arg()), dst);
279   } else {
280     switch (mir->input()->type()) {
281       case MIRType::Double:
282         masm.storeDouble(ToFloatRegister(ins->arg()), dst);
283         return;
284       case MIRType::Float32:
285         masm.storeFloat32(ToFloatRegister(ins->arg()), dst);
286         return;
287 #ifdef ENABLE_WASM_SIMD
288       case MIRType::Simd128:
289         masm.storeUnalignedSimd128(ToFloatRegister(ins->arg()), dst);
290         return;
291 #endif
292       default:
293         break;
294     }
295     MOZ_CRASH("unexpected mir type in WasmStackArg");
296   }
297 }
298 
visitWasmStackArgI64(LWasmStackArgI64 * ins)299 void CodeGenerator::visitWasmStackArgI64(LWasmStackArgI64* ins) {
300   const MWasmStackArg* mir = ins->mir();
301   Address dst(StackPointer, mir->spOffset());
302   if (IsConstant(ins->arg())) {
303     masm.store64(Imm64(ToInt64(ins->arg())), dst);
304   } else {
305     masm.store64(ToRegister64(ins->arg()), dst);
306   }
307 }
308 
visitWasmSelect(LWasmSelect * ins)309 void CodeGenerator::visitWasmSelect(LWasmSelect* ins) {
310   MIRType mirType = ins->mir()->type();
311 
312   Register cond = ToRegister(ins->condExpr());
313   Operand falseExpr = ToOperand(ins->falseExpr());
314 
315   masm.test32(cond, cond);
316 
317   if (mirType == MIRType::Int32 || mirType == MIRType::RefOrNull) {
318     Register out = ToRegister(ins->output());
319     MOZ_ASSERT(ToRegister(ins->trueExpr()) == out,
320                "true expr input is reused for output");
321     if (mirType == MIRType::Int32) {
322       masm.cmovz32(falseExpr, out);
323     } else {
324       masm.cmovzPtr(falseExpr, out);
325     }
326     return;
327   }
328 
329   FloatRegister out = ToFloatRegister(ins->output());
330   MOZ_ASSERT(ToFloatRegister(ins->trueExpr()) == out,
331              "true expr input is reused for output");
332 
333   Label done;
334   masm.j(Assembler::NonZero, &done);
335 
336   if (mirType == MIRType::Float32) {
337     if (falseExpr.kind() == Operand::FPREG) {
338       masm.moveFloat32(ToFloatRegister(ins->falseExpr()), out);
339     } else {
340       masm.loadFloat32(falseExpr, out);
341     }
342   } else if (mirType == MIRType::Double) {
343     if (falseExpr.kind() == Operand::FPREG) {
344       masm.moveDouble(ToFloatRegister(ins->falseExpr()), out);
345     } else {
346       masm.loadDouble(falseExpr, out);
347     }
348   } else if (mirType == MIRType::Simd128) {
349     if (falseExpr.kind() == Operand::FPREG) {
350       masm.moveSimd128(ToFloatRegister(ins->falseExpr()), out);
351     } else {
352       masm.loadUnalignedSimd128(falseExpr, out);
353     }
354   } else {
355     MOZ_CRASH("unhandled type in visitWasmSelect!");
356   }
357 
358   masm.bind(&done);
359 }
360 
visitWasmCompareAndSelect(LWasmCompareAndSelect * ins)361 void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) {
362   emitWasmCompareAndSelect(ins);
363 }
364 
visitWasmReinterpret(LWasmReinterpret * lir)365 void CodeGenerator::visitWasmReinterpret(LWasmReinterpret* lir) {
366   MOZ_ASSERT(gen->compilingWasm());
367   MWasmReinterpret* ins = lir->mir();
368 
369   MIRType to = ins->type();
370 #ifdef DEBUG
371   MIRType from = ins->input()->type();
372 #endif
373 
374   switch (to) {
375     case MIRType::Int32:
376       MOZ_ASSERT(from == MIRType::Float32);
377       masm.vmovd(ToFloatRegister(lir->input()), ToRegister(lir->output()));
378       break;
379     case MIRType::Float32:
380       MOZ_ASSERT(from == MIRType::Int32);
381       masm.vmovd(ToRegister(lir->input()), ToFloatRegister(lir->output()));
382       break;
383     case MIRType::Double:
384     case MIRType::Int64:
385       MOZ_CRASH("not handled by this LIR opcode");
386     default:
387       MOZ_CRASH("unexpected WasmReinterpret");
388   }
389 }
390 
visitAsmJSLoadHeap(LAsmJSLoadHeap * ins)391 void CodeGenerator::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) {
392   const MAsmJSLoadHeap* mir = ins->mir();
393   MOZ_ASSERT(mir->access().offset() == 0);
394 
395   const LAllocation* ptr = ins->ptr();
396   const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
397   AnyRegister out = ToAnyRegister(ins->output());
398 
399   Scalar::Type accessType = mir->accessType();
400 
401   OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
402   if (mir->needsBoundsCheck()) {
403     ool = new (alloc()) OutOfLineLoadTypedArrayOutOfBounds(out, accessType);
404     addOutOfLineCode(ool, mir);
405 
406     masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ToRegister(ptr),
407                            ToRegister(boundsCheckLimit), ool->entry());
408   }
409 
410   Operand srcAddr = toMemoryAccessOperand(ins, 0);
411   masm.wasmLoad(mir->access(), srcAddr, out);
412 
413   if (ool) {
414     masm.bind(ool->rejoin());
415   }
416 }
417 
visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds * ool)418 void CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(
419     OutOfLineLoadTypedArrayOutOfBounds* ool) {
420   switch (ool->viewType()) {
421     case Scalar::Int64:
422     case Scalar::BigInt64:
423     case Scalar::BigUint64:
424     case Scalar::Simd128:
425     case Scalar::MaxTypedArrayViewType:
426       MOZ_CRASH("unexpected array type");
427     case Scalar::Float32:
428       masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
429       break;
430     case Scalar::Float64:
431       masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
432       break;
433     case Scalar::Int8:
434     case Scalar::Uint8:
435     case Scalar::Int16:
436     case Scalar::Uint16:
437     case Scalar::Int32:
438     case Scalar::Uint32:
439     case Scalar::Uint8Clamped:
440       Register destReg = ool->dest().gpr();
441       masm.mov(ImmWord(0), destReg);
442       break;
443   }
444   masm.jmp(ool->rejoin());
445 }
446 
visitAsmJSStoreHeap(LAsmJSStoreHeap * ins)447 void CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) {
448   const MAsmJSStoreHeap* mir = ins->mir();
449 
450   const LAllocation* ptr = ins->ptr();
451   const LAllocation* value = ins->value();
452   const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
453 
454   Scalar::Type accessType = mir->accessType();
455   canonicalizeIfDeterministic(accessType, value);
456 
457   Label rejoin;
458   if (mir->needsBoundsCheck()) {
459     masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ToRegister(ptr),
460                            ToRegister(boundsCheckLimit), &rejoin);
461   }
462 
463   Operand dstAddr = toMemoryAccessOperand(ins, 0);
464   masm.wasmStore(mir->access(), ToAnyRegister(value), dstAddr);
465 
466   if (rejoin.used()) {
467     masm.bind(&rejoin);
468   }
469 }
470 
visitWasmAddOffset(LWasmAddOffset * lir)471 void CodeGenerator::visitWasmAddOffset(LWasmAddOffset* lir) {
472   MWasmAddOffset* mir = lir->mir();
473   Register base = ToRegister(lir->base());
474   Register out = ToRegister(lir->output());
475 
476   if (base != out) {
477     masm.move32(base, out);
478   }
479   masm.add32(Imm32(mir->offset()), out);
480 
481   Label ok;
482   masm.j(Assembler::CarryClear, &ok);
483   masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
484   masm.bind(&ok);
485 }
486 
visitWasmTruncateToInt32(LWasmTruncateToInt32 * lir)487 void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) {
488   FloatRegister input = ToFloatRegister(lir->input());
489   Register output = ToRegister(lir->output());
490 
491   MWasmTruncateToInt32* mir = lir->mir();
492   MIRType inputType = mir->input()->type();
493 
494   MOZ_ASSERT(inputType == MIRType::Double || inputType == MIRType::Float32);
495 
496   auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output);
497   addOutOfLineCode(ool, mir);
498 
499   Label* oolEntry = ool->entry();
500   if (mir->isUnsigned()) {
501     if (inputType == MIRType::Double) {
502       masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(),
503                                       oolEntry);
504     } else if (inputType == MIRType::Float32) {
505       masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(),
506                                        oolEntry);
507     } else {
508       MOZ_CRASH("unexpected type");
509     }
510     if (mir->isSaturating()) {
511       masm.bind(ool->rejoin());
512     }
513     return;
514   }
515 
516   if (inputType == MIRType::Double) {
517     masm.wasmTruncateDoubleToInt32(input, output, mir->isSaturating(),
518                                    oolEntry);
519   } else if (inputType == MIRType::Float32) {
520     masm.wasmTruncateFloat32ToInt32(input, output, mir->isSaturating(),
521                                     oolEntry);
522   } else {
523     MOZ_CRASH("unexpected type");
524   }
525 
526   masm.bind(ool->rejoin());
527 }
528 
generateOutOfLineCode()529 bool CodeGeneratorX86Shared::generateOutOfLineCode() {
530   if (!CodeGeneratorShared::generateOutOfLineCode()) {
531     return false;
532   }
533 
534   if (deoptLabel_.used()) {
535     // All non-table-based bailouts will go here.
536     masm.bind(&deoptLabel_);
537 
538     // Push the frame size, so the handler can recover the IonScript.
539     masm.push(Imm32(frameSize()));
540 
541     TrampolinePtr handler = gen->jitRuntime()->getGenericBailoutHandler();
542     masm.jump(handler);
543   }
544 
545   return !masm.oom();
546 }
547 
548 class BailoutJump {
549   Assembler::Condition cond_;
550 
551  public:
BailoutJump(Assembler::Condition cond)552   explicit BailoutJump(Assembler::Condition cond) : cond_(cond) {}
553 #ifdef JS_CODEGEN_X86
operator ()(MacroAssembler & masm,uint8_t * code) const554   void operator()(MacroAssembler& masm, uint8_t* code) const {
555     masm.j(cond_, ImmPtr(code), RelocationKind::HARDCODED);
556   }
557 #endif
operator ()(MacroAssembler & masm,Label * label) const558   void operator()(MacroAssembler& masm, Label* label) const {
559     masm.j(cond_, label);
560   }
561 };
562 
563 class BailoutLabel {
564   Label* label_;
565 
566  public:
BailoutLabel(Label * label)567   explicit BailoutLabel(Label* label) : label_(label) {}
568 #ifdef JS_CODEGEN_X86
operator ()(MacroAssembler & masm,uint8_t * code) const569   void operator()(MacroAssembler& masm, uint8_t* code) const {
570     masm.retarget(label_, ImmPtr(code), RelocationKind::HARDCODED);
571   }
572 #endif
operator ()(MacroAssembler & masm,Label * label) const573   void operator()(MacroAssembler& masm, Label* label) const {
574     masm.retarget(label_, label);
575   }
576 };
577 
578 template <typename T>
bailout(const T & binder,LSnapshot * snapshot)579 void CodeGeneratorX86Shared::bailout(const T& binder, LSnapshot* snapshot) {
580   encode(snapshot);
581 
582   // Though the assembler doesn't track all frame pushes, at least make sure
583   // the known value makes sense. We can't use bailout tables if the stack
584   // isn't properly aligned to the static frame size.
585   MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_,
586                 frameClass_.frameSize() == masm.framePushed());
587 
588 #ifdef JS_CODEGEN_X86
589   // On x64, bailout tables are pointless, because 16 extra bytes are
590   // reserved per external jump, whereas it takes only 10 bytes to encode a
591   // a non-table based bailout.
592   if (assignBailoutId(snapshot)) {
593     binder(masm, deoptTable_->value +
594                      snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE);
595     return;
596   }
597 #endif
598 
599   // We could not use a jump table, either because all bailout IDs were
600   // reserved, or a jump table is not optimal for this frame size or
601   // platform. Whatever, we will generate a lazy bailout.
602   //
603   // All bailout code is associated with the bytecodeSite of the block we are
604   // bailing out from.
605   InlineScriptTree* tree = snapshot->mir()->block()->trackedTree();
606   OutOfLineBailout* ool = new (alloc()) OutOfLineBailout(snapshot);
607   addOutOfLineCode(ool,
608                    new (alloc()) BytecodeSite(tree, tree->script()->code()));
609 
610   binder(masm, ool->entry());
611 }
612 
bailoutIf(Assembler::Condition condition,LSnapshot * snapshot)613 void CodeGeneratorX86Shared::bailoutIf(Assembler::Condition condition,
614                                        LSnapshot* snapshot) {
615   bailout(BailoutJump(condition), snapshot);
616 }
617 
bailoutIf(Assembler::DoubleCondition condition,LSnapshot * snapshot)618 void CodeGeneratorX86Shared::bailoutIf(Assembler::DoubleCondition condition,
619                                        LSnapshot* snapshot) {
620   MOZ_ASSERT(Assembler::NaNCondFromDoubleCondition(condition) ==
621              Assembler::NaN_HandledByCond);
622   bailoutIf(Assembler::ConditionFromDoubleCondition(condition), snapshot);
623 }
624 
bailoutFrom(Label * label,LSnapshot * snapshot)625 void CodeGeneratorX86Shared::bailoutFrom(Label* label, LSnapshot* snapshot) {
626   MOZ_ASSERT_IF(!masm.oom(), label->used() && !label->bound());
627   bailout(BailoutLabel(label), snapshot);
628 }
629 
bailout(LSnapshot * snapshot)630 void CodeGeneratorX86Shared::bailout(LSnapshot* snapshot) {
631   Label label;
632   masm.jump(&label);
633   bailoutFrom(&label, snapshot);
634 }
635 
visitOutOfLineBailout(OutOfLineBailout * ool)636 void CodeGeneratorX86Shared::visitOutOfLineBailout(OutOfLineBailout* ool) {
637   masm.push(Imm32(ool->snapshot()->snapshotOffset()));
638   masm.jmp(&deoptLabel_);
639 }
640 
visitMinMaxD(LMinMaxD * ins)641 void CodeGenerator::visitMinMaxD(LMinMaxD* ins) {
642   FloatRegister first = ToFloatRegister(ins->first());
643   FloatRegister second = ToFloatRegister(ins->second());
644 #ifdef DEBUG
645   FloatRegister output = ToFloatRegister(ins->output());
646   MOZ_ASSERT(first == output);
647 #endif
648 
649   bool handleNaN = !ins->mir()->range() || ins->mir()->range()->canBeNaN();
650 
651   if (ins->mir()->isMax()) {
652     masm.maxDouble(second, first, handleNaN);
653   } else {
654     masm.minDouble(second, first, handleNaN);
655   }
656 }
657 
visitMinMaxF(LMinMaxF * ins)658 void CodeGenerator::visitMinMaxF(LMinMaxF* ins) {
659   FloatRegister first = ToFloatRegister(ins->first());
660   FloatRegister second = ToFloatRegister(ins->second());
661 #ifdef DEBUG
662   FloatRegister output = ToFloatRegister(ins->output());
663   MOZ_ASSERT(first == output);
664 #endif
665 
666   bool handleNaN = !ins->mir()->range() || ins->mir()->range()->canBeNaN();
667 
668   if (ins->mir()->isMax()) {
669     masm.maxFloat32(second, first, handleNaN);
670   } else {
671     masm.minFloat32(second, first, handleNaN);
672   }
673 }
674 
visitClzI(LClzI * ins)675 void CodeGenerator::visitClzI(LClzI* ins) {
676   Register input = ToRegister(ins->input());
677   Register output = ToRegister(ins->output());
678   bool knownNotZero = ins->mir()->operandIsNeverZero();
679 
680   masm.clz32(input, output, knownNotZero);
681 }
682 
visitCtzI(LCtzI * ins)683 void CodeGenerator::visitCtzI(LCtzI* ins) {
684   Register input = ToRegister(ins->input());
685   Register output = ToRegister(ins->output());
686   bool knownNotZero = ins->mir()->operandIsNeverZero();
687 
688   masm.ctz32(input, output, knownNotZero);
689 }
690 
visitPopcntI(LPopcntI * ins)691 void CodeGenerator::visitPopcntI(LPopcntI* ins) {
692   Register input = ToRegister(ins->input());
693   Register output = ToRegister(ins->output());
694   Register temp =
695       ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
696 
697   masm.popcnt32(input, output, temp);
698 }
699 
visitPowHalfD(LPowHalfD * ins)700 void CodeGenerator::visitPowHalfD(LPowHalfD* ins) {
701   FloatRegister input = ToFloatRegister(ins->input());
702   FloatRegister output = ToFloatRegister(ins->output());
703 
704   ScratchDoubleScope scratch(masm);
705 
706   Label done, sqrt;
707 
708   if (!ins->mir()->operandIsNeverNegativeInfinity()) {
709     // Branch if not -Infinity.
710     masm.loadConstantDouble(NegativeInfinity<double>(), scratch);
711 
712     Assembler::DoubleCondition cond = Assembler::DoubleNotEqualOrUnordered;
713     if (ins->mir()->operandIsNeverNaN()) {
714       cond = Assembler::DoubleNotEqual;
715     }
716     masm.branchDouble(cond, input, scratch, &sqrt);
717 
718     // Math.pow(-Infinity, 0.5) == Infinity.
719     masm.zeroDouble(output);
720     masm.subDouble(scratch, output);
721     masm.jump(&done);
722 
723     masm.bind(&sqrt);
724   }
725 
726   if (!ins->mir()->operandIsNeverNegativeZero()) {
727     // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5).
728     // Adding 0 converts any -0 to 0.
729     masm.zeroDouble(scratch);
730     masm.addDouble(input, scratch);
731     masm.vsqrtsd(scratch, output, output);
732   } else {
733     masm.vsqrtsd(input, output, output);
734   }
735 
736   masm.bind(&done);
737 }
738 
739 class OutOfLineUndoALUOperation
740     : public OutOfLineCodeBase<CodeGeneratorX86Shared> {
741   LInstruction* ins_;
742 
743  public:
OutOfLineUndoALUOperation(LInstruction * ins)744   explicit OutOfLineUndoALUOperation(LInstruction* ins) : ins_(ins) {}
745 
accept(CodeGeneratorX86Shared * codegen)746   virtual void accept(CodeGeneratorX86Shared* codegen) override {
747     codegen->visitOutOfLineUndoALUOperation(this);
748   }
ins() const749   LInstruction* ins() const { return ins_; }
750 };
751 
visitAddI(LAddI * ins)752 void CodeGenerator::visitAddI(LAddI* ins) {
753   if (ins->rhs()->isConstant()) {
754     masm.addl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs()));
755   } else {
756     masm.addl(ToOperand(ins->rhs()), ToRegister(ins->lhs()));
757   }
758 
759   if (ins->snapshot()) {
760     if (ins->recoversInput()) {
761       OutOfLineUndoALUOperation* ool =
762           new (alloc()) OutOfLineUndoALUOperation(ins);
763       addOutOfLineCode(ool, ins->mir());
764       masm.j(Assembler::Overflow, ool->entry());
765     } else {
766       bailoutIf(Assembler::Overflow, ins->snapshot());
767     }
768   }
769 }
770 
visitAddI64(LAddI64 * lir)771 void CodeGenerator::visitAddI64(LAddI64* lir) {
772   const LInt64Allocation lhs = lir->getInt64Operand(LAddI64::Lhs);
773   const LInt64Allocation rhs = lir->getInt64Operand(LAddI64::Rhs);
774 
775   MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
776 
777   if (IsConstant(rhs)) {
778     masm.add64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
779     return;
780   }
781 
782   masm.add64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
783 }
784 
visitSubI(LSubI * ins)785 void CodeGenerator::visitSubI(LSubI* ins) {
786   if (ins->rhs()->isConstant()) {
787     masm.subl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs()));
788   } else {
789     masm.subl(ToOperand(ins->rhs()), ToRegister(ins->lhs()));
790   }
791 
792   if (ins->snapshot()) {
793     if (ins->recoversInput()) {
794       OutOfLineUndoALUOperation* ool =
795           new (alloc()) OutOfLineUndoALUOperation(ins);
796       addOutOfLineCode(ool, ins->mir());
797       masm.j(Assembler::Overflow, ool->entry());
798     } else {
799       bailoutIf(Assembler::Overflow, ins->snapshot());
800     }
801   }
802 }
803 
visitSubI64(LSubI64 * lir)804 void CodeGenerator::visitSubI64(LSubI64* lir) {
805   const LInt64Allocation lhs = lir->getInt64Operand(LSubI64::Lhs);
806   const LInt64Allocation rhs = lir->getInt64Operand(LSubI64::Rhs);
807 
808   MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
809 
810   if (IsConstant(rhs)) {
811     masm.sub64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
812     return;
813   }
814 
815   masm.sub64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
816 }
817 
visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation * ool)818 void CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(
819     OutOfLineUndoALUOperation* ool) {
820   LInstruction* ins = ool->ins();
821   Register reg = ToRegister(ins->getDef(0));
822 
823   DebugOnly<LAllocation*> lhs = ins->getOperand(0);
824   LAllocation* rhs = ins->getOperand(1);
825 
826   MOZ_ASSERT(reg == ToRegister(lhs));
827   MOZ_ASSERT_IF(rhs->isGeneralReg(), reg != ToRegister(rhs));
828 
829   // Undo the effect of the ALU operation, which was performed on the output
830   // register and overflowed. Writing to the output register clobbered an
831   // input reg, and the original value of the input needs to be recovered
832   // to satisfy the constraint imposed by any RECOVERED_INPUT operands to
833   // the bailout snapshot.
834 
835   if (rhs->isConstant()) {
836     Imm32 constant(ToInt32(rhs));
837     if (ins->isAddI()) {
838       masm.subl(constant, reg);
839     } else {
840       masm.addl(constant, reg);
841     }
842   } else {
843     if (ins->isAddI()) {
844       masm.subl(ToOperand(rhs), reg);
845     } else {
846       masm.addl(ToOperand(rhs), reg);
847     }
848   }
849 
850   bailout(ool->ins()->snapshot());
851 }
852 
853 class MulNegativeZeroCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared> {
854   LMulI* ins_;
855 
856  public:
MulNegativeZeroCheck(LMulI * ins)857   explicit MulNegativeZeroCheck(LMulI* ins) : ins_(ins) {}
858 
accept(CodeGeneratorX86Shared * codegen)859   virtual void accept(CodeGeneratorX86Shared* codegen) override {
860     codegen->visitMulNegativeZeroCheck(this);
861   }
ins() const862   LMulI* ins() const { return ins_; }
863 };
864 
visitMulI(LMulI * ins)865 void CodeGenerator::visitMulI(LMulI* ins) {
866   const LAllocation* lhs = ins->lhs();
867   const LAllocation* rhs = ins->rhs();
868   MMul* mul = ins->mir();
869   MOZ_ASSERT_IF(mul->mode() == MMul::Integer,
870                 !mul->canBeNegativeZero() && !mul->canOverflow());
871 
872   if (rhs->isConstant()) {
873     // Bailout on -0.0
874     int32_t constant = ToInt32(rhs);
875     if (mul->canBeNegativeZero() && constant <= 0) {
876       Assembler::Condition bailoutCond =
877           (constant == 0) ? Assembler::Signed : Assembler::Equal;
878       masm.test32(ToRegister(lhs), ToRegister(lhs));
879       bailoutIf(bailoutCond, ins->snapshot());
880     }
881 
882     switch (constant) {
883       case -1:
884         masm.negl(ToOperand(lhs));
885         break;
886       case 0:
887         masm.xorl(ToOperand(lhs), ToRegister(lhs));
888         return;  // escape overflow check;
889       case 1:
890         // nop
891         return;  // escape overflow check;
892       case 2:
893         masm.addl(ToOperand(lhs), ToRegister(lhs));
894         break;
895       default:
896         if (!mul->canOverflow() && constant > 0) {
897           // Use shift if cannot overflow and constant is power of 2
898           int32_t shift = FloorLog2(constant);
899           if ((1 << shift) == constant) {
900             masm.shll(Imm32(shift), ToRegister(lhs));
901             return;
902           }
903         }
904         masm.imull(Imm32(ToInt32(rhs)), ToRegister(lhs));
905     }
906 
907     // Bailout on overflow
908     if (mul->canOverflow()) {
909       bailoutIf(Assembler::Overflow, ins->snapshot());
910     }
911   } else {
912     masm.imull(ToOperand(rhs), ToRegister(lhs));
913 
914     // Bailout on overflow
915     if (mul->canOverflow()) {
916       bailoutIf(Assembler::Overflow, ins->snapshot());
917     }
918 
919     if (mul->canBeNegativeZero()) {
920       // Jump to an OOL path if the result is 0.
921       MulNegativeZeroCheck* ool = new (alloc()) MulNegativeZeroCheck(ins);
922       addOutOfLineCode(ool, mul);
923 
924       masm.test32(ToRegister(lhs), ToRegister(lhs));
925       masm.j(Assembler::Zero, ool->entry());
926       masm.bind(ool->rejoin());
927     }
928   }
929 }
930 
visitMulI64(LMulI64 * lir)931 void CodeGenerator::visitMulI64(LMulI64* lir) {
932   const LInt64Allocation lhs = lir->getInt64Operand(LMulI64::Lhs);
933   const LInt64Allocation rhs = lir->getInt64Operand(LMulI64::Rhs);
934 
935   MOZ_ASSERT(ToRegister64(lhs) == ToOutRegister64(lir));
936 
937   if (IsConstant(rhs)) {
938     int64_t constant = ToInt64(rhs);
939     switch (constant) {
940       case -1:
941         masm.neg64(ToRegister64(lhs));
942         return;
943       case 0:
944         masm.xor64(ToRegister64(lhs), ToRegister64(lhs));
945         return;
946       case 1:
947         // nop
948         return;
949       case 2:
950         masm.add64(ToRegister64(lhs), ToRegister64(lhs));
951         return;
952       default:
953         if (constant > 0) {
954           // Use shift if constant is power of 2.
955           int32_t shift = mozilla::FloorLog2(constant);
956           if (int64_t(1) << shift == constant) {
957             masm.lshift64(Imm32(shift), ToRegister64(lhs));
958             return;
959           }
960         }
961         Register temp = ToTempRegisterOrInvalid(lir->temp());
962         masm.mul64(Imm64(constant), ToRegister64(lhs), temp);
963     }
964   } else {
965     Register temp = ToTempRegisterOrInvalid(lir->temp());
966     masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp);
967   }
968 }
969 
970 class ReturnZero : public OutOfLineCodeBase<CodeGeneratorX86Shared> {
971   Register reg_;
972 
973  public:
ReturnZero(Register reg)974   explicit ReturnZero(Register reg) : reg_(reg) {}
975 
accept(CodeGeneratorX86Shared * codegen)976   virtual void accept(CodeGeneratorX86Shared* codegen) override {
977     codegen->visitReturnZero(this);
978   }
reg() const979   Register reg() const { return reg_; }
980 };
981 
visitReturnZero(ReturnZero * ool)982 void CodeGeneratorX86Shared::visitReturnZero(ReturnZero* ool) {
983   masm.mov(ImmWord(0), ool->reg());
984   masm.jmp(ool->rejoin());
985 }
986 
visitUDivOrMod(LUDivOrMod * ins)987 void CodeGenerator::visitUDivOrMod(LUDivOrMod* ins) {
988   Register lhs = ToRegister(ins->lhs());
989   Register rhs = ToRegister(ins->rhs());
990   Register output = ToRegister(ins->output());
991 
992   MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
993   MOZ_ASSERT(rhs != edx);
994   MOZ_ASSERT_IF(output == eax, ToRegister(ins->remainder()) == edx);
995 
996   ReturnZero* ool = nullptr;
997 
998   // Put the lhs in eax.
999   if (lhs != eax) {
1000     masm.mov(lhs, eax);
1001   }
1002 
1003   // Prevent divide by zero.
1004   if (ins->canBeDivideByZero()) {
1005     masm.test32(rhs, rhs);
1006     if (ins->mir()->isTruncated()) {
1007       if (ins->trapOnError()) {
1008         Label nonZero;
1009         masm.j(Assembler::NonZero, &nonZero);
1010         masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->bytecodeOffset());
1011         masm.bind(&nonZero);
1012       } else {
1013         ool = new (alloc()) ReturnZero(output);
1014         masm.j(Assembler::Zero, ool->entry());
1015       }
1016     } else {
1017       bailoutIf(Assembler::Zero, ins->snapshot());
1018     }
1019   }
1020 
1021   // Zero extend the lhs into edx to make (edx:eax), since udiv is 64-bit.
1022   masm.mov(ImmWord(0), edx);
1023   masm.udiv(rhs);
1024 
1025   // If the remainder is > 0, bailout since this must be a double.
1026   if (ins->mir()->isDiv() && !ins->mir()->toDiv()->canTruncateRemainder()) {
1027     Register remainder = ToRegister(ins->remainder());
1028     masm.test32(remainder, remainder);
1029     bailoutIf(Assembler::NonZero, ins->snapshot());
1030   }
1031 
1032   // Unsigned div or mod can return a value that's not a signed int32.
1033   // If our users aren't expecting that, bail.
1034   if (!ins->mir()->isTruncated()) {
1035     masm.test32(output, output);
1036     bailoutIf(Assembler::Signed, ins->snapshot());
1037   }
1038 
1039   if (ool) {
1040     addOutOfLineCode(ool, ins->mir());
1041     masm.bind(ool->rejoin());
1042   }
1043 }
1044 
visitUDivOrModConstant(LUDivOrModConstant * ins)1045 void CodeGenerator::visitUDivOrModConstant(LUDivOrModConstant* ins) {
1046   Register lhs = ToRegister(ins->numerator());
1047   Register output = ToRegister(ins->output());
1048   uint32_t d = ins->denominator();
1049 
1050   // This emits the division answer into edx or the modulus answer into eax.
1051   MOZ_ASSERT(output == eax || output == edx);
1052   MOZ_ASSERT(lhs != eax && lhs != edx);
1053   bool isDiv = (output == edx);
1054 
1055   if (d == 0) {
1056     if (ins->mir()->isTruncated()) {
1057       if (ins->trapOnError()) {
1058         masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->bytecodeOffset());
1059       } else {
1060         masm.xorl(output, output);
1061       }
1062     } else {
1063       bailout(ins->snapshot());
1064     }
1065     return;
1066   }
1067 
1068   // The denominator isn't a power of 2 (see LDivPowTwoI and LModPowTwoI).
1069   MOZ_ASSERT((d & (d - 1)) != 0);
1070 
1071   ReciprocalMulConstants rmc = computeDivisionConstants(d, /* maxLog = */ 32);
1072 
1073   // We first compute (M * n) >> 32, where M = rmc.multiplier.
1074   masm.movl(Imm32(rmc.multiplier), eax);
1075   masm.umull(lhs);
1076   if (rmc.multiplier > UINT32_MAX) {
1077     // M >= 2^32 and shift == 0 is impossible, as d >= 2 implies that
1078     // ((M * n) >> (32 + shift)) >= n > floor(n/d) whenever n >= d,
1079     // contradicting the proof of correctness in computeDivisionConstants.
1080     MOZ_ASSERT(rmc.shiftAmount > 0);
1081     MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 33));
1082 
1083     // We actually computed edx = ((uint32_t(M) * n) >> 32) instead. Since
1084     // (M * n) >> (32 + shift) is the same as (edx + n) >> shift, we can
1085     // correct for the overflow. This case is a bit trickier than the signed
1086     // case, though, as the (edx + n) addition itself can overflow; however,
1087     // note that (edx + n) >> shift == (((n - edx) >> 1) + edx) >> (shift - 1),
1088     // which is overflow-free. See Hacker's Delight, section 10-8 for details.
1089 
1090     // Compute (n - edx) >> 1 into eax.
1091     masm.movl(lhs, eax);
1092     masm.subl(edx, eax);
1093     masm.shrl(Imm32(1), eax);
1094 
1095     // Finish the computation.
1096     masm.addl(eax, edx);
1097     masm.shrl(Imm32(rmc.shiftAmount - 1), edx);
1098   } else {
1099     masm.shrl(Imm32(rmc.shiftAmount), edx);
1100   }
1101 
1102   // We now have the truncated division value in edx. If we're
1103   // computing a modulus or checking whether the division resulted
1104   // in an integer, we need to multiply the obtained value by d and
1105   // finish the computation/check.
1106   if (!isDiv) {
1107     masm.imull(Imm32(d), edx, edx);
1108     masm.movl(lhs, eax);
1109     masm.subl(edx, eax);
1110 
1111     // The final result of the modulus op, just computed above by the
1112     // sub instruction, can be a number in the range [2^31, 2^32). If
1113     // this is the case and the modulus is not truncated, we must bail
1114     // out.
1115     if (!ins->mir()->isTruncated()) {
1116       bailoutIf(Assembler::Signed, ins->snapshot());
1117     }
1118   } else if (!ins->mir()->isTruncated()) {
1119     masm.imull(Imm32(d), edx, eax);
1120     masm.cmpl(lhs, eax);
1121     bailoutIf(Assembler::NotEqual, ins->snapshot());
1122   }
1123 }
1124 
visitMulNegativeZeroCheck(MulNegativeZeroCheck * ool)1125 void CodeGeneratorX86Shared::visitMulNegativeZeroCheck(
1126     MulNegativeZeroCheck* ool) {
1127   LMulI* ins = ool->ins();
1128   Register result = ToRegister(ins->output());
1129   Operand lhsCopy = ToOperand(ins->lhsCopy());
1130   Operand rhs = ToOperand(ins->rhs());
1131   MOZ_ASSERT_IF(lhsCopy.kind() == Operand::REG, lhsCopy.reg() != result.code());
1132 
1133   // Result is -0 if lhs or rhs is negative.
1134   masm.movl(lhsCopy, result);
1135   masm.orl(rhs, result);
1136   bailoutIf(Assembler::Signed, ins->snapshot());
1137 
1138   masm.mov(ImmWord(0), result);
1139   masm.jmp(ool->rejoin());
1140 }
1141 
visitDivPowTwoI(LDivPowTwoI * ins)1142 void CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins) {
1143   Register lhs = ToRegister(ins->numerator());
1144   DebugOnly<Register> output = ToRegister(ins->output());
1145 
1146   int32_t shift = ins->shift();
1147   bool negativeDivisor = ins->negativeDivisor();
1148   MDiv* mir = ins->mir();
1149 
1150   // We use defineReuseInput so these should always be the same, which is
1151   // convenient since all of our instructions here are two-address.
1152   MOZ_ASSERT(lhs == output);
1153 
1154   if (!mir->isTruncated() && negativeDivisor) {
1155     // 0 divided by a negative number must return a double.
1156     masm.test32(lhs, lhs);
1157     bailoutIf(Assembler::Zero, ins->snapshot());
1158   }
1159 
1160   if (shift) {
1161     if (!mir->isTruncated()) {
1162       // If the remainder is != 0, bailout since this must be a double.
1163       masm.test32(lhs, Imm32(UINT32_MAX >> (32 - shift)));
1164       bailoutIf(Assembler::NonZero, ins->snapshot());
1165     }
1166 
1167     if (mir->isUnsigned()) {
1168       masm.shrl(Imm32(shift), lhs);
1169     } else {
1170       // Adjust the value so that shifting produces a correctly
1171       // rounded result when the numerator is negative. See 10-1
1172       // "Signed Division by a Known Power of 2" in Henry
1173       // S. Warren, Jr.'s Hacker's Delight.
1174       if (mir->canBeNegativeDividend() && mir->isTruncated()) {
1175         // Note: There is no need to execute this code, which handles how to
1176         // round the signed integer division towards 0, if we previously bailed
1177         // due to a non-zero remainder.
1178         Register lhsCopy = ToRegister(ins->numeratorCopy());
1179         MOZ_ASSERT(lhsCopy != lhs);
1180         if (shift > 1) {
1181           // Copy the sign bit of the numerator. (= (2^32 - 1) or 0)
1182           masm.sarl(Imm32(31), lhs);
1183         }
1184         // Divide by 2^(32 - shift)
1185         // i.e. (= (2^32 - 1) / 2^(32 - shift) or 0)
1186         // i.e. (= (2^shift - 1) or 0)
1187         masm.shrl(Imm32(32 - shift), lhs);
1188         // If signed, make any 1 bit below the shifted bits to bubble up, such
1189         // that once shifted the value would be rounded towards 0.
1190         masm.addl(lhsCopy, lhs);
1191       }
1192       masm.sarl(Imm32(shift), lhs);
1193 
1194       if (negativeDivisor) {
1195         masm.negl(lhs);
1196       }
1197     }
1198     return;
1199   }
1200 
1201   if (negativeDivisor) {
1202     // INT32_MIN / -1 overflows.
1203     masm.negl(lhs);
1204     if (!mir->isTruncated()) {
1205       bailoutIf(Assembler::Overflow, ins->snapshot());
1206     } else if (mir->trapOnError()) {
1207       Label ok;
1208       masm.j(Assembler::NoOverflow, &ok);
1209       masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->bytecodeOffset());
1210       masm.bind(&ok);
1211     }
1212   } else if (mir->isUnsigned() && !mir->isTruncated()) {
1213     // Unsigned division by 1 can overflow if output is not
1214     // truncated.
1215     masm.test32(lhs, lhs);
1216     bailoutIf(Assembler::Signed, ins->snapshot());
1217   }
1218 }
1219 
visitDivOrModConstantI(LDivOrModConstantI * ins)1220 void CodeGenerator::visitDivOrModConstantI(LDivOrModConstantI* ins) {
1221   Register lhs = ToRegister(ins->numerator());
1222   Register output = ToRegister(ins->output());
1223   int32_t d = ins->denominator();
1224 
1225   // This emits the division answer into edx or the modulus answer into eax.
1226   MOZ_ASSERT(output == eax || output == edx);
1227   MOZ_ASSERT(lhs != eax && lhs != edx);
1228   bool isDiv = (output == edx);
1229 
1230   // The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI
1231   // and LModPowTwoI).
1232   MOZ_ASSERT((Abs(d) & (Abs(d) - 1)) != 0);
1233 
1234   // We will first divide by Abs(d), and negate the answer if d is negative.
1235   // If desired, this can be avoided by generalizing computeDivisionConstants.
1236   ReciprocalMulConstants rmc =
1237       computeDivisionConstants(Abs(d), /* maxLog = */ 31);
1238 
1239   // We first compute (M * n) >> 32, where M = rmc.multiplier.
1240   masm.movl(Imm32(rmc.multiplier), eax);
1241   masm.imull(lhs);
1242   if (rmc.multiplier > INT32_MAX) {
1243     MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 32));
1244 
1245     // We actually computed edx = ((int32_t(M) * n) >> 32) instead. Since
1246     // (M * n) >> 32 is the same as (edx + n), we can correct for the overflow.
1247     // (edx + n) can't overflow, as n and edx have opposite signs because
1248     // int32_t(M) is negative.
1249     masm.addl(lhs, edx);
1250   }
1251   // (M * n) >> (32 + shift) is the truncated division answer if n is
1252   // non-negative, as proved in the comments of computeDivisionConstants. We
1253   // must add 1 later if n is negative to get the right answer in all cases.
1254   masm.sarl(Imm32(rmc.shiftAmount), edx);
1255 
1256   // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
1257   // computed with just a sign-extending shift of 31 bits.
1258   if (ins->canBeNegativeDividend()) {
1259     masm.movl(lhs, eax);
1260     masm.sarl(Imm32(31), eax);
1261     masm.subl(eax, edx);
1262   }
1263 
1264   // After this, edx contains the correct truncated division result.
1265   if (d < 0) {
1266     masm.negl(edx);
1267   }
1268 
1269   if (!isDiv) {
1270     masm.imull(Imm32(-d), edx, eax);
1271     masm.addl(lhs, eax);
1272   }
1273 
1274   if (!ins->mir()->isTruncated()) {
1275     if (isDiv) {
1276       // This is a division op. Multiply the obtained value by d to check if
1277       // the correct answer is an integer. This cannot overflow, since |d| > 1.
1278       masm.imull(Imm32(d), edx, eax);
1279       masm.cmp32(lhs, eax);
1280       bailoutIf(Assembler::NotEqual, ins->snapshot());
1281 
1282       // If lhs is zero and the divisor is negative, the answer should have
1283       // been -0.
1284       if (d < 0) {
1285         masm.test32(lhs, lhs);
1286         bailoutIf(Assembler::Zero, ins->snapshot());
1287       }
1288     } else if (ins->canBeNegativeDividend()) {
1289       // This is a mod op. If the computed value is zero and lhs
1290       // is negative, the answer should have been -0.
1291       Label done;
1292 
1293       masm.cmp32(lhs, Imm32(0));
1294       masm.j(Assembler::GreaterThanOrEqual, &done);
1295 
1296       masm.test32(eax, eax);
1297       bailoutIf(Assembler::Zero, ins->snapshot());
1298 
1299       masm.bind(&done);
1300     }
1301   }
1302 }
1303 
visitDivI(LDivI * ins)1304 void CodeGenerator::visitDivI(LDivI* ins) {
1305   Register remainder = ToRegister(ins->remainder());
1306   Register lhs = ToRegister(ins->lhs());
1307   Register rhs = ToRegister(ins->rhs());
1308   Register output = ToRegister(ins->output());
1309 
1310   MDiv* mir = ins->mir();
1311 
1312   MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
1313   MOZ_ASSERT(rhs != edx);
1314   MOZ_ASSERT(remainder == edx);
1315   MOZ_ASSERT(output == eax);
1316 
1317   Label done;
1318   ReturnZero* ool = nullptr;
1319 
1320   // Put the lhs in eax, for either the negative overflow case or the regular
1321   // divide case.
1322   if (lhs != eax) {
1323     masm.mov(lhs, eax);
1324   }
1325 
1326   // Handle divide by zero.
1327   if (mir->canBeDivideByZero()) {
1328     masm.test32(rhs, rhs);
1329     if (mir->trapOnError()) {
1330       Label nonZero;
1331       masm.j(Assembler::NonZero, &nonZero);
1332       masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset());
1333       masm.bind(&nonZero);
1334     } else if (mir->canTruncateInfinities()) {
1335       // Truncated division by zero is zero (Infinity|0 == 0)
1336       if (!ool) {
1337         ool = new (alloc()) ReturnZero(output);
1338       }
1339       masm.j(Assembler::Zero, ool->entry());
1340     } else {
1341       MOZ_ASSERT(mir->fallible());
1342       bailoutIf(Assembler::Zero, ins->snapshot());
1343     }
1344   }
1345 
1346   // Handle an integer overflow exception from -2147483648 / -1.
1347   if (mir->canBeNegativeOverflow()) {
1348     Label notOverflow;
1349     masm.cmp32(lhs, Imm32(INT32_MIN));
1350     masm.j(Assembler::NotEqual, &notOverflow);
1351     masm.cmp32(rhs, Imm32(-1));
1352     if (mir->trapOnError()) {
1353       masm.j(Assembler::NotEqual, &notOverflow);
1354       masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->bytecodeOffset());
1355     } else if (mir->canTruncateOverflow()) {
1356       // (-INT32_MIN)|0 == INT32_MIN and INT32_MIN is already in the
1357       // output register (lhs == eax).
1358       masm.j(Assembler::Equal, &done);
1359     } else {
1360       MOZ_ASSERT(mir->fallible());
1361       bailoutIf(Assembler::Equal, ins->snapshot());
1362     }
1363     masm.bind(&notOverflow);
1364   }
1365 
1366   // Handle negative 0.
1367   if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
1368     Label nonzero;
1369     masm.test32(lhs, lhs);
1370     masm.j(Assembler::NonZero, &nonzero);
1371     masm.cmp32(rhs, Imm32(0));
1372     bailoutIf(Assembler::LessThan, ins->snapshot());
1373     masm.bind(&nonzero);
1374   }
1375 
1376   // Sign extend the lhs into edx to make (edx:eax), since idiv is 64-bit.
1377   if (lhs != eax) {
1378     masm.mov(lhs, eax);
1379   }
1380   masm.cdq();
1381   masm.idiv(rhs);
1382 
1383   if (!mir->canTruncateRemainder()) {
1384     // If the remainder is > 0, bailout since this must be a double.
1385     masm.test32(remainder, remainder);
1386     bailoutIf(Assembler::NonZero, ins->snapshot());
1387   }
1388 
1389   masm.bind(&done);
1390 
1391   if (ool) {
1392     addOutOfLineCode(ool, mir);
1393     masm.bind(ool->rejoin());
1394   }
1395 }
1396 
visitModPowTwoI(LModPowTwoI * ins)1397 void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) {
1398   Register lhs = ToRegister(ins->getOperand(0));
1399   int32_t shift = ins->shift();
1400 
1401   Label negative;
1402 
1403   if (!ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend()) {
1404     // Switch based on sign of the lhs.
1405     // Positive numbers are just a bitmask
1406     masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
1407   }
1408 
1409   masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
1410 
1411   if (!ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend()) {
1412     Label done;
1413     masm.jump(&done);
1414 
1415     // Negative numbers need a negate, bitmask, negate
1416     masm.bind(&negative);
1417 
1418     // Unlike in the visitModI case, we are not computing the mod by means of a
1419     // division. Therefore, the divisor = -1 case isn't problematic (the andl
1420     // always returns 0, which is what we expect).
1421     //
1422     // The negl instruction overflows if lhs == INT32_MIN, but this is also not
1423     // a problem: shift is at most 31, and so the andl also always returns 0.
1424     masm.negl(lhs);
1425     masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
1426     masm.negl(lhs);
1427 
1428     // Since a%b has the same sign as b, and a is negative in this branch,
1429     // an answer of 0 means the correct result is actually -0. Bail out.
1430     if (!ins->mir()->isTruncated()) {
1431       bailoutIf(Assembler::Zero, ins->snapshot());
1432     }
1433     masm.bind(&done);
1434   }
1435 }
1436 
1437 class ModOverflowCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared> {
1438   Label done_;
1439   LModI* ins_;
1440   Register rhs_;
1441 
1442  public:
ModOverflowCheck(LModI * ins,Register rhs)1443   explicit ModOverflowCheck(LModI* ins, Register rhs) : ins_(ins), rhs_(rhs) {}
1444 
accept(CodeGeneratorX86Shared * codegen)1445   virtual void accept(CodeGeneratorX86Shared* codegen) override {
1446     codegen->visitModOverflowCheck(this);
1447   }
done()1448   Label* done() { return &done_; }
ins() const1449   LModI* ins() const { return ins_; }
rhs() const1450   Register rhs() const { return rhs_; }
1451 };
1452 
visitModOverflowCheck(ModOverflowCheck * ool)1453 void CodeGeneratorX86Shared::visitModOverflowCheck(ModOverflowCheck* ool) {
1454   masm.cmp32(ool->rhs(), Imm32(-1));
1455   if (ool->ins()->mir()->isTruncated()) {
1456     masm.j(Assembler::NotEqual, ool->rejoin());
1457     masm.mov(ImmWord(0), edx);
1458     masm.jmp(ool->done());
1459   } else {
1460     bailoutIf(Assembler::Equal, ool->ins()->snapshot());
1461     masm.jmp(ool->rejoin());
1462   }
1463 }
1464 
visitModI(LModI * ins)1465 void CodeGenerator::visitModI(LModI* ins) {
1466   Register remainder = ToRegister(ins->remainder());
1467   Register lhs = ToRegister(ins->lhs());
1468   Register rhs = ToRegister(ins->rhs());
1469 
1470   // Required to use idiv.
1471   MOZ_ASSERT_IF(lhs != rhs, rhs != eax);
1472   MOZ_ASSERT(rhs != edx);
1473   MOZ_ASSERT(remainder == edx);
1474   MOZ_ASSERT(ToRegister(ins->getTemp(0)) == eax);
1475 
1476   Label done;
1477   ReturnZero* ool = nullptr;
1478   ModOverflowCheck* overflow = nullptr;
1479 
1480   // Set up eax in preparation for doing a div.
1481   if (lhs != eax) {
1482     masm.mov(lhs, eax);
1483   }
1484 
1485   MMod* mir = ins->mir();
1486 
1487   // Prevent divide by zero.
1488   if (mir->canBeDivideByZero()) {
1489     masm.test32(rhs, rhs);
1490     if (mir->isTruncated()) {
1491       if (mir->trapOnError()) {
1492         Label nonZero;
1493         masm.j(Assembler::NonZero, &nonZero);
1494         masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset());
1495         masm.bind(&nonZero);
1496       } else {
1497         if (!ool) {
1498           ool = new (alloc()) ReturnZero(edx);
1499         }
1500         masm.j(Assembler::Zero, ool->entry());
1501       }
1502     } else {
1503       bailoutIf(Assembler::Zero, ins->snapshot());
1504     }
1505   }
1506 
1507   Label negative;
1508 
1509   // Switch based on sign of the lhs.
1510   if (mir->canBeNegativeDividend()) {
1511     masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
1512   }
1513 
1514   // If lhs >= 0 then remainder = lhs % rhs. The remainder must be positive.
1515   {
1516     // Check if rhs is a power-of-two.
1517     if (mir->canBePowerOfTwoDivisor()) {
1518       MOZ_ASSERT(rhs != remainder);
1519 
1520       // Rhs y is a power-of-two if (y & (y-1)) == 0. Note that if
1521       // y is any negative number other than INT32_MIN, both y and
1522       // y-1 will have the sign bit set so these are never optimized
1523       // as powers-of-two. If y is INT32_MIN, y-1 will be INT32_MAX
1524       // and because lhs >= 0 at this point, lhs & INT32_MAX returns
1525       // the correct value.
1526       Label notPowerOfTwo;
1527       masm.mov(rhs, remainder);
1528       masm.subl(Imm32(1), remainder);
1529       masm.branchTest32(Assembler::NonZero, remainder, rhs, &notPowerOfTwo);
1530       {
1531         masm.andl(lhs, remainder);
1532         masm.jmp(&done);
1533       }
1534       masm.bind(&notPowerOfTwo);
1535     }
1536 
1537     // Since lhs >= 0, the sign-extension will be 0
1538     masm.mov(ImmWord(0), edx);
1539     masm.idiv(rhs);
1540   }
1541 
1542   // Otherwise, we have to beware of two special cases:
1543   if (mir->canBeNegativeDividend()) {
1544     masm.jump(&done);
1545 
1546     masm.bind(&negative);
1547 
1548     // Prevent an integer overflow exception from -2147483648 % -1
1549     Label notmin;
1550     masm.cmp32(lhs, Imm32(INT32_MIN));
1551     overflow = new (alloc()) ModOverflowCheck(ins, rhs);
1552     masm.j(Assembler::Equal, overflow->entry());
1553     masm.bind(overflow->rejoin());
1554     masm.cdq();
1555     masm.idiv(rhs);
1556 
1557     if (!mir->isTruncated()) {
1558       // A remainder of 0 means that the rval must be -0, which is a double.
1559       masm.test32(remainder, remainder);
1560       bailoutIf(Assembler::Zero, ins->snapshot());
1561     }
1562   }
1563 
1564   masm.bind(&done);
1565 
1566   if (overflow) {
1567     addOutOfLineCode(overflow, mir);
1568     masm.bind(overflow->done());
1569   }
1570 
1571   if (ool) {
1572     addOutOfLineCode(ool, mir);
1573     masm.bind(ool->rejoin());
1574   }
1575 }
1576 
visitBitNotI(LBitNotI * ins)1577 void CodeGenerator::visitBitNotI(LBitNotI* ins) {
1578   const LAllocation* input = ins->getOperand(0);
1579   MOZ_ASSERT(!input->isConstant());
1580 
1581   masm.notl(ToOperand(input));
1582 }
1583 
visitBitOpI(LBitOpI * ins)1584 void CodeGenerator::visitBitOpI(LBitOpI* ins) {
1585   const LAllocation* lhs = ins->getOperand(0);
1586   const LAllocation* rhs = ins->getOperand(1);
1587 
1588   switch (ins->bitop()) {
1589     case JSOp::BitOr:
1590       if (rhs->isConstant()) {
1591         masm.orl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1592       } else {
1593         masm.orl(ToOperand(rhs), ToRegister(lhs));
1594       }
1595       break;
1596     case JSOp::BitXor:
1597       if (rhs->isConstant()) {
1598         masm.xorl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1599       } else {
1600         masm.xorl(ToOperand(rhs), ToRegister(lhs));
1601       }
1602       break;
1603     case JSOp::BitAnd:
1604       if (rhs->isConstant()) {
1605         masm.andl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1606       } else {
1607         masm.andl(ToOperand(rhs), ToRegister(lhs));
1608       }
1609       break;
1610     default:
1611       MOZ_CRASH("unexpected binary opcode");
1612   }
1613 }
1614 
visitBitOpI64(LBitOpI64 * lir)1615 void CodeGenerator::visitBitOpI64(LBitOpI64* lir) {
1616   const LInt64Allocation lhs = lir->getInt64Operand(LBitOpI64::Lhs);
1617   const LInt64Allocation rhs = lir->getInt64Operand(LBitOpI64::Rhs);
1618 
1619   MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
1620 
1621   switch (lir->bitop()) {
1622     case JSOp::BitOr:
1623       if (IsConstant(rhs)) {
1624         masm.or64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
1625       } else {
1626         masm.or64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
1627       }
1628       break;
1629     case JSOp::BitXor:
1630       if (IsConstant(rhs)) {
1631         masm.xor64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
1632       } else {
1633         masm.xor64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
1634       }
1635       break;
1636     case JSOp::BitAnd:
1637       if (IsConstant(rhs)) {
1638         masm.and64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
1639       } else {
1640         masm.and64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
1641       }
1642       break;
1643     default:
1644       MOZ_CRASH("unexpected binary opcode");
1645   }
1646 }
1647 
visitShiftI(LShiftI * ins)1648 void CodeGenerator::visitShiftI(LShiftI* ins) {
1649   Register lhs = ToRegister(ins->lhs());
1650   const LAllocation* rhs = ins->rhs();
1651 
1652   if (rhs->isConstant()) {
1653     int32_t shift = ToInt32(rhs) & 0x1F;
1654     switch (ins->bitop()) {
1655       case JSOp::Lsh:
1656         if (shift) {
1657           masm.lshift32(Imm32(shift), lhs);
1658         }
1659         break;
1660       case JSOp::Rsh:
1661         if (shift) {
1662           masm.rshift32Arithmetic(Imm32(shift), lhs);
1663         }
1664         break;
1665       case JSOp::Ursh:
1666         if (shift) {
1667           masm.rshift32(Imm32(shift), lhs);
1668         } else if (ins->mir()->toUrsh()->fallible()) {
1669           // x >>> 0 can overflow.
1670           masm.test32(lhs, lhs);
1671           bailoutIf(Assembler::Signed, ins->snapshot());
1672         }
1673         break;
1674       default:
1675         MOZ_CRASH("Unexpected shift op");
1676     }
1677   } else {
1678     Register shift = ToRegister(rhs);
1679     switch (ins->bitop()) {
1680       case JSOp::Lsh:
1681         masm.lshift32(shift, lhs);
1682         break;
1683       case JSOp::Rsh:
1684         masm.rshift32Arithmetic(shift, lhs);
1685         break;
1686       case JSOp::Ursh:
1687         masm.rshift32(shift, lhs);
1688         if (ins->mir()->toUrsh()->fallible()) {
1689           // x >>> 0 can overflow.
1690           masm.test32(lhs, lhs);
1691           bailoutIf(Assembler::Signed, ins->snapshot());
1692         }
1693         break;
1694       default:
1695         MOZ_CRASH("Unexpected shift op");
1696     }
1697   }
1698 }
1699 
visitShiftI64(LShiftI64 * lir)1700 void CodeGenerator::visitShiftI64(LShiftI64* lir) {
1701   const LInt64Allocation lhs = lir->getInt64Operand(LShiftI64::Lhs);
1702   LAllocation* rhs = lir->getOperand(LShiftI64::Rhs);
1703 
1704   MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
1705 
1706   if (rhs->isConstant()) {
1707     int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F);
1708     switch (lir->bitop()) {
1709       case JSOp::Lsh:
1710         if (shift) {
1711           masm.lshift64(Imm32(shift), ToRegister64(lhs));
1712         }
1713         break;
1714       case JSOp::Rsh:
1715         if (shift) {
1716           masm.rshift64Arithmetic(Imm32(shift), ToRegister64(lhs));
1717         }
1718         break;
1719       case JSOp::Ursh:
1720         if (shift) {
1721           masm.rshift64(Imm32(shift), ToRegister64(lhs));
1722         }
1723         break;
1724       default:
1725         MOZ_CRASH("Unexpected shift op");
1726     }
1727     return;
1728   }
1729 
1730   Register shift = ToRegister(rhs);
1731 #ifdef JS_CODEGEN_X86
1732   MOZ_ASSERT(shift == ecx);
1733 #endif
1734   switch (lir->bitop()) {
1735     case JSOp::Lsh:
1736       masm.lshift64(shift, ToRegister64(lhs));
1737       break;
1738     case JSOp::Rsh:
1739       masm.rshift64Arithmetic(shift, ToRegister64(lhs));
1740       break;
1741     case JSOp::Ursh:
1742       masm.rshift64(shift, ToRegister64(lhs));
1743       break;
1744     default:
1745       MOZ_CRASH("Unexpected shift op");
1746   }
1747 }
1748 
visitUrshD(LUrshD * ins)1749 void CodeGenerator::visitUrshD(LUrshD* ins) {
1750   Register lhs = ToRegister(ins->lhs());
1751   MOZ_ASSERT(ToRegister(ins->temp()) == lhs);
1752 
1753   const LAllocation* rhs = ins->rhs();
1754   FloatRegister out = ToFloatRegister(ins->output());
1755 
1756   if (rhs->isConstant()) {
1757     int32_t shift = ToInt32(rhs) & 0x1F;
1758     if (shift) {
1759       masm.shrl(Imm32(shift), lhs);
1760     }
1761   } else {
1762     Register shift = ToRegister(rhs);
1763     masm.rshift32(shift, lhs);
1764   }
1765 
1766   masm.convertUInt32ToDouble(lhs, out);
1767 }
1768 
ToOperand(const LAllocation & a)1769 Operand CodeGeneratorX86Shared::ToOperand(const LAllocation& a) {
1770   if (a.isGeneralReg()) {
1771     return Operand(a.toGeneralReg()->reg());
1772   }
1773   if (a.isFloatReg()) {
1774     return Operand(a.toFloatReg()->reg());
1775   }
1776   return Operand(ToAddress(a));
1777 }
1778 
ToOperand(const LAllocation * a)1779 Operand CodeGeneratorX86Shared::ToOperand(const LAllocation* a) {
1780   return ToOperand(*a);
1781 }
1782 
ToOperand(const LDefinition * def)1783 Operand CodeGeneratorX86Shared::ToOperand(const LDefinition* def) {
1784   return ToOperand(def->output());
1785 }
1786 
toMoveOperand(LAllocation a) const1787 MoveOperand CodeGeneratorX86Shared::toMoveOperand(LAllocation a) const {
1788   if (a.isGeneralReg()) {
1789     return MoveOperand(ToRegister(a));
1790   }
1791   if (a.isFloatReg()) {
1792     return MoveOperand(ToFloatRegister(a));
1793   }
1794   MoveOperand::Kind kind =
1795       a.isStackArea() ? MoveOperand::EFFECTIVE_ADDRESS : MoveOperand::MEMORY;
1796   return MoveOperand(ToAddress(a), kind);
1797 }
1798 
1799 class OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorX86Shared> {
1800   MTableSwitch* mir_;
1801   CodeLabel jumpLabel_;
1802 
accept(CodeGeneratorX86Shared * codegen)1803   void accept(CodeGeneratorX86Shared* codegen) override {
1804     codegen->visitOutOfLineTableSwitch(this);
1805   }
1806 
1807  public:
OutOfLineTableSwitch(MTableSwitch * mir)1808   explicit OutOfLineTableSwitch(MTableSwitch* mir) : mir_(mir) {}
1809 
mir() const1810   MTableSwitch* mir() const { return mir_; }
1811 
jumpLabel()1812   CodeLabel* jumpLabel() { return &jumpLabel_; }
1813 };
1814 
visitOutOfLineTableSwitch(OutOfLineTableSwitch * ool)1815 void CodeGeneratorX86Shared::visitOutOfLineTableSwitch(
1816     OutOfLineTableSwitch* ool) {
1817   MTableSwitch* mir = ool->mir();
1818 
1819   masm.haltingAlign(sizeof(void*));
1820   masm.bind(ool->jumpLabel());
1821   masm.addCodeLabel(*ool->jumpLabel());
1822 
1823   for (size_t i = 0; i < mir->numCases(); i++) {
1824     LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
1825     Label* caseheader = caseblock->label();
1826     uint32_t caseoffset = caseheader->offset();
1827 
1828     // The entries of the jump table need to be absolute addresses and thus
1829     // must be patched after codegen is finished.
1830     CodeLabel cl;
1831     masm.writeCodePointer(&cl);
1832     cl.target()->bind(caseoffset);
1833     masm.addCodeLabel(cl);
1834   }
1835 }
1836 
emitTableSwitchDispatch(MTableSwitch * mir,Register index,Register base)1837 void CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch* mir,
1838                                                      Register index,
1839                                                      Register base) {
1840   Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
1841 
1842   // Lower value with low value
1843   if (mir->low() != 0) {
1844     masm.subl(Imm32(mir->low()), index);
1845   }
1846 
1847   // Jump to default case if input is out of range
1848   int32_t cases = mir->numCases();
1849   masm.cmp32(index, Imm32(cases));
1850   masm.j(AssemblerX86Shared::AboveOrEqual, defaultcase);
1851 
1852   // To fill in the CodeLabels for the case entries, we need to first
1853   // generate the case entries (we don't yet know their offsets in the
1854   // instruction stream).
1855   OutOfLineTableSwitch* ool = new (alloc()) OutOfLineTableSwitch(mir);
1856   addOutOfLineCode(ool, mir);
1857 
1858   // Compute the position where a pointer to the right case stands.
1859   masm.mov(ool->jumpLabel(), base);
1860   BaseIndex pointer(base, index, ScalePointer);
1861 
1862   // Jump to the right case
1863   masm.branchToComputedAddress(pointer);
1864 }
1865 
visitMathD(LMathD * math)1866 void CodeGenerator::visitMathD(LMathD* math) {
1867   FloatRegister lhs = ToFloatRegister(math->lhs());
1868   Operand rhs = ToOperand(math->rhs());
1869   FloatRegister output = ToFloatRegister(math->output());
1870 
1871   switch (math->jsop()) {
1872     case JSOp::Add:
1873       masm.vaddsd(rhs, lhs, output);
1874       break;
1875     case JSOp::Sub:
1876       masm.vsubsd(rhs, lhs, output);
1877       break;
1878     case JSOp::Mul:
1879       masm.vmulsd(rhs, lhs, output);
1880       break;
1881     case JSOp::Div:
1882       masm.vdivsd(rhs, lhs, output);
1883       break;
1884     default:
1885       MOZ_CRASH("unexpected opcode");
1886   }
1887 }
1888 
visitMathF(LMathF * math)1889 void CodeGenerator::visitMathF(LMathF* math) {
1890   FloatRegister lhs = ToFloatRegister(math->lhs());
1891   Operand rhs = ToOperand(math->rhs());
1892   FloatRegister output = ToFloatRegister(math->output());
1893 
1894   switch (math->jsop()) {
1895     case JSOp::Add:
1896       masm.vaddss(rhs, lhs, output);
1897       break;
1898     case JSOp::Sub:
1899       masm.vsubss(rhs, lhs, output);
1900       break;
1901     case JSOp::Mul:
1902       masm.vmulss(rhs, lhs, output);
1903       break;
1904     case JSOp::Div:
1905       masm.vdivss(rhs, lhs, output);
1906       break;
1907     default:
1908       MOZ_CRASH("unexpected opcode");
1909   }
1910 }
1911 
visitNearbyInt(LNearbyInt * lir)1912 void CodeGenerator::visitNearbyInt(LNearbyInt* lir) {
1913   FloatRegister input = ToFloatRegister(lir->input());
1914   FloatRegister output = ToFloatRegister(lir->output());
1915 
1916   RoundingMode roundingMode = lir->mir()->roundingMode();
1917   masm.nearbyIntDouble(roundingMode, input, output);
1918 }
1919 
visitNearbyIntF(LNearbyIntF * lir)1920 void CodeGenerator::visitNearbyIntF(LNearbyIntF* lir) {
1921   FloatRegister input = ToFloatRegister(lir->input());
1922   FloatRegister output = ToFloatRegister(lir->output());
1923 
1924   RoundingMode roundingMode = lir->mir()->roundingMode();
1925   masm.nearbyIntFloat32(roundingMode, input, output);
1926 }
1927 
visitEffectiveAddress(LEffectiveAddress * ins)1928 void CodeGenerator::visitEffectiveAddress(LEffectiveAddress* ins) {
1929   const MEffectiveAddress* mir = ins->mir();
1930   Register base = ToRegister(ins->base());
1931   Register index = ToRegister(ins->index());
1932   Register output = ToRegister(ins->output());
1933   masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output);
1934 }
1935 
generateInvalidateEpilogue()1936 void CodeGeneratorX86Shared::generateInvalidateEpilogue() {
1937   // Ensure that there is enough space in the buffer for the OsiPoint
1938   // patching to occur. Otherwise, we could overwrite the invalidation
1939   // epilogue.
1940   for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) {
1941     masm.nop();
1942   }
1943 
1944   masm.bind(&invalidate_);
1945 
1946   // Push the Ion script onto the stack (when we determine what that pointer
1947   // is).
1948   invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
1949 
1950   // Jump to the invalidator which will replace the current frame.
1951   TrampolinePtr thunk = gen->jitRuntime()->getInvalidationThunk();
1952   masm.jump(thunk);
1953 }
1954 
visitNegI(LNegI * ins)1955 void CodeGenerator::visitNegI(LNegI* ins) {
1956   Register input = ToRegister(ins->input());
1957   MOZ_ASSERT(input == ToRegister(ins->output()));
1958 
1959   masm.neg32(input);
1960 }
1961 
visitNegI64(LNegI64 * ins)1962 void CodeGenerator::visitNegI64(LNegI64* ins) {
1963   Register64 input = ToRegister64(ins->getInt64Operand(0));
1964   MOZ_ASSERT(input == ToOutRegister64(ins));
1965   masm.neg64(input);
1966 }
1967 
visitNegD(LNegD * ins)1968 void CodeGenerator::visitNegD(LNegD* ins) {
1969   FloatRegister input = ToFloatRegister(ins->input());
1970   MOZ_ASSERT(input == ToFloatRegister(ins->output()));
1971 
1972   masm.negateDouble(input);
1973 }
1974 
visitNegF(LNegF * ins)1975 void CodeGenerator::visitNegF(LNegF* ins) {
1976   FloatRegister input = ToFloatRegister(ins->input());
1977   MOZ_ASSERT(input == ToFloatRegister(ins->output()));
1978 
1979   masm.negateFloat(input);
1980 }
1981 
visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement * lir)1982 void CodeGenerator::visitCompareExchangeTypedArrayElement(
1983     LCompareExchangeTypedArrayElement* lir) {
1984   Register elements = ToRegister(lir->elements());
1985   AnyRegister output = ToAnyRegister(lir->output());
1986   Register temp =
1987       lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
1988 
1989   Register oldval = ToRegister(lir->oldval());
1990   Register newval = ToRegister(lir->newval());
1991 
1992   Scalar::Type arrayType = lir->mir()->arrayType();
1993 
1994   if (lir->index()->isConstant()) {
1995     Address dest = ToAddress(elements, lir->index(), arrayType);
1996     masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval,
1997                            newval, temp, output);
1998   } else {
1999     BaseIndex dest(elements, ToRegister(lir->index()),
2000                    ScaleFromScalarType(arrayType));
2001     masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval,
2002                            newval, temp, output);
2003   }
2004 }
2005 
visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement * lir)2006 void CodeGenerator::visitAtomicExchangeTypedArrayElement(
2007     LAtomicExchangeTypedArrayElement* lir) {
2008   Register elements = ToRegister(lir->elements());
2009   AnyRegister output = ToAnyRegister(lir->output());
2010   Register temp =
2011       lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
2012 
2013   Register value = ToRegister(lir->value());
2014 
2015   Scalar::Type arrayType = lir->mir()->arrayType();
2016 
2017   if (lir->index()->isConstant()) {
2018     Address dest = ToAddress(elements, lir->index(), arrayType);
2019     masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, temp,
2020                           output);
2021   } else {
2022     BaseIndex dest(elements, ToRegister(lir->index()),
2023                    ScaleFromScalarType(arrayType));
2024     masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, temp,
2025                           output);
2026   }
2027 }
2028 
2029 template <typename T>
AtomicBinopToTypedArray(MacroAssembler & masm,AtomicOp op,Scalar::Type arrayType,const LAllocation * value,const T & mem,Register temp1,Register temp2,AnyRegister output)2030 static inline void AtomicBinopToTypedArray(MacroAssembler& masm, AtomicOp op,
2031                                            Scalar::Type arrayType,
2032                                            const LAllocation* value,
2033                                            const T& mem, Register temp1,
2034                                            Register temp2, AnyRegister output) {
2035   if (value->isConstant()) {
2036     masm.atomicFetchOpJS(arrayType, Synchronization::Full(), op,
2037                          Imm32(ToInt32(value)), mem, temp1, temp2, output);
2038   } else {
2039     masm.atomicFetchOpJS(arrayType, Synchronization::Full(), op,
2040                          ToRegister(value), mem, temp1, temp2, output);
2041   }
2042 }
2043 
visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop * lir)2044 void CodeGenerator::visitAtomicTypedArrayElementBinop(
2045     LAtomicTypedArrayElementBinop* lir) {
2046   MOZ_ASSERT(!lir->mir()->isForEffect());
2047 
2048   AnyRegister output = ToAnyRegister(lir->output());
2049   Register elements = ToRegister(lir->elements());
2050   Register temp1 =
2051       lir->temp1()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp1());
2052   Register temp2 =
2053       lir->temp2()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp2());
2054   const LAllocation* value = lir->value();
2055 
2056   Scalar::Type arrayType = lir->mir()->arrayType();
2057 
2058   if (lir->index()->isConstant()) {
2059     Address mem = ToAddress(elements, lir->index(), arrayType);
2060     AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value,
2061                             mem, temp1, temp2, output);
2062   } else {
2063     BaseIndex mem(elements, ToRegister(lir->index()),
2064                   ScaleFromScalarType(arrayType));
2065     AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value,
2066                             mem, temp1, temp2, output);
2067   }
2068 }
2069 
2070 template <typename T>
AtomicBinopToTypedArray(MacroAssembler & masm,Scalar::Type arrayType,AtomicOp op,const LAllocation * value,const T & mem)2071 static inline void AtomicBinopToTypedArray(MacroAssembler& masm,
2072                                            Scalar::Type arrayType, AtomicOp op,
2073                                            const LAllocation* value,
2074                                            const T& mem) {
2075   if (value->isConstant()) {
2076     masm.atomicEffectOpJS(arrayType, Synchronization::Full(), op,
2077                           Imm32(ToInt32(value)), mem, InvalidReg);
2078   } else {
2079     masm.atomicEffectOpJS(arrayType, Synchronization::Full(), op,
2080                           ToRegister(value), mem, InvalidReg);
2081   }
2082 }
2083 
visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect * lir)2084 void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect(
2085     LAtomicTypedArrayElementBinopForEffect* lir) {
2086   MOZ_ASSERT(lir->mir()->isForEffect());
2087 
2088   Register elements = ToRegister(lir->elements());
2089   const LAllocation* value = lir->value();
2090   Scalar::Type arrayType = lir->mir()->arrayType();
2091 
2092   if (lir->index()->isConstant()) {
2093     Address mem = ToAddress(elements, lir->index(), arrayType);
2094     AtomicBinopToTypedArray(masm, arrayType, lir->mir()->operation(), value,
2095                             mem);
2096   } else {
2097     BaseIndex mem(elements, ToRegister(lir->index()),
2098                   ScaleFromScalarType(arrayType));
2099     AtomicBinopToTypedArray(masm, arrayType, lir->mir()->operation(), value,
2100                             mem);
2101   }
2102 }
2103 
visitMemoryBarrier(LMemoryBarrier * ins)2104 void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) {
2105   if (ins->type() & MembarStoreLoad) {
2106     masm.storeLoadFence();
2107   }
2108 }
2109 
visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck * ool)2110 void CodeGeneratorX86Shared::visitOutOfLineWasmTruncateCheck(
2111     OutOfLineWasmTruncateCheck* ool) {
2112   FloatRegister input = ool->input();
2113   Register output = ool->output();
2114   Register64 output64 = ool->output64();
2115   MIRType fromType = ool->fromType();
2116   MIRType toType = ool->toType();
2117   Label* oolRejoin = ool->rejoin();
2118   TruncFlags flags = ool->flags();
2119   wasm::BytecodeOffset off = ool->bytecodeOffset();
2120 
2121   if (fromType == MIRType::Float32) {
2122     if (toType == MIRType::Int32) {
2123       masm.oolWasmTruncateCheckF32ToI32(input, output, flags, off, oolRejoin);
2124     } else if (toType == MIRType::Int64) {
2125       masm.oolWasmTruncateCheckF32ToI64(input, output64, flags, off, oolRejoin);
2126     } else {
2127       MOZ_CRASH("unexpected type");
2128     }
2129   } else if (fromType == MIRType::Double) {
2130     if (toType == MIRType::Int32) {
2131       masm.oolWasmTruncateCheckF64ToI32(input, output, flags, off, oolRejoin);
2132     } else if (toType == MIRType::Int64) {
2133       masm.oolWasmTruncateCheckF64ToI64(input, output64, flags, off, oolRejoin);
2134     } else {
2135       MOZ_CRASH("unexpected type");
2136     }
2137   } else {
2138     MOZ_CRASH("unexpected type");
2139   }
2140 }
2141 
canonicalizeIfDeterministic(Scalar::Type type,const LAllocation * value)2142 void CodeGeneratorX86Shared::canonicalizeIfDeterministic(
2143     Scalar::Type type, const LAllocation* value) {
2144 #ifdef DEBUG
2145   if (!js::SupportDifferentialTesting()) {
2146     return;
2147   }
2148 
2149   switch (type) {
2150     case Scalar::Float32: {
2151       FloatRegister in = ToFloatRegister(value);
2152       masm.canonicalizeFloatIfDeterministic(in);
2153       break;
2154     }
2155     case Scalar::Float64: {
2156       FloatRegister in = ToFloatRegister(value);
2157       masm.canonicalizeDoubleIfDeterministic(in);
2158       break;
2159     }
2160     default: {
2161       // Other types don't need canonicalization.
2162       break;
2163     }
2164   }
2165 #endif  // DEBUG
2166 }
2167 
2168 template <typename T>
toMemoryAccessOperand(T * lir,int32_t disp)2169 Operand CodeGeneratorX86Shared::toMemoryAccessOperand(T* lir, int32_t disp) {
2170   const LAllocation* ptr = lir->ptr();
2171 #ifdef JS_CODEGEN_X86
2172   const LAllocation* memoryBase = lir->memoryBase();
2173   Operand destAddr = ptr->isBogus() ? Operand(ToRegister(memoryBase), disp)
2174                                     : Operand(ToRegister(memoryBase),
2175                                               ToRegister(ptr), TimesOne, disp);
2176 #else
2177   Operand destAddr = ptr->isBogus()
2178                          ? Operand(HeapReg, disp)
2179                          : Operand(HeapReg, ToRegister(ptr), TimesOne, disp);
2180 #endif
2181   return destAddr;
2182 }
2183 
visitCopySignF(LCopySignF * lir)2184 void CodeGenerator::visitCopySignF(LCopySignF* lir) {
2185   FloatRegister lhs = ToFloatRegister(lir->getOperand(0));
2186   FloatRegister rhs = ToFloatRegister(lir->getOperand(1));
2187 
2188   FloatRegister out = ToFloatRegister(lir->output());
2189 
2190   if (lhs == rhs) {
2191     if (lhs != out) {
2192       masm.moveFloat32(lhs, out);
2193     }
2194     return;
2195   }
2196 
2197   masm.copySignFloat32(lhs, rhs, out);
2198 }
2199 
visitCopySignD(LCopySignD * lir)2200 void CodeGenerator::visitCopySignD(LCopySignD* lir) {
2201   FloatRegister lhs = ToFloatRegister(lir->getOperand(0));
2202   FloatRegister rhs = ToFloatRegister(lir->getOperand(1));
2203 
2204   FloatRegister out = ToFloatRegister(lir->output());
2205 
2206   if (lhs == rhs) {
2207     if (lhs != out) {
2208       masm.moveDouble(lhs, out);
2209     }
2210     return;
2211   }
2212 
2213   masm.copySignDouble(lhs, rhs, out);
2214 }
2215 
visitRotateI64(LRotateI64 * lir)2216 void CodeGenerator::visitRotateI64(LRotateI64* lir) {
2217   MRotate* mir = lir->mir();
2218   LAllocation* count = lir->count();
2219 
2220   Register64 input = ToRegister64(lir->input());
2221   Register64 output = ToOutRegister64(lir);
2222   Register temp = ToTempRegisterOrInvalid(lir->temp());
2223 
2224   MOZ_ASSERT(input == output);
2225 
2226   if (count->isConstant()) {
2227     int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
2228     if (!c) {
2229       return;
2230     }
2231     if (mir->isLeftRotate()) {
2232       masm.rotateLeft64(Imm32(c), input, output, temp);
2233     } else {
2234       masm.rotateRight64(Imm32(c), input, output, temp);
2235     }
2236   } else {
2237     if (mir->isLeftRotate()) {
2238       masm.rotateLeft64(ToRegister(count), input, output, temp);
2239     } else {
2240       masm.rotateRight64(ToRegister(count), input, output, temp);
2241     }
2242   }
2243 }
2244 
visitPopcntI64(LPopcntI64 * lir)2245 void CodeGenerator::visitPopcntI64(LPopcntI64* lir) {
2246   Register64 input = ToRegister64(lir->getInt64Operand(0));
2247   Register64 output = ToOutRegister64(lir);
2248   Register temp = InvalidReg;
2249   if (!AssemblerX86Shared::HasPOPCNT()) {
2250     temp = ToRegister(lir->getTemp(0));
2251   }
2252 
2253   masm.popcnt64(input, output, temp);
2254 }
2255 
visitSimd128(LSimd128 * ins)2256 void CodeGenerator::visitSimd128(LSimd128* ins) {
2257 #ifdef ENABLE_WASM_SIMD
2258   const LDefinition* out = ins->getDef(0);
2259   masm.loadConstantSimd128(ins->getSimd128(), ToFloatRegister(out));
2260 #else
2261   MOZ_CRASH("No SIMD");
2262 #endif
2263 }
2264 
visitWasmBitselectSimd128(LWasmBitselectSimd128 * ins)2265 void CodeGenerator::visitWasmBitselectSimd128(LWasmBitselectSimd128* ins) {
2266 #ifdef ENABLE_WASM_SIMD
2267   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
2268   FloatRegister rhs = ToFloatRegister(ins->rhs());
2269   FloatRegister control = ToFloatRegister(ins->control());
2270   FloatRegister temp = ToFloatRegister(ins->temp());
2271   masm.bitwiseSelectSimd128(control, lhsDest, rhs, lhsDest, temp);
2272 #else
2273   MOZ_CRASH("No SIMD");
2274 #endif
2275 }
2276 
visitWasmBinarySimd128(LWasmBinarySimd128 * ins)2277 void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) {
2278 #ifdef ENABLE_WASM_SIMD
2279   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
2280   FloatRegister rhs = ToFloatRegister(ins->rhs());
2281   FloatRegister temp1 = ToTempFloatRegisterOrInvalid(ins->getTemp(0));
2282   FloatRegister temp2 = ToTempFloatRegisterOrInvalid(ins->getTemp(1));
2283 
2284   MOZ_ASSERT(ToFloatRegister(ins->output()) == lhsDest);
2285 
2286   switch (ins->simdOp()) {
2287     case wasm::SimdOp::V128And:
2288       masm.bitwiseAndSimd128(rhs, lhsDest);
2289       break;
2290     case wasm::SimdOp::V128Or:
2291       masm.bitwiseOrSimd128(rhs, lhsDest);
2292       break;
2293     case wasm::SimdOp::V128Xor:
2294       masm.bitwiseXorSimd128(rhs, lhsDest);
2295       break;
2296     case wasm::SimdOp::V128AndNot:
2297       // x86/x64 specific: The CPU provides ~A & B.  The operands were swapped
2298       // during lowering, and we'll compute A & ~B here as desired.
2299       masm.bitwiseNotAndSimd128(rhs, lhsDest);
2300       break;
2301     case wasm::SimdOp::I8x16AvgrU:
2302       masm.unsignedAverageInt8x16(rhs, lhsDest);
2303       break;
2304     case wasm::SimdOp::I16x8AvgrU:
2305       masm.unsignedAverageInt16x8(rhs, lhsDest);
2306       break;
2307     case wasm::SimdOp::I8x16Add:
2308       masm.addInt8x16(rhs, lhsDest);
2309       break;
2310     case wasm::SimdOp::I8x16AddSaturateS:
2311       masm.addSatInt8x16(rhs, lhsDest);
2312       break;
2313     case wasm::SimdOp::I8x16AddSaturateU:
2314       masm.unsignedAddSatInt8x16(rhs, lhsDest);
2315       break;
2316     case wasm::SimdOp::I8x16Sub:
2317       masm.subInt8x16(rhs, lhsDest);
2318       break;
2319     case wasm::SimdOp::I8x16SubSaturateS:
2320       masm.subSatInt8x16(rhs, lhsDest);
2321       break;
2322     case wasm::SimdOp::I8x16SubSaturateU:
2323       masm.unsignedSubSatInt8x16(rhs, lhsDest);
2324       break;
2325     case wasm::SimdOp::I8x16MinS:
2326       masm.minInt8x16(rhs, lhsDest);
2327       break;
2328     case wasm::SimdOp::I8x16MinU:
2329       masm.unsignedMinInt8x16(rhs, lhsDest);
2330       break;
2331     case wasm::SimdOp::I8x16MaxS:
2332       masm.maxInt8x16(rhs, lhsDest);
2333       break;
2334     case wasm::SimdOp::I8x16MaxU:
2335       masm.unsignedMaxInt8x16(rhs, lhsDest);
2336       break;
2337     case wasm::SimdOp::I16x8Add:
2338       masm.addInt16x8(rhs, lhsDest);
2339       break;
2340     case wasm::SimdOp::I16x8AddSaturateS:
2341       masm.addSatInt16x8(rhs, lhsDest);
2342       break;
2343     case wasm::SimdOp::I16x8AddSaturateU:
2344       masm.unsignedAddSatInt16x8(rhs, lhsDest);
2345       break;
2346     case wasm::SimdOp::I16x8Sub:
2347       masm.subInt16x8(rhs, lhsDest);
2348       break;
2349     case wasm::SimdOp::I16x8SubSaturateS:
2350       masm.subSatInt16x8(rhs, lhsDest);
2351       break;
2352     case wasm::SimdOp::I16x8SubSaturateU:
2353       masm.unsignedSubSatInt16x8(rhs, lhsDest);
2354       break;
2355     case wasm::SimdOp::I16x8Mul:
2356       masm.mulInt16x8(rhs, lhsDest);
2357       break;
2358     case wasm::SimdOp::I16x8MinS:
2359       masm.minInt16x8(rhs, lhsDest);
2360       break;
2361     case wasm::SimdOp::I16x8MinU:
2362       masm.unsignedMinInt16x8(rhs, lhsDest);
2363       break;
2364     case wasm::SimdOp::I16x8MaxS:
2365       masm.maxInt16x8(rhs, lhsDest);
2366       break;
2367     case wasm::SimdOp::I16x8MaxU:
2368       masm.unsignedMaxInt16x8(rhs, lhsDest);
2369       break;
2370     case wasm::SimdOp::I32x4Add:
2371       masm.addInt32x4(rhs, lhsDest);
2372       break;
2373     case wasm::SimdOp::I32x4Sub:
2374       masm.subInt32x4(rhs, lhsDest);
2375       break;
2376     case wasm::SimdOp::I32x4Mul:
2377       masm.mulInt32x4(rhs, lhsDest);
2378       break;
2379     case wasm::SimdOp::I32x4MinS:
2380       masm.minInt32x4(rhs, lhsDest);
2381       break;
2382     case wasm::SimdOp::I32x4MinU:
2383       masm.unsignedMinInt32x4(rhs, lhsDest);
2384       break;
2385     case wasm::SimdOp::I32x4MaxS:
2386       masm.maxInt32x4(rhs, lhsDest);
2387       break;
2388     case wasm::SimdOp::I32x4MaxU:
2389       masm.unsignedMaxInt32x4(rhs, lhsDest);
2390       break;
2391     case wasm::SimdOp::I64x2Add:
2392       masm.addInt64x2(rhs, lhsDest);
2393       break;
2394     case wasm::SimdOp::I64x2Sub:
2395       masm.subInt64x2(rhs, lhsDest);
2396       break;
2397     case wasm::SimdOp::I64x2Mul:
2398       masm.mulInt64x2(lhsDest, rhs, lhsDest, temp1);
2399       break;
2400     case wasm::SimdOp::F32x4Add:
2401       masm.addFloat32x4(rhs, lhsDest);
2402       break;
2403     case wasm::SimdOp::F32x4Sub:
2404       masm.subFloat32x4(rhs, lhsDest);
2405       break;
2406     case wasm::SimdOp::F32x4Mul:
2407       masm.mulFloat32x4(rhs, lhsDest);
2408       break;
2409     case wasm::SimdOp::F32x4Div:
2410       masm.divFloat32x4(rhs, lhsDest);
2411       break;
2412     case wasm::SimdOp::F32x4Min:
2413       masm.minFloat32x4(rhs, lhsDest, temp1, temp2);
2414       break;
2415     case wasm::SimdOp::F32x4Max:
2416       masm.maxFloat32x4(rhs, lhsDest, temp1, temp2);
2417       break;
2418     case wasm::SimdOp::F64x2Add:
2419       masm.addFloat64x2(rhs, lhsDest);
2420       break;
2421     case wasm::SimdOp::F64x2Sub:
2422       masm.subFloat64x2(rhs, lhsDest);
2423       break;
2424     case wasm::SimdOp::F64x2Mul:
2425       masm.mulFloat64x2(rhs, lhsDest);
2426       break;
2427     case wasm::SimdOp::F64x2Div:
2428       masm.divFloat64x2(rhs, lhsDest);
2429       break;
2430     case wasm::SimdOp::F64x2Min:
2431       masm.minFloat64x2(rhs, lhsDest, temp1, temp2);
2432       break;
2433     case wasm::SimdOp::F64x2Max:
2434       masm.maxFloat64x2(rhs, lhsDest, temp1, temp2);
2435       break;
2436     case wasm::SimdOp::V8x16Swizzle:
2437       masm.swizzleInt8x16(rhs, lhsDest);
2438       break;
2439     case wasm::SimdOp::I8x16NarrowSI16x8:
2440       masm.narrowInt16x8(rhs, lhsDest);
2441       break;
2442     case wasm::SimdOp::I8x16NarrowUI16x8:
2443       masm.unsignedNarrowInt16x8(rhs, lhsDest);
2444       break;
2445     case wasm::SimdOp::I16x8NarrowSI32x4:
2446       masm.narrowInt32x4(rhs, lhsDest);
2447       break;
2448     case wasm::SimdOp::I16x8NarrowUI32x4:
2449       masm.unsignedNarrowInt32x4(rhs, lhsDest);
2450       break;
2451     case wasm::SimdOp::I8x16Eq:
2452       masm.compareInt8x16(Assembler::Equal, rhs, lhsDest);
2453       break;
2454     case wasm::SimdOp::I8x16Ne:
2455       masm.compareInt8x16(Assembler::NotEqual, rhs, lhsDest);
2456       break;
2457     case wasm::SimdOp::I8x16LtS:
2458       masm.compareInt8x16(Assembler::LessThan, rhs, lhsDest);
2459       break;
2460     case wasm::SimdOp::I8x16GtS:
2461       masm.compareInt8x16(Assembler::GreaterThan, rhs, lhsDest);
2462       break;
2463     case wasm::SimdOp::I8x16LeS:
2464       masm.compareInt8x16(Assembler::LessThanOrEqual, rhs, lhsDest);
2465       break;
2466     case wasm::SimdOp::I8x16GeS:
2467       masm.compareInt8x16(Assembler::GreaterThanOrEqual, rhs, lhsDest);
2468       break;
2469     case wasm::SimdOp::I8x16LtU:
2470       masm.compareInt8x16(Assembler::Below, rhs, lhsDest);
2471       break;
2472     case wasm::SimdOp::I8x16GtU:
2473       masm.compareInt8x16(Assembler::Above, rhs, lhsDest);
2474       break;
2475     case wasm::SimdOp::I8x16LeU:
2476       masm.compareInt8x16(Assembler::BelowOrEqual, rhs, lhsDest);
2477       break;
2478     case wasm::SimdOp::I8x16GeU:
2479       masm.compareInt8x16(Assembler::AboveOrEqual, rhs, lhsDest);
2480       break;
2481     case wasm::SimdOp::I16x8Eq:
2482       masm.compareInt16x8(Assembler::Equal, rhs, lhsDest);
2483       break;
2484     case wasm::SimdOp::I16x8Ne:
2485       masm.compareInt16x8(Assembler::NotEqual, rhs, lhsDest);
2486       break;
2487     case wasm::SimdOp::I16x8LtS:
2488       masm.compareInt16x8(Assembler::LessThan, rhs, lhsDest);
2489       break;
2490     case wasm::SimdOp::I16x8GtS:
2491       masm.compareInt16x8(Assembler::GreaterThan, rhs, lhsDest);
2492       break;
2493     case wasm::SimdOp::I16x8LeS:
2494       masm.compareInt16x8(Assembler::LessThanOrEqual, rhs, lhsDest);
2495       break;
2496     case wasm::SimdOp::I16x8GeS:
2497       masm.compareInt16x8(Assembler::GreaterThanOrEqual, rhs, lhsDest);
2498       break;
2499     case wasm::SimdOp::I16x8LtU:
2500       masm.compareInt16x8(Assembler::Below, rhs, lhsDest);
2501       break;
2502     case wasm::SimdOp::I16x8GtU:
2503       masm.compareInt16x8(Assembler::Above, rhs, lhsDest);
2504       break;
2505     case wasm::SimdOp::I16x8LeU:
2506       masm.compareInt16x8(Assembler::BelowOrEqual, rhs, lhsDest);
2507       break;
2508     case wasm::SimdOp::I16x8GeU:
2509       masm.compareInt16x8(Assembler::AboveOrEqual, rhs, lhsDest);
2510       break;
2511     case wasm::SimdOp::I32x4Eq:
2512       masm.compareInt32x4(Assembler::Equal, rhs, lhsDest);
2513       break;
2514     case wasm::SimdOp::I32x4Ne:
2515       masm.compareInt32x4(Assembler::NotEqual, rhs, lhsDest);
2516       break;
2517     case wasm::SimdOp::I32x4LtS:
2518       masm.compareInt32x4(Assembler::LessThan, rhs, lhsDest);
2519       break;
2520     case wasm::SimdOp::I32x4GtS:
2521       masm.compareInt32x4(Assembler::GreaterThan, rhs, lhsDest);
2522       break;
2523     case wasm::SimdOp::I32x4LeS:
2524       masm.compareInt32x4(Assembler::LessThanOrEqual, rhs, lhsDest);
2525       break;
2526     case wasm::SimdOp::I32x4GeS:
2527       masm.compareInt32x4(Assembler::GreaterThanOrEqual, rhs, lhsDest);
2528       break;
2529     case wasm::SimdOp::I32x4LtU:
2530       masm.compareInt32x4(Assembler::Below, rhs, lhsDest);
2531       break;
2532     case wasm::SimdOp::I32x4GtU:
2533       masm.compareInt32x4(Assembler::Above, rhs, lhsDest);
2534       break;
2535     case wasm::SimdOp::I32x4LeU:
2536       masm.compareInt32x4(Assembler::BelowOrEqual, rhs, lhsDest);
2537       break;
2538     case wasm::SimdOp::I32x4GeU:
2539       masm.compareInt32x4(Assembler::AboveOrEqual, rhs, lhsDest);
2540       break;
2541     case wasm::SimdOp::I64x2Eq:
2542       masm.compareForEqualityInt64x2(Assembler::Equal, rhs, lhsDest);
2543       break;
2544     case wasm::SimdOp::I64x2Ne:
2545       masm.compareForEqualityInt64x2(Assembler::NotEqual, rhs, lhsDest);
2546       break;
2547     case wasm::SimdOp::I64x2LtS:
2548       masm.compareForOrderingInt64x2(Assembler::LessThan, rhs, lhsDest, temp1,
2549                                      temp2);
2550       break;
2551     case wasm::SimdOp::I64x2GtS:
2552       masm.compareForOrderingInt64x2(Assembler::GreaterThan, rhs, lhsDest,
2553                                      temp1, temp2);
2554       break;
2555     case wasm::SimdOp::I64x2LeS:
2556       masm.compareForOrderingInt64x2(Assembler::LessThanOrEqual, rhs, lhsDest,
2557                                      temp1, temp2);
2558       break;
2559     case wasm::SimdOp::I64x2GeS:
2560       masm.compareForOrderingInt64x2(Assembler::GreaterThanOrEqual, rhs,
2561                                      lhsDest, temp1, temp2);
2562       break;
2563     case wasm::SimdOp::F32x4Eq:
2564       masm.compareFloat32x4(Assembler::Equal, rhs, lhsDest);
2565       break;
2566     case wasm::SimdOp::F32x4Ne:
2567       masm.compareFloat32x4(Assembler::NotEqual, rhs, lhsDest);
2568       break;
2569     case wasm::SimdOp::F32x4Lt:
2570       masm.compareFloat32x4(Assembler::LessThan, rhs, lhsDest);
2571       break;
2572     case wasm::SimdOp::F32x4Le:
2573       masm.compareFloat32x4(Assembler::LessThanOrEqual, rhs, lhsDest);
2574       break;
2575     case wasm::SimdOp::F64x2Eq:
2576       masm.compareFloat64x2(Assembler::Equal, rhs, lhsDest);
2577       break;
2578     case wasm::SimdOp::F64x2Ne:
2579       masm.compareFloat64x2(Assembler::NotEqual, rhs, lhsDest);
2580       break;
2581     case wasm::SimdOp::F64x2Lt:
2582       masm.compareFloat64x2(Assembler::LessThan, rhs, lhsDest);
2583       break;
2584     case wasm::SimdOp::F64x2Le:
2585       masm.compareFloat64x2(Assembler::LessThanOrEqual, rhs, lhsDest);
2586       break;
2587     case wasm::SimdOp::F32x4PMax:
2588       // `lhsDest` is actually rhsDest, and `rhs` is actually lhs
2589       masm.pseudoMaxFloat32x4(lhsDest, rhs);
2590       break;
2591     case wasm::SimdOp::F32x4PMin:
2592       // `lhsDest` is actually rhsDest, and `rhs` is actually lhs
2593       masm.pseudoMinFloat32x4(lhsDest, rhs);
2594       break;
2595     case wasm::SimdOp::F64x2PMax:
2596       // `lhsDest` is actually rhsDest, and `rhs` is actually lhs
2597       masm.pseudoMaxFloat64x2(lhsDest, rhs);
2598       break;
2599     case wasm::SimdOp::F64x2PMin:
2600       // `lhsDest` is actually rhsDest, and `rhs` is actually lhs
2601       masm.pseudoMinFloat64x2(lhsDest, rhs);
2602       break;
2603     case wasm::SimdOp::I32x4DotSI16x8:
2604       masm.widenDotInt16x8(rhs, lhsDest);
2605       break;
2606     case wasm::SimdOp::I16x8ExtMulLowSI8x16:
2607       masm.extMulLowInt8x16(rhs, lhsDest);
2608       break;
2609     case wasm::SimdOp::I16x8ExtMulHighSI8x16:
2610       masm.extMulHighInt8x16(rhs, lhsDest);
2611       break;
2612     case wasm::SimdOp::I16x8ExtMulLowUI8x16:
2613       masm.unsignedExtMulLowInt8x16(rhs, lhsDest);
2614       break;
2615     case wasm::SimdOp::I16x8ExtMulHighUI8x16:
2616       masm.unsignedExtMulHighInt8x16(rhs, lhsDest);
2617       break;
2618     case wasm::SimdOp::I32x4ExtMulLowSI16x8:
2619       masm.extMulLowInt16x8(rhs, lhsDest);
2620       break;
2621     case wasm::SimdOp::I32x4ExtMulHighSI16x8:
2622       masm.extMulHighInt16x8(rhs, lhsDest);
2623       break;
2624     case wasm::SimdOp::I32x4ExtMulLowUI16x8:
2625       masm.unsignedExtMulLowInt16x8(rhs, lhsDest);
2626       break;
2627     case wasm::SimdOp::I32x4ExtMulHighUI16x8:
2628       masm.unsignedExtMulHighInt16x8(rhs, lhsDest);
2629       break;
2630     case wasm::SimdOp::I64x2ExtMulLowSI32x4:
2631       masm.extMulLowInt32x4(rhs, lhsDest);
2632       break;
2633     case wasm::SimdOp::I64x2ExtMulHighSI32x4:
2634       masm.extMulHighInt32x4(rhs, lhsDest);
2635       break;
2636     case wasm::SimdOp::I64x2ExtMulLowUI32x4:
2637       masm.unsignedExtMulLowInt32x4(rhs, lhsDest);
2638       break;
2639     case wasm::SimdOp::I64x2ExtMulHighUI32x4:
2640       masm.unsignedExtMulHighInt32x4(rhs, lhsDest);
2641       break;
2642     case wasm::SimdOp::I16x8Q15MulrSatS:
2643       masm.q15MulrSatInt16x8(rhs, lhsDest);
2644       break;
2645 #  ifdef ENABLE_WASM_SIMD_WORMHOLE
2646     case wasm::SimdOp::MozWHSELFTEST:
2647       masm.loadConstantSimd128(wasm::WormholeSignature(), lhsDest);
2648       break;
2649     case wasm::SimdOp::MozWHPMADDUBSW:
2650       masm.vpmaddubsw(rhs, lhsDest, lhsDest);
2651       break;
2652     case wasm::SimdOp::MozWHPMADDWD:
2653       masm.vpmaddwd(Operand(rhs), lhsDest, lhsDest);
2654       break;
2655 #  endif
2656     default:
2657       MOZ_CRASH("Binary SimdOp not implemented");
2658   }
2659 #else
2660   MOZ_CRASH("No SIMD");
2661 #endif
2662 }
2663 
visitWasmBinarySimd128WithConstant(LWasmBinarySimd128WithConstant * ins)2664 void CodeGenerator::visitWasmBinarySimd128WithConstant(
2665     LWasmBinarySimd128WithConstant* ins) {
2666 #ifdef ENABLE_WASM_SIMD
2667   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
2668   const SimdConstant& rhs = ins->rhs();
2669 
2670   MOZ_ASSERT(ToFloatRegister(ins->output()) == lhsDest);
2671 
2672   switch (ins->simdOp()) {
2673     case wasm::SimdOp::I8x16Add:
2674       masm.addInt8x16(rhs, lhsDest);
2675       break;
2676     case wasm::SimdOp::I16x8Add:
2677       masm.addInt16x8(rhs, lhsDest);
2678       break;
2679     case wasm::SimdOp::I32x4Add:
2680       masm.addInt32x4(rhs, lhsDest);
2681       break;
2682     case wasm::SimdOp::I64x2Add:
2683       masm.addInt64x2(rhs, lhsDest);
2684       break;
2685     case wasm::SimdOp::I8x16Sub:
2686       masm.subInt8x16(rhs, lhsDest);
2687       break;
2688     case wasm::SimdOp::I16x8Sub:
2689       masm.subInt16x8(rhs, lhsDest);
2690       break;
2691     case wasm::SimdOp::I32x4Sub:
2692       masm.subInt32x4(rhs, lhsDest);
2693       break;
2694     case wasm::SimdOp::I64x2Sub:
2695       masm.subInt64x2(rhs, lhsDest);
2696       break;
2697     case wasm::SimdOp::I16x8Mul:
2698       masm.mulInt16x8(rhs, lhsDest);
2699       break;
2700     case wasm::SimdOp::I32x4Mul:
2701       masm.mulInt32x4(rhs, lhsDest);
2702       break;
2703     case wasm::SimdOp::I8x16AddSaturateS:
2704       masm.addSatInt8x16(rhs, lhsDest);
2705       break;
2706     case wasm::SimdOp::I8x16AddSaturateU:
2707       masm.unsignedAddSatInt8x16(rhs, lhsDest);
2708       break;
2709     case wasm::SimdOp::I16x8AddSaturateS:
2710       masm.addSatInt16x8(rhs, lhsDest);
2711       break;
2712     case wasm::SimdOp::I16x8AddSaturateU:
2713       masm.unsignedAddSatInt16x8(rhs, lhsDest);
2714       break;
2715     case wasm::SimdOp::I8x16SubSaturateS:
2716       masm.subSatInt8x16(rhs, lhsDest);
2717       break;
2718     case wasm::SimdOp::I8x16SubSaturateU:
2719       masm.unsignedSubSatInt8x16(rhs, lhsDest);
2720       break;
2721     case wasm::SimdOp::I16x8SubSaturateS:
2722       masm.subSatInt16x8(rhs, lhsDest);
2723       break;
2724     case wasm::SimdOp::I16x8SubSaturateU:
2725       masm.unsignedSubSatInt16x8(rhs, lhsDest);
2726       break;
2727     case wasm::SimdOp::I8x16MinS:
2728       masm.minInt8x16(rhs, lhsDest);
2729       break;
2730     case wasm::SimdOp::I8x16MinU:
2731       masm.unsignedMinInt8x16(rhs, lhsDest);
2732       break;
2733     case wasm::SimdOp::I16x8MinS:
2734       masm.minInt16x8(rhs, lhsDest);
2735       break;
2736     case wasm::SimdOp::I16x8MinU:
2737       masm.unsignedMinInt16x8(rhs, lhsDest);
2738       break;
2739     case wasm::SimdOp::I32x4MinS:
2740       masm.minInt32x4(rhs, lhsDest);
2741       break;
2742     case wasm::SimdOp::I32x4MinU:
2743       masm.unsignedMinInt32x4(rhs, lhsDest);
2744       break;
2745     case wasm::SimdOp::I8x16MaxS:
2746       masm.maxInt8x16(rhs, lhsDest);
2747       break;
2748     case wasm::SimdOp::I8x16MaxU:
2749       masm.unsignedMaxInt8x16(rhs, lhsDest);
2750       break;
2751     case wasm::SimdOp::I16x8MaxS:
2752       masm.maxInt16x8(rhs, lhsDest);
2753       break;
2754     case wasm::SimdOp::I16x8MaxU:
2755       masm.unsignedMaxInt16x8(rhs, lhsDest);
2756       break;
2757     case wasm::SimdOp::I32x4MaxS:
2758       masm.maxInt32x4(rhs, lhsDest);
2759       break;
2760     case wasm::SimdOp::I32x4MaxU:
2761       masm.unsignedMaxInt32x4(rhs, lhsDest);
2762       break;
2763     case wasm::SimdOp::V128And:
2764       masm.bitwiseAndSimd128(rhs, lhsDest);
2765       break;
2766     case wasm::SimdOp::V128Or:
2767       masm.bitwiseOrSimd128(rhs, lhsDest);
2768       break;
2769     case wasm::SimdOp::V128Xor:
2770       masm.bitwiseXorSimd128(rhs, lhsDest);
2771       break;
2772     case wasm::SimdOp::I8x16Eq:
2773       masm.compareInt8x16(Assembler::Equal, rhs, lhsDest);
2774       break;
2775     case wasm::SimdOp::I8x16Ne:
2776       masm.compareInt8x16(Assembler::NotEqual, rhs, lhsDest);
2777       break;
2778     case wasm::SimdOp::I8x16GtS:
2779       masm.compareInt8x16(Assembler::GreaterThan, rhs, lhsDest);
2780       break;
2781     case wasm::SimdOp::I8x16LeS:
2782       masm.compareInt8x16(Assembler::LessThanOrEqual, rhs, lhsDest);
2783       break;
2784     case wasm::SimdOp::I16x8Eq:
2785       masm.compareInt16x8(Assembler::Equal, rhs, lhsDest);
2786       break;
2787     case wasm::SimdOp::I16x8Ne:
2788       masm.compareInt16x8(Assembler::NotEqual, rhs, lhsDest);
2789       break;
2790     case wasm::SimdOp::I16x8GtS:
2791       masm.compareInt16x8(Assembler::GreaterThan, rhs, lhsDest);
2792       break;
2793     case wasm::SimdOp::I16x8LeS:
2794       masm.compareInt16x8(Assembler::LessThanOrEqual, rhs, lhsDest);
2795       break;
2796     case wasm::SimdOp::I32x4Eq:
2797       masm.compareInt32x4(Assembler::Equal, rhs, lhsDest);
2798       break;
2799     case wasm::SimdOp::I32x4Ne:
2800       masm.compareInt32x4(Assembler::NotEqual, rhs, lhsDest);
2801       break;
2802     case wasm::SimdOp::I32x4GtS:
2803       masm.compareInt32x4(Assembler::GreaterThan, rhs, lhsDest);
2804       break;
2805     case wasm::SimdOp::I32x4LeS:
2806       masm.compareInt32x4(Assembler::LessThanOrEqual, rhs, lhsDest);
2807       break;
2808     case wasm::SimdOp::F32x4Eq:
2809       masm.compareFloat32x4(Assembler::Equal, rhs, lhsDest);
2810       break;
2811     case wasm::SimdOp::F32x4Ne:
2812       masm.compareFloat32x4(Assembler::NotEqual, rhs, lhsDest);
2813       break;
2814     case wasm::SimdOp::F32x4Lt:
2815       masm.compareFloat32x4(Assembler::LessThan, rhs, lhsDest);
2816       break;
2817     case wasm::SimdOp::F32x4Le:
2818       masm.compareFloat32x4(Assembler::LessThanOrEqual, rhs, lhsDest);
2819       break;
2820     case wasm::SimdOp::F64x2Eq:
2821       masm.compareFloat64x2(Assembler::Equal, rhs, lhsDest);
2822       break;
2823     case wasm::SimdOp::F64x2Ne:
2824       masm.compareFloat64x2(Assembler::NotEqual, rhs, lhsDest);
2825       break;
2826     case wasm::SimdOp::F64x2Lt:
2827       masm.compareFloat64x2(Assembler::LessThan, rhs, lhsDest);
2828       break;
2829     case wasm::SimdOp::F64x2Le:
2830       masm.compareFloat64x2(Assembler::LessThanOrEqual, rhs, lhsDest);
2831       break;
2832     case wasm::SimdOp::I32x4DotSI16x8:
2833       masm.widenDotInt16x8(rhs, lhsDest);
2834       break;
2835     case wasm::SimdOp::F32x4Add:
2836       masm.addFloat32x4(rhs, lhsDest);
2837       break;
2838     case wasm::SimdOp::F64x2Add:
2839       masm.addFloat64x2(rhs, lhsDest);
2840       break;
2841     case wasm::SimdOp::F32x4Sub:
2842       masm.subFloat32x4(rhs, lhsDest);
2843       break;
2844     case wasm::SimdOp::F64x2Sub:
2845       masm.subFloat64x2(rhs, lhsDest);
2846       break;
2847     case wasm::SimdOp::F32x4Div:
2848       masm.divFloat32x4(rhs, lhsDest);
2849       break;
2850     case wasm::SimdOp::F64x2Div:
2851       masm.divFloat64x2(rhs, lhsDest);
2852       break;
2853     case wasm::SimdOp::F32x4Mul:
2854       masm.mulFloat32x4(rhs, lhsDest);
2855       break;
2856     case wasm::SimdOp::F64x2Mul:
2857       masm.mulFloat64x2(rhs, lhsDest);
2858       break;
2859     case wasm::SimdOp::I8x16NarrowSI16x8:
2860       masm.narrowInt16x8(rhs, lhsDest);
2861       break;
2862     case wasm::SimdOp::I8x16NarrowUI16x8:
2863       masm.unsignedNarrowInt16x8(rhs, lhsDest);
2864       break;
2865     case wasm::SimdOp::I16x8NarrowSI32x4:
2866       masm.narrowInt32x4(rhs, lhsDest);
2867       break;
2868     case wasm::SimdOp::I16x8NarrowUI32x4:
2869       masm.unsignedNarrowInt32x4(rhs, lhsDest);
2870       break;
2871     default:
2872       MOZ_CRASH("Binary SimdOp with constant not implemented");
2873   }
2874 #else
2875   MOZ_CRASH("No SIMD");
2876 #endif
2877 }
2878 
visitWasmVariableShiftSimd128(LWasmVariableShiftSimd128 * ins)2879 void CodeGenerator::visitWasmVariableShiftSimd128(
2880     LWasmVariableShiftSimd128* ins) {
2881 #ifdef ENABLE_WASM_SIMD
2882   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
2883   Register rhs = ToRegister(ins->rhs());
2884   Register temp1 = ToTempRegisterOrInvalid(ins->getTemp(0));
2885   FloatRegister temp2 = ToTempFloatRegisterOrInvalid(ins->getTemp(1));
2886 
2887   MOZ_ASSERT(ToFloatRegister(ins->output()) == lhsDest);
2888 
2889   switch (ins->simdOp()) {
2890     case wasm::SimdOp::I8x16Shl:
2891       masm.leftShiftInt8x16(rhs, lhsDest, temp1, temp2);
2892       break;
2893     case wasm::SimdOp::I8x16ShrS:
2894       masm.rightShiftInt8x16(rhs, lhsDest, temp1, temp2);
2895       break;
2896     case wasm::SimdOp::I8x16ShrU:
2897       masm.unsignedRightShiftInt8x16(rhs, lhsDest, temp1, temp2);
2898       break;
2899     case wasm::SimdOp::I16x8Shl:
2900       masm.leftShiftInt16x8(rhs, lhsDest, temp1);
2901       break;
2902     case wasm::SimdOp::I16x8ShrS:
2903       masm.rightShiftInt16x8(rhs, lhsDest, temp1);
2904       break;
2905     case wasm::SimdOp::I16x8ShrU:
2906       masm.unsignedRightShiftInt16x8(rhs, lhsDest, temp1);
2907       break;
2908     case wasm::SimdOp::I32x4Shl:
2909       masm.leftShiftInt32x4(rhs, lhsDest, temp1);
2910       break;
2911     case wasm::SimdOp::I32x4ShrS:
2912       masm.rightShiftInt32x4(rhs, lhsDest, temp1);
2913       break;
2914     case wasm::SimdOp::I32x4ShrU:
2915       masm.unsignedRightShiftInt32x4(rhs, lhsDest, temp1);
2916       break;
2917     case wasm::SimdOp::I64x2Shl:
2918       masm.leftShiftInt64x2(rhs, lhsDest, temp1);
2919       break;
2920     case wasm::SimdOp::I64x2ShrS:
2921       masm.rightShiftInt64x2(rhs, lhsDest, temp1, temp2);
2922       break;
2923     case wasm::SimdOp::I64x2ShrU:
2924       masm.unsignedRightShiftInt64x2(rhs, lhsDest, temp1);
2925       break;
2926     default:
2927       MOZ_CRASH("Shift SimdOp not implemented");
2928   }
2929 #else
2930   MOZ_CRASH("No SIMD");
2931 #endif
2932 }
2933 
visitWasmConstantShiftSimd128(LWasmConstantShiftSimd128 * ins)2934 void CodeGenerator::visitWasmConstantShiftSimd128(
2935     LWasmConstantShiftSimd128* ins) {
2936 #ifdef ENABLE_WASM_SIMD
2937   FloatRegister src = ToFloatRegister(ins->src());
2938   FloatRegister dest = ToFloatRegister(ins->output());
2939   int32_t shift = ins->shift();
2940 
2941   if (shift == 0) {
2942     if (src != dest) {
2943       masm.moveSimd128(src, dest);
2944     }
2945     return;
2946   }
2947 
2948   switch (ins->simdOp()) {
2949     case wasm::SimdOp::I8x16Shl:
2950       masm.leftShiftInt8x16(Imm32(shift), src, dest);
2951       break;
2952     case wasm::SimdOp::I8x16ShrS:
2953       masm.rightShiftInt8x16(Imm32(shift), src, dest);
2954       break;
2955     case wasm::SimdOp::I8x16ShrU:
2956       masm.unsignedRightShiftInt8x16(Imm32(shift), src, dest);
2957       break;
2958     case wasm::SimdOp::I16x8Shl:
2959       masm.leftShiftInt16x8(Imm32(shift), src, dest);
2960       break;
2961     case wasm::SimdOp::I16x8ShrS:
2962       masm.rightShiftInt16x8(Imm32(shift), src, dest);
2963       break;
2964     case wasm::SimdOp::I16x8ShrU:
2965       masm.unsignedRightShiftInt16x8(Imm32(shift), src, dest);
2966       break;
2967     case wasm::SimdOp::I32x4Shl:
2968       masm.leftShiftInt32x4(Imm32(shift), src, dest);
2969       break;
2970     case wasm::SimdOp::I32x4ShrS:
2971       masm.rightShiftInt32x4(Imm32(shift), src, dest);
2972       break;
2973     case wasm::SimdOp::I32x4ShrU:
2974       masm.unsignedRightShiftInt32x4(Imm32(shift), src, dest);
2975       break;
2976     case wasm::SimdOp::I64x2Shl:
2977       masm.leftShiftInt64x2(Imm32(shift), src, dest);
2978       break;
2979     case wasm::SimdOp::I64x2ShrS:
2980       masm.rightShiftInt64x2(Imm32(shift), src, dest);
2981       break;
2982     case wasm::SimdOp::I64x2ShrU:
2983       masm.unsignedRightShiftInt64x2(Imm32(shift), src, dest);
2984       break;
2985     default:
2986       MOZ_CRASH("Shift SimdOp not implemented");
2987   }
2988 #else
2989   MOZ_CRASH("No SIMD");
2990 #endif
2991 }
2992 
visitWasmSignReplicationSimd128(LWasmSignReplicationSimd128 * ins)2993 void CodeGenerator::visitWasmSignReplicationSimd128(
2994     LWasmSignReplicationSimd128* ins) {
2995 #ifdef ENABLE_WASM_SIMD
2996   FloatRegister src = ToFloatRegister(ins->src());
2997   FloatRegister dest = ToFloatRegister(ins->output());
2998 
2999   switch (ins->simdOp()) {
3000     case wasm::SimdOp::I8x16ShrS:
3001       masm.signReplicationInt8x16(src, dest);
3002       break;
3003     case wasm::SimdOp::I16x8ShrS:
3004       masm.signReplicationInt16x8(src, dest);
3005       break;
3006     case wasm::SimdOp::I32x4ShrS:
3007       masm.signReplicationInt32x4(src, dest);
3008       break;
3009     case wasm::SimdOp::I64x2ShrS:
3010       masm.signReplicationInt64x2(src, dest);
3011       break;
3012     default:
3013       MOZ_CRASH("Shift SimdOp unsupported sign replication optimization");
3014   }
3015 #else
3016   MOZ_CRASH("No SIMD");
3017 #endif
3018 }
3019 
visitWasmShuffleSimd128(LWasmShuffleSimd128 * ins)3020 void CodeGenerator::visitWasmShuffleSimd128(LWasmShuffleSimd128* ins) {
3021 #ifdef ENABLE_WASM_SIMD
3022   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
3023   FloatRegister rhs = ToFloatRegister(ins->rhs());
3024   SimdConstant control = ins->control();
3025   switch (ins->op()) {
3026     case LWasmShuffleSimd128::BLEND_8x16: {
3027       masm.blendInt8x16(reinterpret_cast<const uint8_t*>(control.asInt8x16()),
3028                         lhsDest, rhs, lhsDest, ToFloatRegister(ins->temp()));
3029       break;
3030     }
3031     case LWasmShuffleSimd128::BLEND_16x8: {
3032       MOZ_ASSERT(ins->temp()->isBogusTemp());
3033       masm.blendInt16x8(reinterpret_cast<const uint16_t*>(control.asInt16x8()),
3034                         lhsDest, rhs, lhsDest);
3035       break;
3036     }
3037     case LWasmShuffleSimd128::CONCAT_RIGHT_SHIFT_8x16: {
3038       MOZ_ASSERT(ins->temp()->isBogusTemp());
3039       int8_t count = 16 - control.asInt8x16()[0];
3040       MOZ_ASSERT(count > 0, "Should have been a MOVE operation");
3041       masm.concatAndRightShiftSimd128(rhs, lhsDest, count);
3042       break;
3043     }
3044     case LWasmShuffleSimd128::INTERLEAVE_HIGH_8x16: {
3045       MOZ_ASSERT(ins->temp()->isBogusTemp());
3046       masm.interleaveHighInt8x16(rhs, lhsDest);
3047       break;
3048     }
3049     case LWasmShuffleSimd128::INTERLEAVE_HIGH_16x8: {
3050       MOZ_ASSERT(ins->temp()->isBogusTemp());
3051       masm.interleaveHighInt16x8(rhs, lhsDest);
3052       break;
3053     }
3054     case LWasmShuffleSimd128::INTERLEAVE_HIGH_32x4: {
3055       MOZ_ASSERT(ins->temp()->isBogusTemp());
3056       masm.interleaveHighInt32x4(rhs, lhsDest);
3057       break;
3058     }
3059     case LWasmShuffleSimd128::INTERLEAVE_HIGH_64x2: {
3060       MOZ_ASSERT(ins->temp()->isBogusTemp());
3061       masm.interleaveHighInt64x2(rhs, lhsDest);
3062       break;
3063     }
3064     case LWasmShuffleSimd128::INTERLEAVE_LOW_8x16: {
3065       MOZ_ASSERT(ins->temp()->isBogusTemp());
3066       masm.interleaveLowInt8x16(rhs, lhsDest);
3067       break;
3068     }
3069     case LWasmShuffleSimd128::INTERLEAVE_LOW_16x8: {
3070       MOZ_ASSERT(ins->temp()->isBogusTemp());
3071       masm.interleaveLowInt16x8(rhs, lhsDest);
3072       break;
3073     }
3074     case LWasmShuffleSimd128::INTERLEAVE_LOW_32x4: {
3075       MOZ_ASSERT(ins->temp()->isBogusTemp());
3076       masm.interleaveLowInt32x4(rhs, lhsDest);
3077       break;
3078     }
3079     case LWasmShuffleSimd128::INTERLEAVE_LOW_64x2: {
3080       MOZ_ASSERT(ins->temp()->isBogusTemp());
3081       masm.interleaveLowInt64x2(rhs, lhsDest);
3082       break;
3083     }
3084     case LWasmShuffleSimd128::SHUFFLE_BLEND_8x16: {
3085       masm.shuffleInt8x16(reinterpret_cast<const uint8_t*>(control.asInt8x16()),
3086                           rhs, lhsDest);
3087       break;
3088     }
3089     default: {
3090       MOZ_CRASH("Unsupported SIMD shuffle operation");
3091     }
3092   }
3093 #else
3094   MOZ_CRASH("No SIMD");
3095 #endif
3096 }
3097 
3098 #ifdef ENABLE_WASM_SIMD
3099 
3100 enum PermuteX64I16x8Action : uint16_t {
3101   UNAVAILABLE = 0,
3102   SWAP_QWORDS = 1,  // Swap qwords first
3103   PERM_LOW = 2,     // Permute low qword by control_[0..3]
3104   PERM_HIGH = 4     // Permute high qword by control_[4..7]
3105 };
3106 
3107 // Skip lanes that equal v starting at i, returning the index just beyond the
3108 // last of those.  There is no requirement that the initial lanes[i] == v.
3109 template <typename T>
ScanConstant(const T * lanes,int v,int i)3110 static int ScanConstant(const T* lanes, int v, int i) {
3111   int len = int(16 / sizeof(T));
3112   MOZ_ASSERT(i <= len);
3113   while (i < len && lanes[i] == v) {
3114     i++;
3115   }
3116   return i;
3117 }
3118 
3119 // Apply a transformation to each lane value.
3120 template <typename T>
MapLanes(T * result,const T * input,int (* f)(int))3121 static void MapLanes(T* result, const T* input, int (*f)(int)) {
3122   int len = int(16 / sizeof(T));
3123   for (int i = 0; i < len; i++) {
3124     result[i] = f(input[i]);
3125   }
3126 }
3127 
3128 // Recognize part of an identity permutation starting at start, with
3129 // the first value of the permutation expected to be bias.
3130 template <typename T>
IsIdentity(const T * lanes,int start,int len,int bias)3131 static bool IsIdentity(const T* lanes, int start, int len, int bias) {
3132   if (lanes[start] != bias) {
3133     return false;
3134   }
3135   for (int i = start + 1; i < start + len; i++) {
3136     if (lanes[i] != lanes[i - 1] + 1) {
3137       return false;
3138     }
3139   }
3140   return true;
3141 }
3142 
3143 // We can permute by words if the mask is reducible to a word mask, but the x64
3144 // lowering is only efficient if we can permute the high and low quadwords
3145 // separately, possibly after swapping quadwords.
CalculateX64Permute16x8(SimdConstant * control)3146 static PermuteX64I16x8Action CalculateX64Permute16x8(SimdConstant* control) {
3147   const SimdConstant::I16x8& lanes = control->asInt16x8();
3148   SimdConstant::I16x8 mapped;
3149   MapLanes(mapped, lanes, [](int x) -> int { return x < 4 ? 0 : 1; });
3150   int i = ScanConstant(mapped, mapped[0], 0);
3151   if (i != 4) {
3152     return PermuteX64I16x8Action::UNAVAILABLE;
3153   }
3154   i = ScanConstant(mapped, mapped[4], 4);
3155   if (i != 8) {
3156     return PermuteX64I16x8Action::UNAVAILABLE;
3157   }
3158   // Now compute the operation bits.  `mapped` holds the adjusted lane mask.
3159   memcpy(mapped, lanes, sizeof(mapped));
3160   uint16_t op = 0;
3161   if (mapped[0] > mapped[4]) {
3162     op |= PermuteX64I16x8Action::SWAP_QWORDS;
3163   }
3164   for (auto& m : mapped) {
3165     m &= 3;
3166   }
3167   if (!IsIdentity(mapped, 0, 4, 0)) {
3168     op |= PermuteX64I16x8Action::PERM_LOW;
3169   }
3170   if (!IsIdentity(mapped, 4, 4, 0)) {
3171     op |= PermuteX64I16x8Action::PERM_HIGH;
3172   }
3173   MOZ_ASSERT(op != PermuteX64I16x8Action::UNAVAILABLE);
3174   *control = SimdConstant::CreateX8(mapped);
3175   return (PermuteX64I16x8Action)op;
3176 }
3177 
3178 #endif
3179 
visitWasmPermuteSimd128(LWasmPermuteSimd128 * ins)3180 void CodeGenerator::visitWasmPermuteSimd128(LWasmPermuteSimd128* ins) {
3181 #ifdef ENABLE_WASM_SIMD
3182   FloatRegister src = ToFloatRegister(ins->src());
3183   FloatRegister dest = ToFloatRegister(ins->output());
3184   SimdConstant control = ins->control();
3185   switch (ins->op()) {
3186     // For broadcast, would MOVDDUP be better than PSHUFD for the last step?
3187     case LWasmPermuteSimd128::BROADCAST_8x16: {
3188       const SimdConstant::I8x16& mask = control.asInt8x16();
3189       int8_t source = mask[0];
3190       if (src != dest) {
3191         masm.moveSimd128(src, dest);
3192       }
3193       if (source < 8) {
3194         masm.interleaveLowInt8x16(dest, dest);
3195       } else {
3196         masm.interleaveHighInt8x16(dest, dest);
3197         source -= 8;
3198       }
3199       uint16_t v = uint16_t(source & 3);
3200       uint16_t wordMask[4] = {v, v, v, v};
3201       if (source < 4) {
3202         masm.permuteLowInt16x8(wordMask, dest, dest);
3203         uint32_t dwordMask[4] = {0, 0, 0, 0};
3204         masm.permuteInt32x4(dwordMask, dest, dest);
3205       } else {
3206         masm.permuteHighInt16x8(wordMask, dest, dest);
3207         uint32_t dwordMask[4] = {2, 2, 2, 2};
3208         masm.permuteInt32x4(dwordMask, dest, dest);
3209       }
3210       break;
3211     }
3212     case LWasmPermuteSimd128::BROADCAST_16x8: {
3213       const SimdConstant::I16x8& mask = control.asInt16x8();
3214       int16_t source = mask[0];
3215       uint16_t v = uint16_t(source & 3);
3216       uint16_t wordMask[4] = {v, v, v, v};
3217       if (source < 4) {
3218         masm.permuteLowInt16x8(wordMask, src, dest);
3219         uint32_t dwordMask[4] = {0, 0, 0, 0};
3220         masm.permuteInt32x4(dwordMask, dest, dest);
3221       } else {
3222         masm.permuteHighInt16x8(wordMask, src, dest);
3223         uint32_t dwordMask[4] = {2, 2, 2, 2};
3224         masm.permuteInt32x4(dwordMask, dest, dest);
3225       }
3226       break;
3227     }
3228     case LWasmPermuteSimd128::MOVE: {
3229       masm.moveSimd128(src, dest);
3230       break;
3231     }
3232     case LWasmPermuteSimd128::PERMUTE_8x16: {
3233       const SimdConstant::I8x16& mask = control.asInt8x16();
3234 #  ifdef DEBUG
3235       DebugOnly<int> i;
3236       for (i = 0; i < 16 && mask[i] == i; i++) {
3237       }
3238       MOZ_ASSERT(i < 16, "Should have been a MOVE operation");
3239 #  endif
3240       masm.permuteInt8x16(reinterpret_cast<const uint8_t*>(mask), src, dest);
3241       break;
3242     }
3243     case LWasmPermuteSimd128::PERMUTE_16x8: {
3244 #  ifdef DEBUG
3245       const SimdConstant::I16x8& mask = control.asInt16x8();
3246       DebugOnly<int> i;
3247       for (i = 0; i < 8 && mask[i] == i; i++) {
3248       }
3249       MOZ_ASSERT(i < 8, "Should have been a MOVE operation");
3250 #  endif
3251       PermuteX64I16x8Action op = CalculateX64Permute16x8(&control);
3252       if (op != PermuteX64I16x8Action::UNAVAILABLE) {
3253         const SimdConstant::I16x8& mask = control.asInt16x8();
3254         if (op & PermuteX64I16x8Action::SWAP_QWORDS) {
3255           uint32_t dwordMask[4] = {2, 3, 0, 1};
3256           masm.permuteInt32x4(dwordMask, src, dest);
3257           src = dest;
3258         }
3259         if (op & PermuteX64I16x8Action::PERM_LOW) {
3260           masm.permuteLowInt16x8(reinterpret_cast<const uint16_t*>(mask) + 0,
3261                                  src, dest);
3262           src = dest;
3263         }
3264         if (op & PermuteX64I16x8Action::PERM_HIGH) {
3265           masm.permuteHighInt16x8(reinterpret_cast<const uint16_t*>(mask) + 4,
3266                                   src, dest);
3267           src = dest;
3268         }
3269       } else {
3270         const SimdConstant::I16x8& wmask = control.asInt16x8();
3271         uint8_t mask[16];
3272         for (unsigned i = 0; i < 16; i += 2) {
3273           mask[i] = wmask[i / 2] * 2;
3274           mask[i + 1] = wmask[i / 2] * 2 + 1;
3275         }
3276         masm.permuteInt8x16(mask, src, dest);
3277       }
3278       break;
3279     }
3280     case LWasmPermuteSimd128::PERMUTE_32x4: {
3281       const SimdConstant::I32x4& mask = control.asInt32x4();
3282 #  ifdef DEBUG
3283       DebugOnly<int> i;
3284       for (i = 0; i < 4 && mask[i] == i; i++) {
3285       }
3286       MOZ_ASSERT(i < 4, "Should have been a MOVE operation");
3287 #  endif
3288       masm.permuteInt32x4(reinterpret_cast<const uint32_t*>(mask), src, dest);
3289       break;
3290     }
3291     case LWasmPermuteSimd128::ROTATE_RIGHT_8x16: {
3292       if (src != dest) {
3293         masm.moveSimd128(src, dest);
3294       }
3295       int8_t count = control.asInt8x16()[0];
3296       MOZ_ASSERT(count > 0, "Should have been a MOVE operation");
3297       masm.concatAndRightShiftSimd128(dest, dest, count);
3298       break;
3299     }
3300     case LWasmPermuteSimd128::SHIFT_LEFT_8x16: {
3301       int8_t count = control.asInt8x16()[0];
3302       MOZ_ASSERT(count > 0, "Should have been a MOVE operation");
3303       masm.leftShiftSimd128(Imm32(count), src, dest);
3304       break;
3305     }
3306     case LWasmPermuteSimd128::SHIFT_RIGHT_8x16: {
3307       int8_t count = control.asInt8x16()[0];
3308       MOZ_ASSERT(count > 0, "Should have been a MOVE operation");
3309       masm.rightShiftSimd128(Imm32(count), src, dest);
3310       break;
3311     }
3312     default: {
3313       MOZ_CRASH("Unsupported SIMD permutation operation");
3314     }
3315   }
3316 #else
3317   MOZ_CRASH("No SIMD");
3318 #endif
3319 }
3320 
visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128 * ins)3321 void CodeGenerator::visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128* ins) {
3322 #ifdef ENABLE_WASM_SIMD
3323   FloatRegister lhsDest = ToFloatRegister(ins->lhsDest());
3324   const LAllocation* rhs = ins->rhs();
3325   uint32_t laneIndex = ins->laneIndex();
3326 
3327   switch (ins->simdOp()) {
3328     case wasm::SimdOp::I8x16ReplaceLane:
3329       masm.replaceLaneInt8x16(laneIndex, ToRegister(rhs), lhsDest);
3330       break;
3331     case wasm::SimdOp::I16x8ReplaceLane:
3332       masm.replaceLaneInt16x8(laneIndex, ToRegister(rhs), lhsDest);
3333       break;
3334     case wasm::SimdOp::I32x4ReplaceLane:
3335       masm.replaceLaneInt32x4(laneIndex, ToRegister(rhs), lhsDest);
3336       break;
3337     case wasm::SimdOp::F32x4ReplaceLane:
3338       masm.replaceLaneFloat32x4(laneIndex, ToFloatRegister(rhs), lhsDest);
3339       break;
3340     case wasm::SimdOp::F64x2ReplaceLane:
3341       masm.replaceLaneFloat64x2(laneIndex, ToFloatRegister(rhs), lhsDest);
3342       break;
3343     default:
3344       MOZ_CRASH("ReplaceLane SimdOp not implemented");
3345   }
3346 #else
3347   MOZ_CRASH("No SIMD");
3348 #endif
3349 }
3350 
visitWasmReplaceInt64LaneSimd128(LWasmReplaceInt64LaneSimd128 * ins)3351 void CodeGenerator::visitWasmReplaceInt64LaneSimd128(
3352     LWasmReplaceInt64LaneSimd128* ins) {
3353 #ifdef ENABLE_WASM_SIMD
3354   MOZ_RELEASE_ASSERT(ins->simdOp() == wasm::SimdOp::I64x2ReplaceLane);
3355   masm.replaceLaneInt64x2(ins->laneIndex(), ToRegister64(ins->rhs()),
3356                           ToFloatRegister(ins->lhsDest()));
3357 #else
3358   MOZ_CRASH("No SIMD");
3359 #endif
3360 }
3361 
visitWasmScalarToSimd128(LWasmScalarToSimd128 * ins)3362 void CodeGenerator::visitWasmScalarToSimd128(LWasmScalarToSimd128* ins) {
3363 #ifdef ENABLE_WASM_SIMD
3364   FloatRegister dest = ToFloatRegister(ins->output());
3365 
3366   switch (ins->simdOp()) {
3367     case wasm::SimdOp::I8x16Splat:
3368       masm.splatX16(ToRegister(ins->src()), dest);
3369       break;
3370     case wasm::SimdOp::I16x8Splat:
3371       masm.splatX8(ToRegister(ins->src()), dest);
3372       break;
3373     case wasm::SimdOp::I32x4Splat:
3374       masm.splatX4(ToRegister(ins->src()), dest);
3375       break;
3376     case wasm::SimdOp::F32x4Splat:
3377       masm.splatX4(ToFloatRegister(ins->src()), dest);
3378       break;
3379     case wasm::SimdOp::F64x2Splat:
3380       masm.splatX2(ToFloatRegister(ins->src()), dest);
3381       break;
3382     default:
3383       MOZ_CRASH("ScalarToSimd128 SimdOp not implemented");
3384   }
3385 #else
3386   MOZ_CRASH("No SIMD");
3387 #endif
3388 }
3389 
visitWasmInt64ToSimd128(LWasmInt64ToSimd128 * ins)3390 void CodeGenerator::visitWasmInt64ToSimd128(LWasmInt64ToSimd128* ins) {
3391 #ifdef ENABLE_WASM_SIMD
3392   Register64 src = ToRegister64(ins->src());
3393   FloatRegister dest = ToFloatRegister(ins->output());
3394 
3395   switch (ins->simdOp()) {
3396     case wasm::SimdOp::I64x2Splat:
3397       masm.splatX2(src, dest);
3398       break;
3399     case wasm::SimdOp::I16x8LoadS8x8:
3400       masm.moveGPR64ToDouble(src, dest);
3401       masm.widenLowInt8x16(dest, dest);
3402       break;
3403     case wasm::SimdOp::I16x8LoadU8x8:
3404       masm.moveGPR64ToDouble(src, dest);
3405       masm.unsignedWidenLowInt8x16(dest, dest);
3406       break;
3407     case wasm::SimdOp::I32x4LoadS16x4:
3408       masm.moveGPR64ToDouble(src, dest);
3409       masm.widenLowInt16x8(dest, dest);
3410       break;
3411     case wasm::SimdOp::I32x4LoadU16x4:
3412       masm.moveGPR64ToDouble(src, dest);
3413       masm.unsignedWidenLowInt16x8(dest, dest);
3414       break;
3415     case wasm::SimdOp::I64x2LoadS32x2:
3416       masm.moveGPR64ToDouble(src, dest);
3417       masm.widenLowInt32x4(dest, dest);
3418       break;
3419     case wasm::SimdOp::I64x2LoadU32x2:
3420       masm.moveGPR64ToDouble(src, dest);
3421       masm.unsignedWidenLowInt32x4(dest, dest);
3422       break;
3423     default:
3424       MOZ_CRASH("Int64ToSimd128 SimdOp not implemented");
3425   }
3426 #else
3427   MOZ_CRASH("No SIMD");
3428 #endif
3429 }
3430 
visitWasmUnarySimd128(LWasmUnarySimd128 * ins)3431 void CodeGenerator::visitWasmUnarySimd128(LWasmUnarySimd128* ins) {
3432 #ifdef ENABLE_WASM_SIMD
3433   FloatRegister src = ToFloatRegister(ins->src());
3434   FloatRegister dest = ToFloatRegister(ins->output());
3435 
3436   switch (ins->simdOp()) {
3437     case wasm::SimdOp::I8x16Neg:
3438       masm.negInt8x16(src, dest);
3439       break;
3440     case wasm::SimdOp::I16x8Neg:
3441       masm.negInt16x8(src, dest);
3442       break;
3443     case wasm::SimdOp::I16x8WidenLowSI8x16:
3444       masm.widenLowInt8x16(src, dest);
3445       break;
3446     case wasm::SimdOp::I16x8WidenHighSI8x16:
3447       masm.widenHighInt8x16(src, dest);
3448       break;
3449     case wasm::SimdOp::I16x8WidenLowUI8x16:
3450       masm.unsignedWidenLowInt8x16(src, dest);
3451       break;
3452     case wasm::SimdOp::I16x8WidenHighUI8x16:
3453       masm.unsignedWidenHighInt8x16(src, dest);
3454       break;
3455     case wasm::SimdOp::I32x4Neg:
3456       masm.negInt32x4(src, dest);
3457       break;
3458     case wasm::SimdOp::I32x4WidenLowSI16x8:
3459       masm.widenLowInt16x8(src, dest);
3460       break;
3461     case wasm::SimdOp::I32x4WidenHighSI16x8:
3462       masm.widenHighInt16x8(src, dest);
3463       break;
3464     case wasm::SimdOp::I32x4WidenLowUI16x8:
3465       masm.unsignedWidenLowInt16x8(src, dest);
3466       break;
3467     case wasm::SimdOp::I32x4WidenHighUI16x8:
3468       masm.unsignedWidenHighInt16x8(src, dest);
3469       break;
3470     case wasm::SimdOp::I32x4TruncSSatF32x4:
3471       masm.truncSatFloat32x4ToInt32x4(src, dest);
3472       break;
3473     case wasm::SimdOp::I32x4TruncUSatF32x4:
3474       masm.unsignedTruncSatFloat32x4ToInt32x4(src, dest,
3475                                               ToFloatRegister(ins->temp()));
3476       break;
3477     case wasm::SimdOp::I64x2Neg:
3478       masm.negInt64x2(src, dest);
3479       break;
3480     case wasm::SimdOp::I64x2WidenLowSI32x4:
3481       masm.widenLowInt32x4(src, dest);
3482       break;
3483     case wasm::SimdOp::I64x2WidenHighSI32x4:
3484       masm.widenHighInt32x4(src, dest);
3485       break;
3486     case wasm::SimdOp::I64x2WidenLowUI32x4:
3487       masm.unsignedWidenLowInt32x4(src, dest);
3488       break;
3489     case wasm::SimdOp::I64x2WidenHighUI32x4:
3490       masm.unsignedWidenHighInt32x4(src, dest);
3491       break;
3492     case wasm::SimdOp::F32x4Abs:
3493       masm.absFloat32x4(src, dest);
3494       break;
3495     case wasm::SimdOp::F32x4Neg:
3496       masm.negFloat32x4(src, dest);
3497       break;
3498     case wasm::SimdOp::F32x4Sqrt:
3499       masm.sqrtFloat32x4(src, dest);
3500       break;
3501     case wasm::SimdOp::F32x4ConvertSI32x4:
3502       masm.convertInt32x4ToFloat32x4(src, dest);
3503       break;
3504     case wasm::SimdOp::F32x4ConvertUI32x4:
3505       masm.unsignedConvertInt32x4ToFloat32x4(src, dest);
3506       break;
3507     case wasm::SimdOp::F64x2Abs:
3508       masm.absFloat64x2(src, dest);
3509       break;
3510     case wasm::SimdOp::F64x2Neg:
3511       masm.negFloat64x2(src, dest);
3512       break;
3513     case wasm::SimdOp::F64x2Sqrt:
3514       masm.sqrtFloat64x2(src, dest);
3515       break;
3516     case wasm::SimdOp::V128Not:
3517       masm.bitwiseNotSimd128(src, dest);
3518       break;
3519     case wasm::SimdOp::I8x16Popcnt:
3520       masm.popcntInt8x16(src, dest, ToFloatRegister(ins->temp()));
3521       break;
3522     case wasm::SimdOp::I8x16Abs:
3523       masm.absInt8x16(src, dest);
3524       break;
3525     case wasm::SimdOp::I16x8Abs:
3526       masm.absInt16x8(src, dest);
3527       break;
3528     case wasm::SimdOp::I32x4Abs:
3529       masm.absInt32x4(src, dest);
3530       break;
3531     case wasm::SimdOp::I64x2Abs:
3532       masm.absInt64x2(src, dest);
3533       break;
3534     case wasm::SimdOp::F32x4Ceil:
3535       masm.ceilFloat32x4(src, dest);
3536       break;
3537     case wasm::SimdOp::F32x4Floor:
3538       masm.floorFloat32x4(src, dest);
3539       break;
3540     case wasm::SimdOp::F32x4Trunc:
3541       masm.truncFloat32x4(src, dest);
3542       break;
3543     case wasm::SimdOp::F32x4Nearest:
3544       masm.nearestFloat32x4(src, dest);
3545       break;
3546     case wasm::SimdOp::F64x2Ceil:
3547       masm.ceilFloat64x2(src, dest);
3548       break;
3549     case wasm::SimdOp::F64x2Floor:
3550       masm.floorFloat64x2(src, dest);
3551       break;
3552     case wasm::SimdOp::F64x2Trunc:
3553       masm.truncFloat64x2(src, dest);
3554       break;
3555     case wasm::SimdOp::F64x2Nearest:
3556       masm.nearestFloat64x2(src, dest);
3557       break;
3558     case wasm::SimdOp::F32x4DemoteF64x2Zero:
3559       masm.convertFloat64x2ToFloat32x4(src, dest);
3560       break;
3561     case wasm::SimdOp::F64x2PromoteLowF32x4:
3562       masm.convertFloat32x4ToFloat64x2(src, dest);
3563       break;
3564     case wasm::SimdOp::F64x2ConvertLowI32x4S:
3565       masm.convertInt32x4ToFloat64x2(src, dest);
3566       break;
3567     case wasm::SimdOp::F64x2ConvertLowI32x4U:
3568       masm.unsignedConvertInt32x4ToFloat64x2(src, dest);
3569       break;
3570     case wasm::SimdOp::I32x4TruncSatF64x2SZero:
3571       masm.truncSatFloat64x2ToInt32x4(src, dest, ToFloatRegister(ins->temp()));
3572       break;
3573     case wasm::SimdOp::I32x4TruncSatF64x2UZero:
3574       masm.unsignedTruncSatFloat64x2ToInt32x4(src, dest,
3575                                               ToFloatRegister(ins->temp()));
3576       break;
3577     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16S:
3578       masm.extAddPairwiseInt8x16(src, dest);
3579       break;
3580     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16U:
3581       masm.unsignedExtAddPairwiseInt8x16(src, dest);
3582       break;
3583     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8S:
3584       masm.extAddPairwiseInt16x8(src, dest);
3585       break;
3586     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8U:
3587       masm.unsignedExtAddPairwiseInt16x8(src, dest);
3588       break;
3589     default:
3590       MOZ_CRASH("Unary SimdOp not implemented");
3591   }
3592 #else
3593   MOZ_CRASH("No SIMD");
3594 #endif
3595 }
3596 
visitWasmReduceSimd128(LWasmReduceSimd128 * ins)3597 void CodeGenerator::visitWasmReduceSimd128(LWasmReduceSimd128* ins) {
3598 #ifdef ENABLE_WASM_SIMD
3599   FloatRegister src = ToFloatRegister(ins->src());
3600   const LDefinition* dest = ins->output();
3601   uint32_t imm = ins->imm();
3602 
3603   switch (ins->simdOp()) {
3604     case wasm::SimdOp::V128AnyTrue:
3605       masm.anyTrueSimd128(src, ToRegister(dest));
3606       break;
3607     case wasm::SimdOp::I8x16AllTrue:
3608       masm.allTrueInt8x16(src, ToRegister(dest));
3609       break;
3610     case wasm::SimdOp::I16x8AllTrue:
3611       masm.allTrueInt16x8(src, ToRegister(dest));
3612       break;
3613     case wasm::SimdOp::I32x4AllTrue:
3614       masm.allTrueInt32x4(src, ToRegister(dest));
3615       break;
3616     case wasm::SimdOp::I64x2AllTrue:
3617       masm.allTrueInt64x2(src, ToRegister(dest));
3618       break;
3619     case wasm::SimdOp::I8x16Bitmask:
3620       masm.bitmaskInt8x16(src, ToRegister(dest));
3621       break;
3622     case wasm::SimdOp::I16x8Bitmask:
3623       masm.bitmaskInt16x8(src, ToRegister(dest));
3624       break;
3625     case wasm::SimdOp::I32x4Bitmask:
3626       masm.bitmaskInt32x4(src, ToRegister(dest));
3627       break;
3628     case wasm::SimdOp::I64x2Bitmask:
3629       masm.bitmaskInt64x2(src, ToRegister(dest));
3630       break;
3631     case wasm::SimdOp::I8x16ExtractLaneS:
3632       masm.extractLaneInt8x16(imm, src, ToRegister(dest));
3633       break;
3634     case wasm::SimdOp::I8x16ExtractLaneU:
3635       masm.unsignedExtractLaneInt8x16(imm, src, ToRegister(dest));
3636       break;
3637     case wasm::SimdOp::I16x8ExtractLaneS:
3638       masm.extractLaneInt16x8(imm, src, ToRegister(dest));
3639       break;
3640     case wasm::SimdOp::I16x8ExtractLaneU:
3641       masm.unsignedExtractLaneInt16x8(imm, src, ToRegister(dest));
3642       break;
3643     case wasm::SimdOp::I32x4ExtractLane:
3644       masm.extractLaneInt32x4(imm, src, ToRegister(dest));
3645       break;
3646     case wasm::SimdOp::F32x4ExtractLane:
3647       masm.extractLaneFloat32x4(imm, src, ToFloatRegister(dest));
3648       break;
3649     case wasm::SimdOp::F64x2ExtractLane:
3650       masm.extractLaneFloat64x2(imm, src, ToFloatRegister(dest));
3651       break;
3652     default:
3653       MOZ_CRASH("Reduce SimdOp not implemented");
3654   }
3655 #else
3656   MOZ_CRASH("No SIMD");
3657 #endif
3658 }
3659 
visitWasmReduceAndBranchSimd128(LWasmReduceAndBranchSimd128 * ins)3660 void CodeGenerator::visitWasmReduceAndBranchSimd128(
3661     LWasmReduceAndBranchSimd128* ins) {
3662 #ifdef ENABLE_WASM_SIMD
3663   FloatRegister src = ToFloatRegister(ins->src());
3664 
3665   switch (ins->simdOp()) {
3666     case wasm::SimdOp::V128AnyTrue:
3667       // Set the zero flag if all of the lanes are zero, and branch on that.
3668       masm.vptest(src, src);
3669       emitBranch(Assembler::NotEqual, ins->ifTrue(), ins->ifFalse());
3670       break;
3671     case wasm::SimdOp::I8x16AllTrue:
3672     case wasm::SimdOp::I16x8AllTrue:
3673     case wasm::SimdOp::I32x4AllTrue:
3674     case wasm::SimdOp::I64x2AllTrue: {
3675       // Compare all lanes to zero, set the zero flag if none of the lanes are
3676       // zero, and branch on that.
3677       ScratchSimd128Scope tmp(masm);
3678       masm.vpxor(tmp, tmp, tmp);
3679       switch (ins->simdOp()) {
3680         case wasm::SimdOp::I8x16AllTrue:
3681           masm.vpcmpeqb(Operand(src), tmp, tmp);
3682           break;
3683         case wasm::SimdOp::I16x8AllTrue:
3684           masm.vpcmpeqw(Operand(src), tmp, tmp);
3685           break;
3686         case wasm::SimdOp::I32x4AllTrue:
3687           masm.vpcmpeqd(Operand(src), tmp, tmp);
3688           break;
3689         case wasm::SimdOp::I64x2AllTrue:
3690           masm.vpcmpeqq(Operand(src), tmp, tmp);
3691           break;
3692         default:
3693           MOZ_CRASH();
3694       }
3695       masm.vptest(tmp, tmp);
3696       emitBranch(Assembler::Equal, ins->ifTrue(), ins->ifFalse());
3697       break;
3698     }
3699     case wasm::SimdOp::I16x8Bitmask: {
3700       masm.bitwiseTestSimd128(SimdConstant::SplatX8(0x8000), src);
3701       emitBranch(Assembler::NotEqual, ins->ifTrue(), ins->ifFalse());
3702       break;
3703     }
3704     default:
3705       MOZ_CRASH("Reduce-and-branch SimdOp not implemented");
3706   }
3707 #else
3708   MOZ_CRASH("No SIMD");
3709 #endif
3710 }
3711 
visitWasmReduceSimd128ToInt64(LWasmReduceSimd128ToInt64 * ins)3712 void CodeGenerator::visitWasmReduceSimd128ToInt64(
3713     LWasmReduceSimd128ToInt64* ins) {
3714 #ifdef ENABLE_WASM_SIMD
3715   FloatRegister src = ToFloatRegister(ins->src());
3716   Register64 dest = ToOutRegister64(ins);
3717   uint32_t imm = ins->imm();
3718 
3719   switch (ins->simdOp()) {
3720     case wasm::SimdOp::I64x2ExtractLane:
3721       masm.extractLaneInt64x2(imm, src, dest);
3722       break;
3723     default:
3724       MOZ_CRASH("Reduce SimdOp not implemented");
3725   }
3726 #else
3727   MOZ_CRASH("No SIMD");
3728 #endif
3729 }
3730 
visitWasmLoadLaneSimd128(LWasmLoadLaneSimd128 * ins)3731 void CodeGenerator::visitWasmLoadLaneSimd128(LWasmLoadLaneSimd128* ins) {
3732 #ifdef ENABLE_WASM_SIMD
3733   const MWasmLoadLaneSimd128* mir = ins->mir();
3734   const wasm::MemoryAccessDesc& access = mir->access();
3735 
3736   uint32_t offset = access.offset();
3737   MOZ_ASSERT(offset < masm.wasmMaxOffsetGuardLimit());
3738 
3739   const LAllocation* value = ins->src();
3740   Operand srcAddr = toMemoryAccessOperand(ins, offset);
3741 
3742   masm.append(access, masm.size());
3743   switch (ins->laneSize()) {
3744     case 1: {
3745       masm.vpinsrb(ins->laneIndex(), srcAddr, ToFloatRegister(value),
3746                    ToFloatRegister(value));
3747       break;
3748     }
3749     case 2: {
3750       masm.vpinsrw(ins->laneIndex(), srcAddr, ToFloatRegister(value),
3751                    ToFloatRegister(value));
3752       break;
3753     }
3754     case 4: {
3755       masm.vinsertps(ins->laneIndex() << 4, srcAddr, ToFloatRegister(value),
3756                      ToFloatRegister(value));
3757       break;
3758     }
3759     case 8: {
3760       if (ins->laneIndex() == 0) {
3761         masm.vmovlps(srcAddr, ToFloatRegister(value), ToFloatRegister(value));
3762       } else {
3763         masm.vmovhps(srcAddr, ToFloatRegister(value), ToFloatRegister(value));
3764       }
3765       break;
3766     }
3767     default:
3768       MOZ_CRASH("Unsupported load lane size");
3769   }
3770 #else
3771   MOZ_CRASH("No SIMD");
3772 #endif
3773 }
3774 
visitWasmStoreLaneSimd128(LWasmStoreLaneSimd128 * ins)3775 void CodeGenerator::visitWasmStoreLaneSimd128(LWasmStoreLaneSimd128* ins) {
3776 #ifdef ENABLE_WASM_SIMD
3777   const MWasmStoreLaneSimd128* mir = ins->mir();
3778   const wasm::MemoryAccessDesc& access = mir->access();
3779 
3780   uint32_t offset = access.offset();
3781   MOZ_ASSERT(offset < masm.wasmMaxOffsetGuardLimit());
3782 
3783   const LAllocation* src = ins->src();
3784   Operand destAddr = toMemoryAccessOperand(ins, offset);
3785 
3786   masm.append(access, masm.size());
3787   switch (ins->laneSize()) {
3788     case 1: {
3789       masm.vpextrb(ins->laneIndex(), ToFloatRegister(src), destAddr);
3790       break;
3791     }
3792     case 2: {
3793       masm.vpextrw(ins->laneIndex(), ToFloatRegister(src), destAddr);
3794       break;
3795     }
3796     case 4: {
3797       unsigned lane = ins->laneIndex();
3798       if (lane == 0) {
3799         masm.vmovss(ToFloatRegister(src), destAddr);
3800       } else {
3801         masm.vextractps(lane, ToFloatRegister(src), destAddr);
3802       }
3803       break;
3804     }
3805     case 8: {
3806       if (ins->laneIndex() == 0) {
3807         masm.vmovlps(ToFloatRegister(src), destAddr);
3808       } else {
3809         masm.vmovhps(ToFloatRegister(src), destAddr);
3810       }
3811       break;
3812     }
3813     default:
3814       MOZ_CRASH("Unsupported store lane size");
3815   }
3816 #else
3817   MOZ_CRASH("No SIMD");
3818 #endif
3819 }
3820 
3821 }  // namespace jit
3822 }  // namespace js
3823