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, ¬Overflow);
1351 masm.cmp32(rhs, Imm32(-1));
1352 if (mir->trapOnError()) {
1353 masm.j(Assembler::NotEqual, ¬Overflow);
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(¬Overflow);
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, ¬PowerOfTwo);
1530 {
1531 masm.andl(lhs, remainder);
1532 masm.jmp(&done);
1533 }
1534 masm.bind(¬PowerOfTwo);
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