LIR.cpp (23367B)
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/LIR.h" 8 9 #include "mozilla/ScopeExit.h" 10 11 #include <type_traits> 12 13 #include "jit/JitSpewer.h" 14 #include "jit/MIR-wasm.h" 15 #include "jit/MIR.h" 16 #include "jit/MIRGenerator.h" 17 #include "js/Printf.h" 18 #include "util/Unicode.h" 19 20 using namespace js; 21 using namespace js::jit; 22 23 const char* const js::jit::LIROpNames[] = { 24 #define OPNAME(op, ...) #op, 25 LIR_OPCODE_LIST(OPNAME) 26 #undef OPNAME 27 }; 28 29 LIRGraph::LIRGraph(MIRGraph* mir) 30 : constantPool_(mir->alloc()), 31 constantPoolMap_(mir->alloc()), 32 numVirtualRegisters_(0), 33 numInstructions_(1), // First id is 1. 34 localSlotsSize_(0), 35 argumentSlotCount_(0), 36 extraSafepointUses_(0), 37 mir_(*mir) {} 38 39 bool LIRGraph::addConstantToPool(const Value& v, uint32_t* index) { 40 ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v); 41 if (p) { 42 *index = p->value(); 43 return true; 44 } 45 *index = constantPool_.length(); 46 return constantPool_.append(v) && constantPoolMap_.add(p, v, *index); 47 } 48 49 #ifdef JS_JITSPEW 50 void LIRGraph::dump(GenericPrinter& out) { 51 for (size_t i = 0; i < numBlocks(); i++) { 52 getBlock(i)->dump(out); 53 out.printf("\n"); 54 } 55 } 56 57 void LIRGraph::dump() { 58 Fprinter out(stderr); 59 dump(out); 60 out.finish(); 61 } 62 #endif 63 64 LBlock::LBlock(MBasicBlock* from) 65 : block_(from), 66 entryMoveGroup_(nullptr), 67 exitMoveGroup_(nullptr), 68 isOutOfLine_(false) { 69 from->assignLir(this); 70 71 // If branch hinting is enabled, and this block is unlikely to be executed, 72 // it will be generated out of line. 73 if (from->info().branchHintingEnabled() && from->isUnlikelyFrequency()) { 74 isOutOfLine_ = true; 75 } 76 } 77 78 bool LBlock::init(TempAllocator& alloc) { 79 // Count the number of LPhis we'll need. 80 size_t numLPhis = 0; 81 for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) { 82 MPhi* phi = *i; 83 switch (phi->type()) { 84 case MIRType::Value: 85 numLPhis += BOX_PIECES; 86 break; 87 case MIRType::Int64: 88 numLPhis += INT64_PIECES; 89 break; 90 default: 91 numLPhis += 1; 92 break; 93 } 94 } 95 96 // Allocate space for the LPhis. 97 if (!phis_.init(alloc, numLPhis)) { 98 return false; 99 } 100 101 // For each MIR phi, set up LIR phis as appropriate. We'll fill in their 102 // operands on each incoming edge, and set their definitions at the start of 103 // their defining block. 104 size_t phiIndex = 0; 105 size_t numPreds = block_->numPredecessors(); 106 for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) { 107 MPhi* phi = *i; 108 MOZ_ASSERT(phi->numOperands() == numPreds); 109 110 int numPhis; 111 switch (phi->type()) { 112 case MIRType::Value: 113 numPhis = BOX_PIECES; 114 break; 115 case MIRType::Int64: 116 numPhis = INT64_PIECES; 117 break; 118 default: 119 numPhis = 1; 120 break; 121 } 122 for (int i = 0; i < numPhis; i++) { 123 LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds); 124 if (!inputs) { 125 return false; 126 } 127 128 void* addr = &phis_[phiIndex++]; 129 LPhi* lphi = new (addr) LPhi(phi, inputs); 130 lphi->setBlock(this); 131 } 132 } 133 return true; 134 } 135 136 const LInstruction* LBlock::firstInstructionWithId() const { 137 for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); 138 ++i) { 139 if (i->id()) { 140 return *i; 141 } 142 } 143 return 0; 144 } 145 146 LMoveGroup* LBlock::getEntryMoveGroup(TempAllocator& alloc) { 147 if (entryMoveGroup_) { 148 return entryMoveGroup_; 149 } 150 entryMoveGroup_ = LMoveGroup::New(alloc); 151 insertBefore(*begin(), entryMoveGroup_); 152 return entryMoveGroup_; 153 } 154 155 LMoveGroup* LBlock::getExitMoveGroup(TempAllocator& alloc) { 156 if (exitMoveGroup_) { 157 return exitMoveGroup_; 158 } 159 exitMoveGroup_ = LMoveGroup::New(alloc); 160 insertBefore(*rbegin(), exitMoveGroup_); 161 return exitMoveGroup_; 162 } 163 164 LBlock* LBlock::isMoveGroupsThenGoto() { 165 if (mir()->isLoopHeader()) { 166 return nullptr; 167 } 168 auto riter = rbegin(); 169 if (!riter->isGoto()) { 170 return nullptr; 171 } 172 riter++; 173 // This loop doesn't iterate much. Its highest trip-count for all of 174 // JetStream3 is 3. 175 while (riter != rend()) { 176 if (!(*riter)->isMoveGroup()) { 177 return nullptr; 178 } 179 riter++; 180 } 181 LGoto* ins = rbegin()->toGoto(); 182 MOZ_ASSERT(ins->numSuccessors() == 1); 183 return ins->getSuccessor(0)->lir(); 184 } 185 186 #ifdef JS_JITSPEW 187 void LBlock::dump(GenericPrinter& out) { 188 out.printf("block%u:\n", mir()->id()); 189 for (size_t i = 0; i < numPhis(); ++i) { 190 out.printf(" "); 191 getPhi(i)->dump(out); 192 out.printf("\n"); 193 } 194 for (LInstructionIterator iter = begin(); iter != end(); iter++) { 195 out.printf(" "); 196 iter->dump(out); 197 if (iter->safepoint()) { 198 out.printf(" SAFEPOINT(0x%p) ", iter->safepoint()); 199 } 200 out.printf("\n"); 201 } 202 } 203 204 void LBlock::dump() { 205 Fprinter out(stderr); 206 dump(out); 207 out.finish(); 208 } 209 #endif 210 211 static size_t TotalOperandCount(LRecoverInfo* recoverInfo) { 212 size_t accum = 0; 213 for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) { 214 if (!it->isRecoveredOnBailout()) { 215 accum++; 216 } 217 } 218 return accum; 219 } 220 221 LRecoverInfo::LRecoverInfo(TempAllocator& alloc) 222 : instructions_(alloc), recoverOffset_(INVALID_RECOVER_OFFSET) {} 223 224 LRecoverInfo* LRecoverInfo::New(MIRGenerator* gen, MResumePoint* mir) { 225 LRecoverInfo* recoverInfo = new (gen->alloc()) LRecoverInfo(gen->alloc()); 226 if (!recoverInfo || !recoverInfo->init(mir)) { 227 return nullptr; 228 } 229 230 JitSpew(JitSpew_IonSnapshots, "Generating LIR recover info %p from MIR (%p)", 231 (void*)recoverInfo, (void*)mir); 232 233 return recoverInfo; 234 } 235 236 // de-virtualise MResumePoint::getOperand calls. 237 template <typename Node> 238 bool LRecoverInfo::appendOperands(Node* ins) { 239 for (size_t i = 0, end = ins->numOperands(); i < end; i++) { 240 MDefinition* def = ins->getOperand(i); 241 242 // As there is no cycle in the data-flow (without MPhi), checking for 243 // isInWorkList implies that the definition is already in the 244 // instruction vector, and not processed by a caller of the current 245 // function. 246 if (def->isRecoveredOnBailout() && !def->isInWorklist()) { 247 if (!appendDefinition(def)) { 248 return false; 249 } 250 } 251 } 252 253 return true; 254 } 255 256 bool LRecoverInfo::appendDefinition(MDefinition* def) { 257 MOZ_ASSERT(def->isRecoveredOnBailout()); 258 def->setInWorklist(); 259 auto clearWorklistFlagOnFailure = 260 mozilla::MakeScopeExit([&] { def->setNotInWorklist(); }); 261 262 if (!appendOperands(def)) { 263 return false; 264 } 265 266 if (!instructions_.append(def)) { 267 return false; 268 } 269 270 clearWorklistFlagOnFailure.release(); 271 return true; 272 } 273 274 bool LRecoverInfo::appendResumePoint(MResumePoint* rp) { 275 // Stores should be recovered first. 276 if (!rp->storesEmpty()) { 277 hasSideEffects_ = true; 278 } 279 for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end; 280 ++iter) { 281 if (!appendDefinition(iter->operand)) { 282 return false; 283 } 284 } 285 286 if (rp->caller() && !appendResumePoint(rp->caller())) { 287 return false; 288 } 289 290 if (!appendOperands(rp)) { 291 return false; 292 } 293 294 return instructions_.append(rp); 295 } 296 297 bool LRecoverInfo::init(MResumePoint* rp) { 298 // Before exiting this function, remove temporary flags from all definitions 299 // added in the vector. 300 auto clearWorklistFlags = mozilla::MakeScopeExit([&] { 301 for (MNode** it = begin(); it != end(); it++) { 302 if (!(*it)->isDefinition()) { 303 continue; 304 } 305 (*it)->toDefinition()->setNotInWorklist(); 306 } 307 }); 308 309 // Sort operations in the order in which we need to restore the stack. This 310 // implies that outer frames, as well as operations needed to recover the 311 // current frame, are located before the current frame. The inner-most 312 // resume point should be the last element in the list. 313 if (!appendResumePoint(rp)) { 314 return false; 315 } 316 317 MOZ_ASSERT(mir() == rp); 318 return true; 319 } 320 321 LSnapshot::LSnapshot(LRecoverInfo* recoverInfo, BailoutKind kind) 322 : slots_(nullptr), 323 recoverInfo_(recoverInfo), 324 snapshotOffset_(INVALID_SNAPSHOT_OFFSET), 325 numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES), 326 bailoutKind_(kind) {} 327 328 bool LSnapshot::init(MIRGenerator* gen) { 329 slots_ = gen->allocate<LAllocation>(numSlots_); 330 return !!slots_; 331 } 332 333 LSnapshot* LSnapshot::New(MIRGenerator* gen, LRecoverInfo* recover, 334 BailoutKind kind) { 335 LSnapshot* snapshot = new (gen->alloc()) LSnapshot(recover, kind); 336 if (!snapshot || !snapshot->init(gen)) { 337 return nullptr; 338 } 339 340 JitSpew(JitSpew_IonSnapshots, "Generating LIR snapshot %p from recover (%p)", 341 (void*)snapshot, (void*)recover); 342 343 return snapshot; 344 } 345 346 void LSnapshot::rewriteRecoveredInput(LUse input) { 347 // Mark any operands to this snapshot with the same value as input as being 348 // equal to the instruction's result. 349 for (size_t i = 0; i < numEntries(); i++) { 350 if (getEntry(i)->isUse() && 351 getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) { 352 setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT)); 353 } 354 } 355 } 356 357 #ifdef JS_JITSPEW 358 void LNode::printName(GenericPrinter& out, Opcode op) { 359 static const char* const names[] = { 360 # define LIROP(x) #x, 361 LIR_OPCODE_LIST(LIROP) 362 # undef LIROP 363 }; 364 const char* name = names[uint32_t(op)]; 365 size_t len = strlen(name); 366 for (size_t i = 0; i < len; i++) { 367 out.printf("%c", unicode::ToLowerCase(name[i])); 368 } 369 } 370 371 void LNode::printName(GenericPrinter& out) { printName(out, op()); } 372 #endif 373 374 bool LAllocation::aliases(const LAllocation& other) const { 375 if (isFloatReg() && other.isFloatReg()) { 376 return toFloatReg()->reg().aliases(other.toFloatReg()->reg()); 377 } 378 return *this == other; 379 } 380 381 #ifdef JS_JITSPEW 382 static const char* DefTypeName(LDefinition::Type type) { 383 switch (type) { 384 case LDefinition::GENERAL: 385 return "g"; 386 case LDefinition::INT32: 387 return "i"; 388 case LDefinition::OBJECT: 389 return "o"; 390 case LDefinition::SLOTS: 391 return "s"; 392 case LDefinition::WASM_ANYREF: 393 return "wr"; 394 case LDefinition::WASM_STRUCT_DATA: 395 return "wsd"; 396 case LDefinition::WASM_ARRAY_DATA: 397 return "wad"; 398 case LDefinition::FLOAT32: 399 return "f"; 400 case LDefinition::DOUBLE: 401 return "d"; 402 case LDefinition::SIMD128: 403 return "simd128"; 404 case LDefinition::STACKRESULTS: 405 return "stackresults"; 406 # ifdef JS_NUNBOX32 407 case LDefinition::TYPE: 408 return "t"; 409 case LDefinition::PAYLOAD: 410 return "p"; 411 # else 412 case LDefinition::BOX: 413 return "x"; 414 # endif 415 } 416 MOZ_CRASH("Invalid type"); 417 } 418 419 UniqueChars LDefinition::toString() const { 420 AutoEnterOOMUnsafeRegion oomUnsafe; 421 422 UniqueChars buf; 423 if (isBogusTemp()) { 424 buf = JS_smprintf("bogus"); 425 } else { 426 buf = JS_smprintf("v%u<%s>", virtualRegister(), DefTypeName(type())); 427 if (buf) { 428 if (policy() == LDefinition::FIXED) { 429 buf = JS_sprintf_append(std::move(buf), ":%s", 430 output()->toString().get()); 431 } else if (policy() == LDefinition::MUST_REUSE_INPUT) { 432 buf = JS_sprintf_append(std::move(buf), ":tied(%u)", getReusedInput()); 433 } 434 } 435 } 436 437 if (!buf) { 438 oomUnsafe.crash("LDefinition::toString()"); 439 } 440 441 return buf; 442 } 443 444 static UniqueChars PrintUse(const LUse* use) { 445 switch (use->policy()) { 446 case LUse::REGISTER: 447 return JS_smprintf("v%u:R", use->virtualRegister()); 448 case LUse::FIXED: 449 return JS_smprintf("v%u:F:%s", use->virtualRegister(), 450 AnyRegister::FromCode(use->registerCode()).name()); 451 case LUse::ANY: 452 return JS_smprintf("v%u:A", use->virtualRegister()); 453 case LUse::KEEPALIVE: 454 return JS_smprintf("v%u:KA", use->virtualRegister()); 455 case LUse::STACK: 456 return JS_smprintf("v%u:S", use->virtualRegister()); 457 case LUse::RECOVERED_INPUT: 458 return JS_smprintf("v%u:RI", use->virtualRegister()); 459 default: 460 MOZ_CRASH("invalid use policy"); 461 } 462 } 463 464 UniqueChars LAllocation::toString() const { 465 AutoEnterOOMUnsafeRegion oomUnsafe; 466 467 UniqueChars buf; 468 if (isBogus()) { 469 buf = JS_smprintf("bogus"); 470 } else { 471 switch (kind()) { 472 case LAllocation::CONSTANT_VALUE: 473 case LAllocation::CONSTANT_INDEX: { 474 const MConstant* c = toConstant(); 475 switch (c->type()) { 476 case MIRType::Int32: 477 buf = JS_smprintf("%d", c->toInt32()); 478 break; 479 case MIRType::Int64: 480 buf = JS_smprintf("%" PRId64, c->toInt64()); 481 break; 482 case MIRType::IntPtr: 483 buf = JS_smprintf("%" PRIxPTR, c->toIntPtr()); 484 break; 485 case MIRType::String: 486 // If a JSContext is a available, output the actual string 487 if (JSContext* cx = TlsContext.get()) { 488 Sprinter spr(cx); 489 if (!spr.init()) { 490 oomUnsafe.crash("LAllocation::toString()"); 491 } 492 spr.putString(cx, c->toString()->unwrap()); 493 buf = spr.release(); 494 } else { 495 buf = JS_smprintf("string"); 496 } 497 break; 498 case MIRType::Symbol: 499 buf = JS_smprintf("sym"); 500 break; 501 case MIRType::Object: 502 case MIRType::Null: 503 buf = JS_smprintf("obj %p", c->toObjectOrNull()); 504 break; 505 case MIRType::Shape: 506 buf = JS_smprintf("shape"); 507 break; 508 default: 509 if (c->isTypeRepresentableAsDouble()) { 510 buf = JS_smprintf("%g", c->numberToDouble()); 511 } else { 512 buf = JS_smprintf("const"); 513 } 514 } 515 } break; 516 case LAllocation::GPR: 517 buf = JS_smprintf("%s", toGeneralReg()->reg().name()); 518 break; 519 case LAllocation::FPU: 520 buf = JS_smprintf("%s", toFloatReg()->reg().name()); 521 break; 522 case LAllocation::STACK_SLOT: 523 buf = JS_smprintf("stack:%u(%u)", toStackSlot()->slot(), 524 LStackSlot::ByteWidth(toStackSlot()->width())); 525 break; 526 case LAllocation::ARGUMENT_SLOT: 527 buf = JS_smprintf("arg:%u", toArgument()->index()); 528 break; 529 case LAllocation::STACK_AREA: 530 buf = JS_smprintf("stackarea:%u+%u", toStackArea()->base(), 531 toStackArea()->size()); 532 break; 533 case LAllocation::USE: 534 buf = PrintUse(toUse()); 535 break; 536 default: 537 MOZ_CRASH("what?"); 538 } 539 } 540 541 if (!buf) { 542 oomUnsafe.crash("LAllocation::toString()"); 543 } 544 545 return buf; 546 } 547 548 void LAllocation::dump() const { fprintf(stderr, "%s\n", toString().get()); } 549 550 void LDefinition::dump() const { fprintf(stderr, "%s\n", toString().get()); } 551 552 template <typename T> 553 static void PrintOperands(GenericPrinter& out, T* node) { 554 size_t numOperands = node->numOperands(); 555 556 for (size_t i = 0; i < numOperands; i++) { 557 out.printf(" (%s)", node->getOperand(i)->toString().get()); 558 if (i != numOperands - 1) { 559 out.printf(","); 560 } 561 } 562 } 563 564 void LNode::printOperands(GenericPrinter& out) { 565 if (isMoveGroup()) { 566 toMoveGroup()->printOperands(out); 567 return; 568 } 569 if (isInteger()) { 570 out.printf(" (%d)", toInteger()->i32()); 571 return; 572 } 573 if (isInteger64()) { 574 out.printf(" (%" PRId64 ")", toInteger64()->i64()); 575 return; 576 } 577 578 if (isPhi()) { 579 PrintOperands(out, toPhi()); 580 } else { 581 PrintOperands(out, toInstruction()); 582 } 583 } 584 #endif 585 586 void LInstruction::assignSnapshot(LSnapshot* snapshot) { 587 MOZ_ASSERT(!snapshot_); 588 snapshot_ = snapshot; 589 590 #ifdef JS_JITSPEW 591 if (JitSpewEnabled(JitSpew_IonSnapshots)) { 592 JitSpewHeader(JitSpew_IonSnapshots); 593 Fprinter& out = JitSpewPrinter(); 594 out.printf("Assigning snapshot %p to instruction %p (", (void*)snapshot, 595 (void*)this); 596 printName(out); 597 out.printf(")\n"); 598 } 599 #endif 600 } 601 602 #ifdef JS_JITSPEW 603 static size_t NumSuccessorsHelper(const LNode* ins) { return 0; } 604 605 template <size_t Succs, size_t Operands, size_t Temps> 606 static size_t NumSuccessorsHelper( 607 const LControlInstructionHelper<Succs, Operands, Temps>* ins) { 608 return Succs; 609 } 610 611 static size_t NumSuccessors(const LInstruction* ins) { 612 switch (ins->op()) { 613 default: 614 MOZ_CRASH("Unexpected LIR op"); 615 # define LIROP(x) \ 616 case LNode::Opcode::x: \ 617 return NumSuccessorsHelper(ins->to##x()); 618 LIR_OPCODE_LIST(LIROP) 619 # undef LIROP 620 } 621 } 622 623 static MBasicBlock* GetSuccessorHelper(const LNode* ins, size_t i) { 624 MOZ_CRASH("Unexpected instruction with successors"); 625 } 626 627 template <size_t Succs, size_t Operands, size_t Temps> 628 static MBasicBlock* GetSuccessorHelper( 629 const LControlInstructionHelper<Succs, Operands, Temps>* ins, size_t i) { 630 return ins->getSuccessor(i); 631 } 632 633 static MBasicBlock* GetSuccessor(const LInstruction* ins, size_t i) { 634 MOZ_ASSERT(i < NumSuccessors(ins)); 635 636 switch (ins->op()) { 637 default: 638 MOZ_CRASH("Unexpected LIR op"); 639 # define LIROP(x) \ 640 case LNode::Opcode::x: \ 641 return GetSuccessorHelper(ins->to##x(), i); 642 LIR_OPCODE_LIST(LIROP) 643 # undef LIROP 644 } 645 } 646 #endif 647 648 #ifdef JS_JITSPEW 649 void LNode::dump(GenericPrinter& out) { 650 if (numDefs() != 0) { 651 out.printf("{"); 652 for (size_t i = 0; i < numDefs(); i++) { 653 const LDefinition* def = 654 isPhi() ? toPhi()->getDef(i) : toInstruction()->getDef(i); 655 out.printf("%s", def->toString().get()); 656 if (i != numDefs() - 1) { 657 out.printf(", "); 658 } 659 } 660 out.printf("} <- "); 661 } 662 663 printName(out); 664 printOperands(out); 665 666 if (isInstruction()) { 667 LInstruction* ins = toInstruction(); 668 size_t numTemps = ins->numTemps(); 669 if (numTemps > 0) { 670 out.printf(" t=("); 671 for (size_t i = 0; i < numTemps; i++) { 672 out.printf("%s", ins->getTemp(i)->toString().get()); 673 if (i != numTemps - 1) { 674 out.printf(", "); 675 } 676 } 677 out.printf(")"); 678 } 679 680 size_t numSuccessors = NumSuccessors(ins); 681 if (numSuccessors > 0) { 682 out.printf(" s=("); 683 for (size_t i = 0; i < numSuccessors; i++) { 684 MBasicBlock* succ = GetSuccessor(ins, i); 685 out.printf("block %u", succ->id()); 686 if (i != numSuccessors - 1) { 687 out.printf(", "); 688 } 689 } 690 out.printf(")"); 691 } 692 } 693 } 694 695 void LNode::dump() { 696 Fprinter out(stderr); 697 dump(out); 698 out.printf("\n"); 699 out.finish(); 700 } 701 702 const char* LNode::getExtraName() const { 703 switch (op()) { 704 default: 705 MOZ_CRASH("Unexpected LIR op"); 706 # define LIROP(x) \ 707 case LNode::Opcode::x: \ 708 return to##x()->extraName(); 709 LIR_OPCODE_LIST(LIROP) 710 # undef LIROP 711 } 712 } 713 #endif 714 715 void LInstruction::initSafepoint(TempAllocator& alloc) { 716 MOZ_ASSERT(!safepoint_); 717 safepoint_ = new (alloc) LSafepoint(alloc); 718 MOZ_ASSERT(safepoint_); 719 } 720 721 bool LSafepoint::addGCAllocation(uint32_t vregId, LDefinition* def, 722 LAllocation a) { 723 switch (def->type()) { 724 case LDefinition::OBJECT: 725 return addGcPointer(a); 726 727 case LDefinition::SLOTS: 728 return addSlotsOrElementsPointer(a); 729 730 case LDefinition::WASM_ANYREF: 731 return addWasmAnyRef(a); 732 case LDefinition::WASM_STRUCT_DATA: 733 return addWasmStructDataPointer(a); 734 case LDefinition::WASM_ARRAY_DATA: 735 return addWasmArrayDataPointer(a); 736 737 #ifdef JS_NUNBOX32 738 case LDefinition::TYPE: 739 return addNunboxPart(/* isType = */ true, vregId, a); 740 741 case LDefinition::PAYLOAD: 742 return addNunboxPart(/* isType = */ false, vregId, a); 743 #else 744 case LDefinition::BOX: 745 return addBoxedValue(a); 746 #endif 747 748 case LDefinition::STACKRESULTS: { 749 MOZ_ASSERT(a.isStackArea()); 750 for (auto iter = a.toStackArea()->results(); iter; iter.next()) { 751 if (iter.isWasmAnyRef()) { 752 if (!addWasmAnyRef(iter.alloc())) { 753 return false; 754 } 755 } 756 } 757 return true; 758 } 759 760 case LDefinition::GENERAL: 761 case LDefinition::INT32: 762 case LDefinition::FLOAT32: 763 case LDefinition::DOUBLE: 764 case LDefinition::SIMD128: 765 break; 766 } 767 MOZ_CRASH("Bad register type"); 768 } 769 770 bool LMoveGroup::add(LAllocation from, LAllocation to, LDefinition::Type type) { 771 #ifdef DEBUG 772 MOZ_ASSERT(from != to); 773 for (size_t i = 0; i < moves_.length(); i++) { 774 MOZ_ASSERT(to != moves_[i].to()); 775 } 776 777 // Check that SIMD moves are aligned according to ABI requirements. 778 // clang-format off 779 # ifdef ENABLE_WASM_SIMD 780 // Alignment is not currently required for SIMD on x86/x64/arm64. See also 781 // CodeGeneratorShared::CodeGeneratorShared and in general everywhere 782 // SimdMemoryAignment is used. Likely, alignment requirements will return. 783 # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \ 784 defined(JS_CODEGEN_ARM64) 785 // No need for any check on x86/x64/arm64. 786 # else 787 # error "Need to consider SIMD alignment on this target." 788 // The following code may be of use if we need alignment checks on 789 // some future target. 790 //if (LDefinition(type).type() == LDefinition::SIMD128) { 791 // MOZ_ASSERT(from.isMemory() || from.isFloatReg()); 792 // if (from.isMemory()) { 793 // if (from.isArgument()) { 794 // MOZ_ASSERT(from.toArgument()->index() % SimdMemoryAlignment == 0); 795 // } else { 796 // MOZ_ASSERT(from.toStackSlot()->slot() % SimdMemoryAlignment == 0); 797 // } 798 // } 799 // MOZ_ASSERT(to.isMemory() || to.isFloatReg()); 800 // if (to.isMemory()) { 801 // if (to.isArgument()) { 802 // MOZ_ASSERT(to.toArgument()->index() % SimdMemoryAlignment == 0); 803 // } else { 804 // MOZ_ASSERT(to.toStackSlot()->slot() % SimdMemoryAlignment == 0); 805 // } 806 // } 807 //} 808 # endif 809 # endif 810 // clang-format on 811 812 #endif 813 return moves_.append(LMove(from, to, type)); 814 } 815 816 bool LMoveGroup::addAfter(LAllocation from, LAllocation to, 817 LDefinition::Type type) { 818 // Transform the operands to this move so that performing the result 819 // simultaneously with existing moves in the group will have the same 820 // effect as if the original move took place after the existing moves. 821 822 for (size_t i = 0; i < moves_.length(); i++) { 823 if (moves_[i].to() == from) { 824 from = moves_[i].from(); 825 break; 826 } 827 } 828 829 if (from == to) { 830 return true; 831 } 832 833 for (size_t i = 0; i < moves_.length(); i++) { 834 if (to == moves_[i].to()) { 835 moves_[i] = LMove(from, to, type); 836 return true; 837 } 838 } 839 840 return add(from, to, type); 841 } 842 843 #ifdef JS_JITSPEW 844 void LMoveGroup::printOperands(GenericPrinter& out) { 845 for (size_t i = 0; i < numMoves(); i++) { 846 const LMove& move = getMove(i); 847 out.printf(" [%s -> %s", move.from().toString().get(), 848 move.to().toString().get()); 849 out.printf(", %s", DefTypeName(move.type())); 850 out.printf("]"); 851 if (i != numMoves() - 1) { 852 out.printf(","); 853 } 854 } 855 } 856 #endif 857 858 #define LIROP(x) \ 859 static_assert(!std::is_polymorphic_v<L##x>, \ 860 "LIR instructions should not have virtual methods"); 861 LIR_OPCODE_LIST(LIROP) 862 #undef LIROP