Lowering-arm64.cpp (48829B)
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/arm64/Lowering-arm64.h" 8 9 #include "mozilla/MathAlgorithms.h" 10 11 #include "jit/arm64/Assembler-arm64.h" 12 #include "jit/Lowering.h" 13 #include "jit/MIR-wasm.h" 14 #include "jit/MIR.h" 15 #include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis 16 17 #include "jit/shared/Lowering-shared-inl.h" 18 19 using namespace js; 20 using namespace js::jit; 21 22 LBoxAllocation LIRGeneratorARM64::useBoxFixed(MDefinition* mir, Register reg1, 23 Register, bool useAtStart) { 24 MOZ_ASSERT(mir->type() == MIRType::Value); 25 26 ensureDefined(mir); 27 return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart)); 28 } 29 30 LAllocation LIRGeneratorARM64::useByteOpRegister(MDefinition* mir) { 31 return useRegister(mir); 32 } 33 34 LAllocation LIRGeneratorARM64::useByteOpRegisterAtStart(MDefinition* mir) { 35 return useRegisterAtStart(mir); 36 } 37 38 LAllocation LIRGeneratorARM64::useByteOpRegisterOrNonDoubleConstant( 39 MDefinition* mir) { 40 return useRegisterOrNonDoubleConstant(mir); 41 } 42 43 LDefinition LIRGeneratorARM64::tempByteOpRegister() { return temp(); } 44 45 LDefinition LIRGeneratorARM64::tempToUnbox() { return temp(); } 46 47 void LIRGenerator::visitBox(MBox* box) { 48 MDefinition* opd = box->getOperand(0); 49 50 // If the operand is a constant, emit near its uses. 51 if (opd->isConstant() && box->canEmitAtUses()) { 52 emitAtUses(box); 53 return; 54 } 55 56 if (opd->isConstant()) { 57 define(new (alloc()) LValue(opd->toConstant()->toJSValue()), box, 58 LDefinition(LDefinition::BOX)); 59 } else { 60 LBox* ins = new (alloc()) LBox(useRegisterAtStart(opd), opd->type()); 61 define(ins, box, LDefinition(LDefinition::BOX)); 62 } 63 } 64 65 void LIRGenerator::visitUnbox(MUnbox* unbox) { 66 MDefinition* box = unbox->getOperand(0); 67 MOZ_ASSERT(box->type() == MIRType::Value); 68 69 LInstructionHelper<1, BOX_PIECES, 0>* lir; 70 if (IsFloatingPointType(unbox->type())) { 71 MOZ_ASSERT(unbox->type() == MIRType::Double); 72 lir = new (alloc()) LUnboxFloatingPoint(useBoxAtStart(box)); 73 } else if (unbox->fallible()) { 74 // If the unbox is fallible, load the Value in a register first to 75 // avoid multiple loads. 76 lir = new (alloc()) LUnbox(useRegisterAtStart(box)); 77 } else { 78 // FIXME: It should be possible to useAtStart() here, but the DEBUG 79 // code in CodeGenerator::visitUnbox() needs to handle non-Register 80 // cases. ARM64 doesn't have an Operand type. 81 lir = new (alloc()) LUnbox(useRegisterAtStart(box)); 82 } 83 84 if (unbox->fallible()) { 85 assignSnapshot(lir, unbox->bailoutKind()); 86 } 87 88 define(lir, unbox); 89 } 90 91 void LIRGenerator::visitReturnImpl(MDefinition* opd, bool isGenerator) { 92 MOZ_ASSERT(opd->type() == MIRType::Value); 93 94 LReturn* ins = new (alloc()) LReturn(isGenerator); 95 ins->setOperand(0, useFixed(opd, JSReturnReg)); 96 add(ins); 97 } 98 99 // x = !y 100 void LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 1, 0>* ins, 101 MDefinition* mir, MDefinition* input) { 102 // Unary ALU operations don't read the input after writing to the output, even 103 // for fallible operations, so we can use at-start allocations. 104 ins->setOperand(0, useRegisterAtStart(input)); 105 define(ins, mir); 106 } 107 108 // z = x+y 109 void LIRGeneratorARM64::lowerForALU(LInstructionHelper<1, 2, 0>* ins, 110 MDefinition* mir, MDefinition* lhs, 111 MDefinition* rhs) { 112 // Binary ALU operations don't read any input after writing to the output, 113 // even for fallible operations, so we can use at-start allocations. 114 ins->setOperand(0, useRegisterAtStart(lhs)); 115 ins->setOperand(1, useRegisterOrConstantAtStart(rhs)); 116 define(ins, mir); 117 } 118 119 void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, 120 MDefinition* mir, MDefinition* input) { 121 ins->setOperand(0, useRegisterAtStart(input)); 122 define(ins, mir); 123 } 124 125 void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, 126 MDefinition* mir, MDefinition* lhs, 127 MDefinition* rhs) { 128 ins->setOperand(0, useRegisterAtStart(lhs)); 129 ins->setOperand(1, useRegisterAtStart(rhs)); 130 define(ins, mir); 131 } 132 133 void LIRGeneratorARM64::lowerForALUInt64( 134 LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>* ins, MDefinition* mir, 135 MDefinition* input) { 136 ins->setInt64Operand(0, useInt64RegisterAtStart(input)); 137 defineInt64(ins, mir); 138 } 139 140 void LIRGeneratorARM64::lowerForALUInt64( 141 LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, 142 MDefinition* mir, MDefinition* lhs, MDefinition* rhs) { 143 ins->setInt64Operand(0, useInt64RegisterAtStart(lhs)); 144 ins->setInt64Operand(INT64_PIECES, useInt64RegisterOrConstantAtStart(rhs)); 145 defineInt64(ins, mir); 146 } 147 148 void LIRGeneratorARM64::lowerForMulInt64(LMulI64* ins, MMul* mir, 149 MDefinition* lhs, MDefinition* rhs) { 150 lowerForALUInt64(ins, mir, lhs, rhs); 151 } 152 153 template <class LInstr> 154 void LIRGeneratorARM64::lowerForShiftInt64(LInstr* ins, MDefinition* mir, 155 MDefinition* lhs, MDefinition* rhs) { 156 if constexpr (std::is_same_v<LInstr, LShiftI64>) { 157 ins->setLhs(useInt64RegisterAtStart(lhs)); 158 ins->setRhs(useRegisterOrConstantAtStart(rhs)); 159 } else { 160 ins->setInput(useInt64RegisterAtStart(lhs)); 161 ins->setCount(useRegisterOrConstantAtStart(rhs)); 162 } 163 defineInt64(ins, mir); 164 } 165 166 template void LIRGeneratorARM64::lowerForShiftInt64(LShiftI64* ins, 167 MDefinition* mir, 168 MDefinition* lhs, 169 MDefinition* rhs); 170 template void LIRGeneratorARM64::lowerForShiftInt64(LRotateI64* ins, 171 MDefinition* mir, 172 MDefinition* lhs, 173 MDefinition* rhs); 174 175 void LIRGeneratorARM64::lowerWasmBuiltinTruncateToInt32( 176 MWasmBuiltinTruncateToInt32* ins) { 177 MDefinition* opd = ins->input(); 178 MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32); 179 180 if (opd->type() == MIRType::Double) { 181 define(new (alloc()) LWasmBuiltinTruncateDToInt32( 182 useRegister(opd), useFixed(ins->instance(), InstanceReg), 183 LDefinition::BogusTemp()), 184 ins); 185 return; 186 } 187 188 define(new (alloc()) LWasmBuiltinTruncateFToInt32( 189 useRegister(opd), LAllocation(), LDefinition::BogusTemp()), 190 ins); 191 } 192 193 void LIRGeneratorARM64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, 194 LBlock* block, size_t lirIndex) { 195 lowerTypedPhiInput(phi, inputPosition, block, lirIndex); 196 } 197 198 void LIRGeneratorARM64::lowerForShift(LInstructionHelper<1, 2, 0>* ins, 199 MDefinition* mir, MDefinition* lhs, 200 MDefinition* rhs) { 201 lowerForALU(ins, mir, lhs, rhs); 202 } 203 204 void LIRGeneratorARM64::lowerDivI(MDiv* div) { 205 if (div->rhs()->isConstant()) { 206 LAllocation lhs = useRegister(div->lhs()); 207 208 int32_t rhs = div->rhs()->toConstant()->toInt32(); 209 int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs)); 210 211 if (rhs != 0 && uint32_t(1) << shift == mozilla::Abs(rhs)) { 212 auto* lir = new (alloc()) LDivPowTwoI(lhs, shift, rhs < 0); 213 if (div->fallible()) { 214 assignSnapshot(lir, div->bailoutKind()); 215 } 216 define(lir, div); 217 return; 218 } 219 220 auto* lir = new (alloc()) LDivConstantI(lhs, rhs); 221 if (div->fallible()) { 222 assignSnapshot(lir, div->bailoutKind()); 223 } 224 define(lir, div); 225 return; 226 } 227 228 LAllocation lhs, rhs; 229 if (div->canTruncateRemainder()) { 230 lhs = useRegisterAtStart(div->lhs()); 231 rhs = useRegisterAtStart(div->rhs()); 232 } else { 233 lhs = useRegister(div->lhs()); 234 rhs = useRegister(div->rhs()); 235 } 236 237 // ARM64 has plenty of scratch registers, so we don't need to request an 238 // additonal temp register from the register allocator. 239 auto* lir = new (alloc()) LDivI(lhs, rhs, LDefinition::BogusTemp()); 240 if (div->fallible()) { 241 assignSnapshot(lir, div->bailoutKind()); 242 } 243 define(lir, div); 244 } 245 246 void LIRGeneratorARM64::lowerMulI(MMul* mul, MDefinition* lhs, 247 MDefinition* rhs) { 248 LMulI* lir = new (alloc()) LMulI; 249 if (mul->fallible()) { 250 assignSnapshot(lir, mul->bailoutKind()); 251 } 252 253 // Negative zero check reads |lhs| and |rhs| after writing to the output, so 254 // we can't use at-start allocations. 255 if (mul->canBeNegativeZero() && !rhs->isConstant()) { 256 lir->setOperand(0, useRegister(lhs)); 257 lir->setOperand(1, useRegister(rhs)); 258 define(lir, mul); 259 return; 260 } 261 262 lowerForALU(lir, mul, lhs, rhs); 263 } 264 265 void LIRGeneratorARM64::lowerModI(MMod* mod) { 266 LAllocation lhs = useRegister(mod->lhs()); 267 268 if (mod->rhs()->isConstant()) { 269 int32_t rhs = mod->rhs()->toConstant()->toInt32(); 270 int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs)); 271 272 if (rhs != 0 && uint32_t(1) << shift == mozilla::Abs(rhs)) { 273 auto* lir = new (alloc()) LModPowTwoI(lhs, shift); 274 if (mod->fallible()) { 275 assignSnapshot(lir, mod->bailoutKind()); 276 } 277 define(lir, mod); 278 return; 279 } 280 281 auto* lir = new (alloc()) LModConstantI(lhs, rhs); 282 if (mod->fallible()) { 283 assignSnapshot(lir, mod->bailoutKind()); 284 } 285 define(lir, mod); 286 return; 287 } 288 289 auto* lir = new (alloc()) LModI(lhs, useRegister(mod->rhs())); 290 if (mod->fallible()) { 291 assignSnapshot(lir, mod->bailoutKind()); 292 } 293 define(lir, mod); 294 } 295 296 void LIRGeneratorARM64::lowerDivI64(MDiv* div) { 297 if (div->rhs()->isConstant()) { 298 LAllocation lhs = useRegister(div->lhs()); 299 int64_t rhs = div->rhs()->toConstant()->toInt64(); 300 301 if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) { 302 int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs)); 303 304 auto* lir = new (alloc()) LDivPowTwoI64(lhs, shift, rhs < 0); 305 define(lir, div); 306 return; 307 } 308 309 auto* lir = new (alloc()) LDivConstantI64(lhs, rhs); 310 define(lir, div); 311 return; 312 } 313 314 auto* lir = new (alloc()) 315 LDivI64(useRegisterAtStart(div->lhs()), useRegisterAtStart(div->rhs())); 316 define(lir, div); 317 } 318 319 void LIRGeneratorARM64::lowerModI64(MMod* mod) { 320 LAllocation lhs = useRegister(mod->lhs()); 321 322 if (mod->rhs()->isConstant()) { 323 int64_t rhs = mod->rhs()->toConstant()->toInt64(); 324 325 if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) { 326 int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs)); 327 328 auto* lir = new (alloc()) LModPowTwoI64(lhs, shift); 329 define(lir, mod); 330 return; 331 } 332 333 auto* lir = new (alloc()) LModConstantI64(lhs, rhs); 334 define(lir, mod); 335 return; 336 } 337 338 auto* lir = new (alloc()) LModI64(lhs, useRegister(mod->rhs())); 339 define(lir, mod); 340 } 341 342 void LIRGeneratorARM64::lowerUDivI64(MDiv* div) { 343 if (div->rhs()->isConstant()) { 344 LAllocation lhs = useRegister(div->lhs()); 345 346 // NOTE: the result of toInt64 is coerced to uint64_t. 347 uint64_t rhs = div->rhs()->toConstant()->toInt64(); 348 349 if (mozilla::IsPowerOfTwo(rhs)) { 350 int32_t shift = mozilla::FloorLog2(rhs); 351 352 auto* lir = new (alloc()) LDivPowTwoI64(lhs, shift, false); 353 define(lir, div); 354 return; 355 } 356 357 auto* lir = new (alloc()) LUDivConstantI64(lhs, rhs); 358 define(lir, div); 359 return; 360 } 361 362 auto* lir = new (alloc()) 363 LUDivI64(useRegisterAtStart(div->lhs()), useRegisterAtStart(div->rhs())); 364 define(lir, div); 365 } 366 367 void LIRGeneratorARM64::lowerUModI64(MMod* mod) { 368 LAllocation lhs = useRegister(mod->lhs()); 369 370 if (mod->rhs()->isConstant()) { 371 // NOTE: the result of toInt64 is coerced to uint64_t. 372 uint64_t rhs = mod->rhs()->toConstant()->toInt64(); 373 374 if (mozilla::IsPowerOfTwo(rhs)) { 375 int32_t shift = mozilla::FloorLog2(rhs); 376 377 auto* lir = new (alloc()) LModPowTwoI64(lhs, shift); 378 define(lir, mod); 379 return; 380 } 381 382 auto* lir = new (alloc()) LUModConstantI64(lhs, rhs); 383 define(lir, mod); 384 return; 385 } 386 387 auto* lir = new (alloc()) LUModI64(lhs, useRegister(mod->rhs())); 388 define(lir, mod); 389 } 390 391 void LIRGeneratorARM64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { 392 MOZ_CRASH("We don't use runtime div for this architecture"); 393 } 394 395 void LIRGeneratorARM64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { 396 MOZ_CRASH("We don't use runtime mod for this architecture"); 397 } 398 399 void LIRGeneratorARM64::lowerWasmSelectI(MWasmSelect* select) { 400 if (select->type() == MIRType::Simd128) { 401 LAllocation t = useRegisterAtStart(select->trueExpr()); 402 LAllocation f = useRegister(select->falseExpr()); 403 LAllocation c = useRegister(select->condExpr()); 404 auto* lir = new (alloc()) LWasmSelect(t, f, c); 405 defineReuseInput(lir, select, LWasmSelect::TrueExprIndex); 406 } else { 407 LAllocation t = useRegisterAtStart(select->trueExpr()); 408 LAllocation f = useRegisterAtStart(select->falseExpr()); 409 LAllocation c = useRegisterAtStart(select->condExpr()); 410 define(new (alloc()) LWasmSelect(t, f, c), select); 411 } 412 } 413 414 void LIRGeneratorARM64::lowerWasmSelectI64(MWasmSelect* select) { 415 LInt64Allocation t = useInt64RegisterAtStart(select->trueExpr()); 416 LInt64Allocation f = useInt64RegisterAtStart(select->falseExpr()); 417 LAllocation c = useRegisterAtStart(select->condExpr()); 418 defineInt64(new (alloc()) LWasmSelectI64(t, f, c), select); 419 } 420 421 // On arm64 we specialize the cases: compare is {{U,}Int32, {U,}Int64}, 422 // Float32, Double}, and select is {{U,}Int32, {U,}Int64}, Float32, Double}, 423 // independently. 424 bool LIRGeneratorARM64::canSpecializeWasmCompareAndSelect( 425 MCompare::CompareType compTy, MIRType insTy) { 426 return (insTy == MIRType::Int32 || insTy == MIRType::Int64 || 427 insTy == MIRType::Float32 || insTy == MIRType::Double) && 428 (compTy == MCompare::Compare_Int32 || 429 compTy == MCompare::Compare_UInt32 || 430 compTy == MCompare::Compare_Int64 || 431 compTy == MCompare::Compare_UInt64 || 432 compTy == MCompare::Compare_Float32 || 433 compTy == MCompare::Compare_Double); 434 } 435 436 void LIRGeneratorARM64::lowerWasmCompareAndSelect(MWasmSelect* ins, 437 MDefinition* lhs, 438 MDefinition* rhs, 439 MCompare::CompareType compTy, 440 JSOp jsop) { 441 MOZ_ASSERT(canSpecializeWasmCompareAndSelect(compTy, ins->type())); 442 LAllocation rhsAlloc; 443 if (compTy == MCompare::Compare_Float32 || 444 compTy == MCompare::Compare_Double) { 445 rhsAlloc = useRegisterAtStart(rhs); 446 } else if (compTy == MCompare::Compare_Int32 || 447 compTy == MCompare::Compare_UInt32 || 448 compTy == MCompare::Compare_Int64 || 449 compTy == MCompare::Compare_UInt64) { 450 rhsAlloc = useRegisterOrConstantAtStart(rhs); 451 } else { 452 MOZ_CRASH("Unexpected type"); 453 } 454 auto* lir = new (alloc()) LWasmCompareAndSelect( 455 useRegisterAtStart(lhs), rhsAlloc, useRegisterAtStart(ins->trueExpr()), 456 useRegisterAtStart(ins->falseExpr()), compTy, jsop); 457 define(lir, ins); 458 } 459 460 LTableSwitch* LIRGeneratorARM64::newLTableSwitch(const LAllocation& in, 461 const LDefinition& inputCopy) { 462 return new (alloc()) LTableSwitch(in, inputCopy, temp()); 463 } 464 465 LTableSwitchV* LIRGeneratorARM64::newLTableSwitchV(const LBoxAllocation& in) { 466 return new (alloc()) LTableSwitchV(in, temp(), tempDouble(), temp()); 467 } 468 469 void LIRGeneratorARM64::lowerUrshD(MUrsh* mir) { 470 MDefinition* lhs = mir->lhs(); 471 MDefinition* rhs = mir->rhs(); 472 473 MOZ_ASSERT(lhs->type() == MIRType::Int32); 474 MOZ_ASSERT(rhs->type() == MIRType::Int32); 475 476 LUrshD* lir = new (alloc()) 477 LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp()); 478 define(lir, mir); 479 } 480 481 void LIRGeneratorARM64::lowerPowOfTwoI(MPow* mir) { 482 int32_t base = mir->input()->toConstant()->toInt32(); 483 MDefinition* power = mir->power(); 484 485 auto* lir = new (alloc()) LPowOfTwoI(useRegister(power), base); 486 assignSnapshot(lir, mir->bailoutKind()); 487 define(lir, mir); 488 } 489 490 void LIRGeneratorARM64::lowerBigIntPtrLsh(MBigIntPtrLsh* ins) { 491 auto* lir = new (alloc()) LBigIntPtrLsh( 492 useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp()); 493 assignSnapshot(lir, ins->bailoutKind()); 494 define(lir, ins); 495 } 496 497 void LIRGeneratorARM64::lowerBigIntPtrRsh(MBigIntPtrRsh* ins) { 498 auto* lir = new (alloc()) LBigIntPtrRsh( 499 useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp()); 500 assignSnapshot(lir, ins->bailoutKind()); 501 define(lir, ins); 502 } 503 504 void LIRGeneratorARM64::lowerBigIntPtrDiv(MBigIntPtrDiv* ins) { 505 auto* lir = new (alloc()) 506 LBigIntPtrDiv(useRegister(ins->lhs()), useRegister(ins->rhs()), 507 LDefinition::BogusTemp(), LDefinition::BogusTemp()); 508 assignSnapshot(lir, ins->bailoutKind()); 509 define(lir, ins); 510 } 511 512 void LIRGeneratorARM64::lowerBigIntPtrMod(MBigIntPtrMod* ins) { 513 auto* lir = new (alloc()) 514 LBigIntPtrMod(useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), 515 LDefinition::BogusTemp()); 516 if (ins->canBeDivideByZero()) { 517 assignSnapshot(lir, ins->bailoutKind()); 518 } 519 define(lir, ins); 520 } 521 522 #ifdef ENABLE_WASM_SIMD 523 524 bool LIRGeneratorARM64::canFoldReduceSimd128AndBranch(wasm::SimdOp op) { 525 switch (op) { 526 case wasm::SimdOp::V128AnyTrue: 527 case wasm::SimdOp::I8x16AllTrue: 528 case wasm::SimdOp::I16x8AllTrue: 529 case wasm::SimdOp::I32x4AllTrue: 530 case wasm::SimdOp::I64x2AllTrue: 531 return true; 532 default: 533 return false; 534 } 535 } 536 537 bool LIRGeneratorARM64::canEmitWasmReduceSimd128AtUses( 538 MWasmReduceSimd128* ins) { 539 if (!ins->canEmitAtUses()) { 540 return false; 541 } 542 // Only specific ops generating int32. 543 if (ins->type() != MIRType::Int32) { 544 return false; 545 } 546 if (!canFoldReduceSimd128AndBranch(ins->simdOp())) { 547 return false; 548 } 549 // If never used then defer (it will be removed). 550 MUseIterator iter(ins->usesBegin()); 551 if (iter == ins->usesEnd()) { 552 return true; 553 } 554 // We require an MTest consumer. 555 MNode* node = iter->consumer(); 556 if (!node->isDefinition() || !node->toDefinition()->isTest()) { 557 return false; 558 } 559 // Defer only if there's only one use. 560 iter++; 561 return iter == ins->usesEnd(); 562 } 563 564 #endif 565 566 void LIRGeneratorARM64::lowerUDiv(MDiv* div) { 567 if (div->rhs()->isConstant()) { 568 LAllocation lhs = useRegister(div->lhs()); 569 570 // NOTE: the result of toInt32 is coerced to uint32_t. 571 uint32_t rhs = div->rhs()->toConstant()->toInt32(); 572 int32_t shift = mozilla::FloorLog2(rhs); 573 574 if (rhs != 0 && uint32_t(1) << shift == rhs) { 575 auto* lir = new (alloc()) LDivPowTwoI(lhs, shift, false); 576 if (div->fallible()) { 577 assignSnapshot(lir, div->bailoutKind()); 578 } 579 define(lir, div); 580 return; 581 } 582 583 auto* lir = new (alloc()) LUDivConstant(lhs, rhs); 584 if (div->fallible()) { 585 assignSnapshot(lir, div->bailoutKind()); 586 } 587 define(lir, div); 588 return; 589 } 590 591 // Generate UDiv 592 LAllocation lhs, rhs; 593 if (div->canTruncateRemainder()) { 594 lhs = useRegisterAtStart(div->lhs()); 595 rhs = useRegisterAtStart(div->rhs()); 596 } else { 597 lhs = useRegister(div->lhs()); 598 rhs = useRegister(div->rhs()); 599 } 600 601 auto* lir = new (alloc()) LUDiv(lhs, rhs); 602 if (div->fallible()) { 603 assignSnapshot(lir, div->bailoutKind()); 604 } 605 define(lir, div); 606 } 607 608 void LIRGeneratorARM64::lowerUMod(MMod* mod) { 609 LAllocation lhs = useRegister(mod->lhs()); 610 611 if (mod->rhs()->isConstant()) { 612 // NOTE: the result of toInt32 is coerced to uint32_t. 613 uint32_t rhs = mod->rhs()->toConstant()->toInt32(); 614 int32_t shift = mozilla::FloorLog2(rhs); 615 616 if (rhs != 0 && uint32_t(1) << shift == rhs) { 617 auto* lir = new (alloc()) LModPowTwoI(lhs, shift); 618 if (mod->fallible()) { 619 assignSnapshot(lir, mod->bailoutKind()); 620 } 621 define(lir, mod); 622 return; 623 } 624 625 auto* lir = new (alloc()) LUModConstant(lhs, rhs); 626 if (mod->fallible()) { 627 assignSnapshot(lir, mod->bailoutKind()); 628 } 629 define(lir, mod); 630 return; 631 } 632 633 auto* lir = new (alloc()) LUMod(lhs, useRegister(mod->rhs())); 634 if (mod->fallible()) { 635 assignSnapshot(lir, mod->bailoutKind()); 636 } 637 define(lir, mod); 638 } 639 640 void LIRGenerator::visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) { 641 MOZ_ASSERT(ins->input()->type() == MIRType::Int32); 642 LWasmUint32ToDouble* lir = 643 new (alloc()) LWasmUint32ToDouble(useRegisterAtStart(ins->input())); 644 define(lir, ins); 645 } 646 647 void LIRGenerator::visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) { 648 MOZ_ASSERT(ins->input()->type() == MIRType::Int32); 649 LWasmUint32ToFloat32* lir = 650 new (alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input())); 651 define(lir, ins); 652 } 653 654 void LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) { 655 MDefinition* base = ins->base(); 656 MOZ_ASSERT(base->type() == MIRType::Int32); 657 658 MDefinition* boundsCheckLimit = ins->boundsCheckLimit(); 659 MOZ_ASSERT_IF(ins->needsBoundsCheck(), 660 boundsCheckLimit->type() == MIRType::Int32); 661 662 LAllocation baseAlloc = useRegisterAtStart(base); 663 664 LAllocation limitAlloc = ins->needsBoundsCheck() 665 ? useRegisterAtStart(boundsCheckLimit) 666 : LAllocation(); 667 668 // We have no memory-base value, meaning that HeapReg is to be used as the 669 // memory base. This follows from the definition of 670 // FunctionCompiler::maybeLoadMemoryBase() in WasmIonCompile.cpp. 671 MOZ_ASSERT(!ins->hasMemoryBase()); 672 auto* lir = 673 new (alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc, LAllocation()); 674 define(lir, ins); 675 } 676 677 void LIRGenerator::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) { 678 MDefinition* base = ins->base(); 679 MOZ_ASSERT(base->type() == MIRType::Int32); 680 681 MDefinition* boundsCheckLimit = ins->boundsCheckLimit(); 682 MOZ_ASSERT_IF(ins->needsBoundsCheck(), 683 boundsCheckLimit->type() == MIRType::Int32); 684 685 LAllocation baseAlloc = useRegisterAtStart(base); 686 687 LAllocation limitAlloc = ins->needsBoundsCheck() 688 ? useRegisterAtStart(boundsCheckLimit) 689 : LAllocation(); 690 691 // See comment in LIRGenerator::visitAsmJSStoreHeap just above. 692 MOZ_ASSERT(!ins->hasMemoryBase()); 693 add(new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()), 694 limitAlloc, LAllocation()), 695 ins); 696 } 697 698 void LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) { 699 MDefinition* base = ins->base(); 700 // See comment in visitWasmLoad re the type of 'base'. 701 MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); 702 703 LAllocation memoryBase = ins->hasMemoryBase() 704 ? LAllocation(useRegister(ins->memoryBase())) 705 : LGeneralReg(HeapReg); 706 707 // Note, the access type may be Int64 here. 708 709 LWasmCompareExchangeHeap* lir = new (alloc()) 710 LWasmCompareExchangeHeap(useRegister(base), useRegister(ins->oldValue()), 711 useRegister(ins->newValue()), memoryBase); 712 713 define(lir, ins); 714 } 715 716 void LIRGenerator::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins) { 717 MDefinition* base = ins->base(); 718 // See comment in visitWasmLoad re the type of 'base'. 719 MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); 720 721 LAllocation memoryBase = ins->hasMemoryBase() 722 ? LAllocation(useRegister(ins->memoryBase())) 723 : LGeneralReg(HeapReg); 724 725 // Note, the access type may be Int64 here. 726 727 LWasmAtomicExchangeHeap* lir = new (alloc()) LWasmAtomicExchangeHeap( 728 useRegister(base), useRegister(ins->value()), memoryBase); 729 define(lir, ins); 730 } 731 732 void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { 733 MDefinition* base = ins->base(); 734 // See comment in visitWasmLoad re the type of 'base'. 735 MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); 736 737 LAllocation memoryBase = ins->hasMemoryBase() 738 ? LAllocation(useRegister(ins->memoryBase())) 739 : LGeneralReg(HeapReg); 740 741 // Note, the access type may be Int64 here. 742 743 if (!ins->hasUses()) { 744 auto* lir = new (alloc()) LWasmAtomicBinopHeapForEffect( 745 useRegister(base), useRegister(ins->value()), memoryBase, temp()); 746 add(lir, ins); 747 return; 748 } 749 750 auto* lir = new (alloc()) LWasmAtomicBinopHeap( 751 useRegister(base), useRegister(ins->value()), memoryBase, temp()); 752 define(lir, ins); 753 } 754 755 void LIRGeneratorARM64::lowerTruncateDToInt32(MTruncateToInt32* ins) { 756 MDefinition* opd = ins->input(); 757 MOZ_ASSERT(opd->type() == MIRType::Double); 758 define(new (alloc()) 759 LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), 760 ins); 761 } 762 763 void LIRGeneratorARM64::lowerTruncateFToInt32(MTruncateToInt32* ins) { 764 MDefinition* opd = ins->input(); 765 MOZ_ASSERT(opd->type() == MIRType::Float32); 766 define(new (alloc()) 767 LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), 768 ins); 769 } 770 771 void LIRGenerator::visitAtomicTypedArrayElementBinop( 772 MAtomicTypedArrayElementBinop* ins) { 773 MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped); 774 MOZ_ASSERT(!Scalar::isFloatingType(ins->arrayType())); 775 MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); 776 MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); 777 778 const LUse elements = useRegister(ins->elements()); 779 const LAllocation index = 780 useRegisterOrIndexConstant(ins->index(), ins->arrayType()); 781 782 if (Scalar::isBigIntType(ins->arrayType())) { 783 LInt64Allocation value = useInt64Register(ins->value()); 784 LInt64Definition temp = tempInt64(); 785 786 // Case 1: the result of the operation is not used. 787 788 if (ins->isForEffect()) { 789 auto* lir = new (alloc()) LAtomicTypedArrayElementBinopForEffect64( 790 elements, index, value, temp); 791 add(lir, ins); 792 return; 793 } 794 795 // Case 2: the result of the operation is used. 796 797 auto* lir = new (alloc()) 798 LAtomicTypedArrayElementBinop64(elements, index, value, temp); 799 defineInt64(lir, ins); 800 return; 801 } 802 803 LAllocation value = useRegister(ins->value()); 804 805 if (ins->isForEffect()) { 806 auto* lir = new (alloc()) 807 LAtomicTypedArrayElementBinopForEffect(elements, index, value, temp()); 808 add(lir, ins); 809 return; 810 } 811 812 LDefinition tempDef1 = temp(); 813 LDefinition tempDef2 = LDefinition::BogusTemp(); 814 if (ins->arrayType() == Scalar::Uint32) { 815 tempDef2 = temp(); 816 } 817 818 LAtomicTypedArrayElementBinop* lir = new (alloc()) 819 LAtomicTypedArrayElementBinop(elements, index, value, tempDef1, tempDef2); 820 821 define(lir, ins); 822 } 823 824 void LIRGenerator::visitCompareExchangeTypedArrayElement( 825 MCompareExchangeTypedArrayElement* ins) { 826 MOZ_ASSERT(!Scalar::isFloatingType(ins->arrayType())); 827 MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); 828 MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); 829 830 const LUse elements = useRegister(ins->elements()); 831 const LAllocation index = 832 useRegisterOrIndexConstant(ins->index(), ins->arrayType()); 833 834 if (Scalar::isBigIntType(ins->arrayType())) { 835 LInt64Allocation oldval = useInt64Register(ins->oldval()); 836 LInt64Allocation newval = useInt64Register(ins->newval()); 837 838 auto* lir = new (alloc()) 839 LCompareExchangeTypedArrayElement64(elements, index, oldval, newval); 840 defineInt64(lir, ins); 841 return; 842 } 843 844 const LAllocation oldval = useRegister(ins->oldval()); 845 const LAllocation newval = useRegister(ins->newval()); 846 847 // If the target is an FPReg then we need a temporary at the CodeGenerator 848 // level for creating the result. 849 850 LDefinition outTemp = LDefinition::BogusTemp(); 851 if (ins->arrayType() == Scalar::Uint32) { 852 outTemp = temp(); 853 } 854 855 LCompareExchangeTypedArrayElement* lir = 856 new (alloc()) LCompareExchangeTypedArrayElement(elements, index, oldval, 857 newval, outTemp); 858 859 define(lir, ins); 860 } 861 862 void LIRGenerator::visitAtomicExchangeTypedArrayElement( 863 MAtomicExchangeTypedArrayElement* ins) { 864 MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); 865 MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); 866 867 const LUse elements = useRegister(ins->elements()); 868 const LAllocation index = 869 useRegisterOrIndexConstant(ins->index(), ins->arrayType()); 870 871 if (Scalar::isBigIntType(ins->arrayType())) { 872 LInt64Allocation value = useInt64Register(ins->value()); 873 874 auto* lir = new (alloc()) 875 LAtomicExchangeTypedArrayElement64(elements, index, value); 876 defineInt64(lir, ins); 877 return; 878 } 879 880 MOZ_ASSERT(ins->arrayType() <= Scalar::Uint32); 881 882 const LAllocation value = useRegister(ins->value()); 883 884 LDefinition tempDef = LDefinition::BogusTemp(); 885 if (ins->arrayType() == Scalar::Uint32) { 886 tempDef = temp(); 887 } 888 889 LAtomicExchangeTypedArrayElement* lir = new (alloc()) 890 LAtomicExchangeTypedArrayElement(elements, index, value, tempDef); 891 892 define(lir, ins); 893 } 894 895 void LIRGeneratorARM64::lowerAtomicLoad64(MLoadUnboxedScalar* ins) { 896 const LUse elements = useRegister(ins->elements()); 897 const LAllocation index = 898 useRegisterOrIndexConstant(ins->index(), ins->storageType()); 899 900 auto* lir = new (alloc()) LAtomicLoad64(elements, index); 901 defineInt64(lir, ins); 902 } 903 904 void LIRGeneratorARM64::lowerAtomicStore64(MStoreUnboxedScalar* ins) { 905 LUse elements = useRegister(ins->elements()); 906 LAllocation index = 907 useRegisterOrIndexConstant(ins->index(), ins->writeType()); 908 LInt64Allocation value = useInt64Register(ins->value()); 909 910 add(new (alloc()) LAtomicStore64(elements, index, value), ins); 911 } 912 913 void LIRGenerator::visitSubstr(MSubstr* ins) { 914 LSubstr* lir = new (alloc()) 915 LSubstr(useRegister(ins->string()), useRegister(ins->begin()), 916 useRegister(ins->length()), temp(), temp(), temp()); 917 define(lir, ins); 918 assignSafepoint(lir, ins); 919 } 920 921 void LIRGenerator::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins) { 922 MDefinition* opd = ins->input(); 923 MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32); 924 925 defineInt64(new (alloc()) LWasmTruncateToInt64(useRegister(opd)), ins); 926 } 927 928 void LIRGeneratorARM64::lowerWasmBuiltinTruncateToInt64( 929 MWasmBuiltinTruncateToInt64* ins) { 930 MOZ_CRASH("We don't use WasmBuiltinTruncateToInt64 for arm64"); 931 } 932 933 void LIRGeneratorARM64::lowerBuiltinInt64ToFloatingPoint( 934 MBuiltinInt64ToFloatingPoint* ins) { 935 MOZ_CRASH("We don't use it for this architecture"); 936 } 937 938 void LIRGenerator::visitWasmLoad(MWasmLoad* ins) { 939 MDefinition* base = ins->base(); 940 // 'base' is a GPR but may be of either type. If it is 32-bit it is 941 // zero-extended and can act as 64-bit. 942 MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); 943 944 LAllocation memoryBase = 945 ins->hasMemoryBase() ? LAllocation(useRegisterAtStart(ins->memoryBase())) 946 : LGeneralReg(HeapReg); 947 LAllocation ptr = useRegisterOrConstantAtStart(base); 948 949 if (ins->type() == MIRType::Int64) { 950 auto* lir = new (alloc()) LWasmLoadI64(ptr, memoryBase); 951 defineInt64(lir, ins); 952 } else { 953 auto* lir = new (alloc()) LWasmLoad(ptr, memoryBase); 954 define(lir, ins); 955 } 956 } 957 958 void LIRGenerator::visitWasmStore(MWasmStore* ins) { 959 MDefinition* base = ins->base(); 960 // See comment in visitWasmLoad re the type of 'base'. 961 MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); 962 963 MDefinition* value = ins->value(); 964 965 LAllocation memoryBase = 966 ins->hasMemoryBase() ? LAllocation(useRegisterAtStart(ins->memoryBase())) 967 : LGeneralReg(HeapReg); 968 LAllocation baseAlloc = useRegisterOrConstantAtStart(base); 969 970 if (ins->access().type() == Scalar::Int64) { 971 LInt64Allocation valueAlloc = useInt64RegisterAtStart(value); 972 auto* lir = new (alloc()) LWasmStoreI64(baseAlloc, valueAlloc, memoryBase); 973 add(lir, ins); 974 return; 975 } 976 977 LAllocation valueAlloc = useRegisterAtStart(value); 978 auto* lir = new (alloc()) LWasmStore(baseAlloc, valueAlloc, memoryBase); 979 add(lir, ins); 980 } 981 982 void LIRGenerator::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins) { 983 MDefinition* opd = ins->input(); 984 MOZ_ASSERT(opd->type() == MIRType::Int64); 985 MOZ_ASSERT(IsFloatingPointType(ins->type())); 986 987 define(new (alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins); 988 } 989 990 void LIRGenerator::visitCopySign(MCopySign* ins) { 991 MDefinition* lhs = ins->lhs(); 992 MDefinition* rhs = ins->rhs(); 993 994 MOZ_ASSERT(IsFloatingPointType(lhs->type())); 995 MOZ_ASSERT(lhs->type() == rhs->type()); 996 MOZ_ASSERT(lhs->type() == ins->type()); 997 998 LInstructionHelper<1, 2, 0>* lir; 999 if (lhs->type() == MIRType::Double) { 1000 lir = new (alloc()) LCopySignD(); 1001 } else { 1002 lir = new (alloc()) LCopySignF(); 1003 } 1004 1005 lir->setOperand(0, useRegisterAtStart(lhs)); 1006 lir->setOperand(1, willHaveDifferentLIRNodes(lhs, rhs) 1007 ? useRegister(rhs) 1008 : useRegisterAtStart(rhs)); 1009 // The copySignDouble and copySignFloat32 are optimized for lhs == output. 1010 // It also prevents rhs == output when lhs != output, avoids clobbering. 1011 defineReuseInput(lir, ins, 0); 1012 } 1013 1014 void LIRGenerator::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins) { 1015 defineInt64( 1016 new (alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins); 1017 } 1018 1019 void LIRGenerator::visitSignExtendInt64(MSignExtendInt64* ins) { 1020 defineInt64(new (alloc()) 1021 LSignExtendInt64(useInt64RegisterAtStart(ins->input())), 1022 ins); 1023 } 1024 1025 void LIRGenerator::visitWasmTernarySimd128(MWasmTernarySimd128* ins) { 1026 #ifdef ENABLE_WASM_SIMD 1027 MOZ_ASSERT(ins->v0()->type() == MIRType::Simd128); 1028 MOZ_ASSERT(ins->v1()->type() == MIRType::Simd128); 1029 MOZ_ASSERT(ins->v2()->type() == MIRType::Simd128); 1030 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1031 1032 switch (ins->simdOp()) { 1033 case wasm::SimdOp::V128Bitselect: { 1034 auto* lir = new (alloc()) 1035 LWasmTernarySimd128(useRegister(ins->v0()), useRegister(ins->v1()), 1036 useRegisterAtStart(ins->v2()), 1037 LDefinition::BogusTemp(), ins->simdOp()); 1038 // On ARM64, control register is used as output at machine instruction. 1039 defineReuseInput(lir, ins, LWasmTernarySimd128::V2Index); 1040 break; 1041 } 1042 case wasm::SimdOp::F32x4RelaxedMadd: 1043 case wasm::SimdOp::F32x4RelaxedNmadd: 1044 case wasm::SimdOp::F64x2RelaxedMadd: 1045 case wasm::SimdOp::F64x2RelaxedNmadd: { 1046 auto* lir = new (alloc()) 1047 LWasmTernarySimd128(useRegister(ins->v0()), useRegister(ins->v1()), 1048 useRegisterAtStart(ins->v2()), 1049 LDefinition::BogusTemp(), ins->simdOp()); 1050 defineReuseInput(lir, ins, LWasmTernarySimd128::V2Index); 1051 break; 1052 } 1053 case wasm::SimdOp::I32x4RelaxedDotI8x16I7x16AddS: { 1054 auto* lir = new (alloc()) LWasmTernarySimd128( 1055 useRegister(ins->v0()), useRegister(ins->v1()), 1056 useRegisterAtStart(ins->v2()), tempSimd128(), ins->simdOp()); 1057 defineReuseInput(lir, ins, LWasmTernarySimd128::V2Index); 1058 break; 1059 } 1060 case wasm::SimdOp::I8x16RelaxedLaneSelect: 1061 case wasm::SimdOp::I16x8RelaxedLaneSelect: 1062 case wasm::SimdOp::I32x4RelaxedLaneSelect: 1063 case wasm::SimdOp::I64x2RelaxedLaneSelect: { 1064 auto* lir = new (alloc()) 1065 LWasmTernarySimd128(useRegister(ins->v0()), useRegister(ins->v1()), 1066 useRegisterAtStart(ins->v2()), 1067 LDefinition::BogusTemp(), ins->simdOp()); 1068 defineReuseInput(lir, ins, LWasmTernarySimd128::V2Index); 1069 break; 1070 } 1071 default: 1072 MOZ_CRASH("NYI"); 1073 } 1074 #else 1075 MOZ_CRASH("No SIMD"); 1076 #endif 1077 } 1078 1079 void LIRGenerator::visitWasmBinarySimd128(MWasmBinarySimd128* ins) { 1080 #ifdef ENABLE_WASM_SIMD 1081 MDefinition* lhs = ins->lhs(); 1082 MDefinition* rhs = ins->rhs(); 1083 wasm::SimdOp op = ins->simdOp(); 1084 1085 MOZ_ASSERT(lhs->type() == MIRType::Simd128); 1086 MOZ_ASSERT(rhs->type() == MIRType::Simd128); 1087 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1088 1089 LAllocation lhsAlloc = useRegisterAtStart(lhs); 1090 LAllocation rhsAlloc = useRegisterAtStart(rhs); 1091 LDefinition tempReg0 = LDefinition::BogusTemp(); 1092 LDefinition tempReg1 = LDefinition::BogusTemp(); 1093 if (op == wasm::SimdOp::I64x2Mul) { 1094 tempReg0 = tempSimd128(); 1095 tempReg1 = tempSimd128(); 1096 } 1097 auto* lir = new (alloc()) 1098 LWasmBinarySimd128(lhsAlloc, rhsAlloc, tempReg0, tempReg1, op); 1099 define(lir, ins); 1100 #else 1101 MOZ_CRASH("No SIMD"); 1102 #endif 1103 } 1104 1105 #ifdef ENABLE_WASM_SIMD 1106 bool MWasmTernarySimd128::specializeBitselectConstantMaskAsShuffle( 1107 int8_t shuffle[16]) { 1108 return false; 1109 } 1110 bool MWasmTernarySimd128::canRelaxBitselect() { return false; } 1111 1112 bool MWasmBinarySimd128::canPmaddubsw() { return false; } 1113 #endif 1114 1115 bool MWasmBinarySimd128::specializeForConstantRhs() { 1116 // Probably many we want to do here 1117 return false; 1118 } 1119 1120 void LIRGenerator::visitWasmBinarySimd128WithConstant( 1121 MWasmBinarySimd128WithConstant* ins) { 1122 MOZ_CRASH("binary SIMD with constant NYI"); 1123 } 1124 1125 void LIRGenerator::visitWasmShiftSimd128(MWasmShiftSimd128* ins) { 1126 #ifdef ENABLE_WASM_SIMD 1127 MDefinition* lhs = ins->lhs(); 1128 MDefinition* rhs = ins->rhs(); 1129 1130 MOZ_ASSERT(lhs->type() == MIRType::Simd128); 1131 MOZ_ASSERT(rhs->type() == MIRType::Int32); 1132 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1133 1134 if (rhs->isConstant()) { 1135 int32_t shiftCount = rhs->toConstant()->toInt32(); 1136 switch (ins->simdOp()) { 1137 case wasm::SimdOp::I8x16Shl: 1138 case wasm::SimdOp::I8x16ShrU: 1139 case wasm::SimdOp::I8x16ShrS: 1140 shiftCount &= 7; 1141 break; 1142 case wasm::SimdOp::I16x8Shl: 1143 case wasm::SimdOp::I16x8ShrU: 1144 case wasm::SimdOp::I16x8ShrS: 1145 shiftCount &= 15; 1146 break; 1147 case wasm::SimdOp::I32x4Shl: 1148 case wasm::SimdOp::I32x4ShrU: 1149 case wasm::SimdOp::I32x4ShrS: 1150 shiftCount &= 31; 1151 break; 1152 case wasm::SimdOp::I64x2Shl: 1153 case wasm::SimdOp::I64x2ShrU: 1154 case wasm::SimdOp::I64x2ShrS: 1155 shiftCount &= 63; 1156 break; 1157 default: 1158 MOZ_CRASH("Unexpected shift operation"); 1159 } 1160 # ifdef DEBUG 1161 js::wasm::ReportSimdAnalysis("shift -> constant shift"); 1162 # endif 1163 auto* lir = new (alloc()) 1164 LWasmConstantShiftSimd128(useRegisterAtStart(lhs), shiftCount); 1165 define(lir, ins); 1166 return; 1167 } 1168 1169 # ifdef DEBUG 1170 js::wasm::ReportSimdAnalysis("shift -> variable shift"); 1171 # endif 1172 1173 LAllocation lhsDestAlloc = useRegisterAtStart(lhs); 1174 LAllocation rhsAlloc = useRegisterAtStart(rhs); 1175 auto* lir = new (alloc()) LWasmVariableShiftSimd128(lhsDestAlloc, rhsAlloc); 1176 define(lir, ins); 1177 #else 1178 MOZ_CRASH("No SIMD"); 1179 #endif 1180 } 1181 1182 void LIRGenerator::visitWasmShuffleSimd128(MWasmShuffleSimd128* ins) { 1183 #ifdef ENABLE_WASM_SIMD 1184 MOZ_ASSERT(ins->lhs()->type() == MIRType::Simd128); 1185 MOZ_ASSERT(ins->rhs()->type() == MIRType::Simd128); 1186 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1187 1188 SimdShuffle s = ins->shuffle(); 1189 switch (s.opd) { 1190 case SimdShuffle::Operand::LEFT: 1191 case SimdShuffle::Operand::RIGHT: { 1192 LAllocation src; 1193 switch (*s.permuteOp) { 1194 case SimdPermuteOp::MOVE: 1195 case SimdPermuteOp::BROADCAST_8x16: 1196 case SimdPermuteOp::BROADCAST_16x8: 1197 case SimdPermuteOp::PERMUTE_8x16: 1198 case SimdPermuteOp::PERMUTE_16x8: 1199 case SimdPermuteOp::PERMUTE_32x4: 1200 case SimdPermuteOp::ROTATE_RIGHT_8x16: 1201 case SimdPermuteOp::SHIFT_LEFT_8x16: 1202 case SimdPermuteOp::SHIFT_RIGHT_8x16: 1203 case SimdPermuteOp::REVERSE_16x8: 1204 case SimdPermuteOp::REVERSE_32x4: 1205 case SimdPermuteOp::REVERSE_64x2: 1206 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_16x8: 1207 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_32x4: 1208 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_64x2: 1209 case SimdPermuteOp::ZERO_EXTEND_16x8_TO_32x4: 1210 case SimdPermuteOp::ZERO_EXTEND_16x8_TO_64x2: 1211 case SimdPermuteOp::ZERO_EXTEND_32x4_TO_64x2: 1212 break; 1213 default: 1214 MOZ_CRASH("Unexpected operator"); 1215 } 1216 if (s.opd == SimdShuffle::Operand::LEFT) { 1217 src = useRegisterAtStart(ins->lhs()); 1218 } else { 1219 src = useRegisterAtStart(ins->rhs()); 1220 } 1221 auto* lir = 1222 new (alloc()) LWasmPermuteSimd128(src, *s.permuteOp, s.control); 1223 define(lir, ins); 1224 break; 1225 } 1226 case SimdShuffle::Operand::BOTH: 1227 case SimdShuffle::Operand::BOTH_SWAPPED: { 1228 LAllocation lhs; 1229 LAllocation rhs; 1230 if (s.opd == SimdShuffle::Operand::BOTH) { 1231 lhs = useRegisterAtStart(ins->lhs()); 1232 rhs = useRegisterAtStart(ins->rhs()); 1233 } else { 1234 lhs = useRegisterAtStart(ins->rhs()); 1235 rhs = useRegisterAtStart(ins->lhs()); 1236 } 1237 auto* lir = 1238 new (alloc()) LWasmShuffleSimd128(lhs, rhs, *s.shuffleOp, s.control); 1239 define(lir, ins); 1240 break; 1241 } 1242 } 1243 #else 1244 MOZ_CRASH("No SIMD"); 1245 #endif 1246 } 1247 1248 void LIRGenerator::visitWasmReplaceLaneSimd128(MWasmReplaceLaneSimd128* ins) { 1249 #ifdef ENABLE_WASM_SIMD 1250 MOZ_ASSERT(ins->lhs()->type() == MIRType::Simd128); 1251 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1252 1253 // Optimal code generation reuses the lhs register because the rhs scalar is 1254 // merged into a vector lhs. 1255 LAllocation lhs = useRegisterAtStart(ins->lhs()); 1256 if (ins->rhs()->type() == MIRType::Int64) { 1257 auto* lir = new (alloc()) 1258 LWasmReplaceInt64LaneSimd128(lhs, useInt64Register(ins->rhs())); 1259 defineReuseInput(lir, ins, 0); 1260 } else { 1261 auto* lir = 1262 new (alloc()) LWasmReplaceLaneSimd128(lhs, useRegister(ins->rhs())); 1263 defineReuseInput(lir, ins, 0); 1264 } 1265 #else 1266 MOZ_CRASH("No SIMD"); 1267 #endif 1268 } 1269 1270 void LIRGenerator::visitWasmScalarToSimd128(MWasmScalarToSimd128* ins) { 1271 #ifdef ENABLE_WASM_SIMD 1272 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1273 1274 switch (ins->input()->type()) { 1275 case MIRType::Int64: { 1276 // 64-bit integer splats. 1277 // Load-and-(sign|zero)extend. 1278 auto* lir = new (alloc()) 1279 LWasmInt64ToSimd128(useInt64RegisterAtStart(ins->input())); 1280 define(lir, ins); 1281 break; 1282 } 1283 case MIRType::Float32: 1284 case MIRType::Double: { 1285 // Floating-point splats. 1286 auto* lir = 1287 new (alloc()) LWasmScalarToSimd128(useRegisterAtStart(ins->input())); 1288 define(lir, ins); 1289 break; 1290 } 1291 default: { 1292 // 32-bit integer splats. 1293 auto* lir = 1294 new (alloc()) LWasmScalarToSimd128(useRegisterAtStart(ins->input())); 1295 define(lir, ins); 1296 break; 1297 } 1298 } 1299 #else 1300 MOZ_CRASH("No SIMD"); 1301 #endif 1302 } 1303 1304 void LIRGenerator::visitWasmUnarySimd128(MWasmUnarySimd128* ins) { 1305 #ifdef ENABLE_WASM_SIMD 1306 MOZ_ASSERT(ins->input()->type() == MIRType::Simd128); 1307 MOZ_ASSERT(ins->type() == MIRType::Simd128); 1308 1309 LDefinition tempReg = LDefinition::BogusTemp(); 1310 switch (ins->simdOp()) { 1311 case wasm::SimdOp::I8x16Neg: 1312 case wasm::SimdOp::I16x8Neg: 1313 case wasm::SimdOp::I32x4Neg: 1314 case wasm::SimdOp::I64x2Neg: 1315 case wasm::SimdOp::F32x4Neg: 1316 case wasm::SimdOp::F64x2Neg: 1317 case wasm::SimdOp::F32x4Abs: 1318 case wasm::SimdOp::F64x2Abs: 1319 case wasm::SimdOp::V128Not: 1320 case wasm::SimdOp::F32x4Sqrt: 1321 case wasm::SimdOp::F64x2Sqrt: 1322 case wasm::SimdOp::I8x16Abs: 1323 case wasm::SimdOp::I16x8Abs: 1324 case wasm::SimdOp::I32x4Abs: 1325 case wasm::SimdOp::I64x2Abs: 1326 case wasm::SimdOp::I32x4TruncSatF32x4S: 1327 case wasm::SimdOp::F32x4ConvertI32x4U: 1328 case wasm::SimdOp::I32x4TruncSatF32x4U: 1329 case wasm::SimdOp::I16x8ExtendLowI8x16S: 1330 case wasm::SimdOp::I16x8ExtendHighI8x16S: 1331 case wasm::SimdOp::I16x8ExtendLowI8x16U: 1332 case wasm::SimdOp::I16x8ExtendHighI8x16U: 1333 case wasm::SimdOp::I32x4ExtendLowI16x8S: 1334 case wasm::SimdOp::I32x4ExtendHighI16x8S: 1335 case wasm::SimdOp::I32x4ExtendLowI16x8U: 1336 case wasm::SimdOp::I32x4ExtendHighI16x8U: 1337 case wasm::SimdOp::I64x2ExtendLowI32x4S: 1338 case wasm::SimdOp::I64x2ExtendHighI32x4S: 1339 case wasm::SimdOp::I64x2ExtendLowI32x4U: 1340 case wasm::SimdOp::I64x2ExtendHighI32x4U: 1341 case wasm::SimdOp::F32x4ConvertI32x4S: 1342 case wasm::SimdOp::F32x4Ceil: 1343 case wasm::SimdOp::F32x4Floor: 1344 case wasm::SimdOp::F32x4Trunc: 1345 case wasm::SimdOp::F32x4Nearest: 1346 case wasm::SimdOp::F64x2Ceil: 1347 case wasm::SimdOp::F64x2Floor: 1348 case wasm::SimdOp::F64x2Trunc: 1349 case wasm::SimdOp::F64x2Nearest: 1350 case wasm::SimdOp::F32x4DemoteF64x2Zero: 1351 case wasm::SimdOp::F64x2PromoteLowF32x4: 1352 case wasm::SimdOp::F64x2ConvertLowI32x4S: 1353 case wasm::SimdOp::F64x2ConvertLowI32x4U: 1354 case wasm::SimdOp::I16x8ExtaddPairwiseI8x16S: 1355 case wasm::SimdOp::I16x8ExtaddPairwiseI8x16U: 1356 case wasm::SimdOp::I32x4ExtaddPairwiseI16x8S: 1357 case wasm::SimdOp::I32x4ExtaddPairwiseI16x8U: 1358 case wasm::SimdOp::I8x16Popcnt: 1359 case wasm::SimdOp::I32x4RelaxedTruncF32x4S: 1360 case wasm::SimdOp::I32x4RelaxedTruncF32x4U: 1361 case wasm::SimdOp::I32x4RelaxedTruncF64x2SZero: 1362 case wasm::SimdOp::I32x4RelaxedTruncF64x2UZero: 1363 break; 1364 case wasm::SimdOp::I32x4TruncSatF64x2SZero: 1365 case wasm::SimdOp::I32x4TruncSatF64x2UZero: 1366 tempReg = tempSimd128(); 1367 break; 1368 default: 1369 MOZ_CRASH("Unary SimdOp not implemented"); 1370 } 1371 1372 LUse input = useRegisterAtStart(ins->input()); 1373 LWasmUnarySimd128* lir = new (alloc()) LWasmUnarySimd128(input, tempReg); 1374 define(lir, ins); 1375 #else 1376 MOZ_CRASH("No SIMD"); 1377 #endif 1378 } 1379 1380 void LIRGenerator::visitWasmReduceSimd128(MWasmReduceSimd128* ins) { 1381 #ifdef ENABLE_WASM_SIMD 1382 if (canEmitWasmReduceSimd128AtUses(ins)) { 1383 emitAtUses(ins); 1384 return; 1385 } 1386 1387 // Reductions (any_true, all_true, bitmask, extract_lane) uniformly prefer 1388 // useRegisterAtStart: 1389 // 1390 // - In most cases, the input type differs from the output type, so there's no 1391 // conflict and it doesn't really matter. 1392 // 1393 // - For extract_lane(0) on F32x4 and F64x2, input == output results in zero 1394 // code being generated. 1395 // 1396 // - For extract_lane(k > 0) on F32x4 and F64x2, allowing the input register 1397 // to be targeted lowers register pressure if it's the last use of the 1398 // input. 1399 1400 if (ins->type() == MIRType::Int64) { 1401 auto* lir = new (alloc()) 1402 LWasmReduceSimd128ToInt64(useRegisterAtStart(ins->input())); 1403 defineInt64(lir, ins); 1404 } else { 1405 LDefinition tempReg = LDefinition::BogusTemp(); 1406 switch (ins->simdOp()) { 1407 case wasm::SimdOp::I8x16Bitmask: 1408 case wasm::SimdOp::I16x8Bitmask: 1409 case wasm::SimdOp::I32x4Bitmask: 1410 case wasm::SimdOp::I64x2Bitmask: 1411 tempReg = tempSimd128(); 1412 break; 1413 default: 1414 break; 1415 } 1416 1417 // Ideally we would reuse the input register for floating extract_lane if 1418 // the lane is zero, but constraints in the register allocator require the 1419 // input and output register types to be the same. 1420 auto* lir = new (alloc()) 1421 LWasmReduceSimd128(useRegisterAtStart(ins->input()), tempReg); 1422 define(lir, ins); 1423 } 1424 #else 1425 MOZ_CRASH("No SIMD"); 1426 #endif 1427 } 1428 1429 void LIRGenerator::visitWasmLoadLaneSimd128(MWasmLoadLaneSimd128* ins) { 1430 #ifdef ENABLE_WASM_SIMD 1431 // On 64-bit systems, the base pointer can be 32 bits or 64 bits. Either way, 1432 // it fits in a GPR so we can ignore the Register/Register64 distinction here. 1433 1434 // Optimal allocation here reuses the value input for the output register 1435 // because codegen otherwise has to copy the input to the output; this is 1436 // because load-lane is implemented as load + replace-lane. Bug 1706106 may 1437 // change all of that, so leave it alone for now. 1438 LUse base = useRegisterAtStart(ins->base()); 1439 LUse inputUse = useRegisterAtStart(ins->value()); 1440 LAllocation memoryBase = 1441 ins->hasMemoryBase() ? LAllocation(useRegisterAtStart(ins->memoryBase())) 1442 : LGeneralReg(HeapReg); 1443 auto* lir = 1444 new (alloc()) LWasmLoadLaneSimd128(base, inputUse, memoryBase, temp()); 1445 define(lir, ins); 1446 #else 1447 MOZ_CRASH("No SIMD"); 1448 #endif 1449 } 1450 1451 void LIRGenerator::visitWasmStoreLaneSimd128(MWasmStoreLaneSimd128* ins) { 1452 #ifdef ENABLE_WASM_SIMD 1453 // See comment above about the base pointer. 1454 1455 LUse base = useRegisterAtStart(ins->base()); 1456 LUse input = useRegisterAtStart(ins->value()); 1457 LAllocation memoryBase = 1458 ins->hasMemoryBase() ? LAllocation(useRegisterAtStart(ins->memoryBase())) 1459 : LGeneralReg(HeapReg); 1460 auto* lir = 1461 new (alloc()) LWasmStoreLaneSimd128(base, input, memoryBase, temp()); 1462 add(lir, ins); 1463 #else 1464 MOZ_CRASH("No SIMD"); 1465 #endif 1466 }