MIR.cpp (244742B)
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/MIR.h" 8 9 #include "mozilla/EndianUtils.h" 10 #include "mozilla/FloatingPoint.h" 11 #include "mozilla/MathAlgorithms.h" 12 #include "mozilla/Maybe.h" 13 14 #include <algorithm> 15 #include <array> 16 #include <utility> 17 18 #include "jslibmath.h" 19 #include "jsmath.h" 20 #include "jsnum.h" 21 22 #include "builtin/RegExp.h" 23 #include "jit/AtomicOperations.h" 24 #include "jit/CompileInfo.h" 25 #include "jit/KnownClass.h" 26 #include "jit/MIR-wasm.h" 27 #include "jit/MIRGraph.h" 28 #include "jit/RangeAnalysis.h" 29 #include "jit/VMFunctions.h" 30 #include "jit/WarpBuilderShared.h" 31 #include "jit/WarpSnapshot.h" 32 #include "js/Conversions.h" 33 #include "js/experimental/JitInfo.h" // JSJitInfo, JSTypedMethodJitInfo 34 #include "js/ScalarType.h" // js::Scalar::Type 35 #include "util/Text.h" 36 #include "util/Unicode.h" 37 #include "vm/BigIntType.h" 38 #include "vm/Float16.h" 39 #include "vm/Iteration.h" // js::NativeIterator 40 #include "vm/PlainObject.h" // js::PlainObject 41 #include "vm/Uint8Clamped.h" 42 43 #include "vm/BytecodeUtil-inl.h" 44 #include "vm/JSAtomUtils-inl.h" // TypeName 45 46 using namespace js; 47 using namespace js::jit; 48 49 using JS::ToInt32; 50 51 using mozilla::IsFloat32Representable; 52 using mozilla::IsPowerOfTwo; 53 using mozilla::NumbersAreIdentical; 54 55 NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED 56 57 #ifdef DEBUG 58 size_t MUse::index() const { return consumer()->indexOf(this); } 59 #endif 60 61 template <size_t Op> 62 static void ConvertDefinitionToDouble(TempAllocator& alloc, MDefinition* def, 63 MInstruction* consumer) { 64 MInstruction* replace = MToDouble::New(alloc, def); 65 consumer->replaceOperand(Op, replace); 66 consumer->block()->insertBefore(consumer, replace); 67 } 68 69 template <size_t Arity, size_t Index> 70 static void ConvertOperandToDouble(MAryInstruction<Arity>* def, 71 TempAllocator& alloc) { 72 static_assert(Index < Arity); 73 auto* operand = def->getOperand(Index); 74 if (operand->type() == MIRType::Float32) { 75 ConvertDefinitionToDouble<Index>(alloc, operand, def); 76 } 77 } 78 79 template <size_t Arity, size_t... ISeq> 80 static void ConvertOperandsToDouble(MAryInstruction<Arity>* def, 81 TempAllocator& alloc, 82 std::index_sequence<ISeq...>) { 83 (ConvertOperandToDouble<Arity, ISeq>(def, alloc), ...); 84 } 85 86 template <size_t Arity> 87 static void ConvertOperandsToDouble(MAryInstruction<Arity>* def, 88 TempAllocator& alloc) { 89 ConvertOperandsToDouble<Arity>(def, alloc, std::make_index_sequence<Arity>{}); 90 } 91 92 template <size_t Arity, size_t... ISeq> 93 static bool AllOperandsCanProduceFloat32(MAryInstruction<Arity>* def, 94 std::index_sequence<ISeq...>) { 95 return (def->getOperand(ISeq)->canProduceFloat32() && ...); 96 } 97 98 template <size_t Arity> 99 static bool AllOperandsCanProduceFloat32(MAryInstruction<Arity>* def) { 100 return AllOperandsCanProduceFloat32<Arity>(def, 101 std::make_index_sequence<Arity>{}); 102 } 103 104 static bool CheckUsesAreFloat32Consumers(const MInstruction* ins) { 105 if (ins->isImplicitlyUsed()) { 106 return false; 107 } 108 bool allConsumerUses = true; 109 for (MUseDefIterator use(ins); allConsumerUses && use; use++) { 110 allConsumerUses &= use.def()->canConsumeFloat32(use.use()); 111 } 112 return allConsumerUses; 113 } 114 115 #ifdef JS_JITSPEW 116 static const char* OpcodeName(MDefinition::Opcode op) { 117 static const char* const names[] = { 118 # define NAME(x) #x, 119 MIR_OPCODE_LIST(NAME) 120 # undef NAME 121 }; 122 return names[unsigned(op)]; 123 } 124 125 void MDefinition::PrintOpcodeName(GenericPrinter& out, Opcode op) { 126 out.printf("%s", OpcodeName(op)); 127 } 128 129 uint32_t js::jit::GetMBasicBlockId(const MBasicBlock* block) { 130 return block->id(); 131 } 132 #endif 133 134 template <MIRType Type> 135 static auto ToIntConstant(MConstant* cst) { 136 MOZ_ASSERT(cst->type() == Type); 137 if constexpr (Type == MIRType::Int32) { 138 return cst->toInt32(); 139 } else if constexpr (Type == MIRType::Int64) { 140 return cst->toInt64(); 141 } else if constexpr (Type == MIRType::IntPtr) { 142 return cst->toIntPtr(); 143 } 144 } 145 146 template <MIRType Type, typename IntT> 147 static MConstant* NewIntConstant(TempAllocator& alloc, IntT i) { 148 if constexpr (Type == MIRType::Int32) { 149 static_assert(std::is_same_v<IntT, int32_t>); 150 return MConstant::NewInt32(alloc, i); 151 } else if constexpr (Type == MIRType::Int64) { 152 static_assert(std::is_same_v<IntT, int64_t>); 153 return MConstant::NewInt64(alloc, i); 154 } else if constexpr (Type == MIRType::IntPtr) { 155 static_assert(std::is_same_v<IntT, intptr_t>); 156 return MConstant::NewIntPtr(alloc, i); 157 } 158 } 159 160 template <MIRType Type> 161 static MConstant* EvaluateIntConstantOperands(TempAllocator& alloc, 162 MBinaryInstruction* ins) { 163 MDefinition* left = ins->lhs(); 164 MDefinition* right = ins->rhs(); 165 166 if (!left->isConstant() || !right->isConstant()) { 167 return nullptr; 168 } 169 170 using IntT = decltype(ToIntConstant<Type>(nullptr)); 171 using UnsigedInt = std::make_unsigned_t<IntT>; 172 173 // Right-hand side operand of shift must be non-negative and be less-than the 174 // number of bits in the left-hand side operand. Otherwise the behavior is 175 // undefined. 176 static constexpr IntT shiftMask = (sizeof(IntT) * CHAR_BIT) - 1; 177 178 IntT lhs = ToIntConstant<Type>(left->toConstant()); 179 IntT rhs = ToIntConstant<Type>(right->toConstant()); 180 IntT ret; 181 182 switch (ins->op()) { 183 case MDefinition::Opcode::BitAnd: 184 case MDefinition::Opcode::BigIntPtrBitAnd: 185 ret = lhs & rhs; 186 break; 187 case MDefinition::Opcode::BitOr: 188 case MDefinition::Opcode::BigIntPtrBitOr: 189 ret = lhs | rhs; 190 break; 191 case MDefinition::Opcode::BitXor: 192 case MDefinition::Opcode::BigIntPtrBitXor: 193 ret = lhs ^ rhs; 194 break; 195 case MDefinition::Opcode::Lsh: 196 // Left-hand side operand must be non-negative, otherwise the behavior is 197 // undefined. Cast to unsigned to ensure the behavior is always defined. 198 // 199 // Note: Cast to unsigned is no longer needed when compiling to C++20. 200 ret = UnsigedInt(lhs) << (rhs & shiftMask); 201 break; 202 case MDefinition::Opcode::Rsh: 203 // The result is implementation-defined if the left-hand side operand is 204 // negative. Most implementations perform an arithmetic right-shift, which 205 // we rely on here. 206 // 207 // Note: Guaranteed to be an arithmetic right-shift in C++20. 208 ret = lhs >> (rhs & shiftMask); 209 break; 210 case MDefinition::Opcode::Ursh: 211 // Decline folding if the output doesn't fit into a signed result and 212 // bailouts are disabled. (Wasm has bailouts disabled.) 213 if (lhs < 0 && rhs == 0 && !ins->toUrsh()->bailoutsDisabled()) { 214 return nullptr; 215 } 216 ret = UnsigedInt(lhs) >> (UnsigedInt(rhs) & shiftMask); 217 break; 218 case MDefinition::Opcode::BigIntPtrLsh: 219 case MDefinition::Opcode::BigIntPtrRsh: { 220 // BigIntPtr shifts are special: 221 // 1. Excess shift amounts produce BigInt larger than IntPtr. 222 // 2. Negative shifts reverse the shift direction. 223 224 // Decline folding for excess shift amounts. 225 UnsigedInt shift = mozilla::Abs(rhs); 226 if ((shift & shiftMask) != shift) { 227 return nullptr; 228 } 229 230 bool isLsh = (ins->isBigIntPtrLsh() && rhs >= 0) || 231 (ins->isBigIntPtrRsh() && rhs < 0); 232 if (isLsh) { 233 ret = UnsigedInt(lhs) << shift; 234 } else { 235 ret = lhs >> shift; 236 } 237 break; 238 } 239 case MDefinition::Opcode::Add: 240 case MDefinition::Opcode::BigIntPtrAdd: { 241 auto checked = mozilla::CheckedInt<IntT>(lhs) + rhs; 242 if (!checked.isValid()) { 243 return nullptr; 244 } 245 ret = checked.value(); 246 break; 247 } 248 case MDefinition::Opcode::Sub: 249 case MDefinition::Opcode::BigIntPtrSub: { 250 auto checked = mozilla::CheckedInt<IntT>(lhs) - rhs; 251 if (!checked.isValid()) { 252 return nullptr; 253 } 254 ret = checked.value(); 255 break; 256 } 257 case MDefinition::Opcode::Mul: 258 case MDefinition::Opcode::BigIntPtrMul: { 259 auto checked = mozilla::CheckedInt<IntT>(lhs) * rhs; 260 if (!checked.isValid()) { 261 return nullptr; 262 } 263 ret = checked.value(); 264 break; 265 } 266 case MDefinition::Opcode::Div: { 267 if (ins->toDiv()->isUnsigned()) { 268 auto checked = 269 mozilla::CheckedInt<UnsigedInt>(UnsigedInt(lhs)) / UnsigedInt(rhs); 270 if (!checked.isValid()) { 271 return nullptr; 272 } 273 ret = IntT(checked.value()); 274 break; 275 } 276 [[fallthrough]]; 277 } 278 case MDefinition::Opcode::BigIntPtrDiv: { 279 auto checked = mozilla::CheckedInt<IntT>(lhs) / rhs; 280 if (!checked.isValid()) { 281 return nullptr; 282 } 283 ret = checked.value(); 284 285 // Decline folding if the numerator isn't evenly divisible by the 286 // denominator. Only applies for non-truncating int32 division. 287 if constexpr (Type == MIRType::Int32) { 288 if (ret * rhs != lhs && !ins->toDiv()->isTruncated()) { 289 return nullptr; 290 } 291 } 292 break; 293 } 294 case MDefinition::Opcode::Mod: { 295 if (ins->toMod()->isUnsigned()) { 296 auto checked = 297 mozilla::CheckedInt<UnsigedInt>(UnsigedInt(lhs)) % UnsigedInt(rhs); 298 if (!checked.isValid()) { 299 return nullptr; 300 } 301 ret = IntT(checked.value()); 302 break; 303 } 304 [[fallthrough]]; 305 } 306 case MDefinition::Opcode::BigIntPtrMod: { 307 auto checked = mozilla::CheckedInt<IntT>(lhs) % rhs; 308 if (!checked.isValid()) { 309 return nullptr; 310 } 311 ret = checked.value(); 312 313 // Decline folding if the result is negative zero. Only applies for 314 // non-truncating int32 remainder. 315 if constexpr (Type == MIRType::Int32) { 316 if (ret == 0 && lhs < 0 && !ins->toMod()->isTruncated()) { 317 return nullptr; 318 } 319 } 320 break; 321 } 322 default: 323 MOZ_CRASH("NYI"); 324 } 325 326 return NewIntConstant<Type>(alloc, ret); 327 } 328 329 static MConstant* EvaluateInt32ConstantOperands(TempAllocator& alloc, 330 MBinaryInstruction* ins) { 331 return EvaluateIntConstantOperands<MIRType::Int32>(alloc, ins); 332 } 333 334 static MConstant* EvaluateInt64ConstantOperands(TempAllocator& alloc, 335 MBinaryInstruction* ins) { 336 return EvaluateIntConstantOperands<MIRType::Int64>(alloc, ins); 337 } 338 339 static MConstant* EvaluateIntPtrConstantOperands(TempAllocator& alloc, 340 MBinaryInstruction* ins) { 341 return EvaluateIntConstantOperands<MIRType::IntPtr>(alloc, ins); 342 } 343 344 static MConstant* EvaluateConstantOperands(TempAllocator& alloc, 345 MBinaryInstruction* ins) { 346 MOZ_ASSERT(IsTypeRepresentableAsDouble(ins->type())); 347 348 if (ins->type() == MIRType::Int32) { 349 return EvaluateInt32ConstantOperands(alloc, ins); 350 } 351 352 MDefinition* left = ins->lhs(); 353 MDefinition* right = ins->rhs(); 354 355 MOZ_ASSERT(IsFloatingPointType(left->type())); 356 MOZ_ASSERT(IsFloatingPointType(right->type())); 357 358 if (!left->isConstant() || !right->isConstant()) { 359 return nullptr; 360 } 361 362 double lhs = left->toConstant()->numberToDouble(); 363 double rhs = right->toConstant()->numberToDouble(); 364 double ret; 365 366 switch (ins->op()) { 367 case MDefinition::Opcode::Add: 368 ret = lhs + rhs; 369 break; 370 case MDefinition::Opcode::Sub: 371 ret = lhs - rhs; 372 break; 373 case MDefinition::Opcode::Mul: 374 ret = lhs * rhs; 375 break; 376 case MDefinition::Opcode::Div: 377 ret = NumberDiv(lhs, rhs); 378 break; 379 case MDefinition::Opcode::Mod: 380 ret = NumberMod(lhs, rhs); 381 break; 382 default: 383 MOZ_CRASH("NYI"); 384 } 385 386 if (ins->type() == MIRType::Float32) { 387 return MConstant::NewFloat32(alloc, float(ret)); 388 } 389 MOZ_ASSERT(ins->type() == MIRType::Double); 390 return MConstant::New(alloc, DoubleValue(ret)); 391 } 392 393 static MConstant* EvaluateConstantNaNOperand(MBinaryInstruction* ins) { 394 auto* left = ins->lhs(); 395 auto* right = ins->rhs(); 396 397 MOZ_ASSERT(IsTypeRepresentableAsDouble(left->type())); 398 MOZ_ASSERT(IsTypeRepresentableAsDouble(right->type())); 399 MOZ_ASSERT(left->type() == ins->type()); 400 MOZ_ASSERT(right->type() == ins->type()); 401 402 // Don't fold NaN if we can't return a floating point type. 403 if (!IsFloatingPointType(ins->type())) { 404 return nullptr; 405 } 406 407 MOZ_ASSERT(!left->isConstant() || !right->isConstant(), 408 "EvaluateConstantOperands should have handled this case"); 409 410 // One operand must be a constant NaN. 411 MConstant* cst; 412 if (left->isConstant()) { 413 cst = left->toConstant(); 414 } else if (right->isConstant()) { 415 cst = right->toConstant(); 416 } else { 417 return nullptr; 418 } 419 if (!std::isnan(cst->numberToDouble())) { 420 return nullptr; 421 } 422 423 // Fold to constant NaN. 424 return cst; 425 } 426 427 static MMul* EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) { 428 // we should fold only when it is a floating point operation 429 if (!IsFloatingPointType(ins->type())) { 430 return nullptr; 431 } 432 433 MDefinition* left = ins->getOperand(0); 434 MDefinition* right = ins->getOperand(1); 435 436 if (!right->isConstant()) { 437 return nullptr; 438 } 439 440 int32_t num; 441 if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num)) { 442 return nullptr; 443 } 444 445 // check if rhs is a power of two or zero 446 if (num != 0 && !mozilla::IsPowerOfTwo(mozilla::Abs(num))) { 447 return nullptr; 448 } 449 450 double ret = 1.0 / double(num); 451 452 MConstant* foldedRhs; 453 if (ins->type() == MIRType::Float32) { 454 foldedRhs = MConstant::NewFloat32(alloc, ret); 455 } else { 456 foldedRhs = MConstant::NewDouble(alloc, ret); 457 } 458 459 MOZ_ASSERT(foldedRhs->type() == ins->type()); 460 ins->block()->insertBefore(ins, foldedRhs); 461 462 MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type()); 463 mul->setMustPreserveNaN(ins->mustPreserveNaN()); 464 return mul; 465 } 466 467 #ifdef JS_JITSPEW 468 const char* MDefinition::opName() const { return OpcodeName(op()); } 469 470 void MDefinition::printName(GenericPrinter& out) const { 471 PrintOpcodeName(out, op()); 472 out.printf("#%u", id()); 473 } 474 #endif 475 476 HashNumber MDefinition::valueHash() const { 477 HashNumber out = HashNumber(op()); 478 for (size_t i = 0, e = numOperands(); i < e; i++) { 479 out = addU32ToHash(out, getOperand(i)->id()); 480 } 481 if (MDefinition* dep = dependency()) { 482 out = addU32ToHash(out, dep->id()); 483 } 484 return out; 485 } 486 487 HashNumber MNullaryInstruction::valueHash() const { 488 HashNumber hash = HashNumber(op()); 489 if (MDefinition* dep = dependency()) { 490 hash = addU32ToHash(hash, dep->id()); 491 } 492 MOZ_ASSERT(hash == MDefinition::valueHash()); 493 return hash; 494 } 495 496 HashNumber MUnaryInstruction::valueHash() const { 497 HashNumber hash = HashNumber(op()); 498 hash = addU32ToHash(hash, getOperand(0)->id()); 499 if (MDefinition* dep = dependency()) { 500 hash = addU32ToHash(hash, dep->id()); 501 } 502 MOZ_ASSERT(hash == MDefinition::valueHash()); 503 return hash; 504 } 505 506 HashNumber MBinaryInstruction::valueHash() const { 507 HashNumber hash = HashNumber(op()); 508 hash = addU32ToHash(hash, getOperand(0)->id()); 509 hash = addU32ToHash(hash, getOperand(1)->id()); 510 if (MDefinition* dep = dependency()) { 511 hash = addU32ToHash(hash, dep->id()); 512 } 513 MOZ_ASSERT(hash == MDefinition::valueHash()); 514 return hash; 515 } 516 517 HashNumber MTernaryInstruction::valueHash() const { 518 HashNumber hash = HashNumber(op()); 519 hash = addU32ToHash(hash, getOperand(0)->id()); 520 hash = addU32ToHash(hash, getOperand(1)->id()); 521 hash = addU32ToHash(hash, getOperand(2)->id()); 522 if (MDefinition* dep = dependency()) { 523 hash = addU32ToHash(hash, dep->id()); 524 } 525 MOZ_ASSERT(hash == MDefinition::valueHash()); 526 return hash; 527 } 528 529 HashNumber MQuaternaryInstruction::valueHash() const { 530 HashNumber hash = HashNumber(op()); 531 hash = addU32ToHash(hash, getOperand(0)->id()); 532 hash = addU32ToHash(hash, getOperand(1)->id()); 533 hash = addU32ToHash(hash, getOperand(2)->id()); 534 hash = addU32ToHash(hash, getOperand(3)->id()); 535 if (MDefinition* dep = dependency()) { 536 hash = addU32ToHash(hash, dep->id()); 537 } 538 MOZ_ASSERT(hash == MDefinition::valueHash()); 539 return hash; 540 } 541 542 HashNumber MQuinaryInstruction::valueHash() const { 543 HashNumber hash = HashNumber(op()); 544 hash = addU32ToHash(hash, getOperand(0)->id()); 545 hash = addU32ToHash(hash, getOperand(1)->id()); 546 hash = addU32ToHash(hash, getOperand(2)->id()); 547 hash = addU32ToHash(hash, getOperand(3)->id()); 548 hash = addU32ToHash(hash, getOperand(4)->id()); 549 if (MDefinition* dep = dependency()) { 550 hash = addU32ToHash(hash, dep->id()); 551 } 552 MOZ_ASSERT(hash == MDefinition::valueHash()); 553 return hash; 554 } 555 556 const MDefinition* MDefinition::skipObjectGuards() const { 557 const MDefinition* result = this; 558 // These instructions don't modify the object and just guard specific 559 // properties. 560 while (true) { 561 if (result->isGuardShape()) { 562 result = result->toGuardShape()->object(); 563 continue; 564 } 565 if (result->isGuardShapeList()) { 566 result = result->toGuardShapeList()->object(); 567 continue; 568 } 569 if (result->isGuardMultipleShapes()) { 570 result = result->toGuardMultipleShapes()->object(); 571 continue; 572 } 573 if (result->isGuardShapeListToOffset()) { 574 result = result->toGuardShapeListToOffset()->object(); 575 continue; 576 } 577 if (result->isGuardMultipleShapesToOffset()) { 578 result = result->toGuardMultipleShapesToOffset()->object(); 579 continue; 580 } 581 if (result->isGuardNullProto()) { 582 result = result->toGuardNullProto()->object(); 583 continue; 584 } 585 if (result->isGuardProto()) { 586 result = result->toGuardProto()->object(); 587 continue; 588 } 589 590 break; 591 } 592 593 return result; 594 } 595 596 bool MDefinition::congruentIfOperandsEqual(const MDefinition* ins) const { 597 if (op() != ins->op()) { 598 return false; 599 } 600 601 if (type() != ins->type()) { 602 return false; 603 } 604 605 if (isEffectful() || ins->isEffectful()) { 606 return false; 607 } 608 609 if (numOperands() != ins->numOperands()) { 610 return false; 611 } 612 613 for (size_t i = 0, e = numOperands(); i < e; i++) { 614 if (getOperand(i) != ins->getOperand(i)) { 615 return false; 616 } 617 } 618 619 return true; 620 } 621 622 MDefinition* MDefinition::foldsTo(TempAllocator& alloc) { 623 // In the default case, there are no constants to fold. 624 return this; 625 } 626 627 MDefinition* MInstruction::foldsToStore(TempAllocator& alloc) { 628 if (!dependency()) { 629 return nullptr; 630 } 631 632 MDefinition* store = dependency(); 633 if (mightAlias(store) != AliasType::MustAlias) { 634 return nullptr; 635 } 636 637 if (!store->block()->dominates(block())) { 638 return nullptr; 639 } 640 641 MDefinition* value; 642 switch (store->op()) { 643 case Opcode::StoreFixedSlot: 644 value = store->toStoreFixedSlot()->value(); 645 break; 646 case Opcode::StoreDynamicSlot: 647 value = store->toStoreDynamicSlot()->value(); 648 break; 649 case Opcode::StoreElement: 650 value = store->toStoreElement()->value(); 651 break; 652 default: 653 MOZ_CRASH("unknown store"); 654 } 655 656 // If the type are matching then we return the value which is used as 657 // argument of the store. 658 if (value->type() != type()) { 659 // If we expect to read a type which is more generic than the type seen 660 // by the store, then we box the value used by the store. 661 if (type() != MIRType::Value) { 662 return nullptr; 663 } 664 665 MOZ_ASSERT(value->type() < MIRType::Value); 666 MBox* box = MBox::New(alloc, value); 667 value = box; 668 } 669 670 return value; 671 } 672 673 void MDefinition::analyzeEdgeCasesForward() {} 674 675 void MDefinition::analyzeEdgeCasesBackward() {} 676 677 void MInstruction::setResumePoint(MResumePoint* resumePoint) { 678 MOZ_ASSERT(!resumePoint_); 679 resumePoint_ = resumePoint; 680 resumePoint_->setInstruction(this); 681 } 682 683 void MInstruction::stealResumePoint(MInstruction* other) { 684 MResumePoint* resumePoint = other->resumePoint_; 685 other->resumePoint_ = nullptr; 686 687 resumePoint->resetInstruction(); 688 setResumePoint(resumePoint); 689 } 690 691 void MInstruction::moveResumePointAsEntry() { 692 MOZ_ASSERT(isNop()); 693 block()->clearEntryResumePoint(); 694 block()->setEntryResumePoint(resumePoint_); 695 resumePoint_->resetInstruction(); 696 resumePoint_ = nullptr; 697 } 698 699 void MInstruction::clearResumePoint() { 700 resumePoint_->resetInstruction(); 701 block()->discardPreAllocatedResumePoint(resumePoint_); 702 resumePoint_ = nullptr; 703 } 704 705 MDefinition* MTest::foldsDoubleNegation(TempAllocator& alloc) { 706 MDefinition* op = getOperand(0); 707 708 if (op->isNot()) { 709 // If the operand of the Not is itself a Not, they cancel out. 710 MDefinition* opop = op->getOperand(0); 711 if (opop->isNot()) { 712 return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse()); 713 } 714 return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue()); 715 } 716 return nullptr; 717 } 718 719 MDefinition* MTest::foldsConstant(TempAllocator& alloc) { 720 MDefinition* op = getOperand(0); 721 if (MConstant* opConst = op->maybeConstantValue()) { 722 bool b; 723 if (opConst->valueToBoolean(&b)) { 724 return MGoto::New(alloc, b ? ifTrue() : ifFalse()); 725 } 726 } 727 return nullptr; 728 } 729 730 MDefinition* MTest::foldsTypes(TempAllocator& alloc) { 731 MDefinition* op = getOperand(0); 732 733 switch (op->type()) { 734 case MIRType::Undefined: 735 case MIRType::Null: 736 return MGoto::New(alloc, ifFalse()); 737 case MIRType::Symbol: 738 return MGoto::New(alloc, ifTrue()); 739 default: 740 break; 741 } 742 return nullptr; 743 } 744 745 class UsesIterator { 746 MDefinition* def_; 747 748 public: 749 explicit UsesIterator(MDefinition* def) : def_(def) {} 750 auto begin() const { return def_->usesBegin(); } 751 auto end() const { return def_->usesEnd(); } 752 }; 753 754 static bool AllInstructionsDeadIfUnused(MBasicBlock* block) { 755 for (auto* ins : *block) { 756 // Skip trivial instructions. 757 if (ins->isNop() || ins->isGoto()) { 758 continue; 759 } 760 761 // All uses must be within the current block. 762 for (auto* use : UsesIterator(ins)) { 763 if (use->consumer()->block() != block) { 764 return false; 765 } 766 } 767 768 // All instructions within this block must be dead if unused. 769 if (!DeadIfUnused(ins)) { 770 return false; 771 } 772 } 773 return true; 774 } 775 776 MDefinition* MTest::foldsNeedlessControlFlow(TempAllocator& alloc) { 777 // All instructions within both successors need be dead if unused. 778 if (!AllInstructionsDeadIfUnused(ifTrue()) || 779 !AllInstructionsDeadIfUnused(ifFalse())) { 780 return nullptr; 781 } 782 783 // Both successors must have the same target successor. 784 if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1) { 785 return nullptr; 786 } 787 if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0)) { 788 return nullptr; 789 } 790 791 // The target successor's phis must be redundant. Redundant phis should have 792 // been removed in an earlier pass, so only check if any phis are present, 793 // which is a stronger condition. 794 if (ifTrue()->successorWithPhis()) { 795 return nullptr; 796 } 797 798 return MGoto::New(alloc, ifTrue()); 799 } 800 801 // If a test is dominated by either the true or false path of a previous test of 802 // the same condition, then the test is redundant and can be converted into a 803 // goto true or goto false, respectively. 804 MDefinition* MTest::foldsRedundantTest(TempAllocator& alloc) { 805 MBasicBlock* myBlock = this->block(); 806 MDefinition* originalInput = getOperand(0); 807 808 // Handle single and double negatives. This ensures that we do not miss a 809 // folding opportunity due to a condition being inverted. 810 MDefinition* newInput = input(); 811 bool inverted = false; 812 if (originalInput->isNot()) { 813 newInput = originalInput->toNot()->input(); 814 inverted = true; 815 if (originalInput->toNot()->input()->isNot()) { 816 newInput = originalInput->toNot()->input()->toNot()->input(); 817 inverted = false; 818 } 819 } 820 821 // The specific order of traversal does not matter. If there are multiple 822 // dominating redundant tests, they will either agree on direction (in which 823 // case we will prune the same way regardless of order), or they will 824 // disagree, in which case we will eventually be marked entirely dead by the 825 // folding of the redundant parent. 826 for (MUseIterator i(newInput->usesBegin()), e(newInput->usesEnd()); i != e; 827 ++i) { 828 if (!i->consumer()->isDefinition()) { 829 continue; 830 } 831 if (!i->consumer()->toDefinition()->isTest()) { 832 continue; 833 } 834 MTest* otherTest = i->consumer()->toDefinition()->toTest(); 835 if (otherTest == this) { 836 continue; 837 } 838 839 if (otherTest->ifFalse()->dominates(myBlock)) { 840 // This test cannot be true, so fold to a goto false. 841 return MGoto::New(alloc, inverted ? ifTrue() : ifFalse()); 842 } 843 if (otherTest->ifTrue()->dominates(myBlock)) { 844 // This test cannot be false, so fold to a goto true. 845 return MGoto::New(alloc, inverted ? ifFalse() : ifTrue()); 846 } 847 } 848 849 return nullptr; 850 } 851 852 MDefinition* MTest::foldsTo(TempAllocator& alloc) { 853 if (MDefinition* def = foldsRedundantTest(alloc)) { 854 return def; 855 } 856 857 if (MDefinition* def = foldsDoubleNegation(alloc)) { 858 return def; 859 } 860 861 if (MDefinition* def = foldsConstant(alloc)) { 862 return def; 863 } 864 865 if (MDefinition* def = foldsTypes(alloc)) { 866 return def; 867 } 868 869 if (MDefinition* def = foldsNeedlessControlFlow(alloc)) { 870 return def; 871 } 872 873 return this; 874 } 875 876 AliasSet MThrow::getAliasSet() const { 877 return AliasSet::Store(AliasSet::ExceptionState); 878 } 879 880 AliasSet MThrowWithStack::getAliasSet() const { 881 return AliasSet::Store(AliasSet::ExceptionState); 882 } 883 884 AliasSet MNewArrayDynamicLength::getAliasSet() const { 885 return AliasSet::Store(AliasSet::ExceptionState); 886 } 887 888 AliasSet MNewTypedArrayDynamicLength::getAliasSet() const { 889 return AliasSet::Store(AliasSet::ExceptionState); 890 } 891 892 #ifdef JS_JITSPEW 893 void MDefinition::printOpcode(GenericPrinter& out) const { 894 PrintOpcodeName(out, op()); 895 if (numOperands() > 0) { 896 out.printf(" <- "); 897 } 898 for (size_t j = 0, e = numOperands(); j < e; j++) { 899 if (j > 0) { 900 out.printf(", "); 901 } 902 if (getUseFor(j)->hasProducer()) { 903 getOperand(j)->printName(out); 904 } else { 905 out.printf("(null)"); 906 } 907 } 908 } 909 910 void MDefinition::dump(GenericPrinter& out) const { 911 printName(out); 912 out.printf(":%s", StringFromMIRType(type())); 913 out.printf(" = "); 914 printOpcode(out); 915 out.printf("\n"); 916 917 if (isInstruction()) { 918 if (MResumePoint* resume = toInstruction()->resumePoint()) { 919 resume->dump(out); 920 } 921 } 922 } 923 924 void MDefinition::dump() const { 925 Fprinter out(stderr); 926 dump(out); 927 out.finish(); 928 } 929 930 void MDefinition::dumpLocation(GenericPrinter& out) const { 931 MResumePoint* rp = nullptr; 932 const char* linkWord = nullptr; 933 if (isInstruction() && toInstruction()->resumePoint()) { 934 rp = toInstruction()->resumePoint(); 935 linkWord = "at"; 936 } else { 937 rp = block()->entryResumePoint(); 938 linkWord = "after"; 939 } 940 941 while (rp) { 942 JSScript* script = rp->block()->info().script(); 943 uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc()); 944 out.printf(" %s %s:%u\n", linkWord, script->filename(), lineno); 945 rp = rp->caller(); 946 linkWord = "in"; 947 } 948 } 949 950 void MDefinition::dumpLocation() const { 951 Fprinter out(stderr); 952 dumpLocation(out); 953 out.finish(); 954 } 955 #endif 956 957 #if defined(DEBUG) || defined(JS_JITSPEW) 958 size_t MDefinition::useCount() const { 959 size_t count = 0; 960 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 961 count++; 962 } 963 return count; 964 } 965 966 size_t MDefinition::defUseCount() const { 967 size_t count = 0; 968 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 969 if ((*i)->consumer()->isDefinition()) { 970 count++; 971 } 972 } 973 return count; 974 } 975 #endif 976 977 bool MDefinition::hasOneUse() const { 978 MUseIterator i(uses_.begin()); 979 if (i == uses_.end()) { 980 return false; 981 } 982 i++; 983 return i == uses_.end(); 984 } 985 986 bool MDefinition::hasOneDefUse() const { 987 bool hasOneDefUse = false; 988 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 989 if (!(*i)->consumer()->isDefinition()) { 990 continue; 991 } 992 993 // We already have a definition use. So 1+ 994 if (hasOneDefUse) { 995 return false; 996 } 997 998 // We saw one definition. Loop to test if there is another. 999 hasOneDefUse = true; 1000 } 1001 1002 return hasOneDefUse; 1003 } 1004 1005 bool MDefinition::hasOneLiveDefUse() const { 1006 bool hasOneDefUse = false; 1007 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 1008 if (!(*i)->consumer()->isDefinition()) { 1009 continue; 1010 } 1011 1012 MDefinition* def = (*i)->consumer()->toDefinition(); 1013 if (def->isRecoveredOnBailout()) { 1014 continue; 1015 } 1016 1017 // We already have a definition use. So 1+ 1018 if (hasOneDefUse) { 1019 return false; 1020 } 1021 1022 // We saw one definition. Loop to test if there is another. 1023 hasOneDefUse = true; 1024 } 1025 1026 return hasOneDefUse; 1027 } 1028 1029 bool MDefinition::hasDefUses() const { 1030 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 1031 if ((*i)->consumer()->isDefinition()) { 1032 return true; 1033 } 1034 } 1035 1036 return false; 1037 } 1038 1039 bool MDefinition::hasLiveDefUses() const { 1040 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { 1041 MNode* ins = (*i)->consumer(); 1042 if (ins->isDefinition()) { 1043 if (!ins->toDefinition()->isRecoveredOnBailout()) { 1044 return true; 1045 } 1046 } else { 1047 MOZ_ASSERT(ins->isResumePoint()); 1048 if (!ins->toResumePoint()->isRecoverableOperand(*i)) { 1049 return true; 1050 } 1051 } 1052 } 1053 return false; 1054 } 1055 1056 MDefinition* MDefinition::maybeSingleDefUse() const { 1057 MUseDefIterator use(this); 1058 if (!use) { 1059 // No def-uses. 1060 return nullptr; 1061 } 1062 1063 MDefinition* useDef = use.def(); 1064 1065 use++; 1066 if (use) { 1067 // More than one def-use. 1068 return nullptr; 1069 } 1070 1071 return useDef; 1072 } 1073 1074 MDefinition* MDefinition::maybeMostRecentlyAddedDefUse() const { 1075 MUseDefIterator use(this); 1076 if (!use) { 1077 // No def-uses. 1078 return nullptr; 1079 } 1080 1081 MDefinition* mostRecentUse = use.def(); 1082 1083 #ifdef DEBUG 1084 // This function relies on addUse adding new uses to the front of the list. 1085 // Check this invariant by asserting the next few uses are 'older'. Skip this 1086 // for phis because setBackedge can add a new use for a loop phi even if the 1087 // loop body has a use with an id greater than the loop phi's id. 1088 if (!mostRecentUse->isPhi()) { 1089 static constexpr size_t NumUsesToCheck = 3; 1090 use++; 1091 for (size_t i = 0; use && i < NumUsesToCheck; i++, use++) { 1092 MOZ_ASSERT(use.def()->id() <= mostRecentUse->id()); 1093 } 1094 } 1095 #endif 1096 1097 return mostRecentUse; 1098 } 1099 1100 void MDefinition::replaceAllUsesWith(MDefinition* dom) { 1101 for (size_t i = 0, e = numOperands(); i < e; ++i) { 1102 getOperand(i)->setImplicitlyUsedUnchecked(); 1103 } 1104 1105 justReplaceAllUsesWith(dom); 1106 } 1107 1108 void MDefinition::justReplaceAllUsesWith(MDefinition* dom) { 1109 MOZ_ASSERT(dom != nullptr); 1110 MOZ_ASSERT(dom != this); 1111 1112 // Carry over the fact the value has uses which are no longer inspectable 1113 // with the graph. 1114 if (isImplicitlyUsed()) { 1115 dom->setImplicitlyUsedUnchecked(); 1116 } 1117 1118 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) { 1119 i->setProducerUnchecked(dom); 1120 } 1121 dom->uses_.takeElements(uses_); 1122 } 1123 1124 bool MDefinition::optimizeOutAllUses(TempAllocator& alloc) { 1125 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) { 1126 MUse* use = *i++; 1127 MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc); 1128 if (!alloc.ensureBallast()) { 1129 return false; 1130 } 1131 1132 // Update the resume point operand to use the optimized-out constant. 1133 use->setProducerUnchecked(constant); 1134 constant->addUseUnchecked(use); 1135 } 1136 1137 // Remove dangling pointers. 1138 this->uses_.clear(); 1139 return true; 1140 } 1141 1142 void MDefinition::replaceAllLiveUsesWith(MDefinition* dom) { 1143 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) { 1144 MUse* use = *i++; 1145 MNode* consumer = use->consumer(); 1146 if (consumer->isResumePoint()) { 1147 continue; 1148 } 1149 if (consumer->isDefinition() && 1150 consumer->toDefinition()->isRecoveredOnBailout()) { 1151 continue; 1152 } 1153 1154 // Update the operand to use the dominating definition. 1155 use->replaceProducer(dom); 1156 } 1157 } 1158 1159 MConstant* MConstant::New(TempAllocator& alloc, const Value& v) { 1160 return new (alloc) MConstant(alloc, v); 1161 } 1162 1163 MConstant* MConstant::New(TempAllocator::Fallible alloc, const Value& v) { 1164 return new (alloc) MConstant(alloc.alloc, v); 1165 } 1166 1167 MConstant* MConstant::NewBoolean(TempAllocator& alloc, bool b) { 1168 return new (alloc) MConstant(b); 1169 } 1170 1171 MConstant* MConstant::NewDouble(TempAllocator& alloc, double d) { 1172 return new (alloc) MConstant(d); 1173 } 1174 1175 MConstant* MConstant::NewFloat32(TempAllocator& alloc, double d) { 1176 MOZ_ASSERT(mozilla::IsFloat32Representable(d)); 1177 return new (alloc) MConstant(float(d)); 1178 } 1179 1180 MConstant* MConstant::NewInt32(TempAllocator& alloc, int32_t i) { 1181 return new (alloc) MConstant(i); 1182 } 1183 1184 MConstant* MConstant::NewInt64(TempAllocator& alloc, int64_t i) { 1185 return new (alloc) MConstant(MIRType::Int64, i); 1186 } 1187 1188 MConstant* MConstant::NewIntPtr(TempAllocator& alloc, intptr_t i) { 1189 return new (alloc) MConstant(MIRType::IntPtr, i); 1190 } 1191 1192 MConstant* MConstant::NewMagic(TempAllocator& alloc, JSWhyMagic m) { 1193 return new (alloc) MConstant(alloc, MagicValue(m)); 1194 } 1195 1196 MConstant* MConstant::NewNull(TempAllocator& alloc) { 1197 return new (alloc) MConstant(MIRType::Null); 1198 } 1199 1200 MConstant* MConstant::NewObject(TempAllocator& alloc, JSObject* v) { 1201 return new (alloc) MConstant(v); 1202 } 1203 1204 MConstant* MConstant::NewShape(TempAllocator& alloc, Shape* s) { 1205 return new (alloc) MConstant(s); 1206 } 1207 1208 MConstant* MConstant::NewString(TempAllocator& alloc, JSString* s) { 1209 return new (alloc) MConstant(alloc, StringValue(s)); 1210 } 1211 1212 MConstant* MConstant::NewUndefined(TempAllocator& alloc) { 1213 return new (alloc) MConstant(MIRType::Undefined); 1214 } 1215 1216 static MIRType MIRTypeFromValue(const js::Value& vp) { 1217 if (vp.isDouble()) { 1218 return MIRType::Double; 1219 } 1220 if (vp.isMagic()) { 1221 switch (vp.whyMagic()) { 1222 case JS_OPTIMIZED_OUT: 1223 return MIRType::MagicOptimizedOut; 1224 case JS_ELEMENTS_HOLE: 1225 return MIRType::MagicHole; 1226 case JS_IS_CONSTRUCTING: 1227 return MIRType::MagicIsConstructing; 1228 case JS_UNINITIALIZED_LEXICAL: 1229 return MIRType::MagicUninitializedLexical; 1230 default: 1231 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant"); 1232 } 1233 } 1234 return MIRTypeFromValueType(vp.extractNonDoubleType()); 1235 } 1236 1237 MConstant::MConstant(TempAllocator& alloc, const js::Value& vp) 1238 : MNullaryInstruction(classOpcode) { 1239 setResultType(MIRTypeFromValue(vp)); 1240 1241 MOZ_ASSERT(payload_.asBits == 0); 1242 1243 switch (type()) { 1244 case MIRType::Undefined: 1245 case MIRType::Null: 1246 break; 1247 case MIRType::Boolean: 1248 payload_.b = vp.toBoolean(); 1249 break; 1250 case MIRType::Int32: 1251 payload_.i32 = vp.toInt32(); 1252 break; 1253 case MIRType::Double: 1254 payload_.d = vp.toDouble(); 1255 break; 1256 case MIRType::String: { 1257 JSString* str = vp.toString(); 1258 MOZ_ASSERT(!IsInsideNursery(str)); 1259 payload_.str = &str->asOffThreadAtom(); 1260 break; 1261 } 1262 case MIRType::Symbol: 1263 payload_.sym = vp.toSymbol(); 1264 break; 1265 case MIRType::BigInt: 1266 MOZ_ASSERT(!IsInsideNursery(vp.toBigInt())); 1267 payload_.bi = vp.toBigInt(); 1268 break; 1269 case MIRType::Object: 1270 MOZ_ASSERT(!IsInsideNursery(&vp.toObject())); 1271 payload_.obj = &vp.toObject(); 1272 break; 1273 case MIRType::MagicOptimizedOut: 1274 case MIRType::MagicHole: 1275 case MIRType::MagicIsConstructing: 1276 case MIRType::MagicUninitializedLexical: 1277 break; 1278 default: 1279 MOZ_CRASH("Unexpected type"); 1280 } 1281 1282 setMovable(); 1283 } 1284 1285 MConstant::MConstant(JSObject* obj) : MConstant(MIRType::Object) { 1286 MOZ_ASSERT(!IsInsideNursery(obj)); 1287 payload_.obj = obj; 1288 } 1289 1290 MConstant::MConstant(Shape* shape) : MConstant(MIRType::Shape) { 1291 payload_.shape = shape; 1292 } 1293 1294 #ifdef DEBUG 1295 void MConstant::assertInitializedPayload() const { 1296 // valueHash() and equals() expect the unused payload bits to be 1297 // initialized to zero. Assert this in debug builds. 1298 1299 switch (type()) { 1300 case MIRType::Int32: 1301 case MIRType::Float32: 1302 # if MOZ_LITTLE_ENDIAN() 1303 MOZ_ASSERT((payload_.asBits >> 32) == 0); 1304 # else 1305 MOZ_ASSERT((payload_.asBits << 32) == 0); 1306 # endif 1307 break; 1308 case MIRType::Boolean: 1309 # if MOZ_LITTLE_ENDIAN() 1310 MOZ_ASSERT((payload_.asBits >> 1) == 0); 1311 # else 1312 MOZ_ASSERT((payload_.asBits & ~(1ULL << 56)) == 0); 1313 # endif 1314 break; 1315 case MIRType::Double: 1316 case MIRType::Int64: 1317 break; 1318 case MIRType::String: 1319 case MIRType::Object: 1320 case MIRType::Symbol: 1321 case MIRType::BigInt: 1322 case MIRType::IntPtr: 1323 case MIRType::Shape: 1324 # if MOZ_LITTLE_ENDIAN() 1325 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0); 1326 # else 1327 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits << 32) == 0); 1328 # endif 1329 break; 1330 default: 1331 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type())); 1332 MOZ_ASSERT(payload_.asBits == 0); 1333 break; 1334 } 1335 } 1336 #endif 1337 1338 HashNumber MConstant::valueHash() const { 1339 static_assert(sizeof(Payload) == sizeof(uint64_t), 1340 "Code below assumes payload fits in 64 bits"); 1341 1342 assertInitializedPayload(); 1343 return ConstantValueHash(type(), payload_.asBits); 1344 } 1345 1346 HashNumber MConstantProto::valueHash() const { 1347 HashNumber hash = protoObject()->valueHash(); 1348 const MDefinition* receiverObject = getReceiverObject(); 1349 if (receiverObject) { 1350 hash = addU32ToHash(hash, receiverObject->id()); 1351 } 1352 return hash; 1353 } 1354 1355 bool MConstant::congruentTo(const MDefinition* ins) const { 1356 return ins->isConstant() && equals(ins->toConstant()); 1357 } 1358 1359 #ifdef JS_JITSPEW 1360 void MConstant::printOpcode(GenericPrinter& out) const { 1361 PrintOpcodeName(out, op()); 1362 out.printf(" "); 1363 switch (type()) { 1364 case MIRType::Undefined: 1365 out.printf("undefined"); 1366 break; 1367 case MIRType::Null: 1368 out.printf("null"); 1369 break; 1370 case MIRType::Boolean: 1371 out.printf(toBoolean() ? "true" : "false"); 1372 break; 1373 case MIRType::Int32: 1374 out.printf("0x%x", uint32_t(toInt32())); 1375 break; 1376 case MIRType::Int64: 1377 out.printf("0x%" PRIx64, uint64_t(toInt64())); 1378 break; 1379 case MIRType::IntPtr: 1380 out.printf("0x%" PRIxPTR, uintptr_t(toIntPtr())); 1381 break; 1382 case MIRType::Double: 1383 out.printf("%.16g", toDouble()); 1384 break; 1385 case MIRType::Float32: { 1386 float val = toFloat32(); 1387 out.printf("%.16g", val); 1388 break; 1389 } 1390 case MIRType::Object: 1391 if (toObject().is<JSFunction>()) { 1392 JSFunction* fun = &toObject().as<JSFunction>(); 1393 if (fun->maybePartialDisplayAtom()) { 1394 out.put("function "); 1395 EscapedStringPrinter(out, fun->maybePartialDisplayAtom(), 0); 1396 } else { 1397 out.put("unnamed function"); 1398 } 1399 if (fun->hasBaseScript()) { 1400 BaseScript* script = fun->baseScript(); 1401 out.printf(" (%s:%u)", script->filename() ? script->filename() : "", 1402 script->lineno()); 1403 } 1404 out.printf(" at %p", (void*)fun); 1405 break; 1406 } 1407 out.printf("object %p (%s)", (void*)&toObject(), 1408 toObject().getClass()->name); 1409 break; 1410 case MIRType::Symbol: 1411 out.printf("symbol at %p", (void*)toSymbol()); 1412 break; 1413 case MIRType::BigInt: 1414 out.printf("BigInt at %p", (void*)toBigInt()); 1415 break; 1416 case MIRType::String: 1417 out.printf("string %p", (void*)toString()); 1418 break; 1419 case MIRType::Shape: 1420 out.printf("shape at %p", (void*)toShape()); 1421 break; 1422 case MIRType::MagicHole: 1423 out.printf("magic hole"); 1424 break; 1425 case MIRType::MagicIsConstructing: 1426 out.printf("magic is-constructing"); 1427 break; 1428 case MIRType::MagicOptimizedOut: 1429 out.printf("magic optimized-out"); 1430 break; 1431 case MIRType::MagicUninitializedLexical: 1432 out.printf("magic uninitialized-lexical"); 1433 break; 1434 default: 1435 MOZ_CRASH("unexpected type"); 1436 } 1437 } 1438 #endif 1439 1440 bool MConstant::canProduceFloat32() const { 1441 if (!isTypeRepresentableAsDouble()) { 1442 return false; 1443 } 1444 1445 if (type() == MIRType::Int32) { 1446 return IsFloat32Representable(static_cast<double>(toInt32())); 1447 } 1448 if (type() == MIRType::Double) { 1449 return IsFloat32Representable(toDouble()); 1450 } 1451 MOZ_ASSERT(type() == MIRType::Float32); 1452 return true; 1453 } 1454 1455 Value MConstant::toJSValue() const { 1456 // Wasm has types like int64 that cannot be stored as js::Value. It also 1457 // doesn't want the NaN canonicalization enforced by js::Value. 1458 MOZ_ASSERT(!IsCompilingWasm()); 1459 1460 switch (type()) { 1461 case MIRType::Undefined: 1462 return UndefinedValue(); 1463 case MIRType::Null: 1464 return NullValue(); 1465 case MIRType::Boolean: 1466 return BooleanValue(toBoolean()); 1467 case MIRType::Int32: 1468 return Int32Value(toInt32()); 1469 case MIRType::Double: 1470 return DoubleValue(toDouble()); 1471 case MIRType::Float32: 1472 return Float32Value(toFloat32()); 1473 case MIRType::String: 1474 return StringValue(toString()->unwrap()); 1475 case MIRType::Symbol: 1476 return SymbolValue(toSymbol()); 1477 case MIRType::BigInt: 1478 return BigIntValue(toBigInt()); 1479 case MIRType::Object: 1480 return ObjectValue(toObject()); 1481 case MIRType::Shape: 1482 return PrivateGCThingValue(toShape()); 1483 case MIRType::MagicOptimizedOut: 1484 return MagicValue(JS_OPTIMIZED_OUT); 1485 case MIRType::MagicHole: 1486 return MagicValue(JS_ELEMENTS_HOLE); 1487 case MIRType::MagicIsConstructing: 1488 return MagicValue(JS_IS_CONSTRUCTING); 1489 case MIRType::MagicUninitializedLexical: 1490 return MagicValue(JS_UNINITIALIZED_LEXICAL); 1491 default: 1492 MOZ_CRASH("Unexpected type"); 1493 } 1494 } 1495 1496 bool MConstant::valueToBoolean(bool* res) const { 1497 switch (type()) { 1498 case MIRType::Boolean: 1499 *res = toBoolean(); 1500 return true; 1501 case MIRType::Int32: 1502 *res = toInt32() != 0; 1503 return true; 1504 case MIRType::Int64: 1505 *res = toInt64() != 0; 1506 return true; 1507 case MIRType::IntPtr: 1508 *res = toIntPtr() != 0; 1509 return true; 1510 case MIRType::Double: 1511 *res = !std::isnan(toDouble()) && toDouble() != 0.0; 1512 return true; 1513 case MIRType::Float32: 1514 *res = !std::isnan(toFloat32()) && toFloat32() != 0.0f; 1515 return true; 1516 case MIRType::Null: 1517 case MIRType::Undefined: 1518 *res = false; 1519 return true; 1520 case MIRType::Symbol: 1521 *res = true; 1522 return true; 1523 case MIRType::BigInt: 1524 *res = !toBigInt()->isZero(); 1525 return true; 1526 case MIRType::String: 1527 *res = toString()->length() != 0; 1528 return true; 1529 case MIRType::Object: 1530 // TODO(Warp): Lazy groups have been removed. 1531 // We have to call EmulatesUndefined but that reads obj->group->clasp 1532 // and so it's racy when the object has a lazy group. The main callers 1533 // of this (MTest, MNot) already know how to fold the object case, so 1534 // just give up. 1535 return false; 1536 default: 1537 MOZ_ASSERT(IsMagicType(type())); 1538 return false; 1539 } 1540 } 1541 1542 #ifdef JS_JITSPEW 1543 void MControlInstruction::printOpcode(GenericPrinter& out) const { 1544 MDefinition::printOpcode(out); 1545 if (numSuccessors() > 0) { 1546 out.printf(" -> "); 1547 } 1548 for (size_t j = 0; j < numSuccessors(); j++) { 1549 if (j > 0) { 1550 out.printf(", "); 1551 } 1552 if (getSuccessor(j)) { 1553 out.printf("block %u", getSuccessor(j)->id()); 1554 } else { 1555 out.printf("(null-to-be-patched)"); 1556 } 1557 } 1558 } 1559 1560 void MCompare::printOpcode(GenericPrinter& out) const { 1561 MDefinition::printOpcode(out); 1562 out.printf(" %s", CodeName(jsop())); 1563 } 1564 1565 void MTypeOfIs::printOpcode(GenericPrinter& out) const { 1566 MDefinition::printOpcode(out); 1567 out.printf(" %s", CodeName(jsop())); 1568 1569 const char* name = ""; 1570 switch (jstype()) { 1571 case JSTYPE_UNDEFINED: 1572 name = "undefined"; 1573 break; 1574 case JSTYPE_OBJECT: 1575 name = "object"; 1576 break; 1577 case JSTYPE_FUNCTION: 1578 name = "function"; 1579 break; 1580 case JSTYPE_STRING: 1581 name = "string"; 1582 break; 1583 case JSTYPE_NUMBER: 1584 name = "number"; 1585 break; 1586 case JSTYPE_BOOLEAN: 1587 name = "boolean"; 1588 break; 1589 case JSTYPE_SYMBOL: 1590 name = "symbol"; 1591 break; 1592 case JSTYPE_BIGINT: 1593 name = "bigint"; 1594 break; 1595 case JSTYPE_LIMIT: 1596 MOZ_CRASH("Unexpected type"); 1597 } 1598 out.printf(" '%s'", name); 1599 } 1600 1601 void MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const { 1602 MDefinition::printOpcode(out); 1603 out.printf(" %s", Scalar::name(storageType())); 1604 } 1605 1606 void MLoadDataViewElement::printOpcode(GenericPrinter& out) const { 1607 MDefinition::printOpcode(out); 1608 out.printf(" %s", Scalar::name(storageType())); 1609 } 1610 1611 void MAssertRange::printOpcode(GenericPrinter& out) const { 1612 MDefinition::printOpcode(out); 1613 out.put(" "); 1614 assertedRange()->dump(out); 1615 } 1616 1617 void MNearbyInt::printOpcode(GenericPrinter& out) const { 1618 MDefinition::printOpcode(out); 1619 const char* roundingModeStr = nullptr; 1620 switch (roundingMode_) { 1621 case RoundingMode::Up: 1622 roundingModeStr = "(up)"; 1623 break; 1624 case RoundingMode::Down: 1625 roundingModeStr = "(down)"; 1626 break; 1627 case RoundingMode::NearestTiesToEven: 1628 roundingModeStr = "(nearest ties even)"; 1629 break; 1630 case RoundingMode::TowardsZero: 1631 roundingModeStr = "(towards zero)"; 1632 break; 1633 } 1634 out.printf(" %s", roundingModeStr); 1635 } 1636 #endif 1637 1638 AliasSet MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG); } 1639 1640 MDefinition* MSign::foldsTo(TempAllocator& alloc) { 1641 MDefinition* input = getOperand(0); 1642 if (!input->isConstant() || 1643 !input->toConstant()->isTypeRepresentableAsDouble()) { 1644 return this; 1645 } 1646 1647 double in = input->toConstant()->numberToDouble(); 1648 double out = js::math_sign_impl(in); 1649 1650 if (type() == MIRType::Int32) { 1651 // Decline folding if this is an int32 operation, but the result type 1652 // isn't an int32. 1653 int32_t i; 1654 if (!mozilla::NumberIsInt32(out, &i)) { 1655 return this; 1656 } 1657 return MConstant::NewInt32(alloc, i); 1658 } 1659 1660 return MConstant::NewDouble(alloc, out); 1661 } 1662 1663 const char* MMathFunction::FunctionName(UnaryMathFunction function) { 1664 return GetUnaryMathFunctionName(function); 1665 } 1666 1667 #ifdef JS_JITSPEW 1668 void MMathFunction::printOpcode(GenericPrinter& out) const { 1669 MDefinition::printOpcode(out); 1670 out.printf(" %s", FunctionName(function())); 1671 } 1672 #endif 1673 1674 MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) { 1675 MDefinition* input = getOperand(0); 1676 if (!input->isConstant() || 1677 !input->toConstant()->isTypeRepresentableAsDouble()) { 1678 return this; 1679 } 1680 1681 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(function()); 1682 1683 double in = input->toConstant()->numberToDouble(); 1684 1685 // The function pointer call can't GC. 1686 JS::AutoSuppressGCAnalysis nogc; 1687 double out = funPtr(in); 1688 1689 if (input->type() == MIRType::Float32) { 1690 return MConstant::NewFloat32(alloc, out); 1691 } 1692 return MConstant::NewDouble(alloc, out); 1693 } 1694 1695 MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) { 1696 MDefinition* input = getOperand(0); 1697 if (!input->isConstant() || input->type() != MIRType::Int32) { 1698 return this; 1699 } 1700 1701 int32_t i = input->toConstant()->toInt32(); 1702 return MConstant::NewBoolean(alloc, AtomicOperations::isLockfreeJS(i)); 1703 } 1704 1705 // Define |THIS_SLOT| as part of this translation unit, as it is used to 1706 // specialized the parameterized |New| function calls introduced by 1707 // TRIVIAL_NEW_WRAPPERS. 1708 const int32_t MParameter::THIS_SLOT; 1709 1710 #ifdef JS_JITSPEW 1711 void MParameter::printOpcode(GenericPrinter& out) const { 1712 PrintOpcodeName(out, op()); 1713 if (index() == THIS_SLOT) { 1714 out.printf(" THIS_SLOT"); 1715 } else { 1716 out.printf(" %d", index()); 1717 } 1718 } 1719 #endif 1720 1721 HashNumber MParameter::valueHash() const { 1722 HashNumber hash = MNullaryInstruction::valueHash(); 1723 hash = addU32ToHash(hash, index_); 1724 return hash; 1725 } 1726 1727 bool MParameter::congruentTo(const MDefinition* ins) const { 1728 if (!ins->isParameter()) { 1729 return false; 1730 } 1731 1732 return ins->toParameter()->index() == index_; 1733 } 1734 1735 WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs, 1736 FunctionFlags flags) 1737 : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) { 1738 MOZ_ASSERT_IF(nativeFun, isNativeWithoutJitEntry()); 1739 1740 #ifdef DEBUG 1741 // If we are not running off-main thread we can assert that the 1742 // metadata is consistent. 1743 if (!CanUseExtraThreads() && nativeFun) { 1744 MOZ_ASSERT(nativeFun->nargs() == nargs); 1745 1746 MOZ_ASSERT(nativeFun->isNativeWithoutJitEntry() == 1747 isNativeWithoutJitEntry()); 1748 MOZ_ASSERT(nativeFun->hasJitEntry() == hasJitEntry()); 1749 MOZ_ASSERT(nativeFun->isConstructor() == isConstructor()); 1750 MOZ_ASSERT(nativeFun->isClassConstructor() == isClassConstructor()); 1751 } 1752 #endif 1753 } 1754 1755 MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, size_t maxArgc, 1756 size_t numActualArgs, bool construct, bool ignoresReturnValue, 1757 bool isDOMCall, mozilla::Maybe<DOMObjectKind> objectKind, 1758 mozilla::Maybe<gc::Heap> initialHeap) { 1759 MOZ_ASSERT(isDOMCall == objectKind.isSome()); 1760 MOZ_ASSERT(isDOMCall == initialHeap.isSome()); 1761 1762 MOZ_ASSERT(maxArgc >= numActualArgs); 1763 MCall* ins; 1764 if (isDOMCall) { 1765 MOZ_ASSERT(!construct); 1766 ins = new (alloc) 1767 MCallDOMNative(target, numActualArgs, *objectKind, *initialHeap); 1768 } else { 1769 ins = 1770 new (alloc) MCall(target, numActualArgs, construct, ignoresReturnValue); 1771 } 1772 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) { 1773 return nullptr; 1774 } 1775 return ins; 1776 } 1777 1778 AliasSet MCallDOMNative::getAliasSet() const { 1779 const JSJitInfo* jitInfo = getJitInfo(); 1780 1781 // If we don't know anything about the types of our arguments, we have to 1782 // assume that type-coercions can have side-effects, so we need to alias 1783 // everything. 1784 if (jitInfo->aliasSet() == JSJitInfo::AliasEverything || 1785 !jitInfo->isTypedMethodJitInfo()) { 1786 return AliasSet::Store(AliasSet::Any); 1787 } 1788 1789 uint32_t argIndex = 0; 1790 const JSTypedMethodJitInfo* methodInfo = 1791 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo); 1792 for (const JSJitInfo::ArgType* argType = methodInfo->argTypes; 1793 *argType != JSJitInfo::ArgTypeListEnd; ++argType, ++argIndex) { 1794 if (argIndex >= numActualArgs()) { 1795 // Passing through undefined can't have side-effects 1796 continue; 1797 } 1798 // getArg(0) is "this", so skip it 1799 MDefinition* arg = getArg(argIndex + 1); 1800 MIRType actualType = arg->type(); 1801 // The only way to reliably avoid side-effects given the information we 1802 // have here is if we're passing in a known primitive value to an 1803 // argument that expects a primitive value. 1804 // 1805 // XXXbz maybe we need to communicate better information. For example, 1806 // a sequence argument will sort of unavoidably have side effects, while 1807 // a typed array argument won't have any, but both are claimed to be 1808 // JSJitInfo::Object. But if we do that, we need to watch out for our 1809 // movability/DCE-ability bits: if we have an arg type that can reliably 1810 // throw an exception on conversion, that might not affect our alias set 1811 // per se, but it should prevent us being moved or DCE-ed, unless we 1812 // know the incoming things match that arg type and won't throw. 1813 // 1814 if ((actualType == MIRType::Value || actualType == MIRType::Object) || 1815 (*argType & JSJitInfo::Object)) { 1816 return AliasSet::Store(AliasSet::Any); 1817 } 1818 } 1819 1820 // We checked all the args, and they check out. So we only alias DOM 1821 // mutations or alias nothing, depending on the alias set in the jitinfo. 1822 if (jitInfo->aliasSet() == JSJitInfo::AliasNone) { 1823 return AliasSet::None(); 1824 } 1825 1826 MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets); 1827 return AliasSet::Load(AliasSet::DOMProperty); 1828 } 1829 1830 void MCallDOMNative::computeMovable() { 1831 // We are movable if the jitinfo says we can be and if we're also not 1832 // effectful. The jitinfo can't check for the latter, since it depends on 1833 // the types of our arguments. 1834 const JSJitInfo* jitInfo = getJitInfo(); 1835 1836 MOZ_ASSERT_IF(jitInfo->isMovable, 1837 jitInfo->aliasSet() != JSJitInfo::AliasEverything); 1838 1839 if (jitInfo->isMovable && !isEffectful()) { 1840 setMovable(); 1841 } 1842 } 1843 1844 bool MCallDOMNative::congruentTo(const MDefinition* ins) const { 1845 if (!isMovable()) { 1846 return false; 1847 } 1848 1849 if (!ins->isCall()) { 1850 return false; 1851 } 1852 1853 const MCall* call = ins->toCall(); 1854 1855 if (!call->isCallDOMNative()) { 1856 return false; 1857 } 1858 1859 if (getSingleTarget() != call->getSingleTarget()) { 1860 return false; 1861 } 1862 1863 if (isConstructing() != call->isConstructing()) { 1864 return false; 1865 } 1866 1867 if (numActualArgs() != call->numActualArgs()) { 1868 return false; 1869 } 1870 1871 if (!congruentIfOperandsEqual(call)) { 1872 return false; 1873 } 1874 1875 // The other call had better be movable at this point! 1876 MOZ_ASSERT(call->isMovable()); 1877 1878 return true; 1879 } 1880 1881 const JSJitInfo* MCallDOMNative::getJitInfo() const { 1882 MOZ_ASSERT(getSingleTarget()->hasJitInfo()); 1883 return getSingleTarget()->jitInfo(); 1884 } 1885 1886 MCallClassHook* MCallClassHook::New(TempAllocator& alloc, JSNative target, 1887 uint32_t argc, bool constructing) { 1888 auto* ins = new (alloc) MCallClassHook(target, constructing); 1889 1890 // Add callee + |this| + (if constructing) newTarget. 1891 uint32_t numOperands = 2 + argc + constructing; 1892 1893 if (!ins->init(alloc, numOperands)) { 1894 return nullptr; 1895 } 1896 1897 return ins; 1898 } 1899 1900 MDefinition* MStringLength::foldsTo(TempAllocator& alloc) { 1901 if (string()->isConstant()) { 1902 JSOffThreadAtom* str = string()->toConstant()->toString(); 1903 return MConstant::NewInt32(alloc, str->length()); 1904 } 1905 1906 // MFromCharCode returns a one-element string. 1907 if (string()->isFromCharCode()) { 1908 return MConstant::NewInt32(alloc, 1); 1909 } 1910 1911 return this; 1912 } 1913 1914 MDefinition* MConcat::foldsTo(TempAllocator& alloc) { 1915 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) { 1916 return rhs(); 1917 } 1918 1919 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) { 1920 return lhs(); 1921 } 1922 1923 return this; 1924 } 1925 1926 MDefinition* MStringConvertCase::foldsTo(TempAllocator& alloc) { 1927 MDefinition* string = this->string(); 1928 1929 // Handle the pattern |str[idx].toUpperCase()| and simplify it from 1930 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just 1931 // |CharCodeConvertCase(CharCodeAt(str, idx))|. 1932 if (string->isFromCharCode()) { 1933 auto* charCode = string->toFromCharCode()->code(); 1934 auto mode = mode_ == Mode::LowerCase ? MCharCodeConvertCase::LowerCase 1935 : MCharCodeConvertCase::UpperCase; 1936 return MCharCodeConvertCase::New(alloc, charCode, mode); 1937 } 1938 1939 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to 1940 // directly return the string representation in the correct case. 1941 if (string->isInt32ToStringWithBase()) { 1942 auto* toString = string->toInt32ToStringWithBase(); 1943 1944 bool lowerCase = mode_ == Mode::LowerCase; 1945 if (toString->lowerCase() == lowerCase) { 1946 return toString; 1947 } 1948 return MInt32ToStringWithBase::New(alloc, toString->input(), 1949 toString->base(), lowerCase); 1950 } 1951 1952 return this; 1953 } 1954 1955 // Return true if |def| is `MConstant(Int32(0))`. 1956 static bool IsConstantZeroInt32(MDefinition* def) { 1957 return def->isConstant() && def->toConstant()->isInt32(0); 1958 } 1959 1960 // If |def| is `MBitOr` and one operand is `MConstant(Int32(0))`, then return 1961 // the other operand. Otherwise return |def|. 1962 static MDefinition* RemoveUnnecessaryBitOps(MDefinition* def) { 1963 if (def->isBitOr()) { 1964 auto* bitOr = def->toBitOr(); 1965 if (IsConstantZeroInt32(bitOr->lhs())) { 1966 return bitOr->rhs(); 1967 } 1968 if (IsConstantZeroInt32(bitOr->rhs())) { 1969 return bitOr->lhs(); 1970 } 1971 } 1972 return def; 1973 } 1974 1975 // Return a match if both operands of |binary| have the requested types. If 1976 // |binary| is commutative, the operands may appear in any order. 1977 template <typename Lhs, typename Rhs> 1978 static mozilla::Maybe<std::pair<Lhs*, Rhs*>> MatchOperands( 1979 MBinaryInstruction* binary) { 1980 auto* lhs = binary->lhs(); 1981 auto* rhs = binary->rhs(); 1982 if (lhs->is<Lhs>() && rhs->is<Rhs>()) { 1983 return mozilla::Some(std::pair{lhs->to<Lhs>(), rhs->to<Rhs>()}); 1984 } 1985 if (binary->isCommutative() && rhs->is<Lhs>() && lhs->is<Rhs>()) { 1986 return mozilla::Some(std::pair{rhs->to<Lhs>(), lhs->to<Rhs>()}); 1987 } 1988 return mozilla::Nothing(); 1989 } 1990 1991 static bool IsSubstrTo(MSubstr* substr, int32_t len) { 1992 // We want to match this pattern: 1993 // 1994 // Substr(string, Constant(0), Min(Constant(length), StringLength(string))) 1995 // 1996 // which is generated for the self-hosted `String.p.{substring,slice,substr}` 1997 // functions when called with constants `start` and `end` parameters. 1998 1999 if (!IsConstantZeroInt32(substr->begin())) { 2000 return false; 2001 } 2002 2003 // Unnecessary bit-ops haven't yet been removed. 2004 auto* length = RemoveUnnecessaryBitOps(substr->length()); 2005 if (!length->isMinMax() || length->toMinMax()->isMax()) { 2006 return false; 2007 } 2008 2009 auto match = MatchOperands<MConstant, MStringLength>(length->toMinMax()); 2010 if (!match) { 2011 return false; 2012 } 2013 2014 // Ensure |len| matches the substring's length. 2015 auto [cst, strLength] = *match; 2016 return cst->isInt32(len) && strLength->string() == substr->string(); 2017 } 2018 2019 static bool IsSubstrLast(MSubstr* substr, int32_t start) { 2020 MOZ_ASSERT(start < 0, "start from end is negative"); 2021 2022 // We want to match either this pattern: 2023 // 2024 // begin = Max(StringLength(string) + start, 0) 2025 // length = Max(StringLength(string) - begin, 0) 2026 // Substr(string, begin, length) 2027 // 2028 // or this pattern: 2029 // 2030 // begin = Max(StringLength(string) + start, 0) 2031 // length = Min(StringLength(string), StringLength(string) - begin) 2032 // Substr(string, begin, length) 2033 // 2034 // which is generated for the self-hosted `String.p.{slice,substr}` 2035 // functions when called with parameters `start < 0` and `end = undefined`. 2036 2037 auto* string = substr->string(); 2038 2039 // Unnecessary bit-ops haven't yet been removed. 2040 auto* begin = RemoveUnnecessaryBitOps(substr->begin()); 2041 auto* length = RemoveUnnecessaryBitOps(substr->length()); 2042 2043 // Matches: Max(StringLength(string) + start, 0) 2044 auto matchesBegin = [&]() { 2045 if (!begin->isMinMax() || !begin->toMinMax()->isMax()) { 2046 return false; 2047 } 2048 2049 auto maxOperands = MatchOperands<MAdd, MConstant>(begin->toMinMax()); 2050 if (!maxOperands) { 2051 return false; 2052 } 2053 2054 auto [add, cst] = *maxOperands; 2055 if (!cst->isInt32(0)) { 2056 return false; 2057 } 2058 2059 auto addOperands = MatchOperands<MStringLength, MConstant>(add); 2060 if (!addOperands) { 2061 return false; 2062 } 2063 2064 auto [strLength, cstAdd] = *addOperands; 2065 return strLength->string() == string && cstAdd->isInt32(start); 2066 }; 2067 2068 // Matches: Max(StringLength(string) - begin, 0) 2069 auto matchesSliceLength = [&]() { 2070 if (!length->isMinMax() || !length->toMinMax()->isMax()) { 2071 return false; 2072 } 2073 2074 auto maxOperands = MatchOperands<MSub, MConstant>(length->toMinMax()); 2075 if (!maxOperands) { 2076 return false; 2077 } 2078 2079 auto [sub, cst] = *maxOperands; 2080 if (!cst->isInt32(0)) { 2081 return false; 2082 } 2083 2084 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub); 2085 if (!subOperands) { 2086 return false; 2087 } 2088 2089 auto [strLength, minmax] = *subOperands; 2090 return strLength->string() == string && minmax == begin; 2091 }; 2092 2093 // Matches: Min(StringLength(string), StringLength(string) - begin) 2094 auto matchesSubstrLength = [&]() { 2095 if (!length->isMinMax() || length->toMinMax()->isMax()) { 2096 return false; 2097 } 2098 2099 auto minOperands = MatchOperands<MStringLength, MSub>(length->toMinMax()); 2100 if (!minOperands) { 2101 return false; 2102 } 2103 2104 auto [strLength1, sub] = *minOperands; 2105 if (strLength1->string() != string) { 2106 return false; 2107 } 2108 2109 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub); 2110 if (!subOperands) { 2111 return false; 2112 } 2113 2114 auto [strLength2, minmax] = *subOperands; 2115 return strLength2->string() == string && minmax == begin; 2116 }; 2117 2118 return matchesBegin() && (matchesSliceLength() || matchesSubstrLength()); 2119 } 2120 2121 MDefinition* MSubstr::foldsTo(TempAllocator& alloc) { 2122 // Fold |str.substring(0, 1)| to |str.charAt(0)|. 2123 if (IsSubstrTo(this, 1)) { 2124 MOZ_ASSERT(IsConstantZeroInt32(begin())); 2125 2126 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), begin()); 2127 block()->insertBefore(this, charCode); 2128 2129 return MFromCharCodeEmptyIfNegative::New(alloc, charCode); 2130 } 2131 2132 // Fold |str.slice(-1)| and |str.substr(-1)| to |str.charAt(str.length + -1)|. 2133 if (IsSubstrLast(this, -1)) { 2134 auto* length = MStringLength::New(alloc, string()); 2135 block()->insertBefore(this, length); 2136 2137 auto* index = MConstant::NewInt32(alloc, -1); 2138 block()->insertBefore(this, index); 2139 2140 // Folded MToRelativeStringIndex, see MToRelativeStringIndex::foldsTo. 2141 // 2142 // Safe to truncate because |length| is never negative. 2143 auto* add = MAdd::New(alloc, index, length, TruncateKind::Truncate); 2144 block()->insertBefore(this, add); 2145 2146 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), add); 2147 block()->insertBefore(this, charCode); 2148 2149 return MFromCharCodeEmptyIfNegative::New(alloc, charCode); 2150 } 2151 2152 return this; 2153 } 2154 2155 MDefinition* MCharCodeAt::foldsTo(TempAllocator& alloc) { 2156 MDefinition* string = this->string(); 2157 if (!string->isConstant() && !string->isFromCharCode()) { 2158 return this; 2159 } 2160 2161 MDefinition* index = this->index(); 2162 if (index->isSpectreMaskIndex()) { 2163 index = index->toSpectreMaskIndex()->index(); 2164 } 2165 if (!index->isConstant()) { 2166 return this; 2167 } 2168 int32_t idx = index->toConstant()->toInt32(); 2169 2170 // Handle the pattern |s[idx].charCodeAt(0)|. 2171 if (string->isFromCharCode()) { 2172 if (idx != 0) { 2173 return this; 2174 } 2175 2176 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just 2177 // |CharCodeAt(s, idx)|. 2178 auto* charCode = string->toFromCharCode()->code(); 2179 if (!charCode->isCharCodeAt()) { 2180 return this; 2181 } 2182 2183 return charCode; 2184 } 2185 2186 JSOffThreadAtom* str = string->toConstant()->toString(); 2187 if (idx < 0 || uint32_t(idx) >= str->length()) { 2188 return this; 2189 } 2190 2191 char16_t ch = str->latin1OrTwoByteChar(idx); 2192 return MConstant::NewInt32(alloc, ch); 2193 } 2194 2195 MDefinition* MCodePointAt::foldsTo(TempAllocator& alloc) { 2196 MDefinition* string = this->string(); 2197 if (!string->isConstant() && !string->isFromCharCode()) { 2198 return this; 2199 } 2200 2201 MDefinition* index = this->index(); 2202 if (index->isSpectreMaskIndex()) { 2203 index = index->toSpectreMaskIndex()->index(); 2204 } 2205 if (!index->isConstant()) { 2206 return this; 2207 } 2208 int32_t idx = index->toConstant()->toInt32(); 2209 2210 // Handle the pattern |s[idx].codePointAt(0)|. 2211 if (string->isFromCharCode()) { 2212 if (idx != 0) { 2213 return this; 2214 } 2215 2216 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just 2217 // |CharCodeAt(s, idx)|. 2218 auto* charCode = string->toFromCharCode()->code(); 2219 if (!charCode->isCharCodeAt()) { 2220 return this; 2221 } 2222 2223 return charCode; 2224 } 2225 2226 JSOffThreadAtom* str = string->toConstant()->toString(); 2227 if (idx < 0 || uint32_t(idx) >= str->length()) { 2228 return this; 2229 } 2230 2231 char32_t first = str->latin1OrTwoByteChar(idx); 2232 if (unicode::IsLeadSurrogate(first) && uint32_t(idx) + 1 < str->length()) { 2233 char32_t second = str->latin1OrTwoByteChar(idx + 1); 2234 if (unicode::IsTrailSurrogate(second)) { 2235 first = unicode::UTF16Decode(first, second); 2236 } 2237 } 2238 return MConstant::NewInt32(alloc, first); 2239 } 2240 2241 MDefinition* MToRelativeStringIndex::foldsTo(TempAllocator& alloc) { 2242 MDefinition* index = this->index(); 2243 MDefinition* length = this->length(); 2244 2245 if (!index->isConstant()) { 2246 return this; 2247 } 2248 if (!length->isStringLength() && !length->isConstant()) { 2249 return this; 2250 } 2251 MOZ_ASSERT_IF(length->isConstant(), length->toConstant()->toInt32() >= 0); 2252 2253 int32_t relativeIndex = index->toConstant()->toInt32(); 2254 if (relativeIndex >= 0) { 2255 return index; 2256 } 2257 2258 // Safe to truncate because |length| is never negative. 2259 return MAdd::New(alloc, index, length, TruncateKind::Truncate); 2260 } 2261 2262 template <size_t Arity> 2263 [[nodiscard]] static bool EnsureFloatInputOrConvert( 2264 MAryInstruction<Arity>* owner, TempAllocator& alloc) { 2265 MOZ_ASSERT(!IsFloatingPointType(owner->type()), 2266 "Floating point types must check consumers"); 2267 2268 if (AllOperandsCanProduceFloat32(owner)) { 2269 return true; 2270 } 2271 ConvertOperandsToDouble(owner, alloc); 2272 return false; 2273 } 2274 2275 template <size_t Arity> 2276 [[nodiscard]] static bool EnsureFloatConsumersAndInputOrConvert( 2277 MAryInstruction<Arity>* owner, TempAllocator& alloc) { 2278 MOZ_ASSERT(IsFloatingPointType(owner->type()), 2279 "Integer types don't need to check consumers"); 2280 2281 if (AllOperandsCanProduceFloat32(owner) && 2282 CheckUsesAreFloat32Consumers(owner)) { 2283 return true; 2284 } 2285 ConvertOperandsToDouble(owner, alloc); 2286 return false; 2287 } 2288 2289 void MFloor::trySpecializeFloat32(TempAllocator& alloc) { 2290 MOZ_ASSERT(type() == MIRType::Int32); 2291 if (EnsureFloatInputOrConvert(this, alloc)) { 2292 specialization_ = MIRType::Float32; 2293 } 2294 } 2295 2296 void MCeil::trySpecializeFloat32(TempAllocator& alloc) { 2297 MOZ_ASSERT(type() == MIRType::Int32); 2298 if (EnsureFloatInputOrConvert(this, alloc)) { 2299 specialization_ = MIRType::Float32; 2300 } 2301 } 2302 2303 void MRound::trySpecializeFloat32(TempAllocator& alloc) { 2304 MOZ_ASSERT(type() == MIRType::Int32); 2305 if (EnsureFloatInputOrConvert(this, alloc)) { 2306 specialization_ = MIRType::Float32; 2307 } 2308 } 2309 2310 void MTrunc::trySpecializeFloat32(TempAllocator& alloc) { 2311 MOZ_ASSERT(type() == MIRType::Int32); 2312 if (EnsureFloatInputOrConvert(this, alloc)) { 2313 specialization_ = MIRType::Float32; 2314 } 2315 } 2316 2317 void MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) { 2318 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 2319 specialization_ = MIRType::Float32; 2320 setResultType(MIRType::Float32); 2321 } 2322 } 2323 2324 void MRoundToDouble::trySpecializeFloat32(TempAllocator& alloc) { 2325 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 2326 specialization_ = MIRType::Float32; 2327 setResultType(MIRType::Float32); 2328 } 2329 } 2330 2331 MGoto* MGoto::New(TempAllocator& alloc, MBasicBlock* target) { 2332 return new (alloc) MGoto(target); 2333 } 2334 2335 MGoto* MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target) { 2336 MOZ_ASSERT(target); 2337 return new (alloc) MGoto(target); 2338 } 2339 2340 MGoto* MGoto::New(TempAllocator& alloc) { return new (alloc) MGoto(nullptr); } 2341 2342 MDefinition* MBox::foldsTo(TempAllocator& alloc) { 2343 if (input()->isUnbox()) { 2344 return input()->toUnbox()->input(); 2345 } 2346 return this; 2347 } 2348 2349 #ifdef JS_JITSPEW 2350 void MUnbox::printOpcode(GenericPrinter& out) const { 2351 PrintOpcodeName(out, op()); 2352 out.printf(" "); 2353 getOperand(0)->printName(out); 2354 out.printf(" "); 2355 2356 switch (type()) { 2357 case MIRType::Int32: 2358 out.printf("to Int32"); 2359 break; 2360 case MIRType::Double: 2361 out.printf("to Double"); 2362 break; 2363 case MIRType::Boolean: 2364 out.printf("to Boolean"); 2365 break; 2366 case MIRType::String: 2367 out.printf("to String"); 2368 break; 2369 case MIRType::Symbol: 2370 out.printf("to Symbol"); 2371 break; 2372 case MIRType::BigInt: 2373 out.printf("to BigInt"); 2374 break; 2375 case MIRType::Object: 2376 out.printf("to Object"); 2377 break; 2378 default: 2379 break; 2380 } 2381 2382 switch (mode()) { 2383 case Fallible: 2384 out.printf(" (fallible)"); 2385 break; 2386 case Infallible: 2387 out.printf(" (infallible)"); 2388 break; 2389 default: 2390 break; 2391 } 2392 } 2393 #endif 2394 2395 MDefinition* MUnbox::foldsTo(TempAllocator& alloc) { 2396 if (input()->isBox()) { 2397 MDefinition* unboxed = input()->toBox()->input(); 2398 2399 // Fold MUnbox(MBox(x)) => x if types match. 2400 if (unboxed->type() == type()) { 2401 if (fallible()) { 2402 unboxed->setImplicitlyUsedUnchecked(); 2403 } 2404 return unboxed; 2405 } 2406 2407 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible. 2408 if (type() == MIRType::Double && 2409 IsTypeRepresentableAsDouble(unboxed->type())) { 2410 if (unboxed->isConstant()) { 2411 return MConstant::NewDouble(alloc, 2412 unboxed->toConstant()->numberToDouble()); 2413 } 2414 2415 return MToDouble::New(alloc, unboxed); 2416 } 2417 2418 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be 2419 // represented as an Int32. Fold to avoid unnecessary bailouts. 2420 if (type() == MIRType::Int32 && unboxed->type() == MIRType::Double) { 2421 auto* folded = MToNumberInt32::New(alloc, unboxed, 2422 IntConversionInputKind::NumbersOnly); 2423 folded->setGuard(); 2424 return folded; 2425 } 2426 } 2427 2428 return this; 2429 } 2430 2431 #ifdef DEBUG 2432 void MPhi::assertLoopPhi() const { 2433 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these 2434 // predecessors being at known indices. 2435 if (block()->numPredecessors() == 2) { 2436 MBasicBlock* pred = block()->getPredecessor(0); 2437 MBasicBlock* back = block()->getPredecessor(1); 2438 MOZ_ASSERT(pred == block()->loopPredecessor()); 2439 MOZ_ASSERT(pred->successorWithPhis() == block()); 2440 MOZ_ASSERT(pred->positionInPhiSuccessor() == 0); 2441 MOZ_ASSERT(back == block()->backedge()); 2442 MOZ_ASSERT(back->successorWithPhis() == block()); 2443 MOZ_ASSERT(back->positionInPhiSuccessor() == 1); 2444 } else { 2445 // After we remove fake loop predecessors for loop headers that 2446 // are only reachable via OSR, the only predecessor is the 2447 // loop backedge. 2448 MOZ_ASSERT(block()->numPredecessors() == 1); 2449 MOZ_ASSERT(block()->graph().osrBlock()); 2450 MOZ_ASSERT(!block()->graph().canBuildDominators()); 2451 MBasicBlock* back = block()->getPredecessor(0); 2452 MOZ_ASSERT(back == block()->backedge()); 2453 MOZ_ASSERT(back->successorWithPhis() == block()); 2454 MOZ_ASSERT(back->positionInPhiSuccessor() == 0); 2455 } 2456 } 2457 #endif 2458 2459 MDefinition* MPhi::getLoopPredecessorOperand() const { 2460 // This should not be called after removing fake loop predecessors. 2461 MOZ_ASSERT(block()->numPredecessors() == 2); 2462 assertLoopPhi(); 2463 return getOperand(0); 2464 } 2465 2466 MDefinition* MPhi::getLoopBackedgeOperand() const { 2467 assertLoopPhi(); 2468 uint32_t idx = block()->numPredecessors() == 2 ? 1 : 0; 2469 return getOperand(idx); 2470 } 2471 2472 void MPhi::removeOperand(size_t index) { 2473 MOZ_ASSERT(index < numOperands()); 2474 MOZ_ASSERT(getUseFor(index)->index() == index); 2475 MOZ_ASSERT(getUseFor(index)->consumer() == this); 2476 2477 // If we have phi(..., a, b, c, d, ..., z) and we plan 2478 // on removing a, then first shift downward so that we have 2479 // phi(..., b, c, d, ..., z, z): 2480 MUse* p = inputs_.begin() + index; 2481 MUse* e = inputs_.end(); 2482 p->producer()->removeUse(p); 2483 for (; p < e - 1; ++p) { 2484 MDefinition* producer = (p + 1)->producer(); 2485 p->setProducerUnchecked(producer); 2486 producer->replaceUse(p + 1, p); 2487 } 2488 2489 // truncate the inputs_ list: 2490 inputs_.popBack(); 2491 } 2492 2493 void MPhi::removeAllOperands() { 2494 for (MUse& p : inputs_) { 2495 p.producer()->removeUse(&p); 2496 } 2497 inputs_.clear(); 2498 } 2499 2500 MDefinition* MPhi::foldsTernary(TempAllocator& alloc) { 2501 /* Look if this MPhi is a ternary construct. 2502 * This is a very loose term as it actually only checks for 2503 * 2504 * MTest X 2505 * / \ 2506 * ... ... 2507 * \ / 2508 * MPhi X Y 2509 * 2510 * Which we will simply call: 2511 * x ? x : y or x ? y : x 2512 */ 2513 2514 if (numOperands() != 2) { 2515 return nullptr; 2516 } 2517 2518 MOZ_ASSERT(block()->numPredecessors() == 2); 2519 2520 MBasicBlock* pred = block()->immediateDominator(); 2521 if (!pred || !pred->lastIns()->isTest()) { 2522 return nullptr; 2523 } 2524 2525 MTest* test = pred->lastIns()->toTest(); 2526 2527 // True branch may only dominate one edge of MPhi. 2528 if (test->ifTrue()->dominates(block()->getPredecessor(0)) == 2529 test->ifTrue()->dominates(block()->getPredecessor(1))) { 2530 return nullptr; 2531 } 2532 2533 // False branch may only dominate one edge of MPhi. 2534 if (test->ifFalse()->dominates(block()->getPredecessor(0)) == 2535 test->ifFalse()->dominates(block()->getPredecessor(1))) { 2536 return nullptr; 2537 } 2538 2539 // True and false branch must dominate different edges of MPhi. 2540 if (test->ifTrue()->dominates(block()->getPredecessor(0)) == 2541 test->ifFalse()->dominates(block()->getPredecessor(0))) { 2542 return nullptr; 2543 } 2544 2545 // We found a ternary construct. 2546 bool firstIsTrueBranch = 2547 test->ifTrue()->dominates(block()->getPredecessor(0)); 2548 MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1); 2549 MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0); 2550 2551 // Accept either 2552 // testArg ? testArg : constant or 2553 // testArg ? constant : testArg 2554 if (!trueDef->isConstant() && !falseDef->isConstant()) { 2555 return nullptr; 2556 } 2557 2558 MConstant* c = 2559 trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant(); 2560 MDefinition* testArg = (trueDef == c) ? falseDef : trueDef; 2561 if (testArg != test->input()) { 2562 return nullptr; 2563 } 2564 2565 // This check should be a tautology, except that the constant might be the 2566 // result of the removal of a branch. In such case the domination scope of 2567 // the block which is holding the constant might be incomplete. This 2568 // condition is used to prevent doing this optimization based on incomplete 2569 // information. 2570 // 2571 // As GVN removed a branch, it will update the dominations rules before 2572 // trying to fold this MPhi again. Thus, this condition does not inhibit 2573 // this optimization. 2574 MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1); 2575 MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0); 2576 if (!trueDef->block()->dominates(truePred) || 2577 !falseDef->block()->dominates(falsePred)) { 2578 return nullptr; 2579 } 2580 2581 // If testArg is an int32 type we can: 2582 // - fold testArg ? testArg : 0 to testArg 2583 // - fold testArg ? 0 : testArg to 0 2584 if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) { 2585 testArg->setGuardRangeBailoutsUnchecked(); 2586 2587 // When folding to the constant we need to hoist it. 2588 if (trueDef == c && !c->block()->dominates(block())) { 2589 c->block()->moveBefore(pred->lastIns(), c); 2590 } 2591 return trueDef; 2592 } 2593 2594 // If testArg is an double type we can: 2595 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg) 2596 if (testArg->type() == MIRType::Double && 2597 mozilla::IsPositiveZero(c->numberToDouble()) && c != trueDef) { 2598 MNaNToZero* replace = MNaNToZero::New(alloc, testArg); 2599 test->block()->insertBefore(test, replace); 2600 return replace; 2601 } 2602 2603 // If testArg is a string type we can: 2604 // - fold testArg ? testArg : "" to testArg 2605 // - fold testArg ? "" : testArg to "" 2606 if (testArg->type() == MIRType::String && c->toString()->empty()) { 2607 // When folding to the constant we need to hoist it. 2608 if (trueDef == c && !c->block()->dominates(block())) { 2609 c->block()->moveBefore(pred->lastIns(), c); 2610 } 2611 return trueDef; 2612 } 2613 2614 return nullptr; 2615 } 2616 2617 MDefinition* MPhi::operandIfRedundant() { 2618 if (inputs_.length() == 0) { 2619 return nullptr; 2620 } 2621 2622 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)), 2623 // returns the operand that it will always be equal to (a, in 2624 // those two cases). 2625 MDefinition* first = getOperand(0); 2626 for (size_t i = 1, e = numOperands(); i < e; i++) { 2627 MDefinition* op = getOperand(i); 2628 if (op != first && op != this) { 2629 return nullptr; 2630 } 2631 } 2632 return first; 2633 } 2634 2635 MDefinition* MPhi::foldsTo(TempAllocator& alloc) { 2636 if (MDefinition* def = operandIfRedundant()) { 2637 return def; 2638 } 2639 2640 if (MDefinition* def = foldsTernary(alloc)) { 2641 return def; 2642 } 2643 2644 return this; 2645 } 2646 2647 bool MPhi::congruentTo(const MDefinition* ins) const { 2648 if (!ins->isPhi()) { 2649 return false; 2650 } 2651 2652 // Phis in different blocks may have different control conditions. 2653 // For example, these phis: 2654 // 2655 // if (p) 2656 // goto a 2657 // a: 2658 // t = phi(x, y) 2659 // 2660 // if (q) 2661 // goto b 2662 // b: 2663 // s = phi(x, y) 2664 // 2665 // have identical operands, but they are not equvalent because t is 2666 // effectively p?x:y and s is effectively q?x:y. 2667 // 2668 // For now, consider phis in different blocks incongruent. 2669 if (ins->block() != block()) { 2670 return false; 2671 } 2672 2673 return congruentIfOperandsEqual(ins); 2674 } 2675 2676 void MPhi::updateForReplacement(MPhi* other) { 2677 // This function is called to fix the current Phi flags using it as a 2678 // replacement of the other Phi instruction |other|. 2679 // 2680 // When dealing with usage analysis, any Use will replace all other values, 2681 // such as Unused and Unknown. Unless both are Unused, the merge would be 2682 // Unknown. 2683 if (usageAnalysis_ == PhiUsage::Used || 2684 other->usageAnalysis_ == PhiUsage::Used) { 2685 usageAnalysis_ = PhiUsage::Used; 2686 } else if (usageAnalysis_ != other->usageAnalysis_) { 2687 // this == unused && other == unknown 2688 // or this == unknown && other == unused 2689 usageAnalysis_ = PhiUsage::Unknown; 2690 } else { 2691 // this == unused && other == unused 2692 // or this == unknown && other = unknown 2693 MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unused || 2694 usageAnalysis_ == PhiUsage::Unknown); 2695 MOZ_ASSERT(usageAnalysis_ == other->usageAnalysis_); 2696 } 2697 } 2698 2699 /* static */ 2700 bool MPhi::markIteratorPhis(const PhiVector& iterators) { 2701 // Find and mark phis that must transitively hold an iterator live. 2702 2703 Vector<MPhi*, 8, SystemAllocPolicy> worklist; 2704 2705 for (MPhi* iter : iterators) { 2706 if (!iter->isInWorklist()) { 2707 if (!worklist.append(iter)) { 2708 return false; 2709 } 2710 iter->setInWorklist(); 2711 } 2712 } 2713 2714 while (!worklist.empty()) { 2715 MPhi* phi = worklist.popCopy(); 2716 phi->setNotInWorklist(); 2717 2718 phi->setIterator(); 2719 phi->setImplicitlyUsedUnchecked(); 2720 2721 for (MUseDefIterator iter(phi); iter; iter++) { 2722 MDefinition* use = iter.def(); 2723 if (!use->isInWorklist() && use->isPhi() && !use->toPhi()->isIterator()) { 2724 if (!worklist.append(use->toPhi())) { 2725 return false; 2726 } 2727 use->setInWorklist(); 2728 } 2729 } 2730 } 2731 2732 return true; 2733 } 2734 2735 bool MPhi::typeIncludes(MDefinition* def) { 2736 MOZ_ASSERT(!IsMagicType(def->type())); 2737 2738 if (def->type() == this->type()) { 2739 return true; 2740 } 2741 2742 // This phi must be able to be any value. 2743 if (this->type() == MIRType::Value) { 2744 return true; 2745 } 2746 2747 if (def->type() == MIRType::Int32 && this->type() == MIRType::Double) { 2748 return true; 2749 } 2750 2751 return false; 2752 } 2753 2754 void MCallBase::addArg(size_t argnum, MDefinition* arg) { 2755 // The operand vector is initialized in reverse order by WarpBuilder. 2756 // It cannot be checked for consistency until all arguments are added. 2757 // FixedList doesn't initialize its elements, so do an unchecked init. 2758 initOperand(argnum + NumNonArgumentOperands, arg); 2759 } 2760 2761 static inline bool IsConstant(MDefinition* def, double v) { 2762 if (!def->isConstant()) { 2763 return false; 2764 } 2765 2766 return NumbersAreIdentical(def->toConstant()->numberToDouble(), v); 2767 } 2768 2769 static inline bool IsConstantInt64(MDefinition* def, int64_t v) { 2770 if (!def->isConstant()) { 2771 return false; 2772 } 2773 2774 return def->toConstant()->toInt64() == v; 2775 } 2776 2777 static inline bool IsConstantIntPtr(MDefinition* def, intptr_t v) { 2778 if (!def->isConstant()) { 2779 return false; 2780 } 2781 2782 return def->toConstant()->toIntPtr() == v; 2783 } 2784 2785 MDefinition* MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) { 2786 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop. 2787 2788 if (type() == MIRType::Int32) { 2789 if (MDefinition* folded = EvaluateInt32ConstantOperands(alloc, this)) { 2790 return folded; 2791 } 2792 } else if (type() == MIRType::Int64) { 2793 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) { 2794 return folded; 2795 } 2796 } else if (type() == MIRType::IntPtr) { 2797 if (MDefinition* folded = EvaluateIntPtrConstantOperands(alloc, this)) { 2798 return folded; 2799 } 2800 } 2801 2802 return this; 2803 } 2804 2805 MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() { 2806 // It's probably OK to perform this optimization only for int32, as it will 2807 // have the greatest effect for asm.js code that is compiled with the JS 2808 // pipeline, and that code will not see int64 values. 2809 2810 if (type() != MIRType::Int32) { 2811 return this; 2812 } 2813 2814 // Fold unsigned shift right operator when the second operand is zero and 2815 // the only use is an unsigned modulo. Thus, the expression 2816 // |(x >>> 0) % y| becomes |x % y|. 2817 if (isUrsh() && IsUint32Type(this)) { 2818 MDefinition* defUse = maybeSingleDefUse(); 2819 if (defUse && defUse->isMod() && defUse->toMod()->isUnsigned()) { 2820 return getOperand(0); 2821 } 2822 } 2823 2824 // Eliminate bitwise operations that are no-ops when used on integer 2825 // inputs, such as (x | 0). 2826 2827 MDefinition* lhs = getOperand(0); 2828 MDefinition* rhs = getOperand(1); 2829 2830 if (IsConstant(lhs, 0)) { 2831 return foldIfZero(0); 2832 } 2833 2834 if (IsConstant(rhs, 0)) { 2835 return foldIfZero(1); 2836 } 2837 2838 if (IsConstant(lhs, -1)) { 2839 return foldIfNegOne(0); 2840 } 2841 2842 if (IsConstant(rhs, -1)) { 2843 return foldIfNegOne(1); 2844 } 2845 2846 if (lhs == rhs) { 2847 return foldIfEqual(); 2848 } 2849 2850 if (maskMatchesRightRange) { 2851 MOZ_ASSERT(lhs->isConstant()); 2852 MOZ_ASSERT(lhs->type() == MIRType::Int32); 2853 return foldIfAllBitsSet(0); 2854 } 2855 2856 if (maskMatchesLeftRange) { 2857 MOZ_ASSERT(rhs->isConstant()); 2858 MOZ_ASSERT(rhs->type() == MIRType::Int32); 2859 return foldIfAllBitsSet(1); 2860 } 2861 2862 return this; 2863 } 2864 2865 static inline bool CanProduceNegativeZero(MDefinition* def) { 2866 // Test if this instruction can produce negative zero even when bailing out 2867 // and changing types. 2868 switch (def->op()) { 2869 case MDefinition::Opcode::Constant: 2870 if (def->type() == MIRType::Double && 2871 def->toConstant()->toDouble() == -0.0) { 2872 return true; 2873 } 2874 [[fallthrough]]; 2875 case MDefinition::Opcode::BitAnd: 2876 case MDefinition::Opcode::BitOr: 2877 case MDefinition::Opcode::BitXor: 2878 case MDefinition::Opcode::BitNot: 2879 case MDefinition::Opcode::Lsh: 2880 case MDefinition::Opcode::Rsh: 2881 return false; 2882 default: 2883 return true; 2884 } 2885 } 2886 2887 static inline bool NeedNegativeZeroCheck(MDefinition* def) { 2888 if (def->isGuard() || def->isGuardRangeBailouts()) { 2889 return true; 2890 } 2891 2892 // Test if all uses have the same semantics for -0 and 0 2893 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { 2894 if (use->consumer()->isResumePoint()) { 2895 return true; 2896 } 2897 2898 MDefinition* use_def = use->consumer()->toDefinition(); 2899 switch (use_def->op()) { 2900 case MDefinition::Opcode::Add: { 2901 // If add is truncating -0 and 0 are observed as the same. 2902 if (use_def->toAdd()->isTruncated()) { 2903 break; 2904 } 2905 2906 // x + y gives -0, when both x and y are -0 2907 2908 // Figure out the order in which the addition's operands will 2909 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR 2910 // definitions for us so that this just requires comparing ids. 2911 MDefinition* first = use_def->toAdd()->lhs(); 2912 MDefinition* second = use_def->toAdd()->rhs(); 2913 if (first->id() > second->id()) { 2914 std::swap(first, second); 2915 } 2916 // Negative zero checks can be removed on the first executed 2917 // operand only if it is guaranteed the second executed operand 2918 // will produce a value other than -0. While the second is 2919 // typed as an int32, a bailout taken between execution of the 2920 // operands may change that type and cause a -0 to flow to the 2921 // second. 2922 // 2923 // There is no way to test whether there are any bailouts 2924 // between execution of the operands, so remove negative 2925 // zero checks from the first only if the second's type is 2926 // independent from type changes that may occur after bailing. 2927 if (def == first && CanProduceNegativeZero(second)) { 2928 return true; 2929 } 2930 2931 // The negative zero check can always be removed on the second 2932 // executed operand; by the time this executes the first will have 2933 // been evaluated as int32 and the addition's result cannot be -0. 2934 break; 2935 } 2936 case MDefinition::Opcode::Sub: { 2937 // If sub is truncating -0 and 0 are observed as the same 2938 if (use_def->toSub()->isTruncated()) { 2939 break; 2940 } 2941 2942 // x + y gives -0, when x is -0 and y is 0 2943 2944 // We can remove the negative zero check on the rhs, only if we 2945 // are sure the lhs isn't negative zero. 2946 2947 // The lhs is typed as integer (i.e. not -0.0), but it can bailout 2948 // and change type. This should be fine if the lhs is executed 2949 // first. However if the rhs is executed first, the lhs can bail, 2950 // change type and become -0.0 while the rhs has already been 2951 // optimized to not make a difference between zero and negative zero. 2952 MDefinition* lhs = use_def->toSub()->lhs(); 2953 MDefinition* rhs = use_def->toSub()->rhs(); 2954 if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) { 2955 return true; 2956 } 2957 2958 [[fallthrough]]; 2959 } 2960 case MDefinition::Opcode::StoreElement: 2961 case MDefinition::Opcode::StoreHoleValueElement: 2962 case MDefinition::Opcode::LoadElement: 2963 case MDefinition::Opcode::LoadElementHole: 2964 case MDefinition::Opcode::LoadUnboxedScalar: 2965 case MDefinition::Opcode::LoadDataViewElement: 2966 case MDefinition::Opcode::LoadTypedArrayElementHole: 2967 case MDefinition::Opcode::CharCodeAt: 2968 case MDefinition::Opcode::Mod: 2969 case MDefinition::Opcode::InArray: 2970 // Only allowed to remove check when definition is the second operand 2971 if (use_def->getOperand(0) == def) { 2972 return true; 2973 } 2974 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) { 2975 if (use_def->getOperand(i) == def) { 2976 return true; 2977 } 2978 } 2979 break; 2980 case MDefinition::Opcode::BoundsCheck: 2981 // Only allowed to remove check when definition is the first operand 2982 if (use_def->toBoundsCheck()->getOperand(1) == def) { 2983 return true; 2984 } 2985 break; 2986 case MDefinition::Opcode::ToString: 2987 case MDefinition::Opcode::FromCharCode: 2988 case MDefinition::Opcode::FromCodePoint: 2989 case MDefinition::Opcode::TableSwitch: 2990 case MDefinition::Opcode::Compare: 2991 case MDefinition::Opcode::BitAnd: 2992 case MDefinition::Opcode::BitOr: 2993 case MDefinition::Opcode::BitXor: 2994 case MDefinition::Opcode::Abs: 2995 case MDefinition::Opcode::TruncateToInt32: 2996 // Always allowed to remove check. No matter which operand. 2997 break; 2998 case MDefinition::Opcode::StoreElementHole: 2999 case MDefinition::Opcode::StoreTypedArrayElementHole: 3000 case MDefinition::Opcode::PostWriteElementBarrier: 3001 // Only allowed to remove check when definition is the third operand. 3002 for (size_t i = 0, e = use_def->numOperands(); i < e; i++) { 3003 if (i == 2) { 3004 continue; 3005 } 3006 if (use_def->getOperand(i) == def) { 3007 return true; 3008 } 3009 } 3010 break; 3011 default: 3012 return true; 3013 } 3014 } 3015 return false; 3016 } 3017 3018 #ifdef JS_JITSPEW 3019 void MBinaryArithInstruction::printOpcode(GenericPrinter& out) const { 3020 MDefinition::printOpcode(out); 3021 3022 switch (type()) { 3023 case MIRType::Int32: 3024 if (isDiv()) { 3025 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32"); 3026 } else if (isMod()) { 3027 out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32"); 3028 } else { 3029 out.printf(" [int32]"); 3030 } 3031 break; 3032 case MIRType::Int64: 3033 if (isDiv()) { 3034 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64"); 3035 } else if (isMod()) { 3036 out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64"); 3037 } else { 3038 out.printf(" [int64]"); 3039 } 3040 break; 3041 case MIRType::Float32: 3042 out.printf(" [float]"); 3043 break; 3044 case MIRType::Double: 3045 out.printf(" [double]"); 3046 break; 3047 default: 3048 break; 3049 } 3050 } 3051 #endif 3052 3053 MDefinition* MRsh::foldsTo(TempAllocator& alloc) { 3054 MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc); 3055 3056 if (f != this) { 3057 return f; 3058 } 3059 3060 MDefinition* lhs = getOperand(0); 3061 MDefinition* rhs = getOperand(1); 3062 3063 // It's probably OK to perform this optimization only for int32, as it will 3064 // have the greatest effect for asm.js code that is compiled with the JS 3065 // pipeline, and that code will not see int64 values. 3066 3067 if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32) { 3068 return this; 3069 } 3070 3071 if (!lhs->getOperand(1)->isConstant() || 3072 lhs->getOperand(1)->type() != MIRType::Int32) { 3073 return this; 3074 } 3075 3076 uint32_t shift = rhs->toConstant()->toInt32(); 3077 uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32(); 3078 if (shift != shift_lhs) { 3079 return this; 3080 } 3081 3082 switch (shift) { 3083 case 16: 3084 return MSignExtendInt32::New(alloc, lhs->getOperand(0), 3085 MSignExtendInt32::Half); 3086 case 24: 3087 return MSignExtendInt32::New(alloc, lhs->getOperand(0), 3088 MSignExtendInt32::Byte); 3089 } 3090 3091 return this; 3092 } 3093 3094 MDefinition* MBinaryArithInstruction::foldsTo(TempAllocator& alloc) { 3095 MOZ_ASSERT(IsNumberType(type())); 3096 MOZ_ASSERT(!isDiv() && !isMod(), "Div and Mod don't call this method"); 3097 3098 MDefinition* lhs = getOperand(0); 3099 MDefinition* rhs = getOperand(1); 3100 3101 if (type() == MIRType::Int64) { 3102 MOZ_ASSERT(!isTruncated()); 3103 3104 if (MConstant* folded = EvaluateInt64ConstantOperands(alloc, this)) { 3105 return folded; 3106 } 3107 if (IsConstantInt64(rhs, int64_t(getIdentity()))) { 3108 return lhs; // x op id => x 3109 } 3110 if (isCommutative() && IsConstantInt64(lhs, int64_t(getIdentity()))) { 3111 return rhs; // id op x => x 3112 } 3113 return this; 3114 } 3115 3116 if (type() == MIRType::IntPtr) { 3117 MOZ_ASSERT(!isTruncated()); 3118 3119 if (MConstant* folded = EvaluateIntPtrConstantOperands(alloc, this)) { 3120 return folded; 3121 } 3122 if (IsConstantIntPtr(rhs, intptr_t(getIdentity()))) { 3123 return lhs; // x op id => x 3124 } 3125 if (isCommutative() && IsConstantIntPtr(lhs, intptr_t(getIdentity()))) { 3126 return rhs; // id op x => x 3127 } 3128 return this; 3129 } 3130 3131 // The remaining operations expect types representable as doubles. 3132 MOZ_ASSERT(IsTypeRepresentableAsDouble(type())); 3133 3134 if (MConstant* folded = EvaluateConstantOperands(alloc, this)) { 3135 if (isTruncated()) { 3136 if (folded->type() != MIRType::Int32) { 3137 if (!folded->block()) { 3138 block()->insertBefore(this, folded); 3139 } 3140 return MTruncateToInt32::New(alloc, folded); 3141 } 3142 } 3143 return folded; 3144 } 3145 3146 if (MConstant* folded = EvaluateConstantNaNOperand(this)) { 3147 MOZ_ASSERT(!isTruncated()); 3148 return folded; 3149 } 3150 3151 if (mustPreserveNaN_) { 3152 return this; 3153 } 3154 3155 // 0 + -0 = 0. So we can't remove addition 3156 if (isAdd() && type() != MIRType::Int32) { 3157 return this; 3158 } 3159 3160 if (IsConstant(rhs, getIdentity())) { 3161 if (isTruncated()) { 3162 return MTruncateToInt32::New(alloc, lhs); 3163 } 3164 return lhs; 3165 } 3166 3167 // subtraction isn't commutative. So we can't remove subtraction when lhs 3168 // equals 0 3169 if (isSub()) { 3170 return this; 3171 } 3172 3173 if (IsConstant(lhs, getIdentity())) { 3174 if (isTruncated()) { 3175 return MTruncateToInt32::New(alloc, rhs); 3176 } 3177 return rhs; // id op x => x 3178 } 3179 3180 return this; 3181 } 3182 3183 void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc) { 3184 MOZ_ASSERT(IsNumberType(type())); 3185 3186 // Do not use Float32 if we can use integer types. 3187 if (!IsFloatingPointType(type())) { 3188 return; 3189 } 3190 3191 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 3192 setResultType(MIRType::Float32); 3193 } 3194 } 3195 3196 void MMinMax::trySpecializeFloat32(TempAllocator& alloc) { 3197 if (!IsFloatingPointType(type())) { 3198 return; 3199 } 3200 3201 MDefinition* left = lhs(); 3202 MDefinition* right = rhs(); 3203 3204 if ((left->canProduceFloat32() || 3205 (left->isMinMax() && left->type() == MIRType::Float32)) && 3206 (right->canProduceFloat32() || 3207 (right->isMinMax() && right->type() == MIRType::Float32))) { 3208 setResultType(MIRType::Float32); 3209 } else { 3210 ConvertOperandsToDouble(this, alloc); 3211 } 3212 } 3213 3214 template <MIRType Type> 3215 static MConstant* EvaluateMinMaxInt(TempAllocator& alloc, MConstant* lhs, 3216 MConstant* rhs, bool isMax) { 3217 auto lnum = ToIntConstant<Type>(lhs); 3218 auto rnum = ToIntConstant<Type>(rhs); 3219 auto result = isMax ? std::max(lnum, rnum) : std::min(lnum, rnum); 3220 return NewIntConstant<Type>(alloc, result); 3221 } 3222 3223 static MConstant* EvaluateMinMax(TempAllocator& alloc, MConstant* lhs, 3224 MConstant* rhs, bool isMax) { 3225 MOZ_ASSERT(lhs->type() == rhs->type()); 3226 MOZ_ASSERT(IsNumberType(lhs->type())); 3227 3228 // The folded MConstant should maintain the same MIRType with the original 3229 // inputs. 3230 switch (lhs->type()) { 3231 case MIRType::Int32: 3232 return EvaluateMinMaxInt<MIRType::Int32>(alloc, lhs, rhs, isMax); 3233 case MIRType::Int64: 3234 return EvaluateMinMaxInt<MIRType::Int64>(alloc, lhs, rhs, isMax); 3235 case MIRType::IntPtr: 3236 return EvaluateMinMaxInt<MIRType::IntPtr>(alloc, lhs, rhs, isMax); 3237 case MIRType::Float32: 3238 case MIRType::Double: { 3239 double lnum = lhs->numberToDouble(); 3240 double rnum = rhs->numberToDouble(); 3241 3242 double result; 3243 if (isMax) { 3244 result = js::math_max_impl(lnum, rnum); 3245 } else { 3246 result = js::math_min_impl(lnum, rnum); 3247 } 3248 3249 if (lhs->type() == MIRType::Float32) { 3250 return MConstant::NewFloat32(alloc, result); 3251 } 3252 return MConstant::NewDouble(alloc, result); 3253 } 3254 default: 3255 MOZ_CRASH("not a number type"); 3256 } 3257 } 3258 3259 MDefinition* MMinMax::foldsTo(TempAllocator& alloc) { 3260 MOZ_ASSERT(lhs()->type() == type()); 3261 MOZ_ASSERT(rhs()->type() == type()); 3262 3263 if (lhs() == rhs()) { 3264 return lhs(); 3265 } 3266 3267 auto foldConstants = [&alloc](MDefinition* lhs, MDefinition* rhs, 3268 bool isMax) -> MConstant* { 3269 return EvaluateMinMax(alloc, lhs->toConstant(), rhs->toConstant(), isMax); 3270 }; 3271 3272 auto foldLength = [](MDefinition* operand, MConstant* constant, 3273 bool isMax) -> MDefinition* { 3274 if (operand->isArrayLength() || operand->isArrayBufferViewLength() || 3275 operand->isArgumentsLength() || operand->isStringLength() || 3276 operand->isNonNegativeIntPtrToInt32()) { 3277 bool isZeroOrNegative; 3278 switch (constant->type()) { 3279 case MIRType::Int32: 3280 isZeroOrNegative = constant->toInt32() <= 0; 3281 break; 3282 case MIRType::IntPtr: 3283 isZeroOrNegative = constant->toIntPtr() <= 0; 3284 break; 3285 default: 3286 isZeroOrNegative = false; 3287 break; 3288 } 3289 3290 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0. 3291 // max(array.length, cte <= 0) = array.length 3292 // min(array.length, cte <= 0) = cte 3293 if (isZeroOrNegative) { 3294 return isMax ? operand : constant; 3295 } 3296 } 3297 return nullptr; 3298 }; 3299 3300 // Try to fold the following patterns when |x| and |y| are constants. 3301 // 3302 // min(min(x, z), min(y, z)) = min(min(x, y), z) 3303 // max(max(x, z), max(y, z)) = max(max(x, y), z) 3304 // max(min(x, z), min(y, z)) = min(max(x, y), z) 3305 // min(max(x, z), max(y, z)) = max(min(x, y), z) 3306 if (lhs()->isMinMax() && rhs()->isMinMax()) { 3307 do { 3308 auto* left = lhs()->toMinMax(); 3309 auto* right = rhs()->toMinMax(); 3310 if (left->isMax() != right->isMax()) { 3311 break; 3312 } 3313 3314 MDefinition* x; 3315 MDefinition* y; 3316 MDefinition* z; 3317 if (left->lhs() == right->lhs()) { 3318 std::tie(x, y, z) = std::tuple{left->rhs(), right->rhs(), left->lhs()}; 3319 } else if (left->lhs() == right->rhs()) { 3320 std::tie(x, y, z) = std::tuple{left->rhs(), right->lhs(), left->lhs()}; 3321 } else if (left->rhs() == right->lhs()) { 3322 std::tie(x, y, z) = std::tuple{left->lhs(), right->rhs(), left->rhs()}; 3323 } else if (left->rhs() == right->rhs()) { 3324 std::tie(x, y, z) = std::tuple{left->lhs(), right->lhs(), left->rhs()}; 3325 } else { 3326 break; 3327 } 3328 3329 if (!x->isConstant() || !y->isConstant()) { 3330 break; 3331 } 3332 3333 if (auto* foldedCst = foldConstants(x, y, isMax())) { 3334 if (auto* folded = foldLength(z, foldedCst, left->isMax())) { 3335 return folded; 3336 } 3337 block()->insertBefore(this, foldedCst); 3338 return MMinMax::New(alloc, foldedCst, z, type(), left->isMax()); 3339 } 3340 } while (false); 3341 } 3342 3343 // Fold min/max operations with same inputs. 3344 if (lhs()->isMinMax() || rhs()->isMinMax()) { 3345 auto* other = lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax(); 3346 auto* operand = lhs()->isMinMax() ? rhs() : lhs(); 3347 3348 if (operand == other->lhs() || operand == other->rhs()) { 3349 if (isMax() == other->isMax()) { 3350 // min(x, min(x, y)) = min(x, y) 3351 // max(x, max(x, y)) = max(x, y) 3352 return other; 3353 } 3354 if (!IsFloatingPointType(type())) { 3355 // When neither value is NaN: 3356 // max(x, min(x, y)) = x 3357 // min(x, max(x, y)) = x 3358 3359 // Ensure that any bailouts that we depend on to guarantee that |y| is 3360 // Int32 are not removed. 3361 auto* otherOp = operand == other->lhs() ? other->rhs() : other->lhs(); 3362 otherOp->setGuardRangeBailoutsUnchecked(); 3363 3364 return operand; 3365 } 3366 } 3367 } 3368 3369 if (!lhs()->isConstant() && !rhs()->isConstant()) { 3370 return this; 3371 } 3372 3373 // Directly apply math utility to compare the rhs() and lhs() when 3374 // they are both constants. 3375 if (lhs()->isConstant() && rhs()->isConstant()) { 3376 if (auto* folded = foldConstants(lhs(), rhs(), isMax())) { 3377 return folded; 3378 } 3379 } 3380 3381 MDefinition* operand = lhs()->isConstant() ? rhs() : lhs(); 3382 MConstant* constant = 3383 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant(); 3384 3385 if (operand->isToDouble() && 3386 operand->getOperand(0)->type() == MIRType::Int32) { 3387 MOZ_ASSERT(constant->type() == MIRType::Double); 3388 3389 // min(int32, cte >= INT32_MAX) = int32 3390 if (!isMax() && constant->toDouble() >= INT32_MAX) { 3391 MLimitedTruncate* limit = MLimitedTruncate::New( 3392 alloc, operand->getOperand(0), TruncateKind::NoTruncate); 3393 block()->insertBefore(this, limit); 3394 MToDouble* toDouble = MToDouble::New(alloc, limit); 3395 return toDouble; 3396 } 3397 3398 // max(int32, cte <= INT32_MIN) = int32 3399 if (isMax() && constant->toDouble() <= INT32_MIN) { 3400 MLimitedTruncate* limit = MLimitedTruncate::New( 3401 alloc, operand->getOperand(0), TruncateKind::NoTruncate); 3402 block()->insertBefore(this, limit); 3403 MToDouble* toDouble = MToDouble::New(alloc, limit); 3404 return toDouble; 3405 } 3406 } 3407 3408 if (auto* folded = foldLength(operand, constant, isMax())) { 3409 return folded; 3410 } 3411 3412 // Attempt to fold nested min/max operations which are produced by 3413 // self-hosted built-in functions. 3414 if (operand->isMinMax()) { 3415 auto* other = operand->toMinMax(); 3416 MOZ_ASSERT(other->lhs()->type() == type()); 3417 MOZ_ASSERT(other->rhs()->type() == type()); 3418 3419 MConstant* otherConstant = nullptr; 3420 MDefinition* otherOperand = nullptr; 3421 if (other->lhs()->isConstant()) { 3422 otherConstant = other->lhs()->toConstant(); 3423 otherOperand = other->rhs(); 3424 } else if (other->rhs()->isConstant()) { 3425 otherConstant = other->rhs()->toConstant(); 3426 otherOperand = other->lhs(); 3427 } 3428 3429 if (otherConstant) { 3430 if (isMax() == other->isMax()) { 3431 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y). 3432 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y). 3433 if (auto* left = foldConstants(constant, otherConstant, isMax())) { 3434 if (auto* folded = foldLength(otherOperand, left, isMax())) { 3435 return folded; 3436 } 3437 block()->insertBefore(this, left); 3438 return MMinMax::New(alloc, left, otherOperand, type(), isMax()); 3439 } 3440 } else { 3441 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)). 3442 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)). 3443 // 3444 // But only do this when min(x, z) can also be simplified. 3445 if (auto* right = foldLength(otherOperand, constant, isMax())) { 3446 if (auto* left = foldConstants(constant, otherConstant, isMax())) { 3447 block()->insertBefore(this, left); 3448 return MMinMax::New(alloc, left, right, type(), !isMax()); 3449 } 3450 } 3451 } 3452 } 3453 } 3454 3455 return this; 3456 } 3457 3458 #ifdef JS_JITSPEW 3459 void MMinMax::printOpcode(GenericPrinter& out) const { 3460 MDefinition::printOpcode(out); 3461 out.printf(" (%s)", isMax() ? "max" : "min"); 3462 } 3463 3464 void MMinMaxArray::printOpcode(GenericPrinter& out) const { 3465 MDefinition::printOpcode(out); 3466 out.printf(" (%s)", isMax() ? "max" : "min"); 3467 } 3468 #endif 3469 3470 MDefinition* MPow::foldsConstant(TempAllocator& alloc) { 3471 // Both `x` and `p` in `x^p` must be constants in order to precompute. 3472 if (!input()->isConstant() || !power()->isConstant()) { 3473 return nullptr; 3474 } 3475 if (!power()->toConstant()->isTypeRepresentableAsDouble()) { 3476 return nullptr; 3477 } 3478 if (!input()->toConstant()->isTypeRepresentableAsDouble()) { 3479 return nullptr; 3480 } 3481 3482 double x = input()->toConstant()->numberToDouble(); 3483 double p = power()->toConstant()->numberToDouble(); 3484 double result = js::ecmaPow(x, p); 3485 if (type() == MIRType::Int32) { 3486 int32_t cast; 3487 if (!mozilla::NumberIsInt32(result, &cast)) { 3488 // Reject folding if the result isn't an int32, because we'll bail anyway. 3489 return nullptr; 3490 } 3491 return MConstant::NewInt32(alloc, cast); 3492 } 3493 return MConstant::NewDouble(alloc, result); 3494 } 3495 3496 MDefinition* MPow::foldsConstantPower(TempAllocator& alloc) { 3497 // If `p` in `x^p` isn't constant, we can't apply these folds. 3498 if (!power()->isConstant()) { 3499 return nullptr; 3500 } 3501 if (!power()->toConstant()->isTypeRepresentableAsDouble()) { 3502 return nullptr; 3503 } 3504 3505 MOZ_ASSERT(type() == MIRType::Double || type() == MIRType::Int32); 3506 3507 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow| 3508 // resp. |js::powi| to avoid differential testing issues. 3509 3510 double pow = power()->toConstant()->numberToDouble(); 3511 3512 // Math.pow(x, 0.5) is a sqrt with edge-case detection. 3513 if (pow == 0.5) { 3514 MOZ_ASSERT(type() == MIRType::Double); 3515 return MPowHalf::New(alloc, input()); 3516 } 3517 3518 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases. 3519 if (pow == -0.5) { 3520 MOZ_ASSERT(type() == MIRType::Double); 3521 MPowHalf* half = MPowHalf::New(alloc, input()); 3522 block()->insertBefore(this, half); 3523 MConstant* one = MConstant::NewDouble(alloc, 1.0); 3524 block()->insertBefore(this, one); 3525 return MDiv::New(alloc, one, half, MIRType::Double); 3526 } 3527 3528 // Math.pow(x, 1) == x. 3529 if (pow == 1.0) { 3530 return input(); 3531 } 3532 3533 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) { 3534 MMul* mul = MMul::New(alloc, lhs, rhs, type()); 3535 mul->setBailoutKind(bailoutKind()); 3536 3537 // Multiplying the same number can't yield negative zero. 3538 mul->setCanBeNegativeZero(lhs != rhs && canBeNegativeZero()); 3539 return mul; 3540 }; 3541 3542 // Math.pow(x, 2) == x*x. 3543 if (pow == 2.0) { 3544 return multiply(input(), input()); 3545 } 3546 3547 // Math.pow(x, 3) == x*x*x. 3548 if (pow == 3.0) { 3549 MMul* mul1 = multiply(input(), input()); 3550 block()->insertBefore(this, mul1); 3551 return multiply(input(), mul1); 3552 } 3553 3554 // Math.pow(x, 4) == y*y, where y = x*x. 3555 if (pow == 4.0) { 3556 MMul* y = multiply(input(), input()); 3557 block()->insertBefore(this, y); 3558 return multiply(y, y); 3559 } 3560 3561 // Math.pow(x, NaN) == NaN. 3562 if (std::isnan(pow)) { 3563 return power(); 3564 } 3565 3566 // No optimization 3567 return nullptr; 3568 } 3569 3570 MDefinition* MPow::foldsTo(TempAllocator& alloc) { 3571 if (MDefinition* def = foldsConstant(alloc)) { 3572 return def; 3573 } 3574 if (MDefinition* def = foldsConstantPower(alloc)) { 3575 return def; 3576 } 3577 return this; 3578 } 3579 3580 MDefinition* MBigIntPow::foldsTo(TempAllocator& alloc) { 3581 auto* base = lhs(); 3582 MOZ_ASSERT(base->type() == MIRType::BigInt); 3583 3584 auto* power = rhs(); 3585 MOZ_ASSERT(power->type() == MIRType::BigInt); 3586 3587 // |power| must be a constant. 3588 if (!power->isConstant()) { 3589 return this; 3590 } 3591 3592 int32_t pow; 3593 if (BigInt::isInt32(power->toConstant()->toBigInt(), &pow)) { 3594 // x ** 1n == x. 3595 if (pow == 1) { 3596 return base; 3597 } 3598 3599 // x ** 2n == x*x. 3600 if (pow == 2) { 3601 auto* mul = MBigIntMul::New(alloc, base, base); 3602 mul->setBailoutKind(bailoutKind()); 3603 return mul; 3604 } 3605 } 3606 3607 // No optimization 3608 return this; 3609 } 3610 3611 MDefinition* MBigIntAsIntN::foldsTo(TempAllocator& alloc) { 3612 auto* bitsDef = bits(); 3613 if (!bitsDef->isConstant()) { 3614 return this; 3615 } 3616 3617 // Negative |bits| throw an error and too large |bits| don't fit into Int64. 3618 int32_t bitsInt = bitsDef->toConstant()->toInt32(); 3619 if (bitsInt < 0 || bitsInt > 64) { 3620 return this; 3621 } 3622 3623 // Prefer sign-extension if possible. 3624 bool canSignExtend = false; 3625 switch (bitsInt) { 3626 case 8: 3627 case 16: 3628 case 32: 3629 case 64: 3630 canSignExtend = true; 3631 break; 3632 } 3633 3634 // Ensure the input is either IntPtr or Int64 typed. 3635 auto* inputDef = input(); 3636 if (inputDef->isIntPtrToBigInt()) { 3637 inputDef = inputDef->toIntPtrToBigInt()->input(); 3638 3639 if (!canSignExtend) { 3640 auto* int64 = MIntPtrToInt64::New(alloc, inputDef); 3641 block()->insertBefore(this, int64); 3642 inputDef = int64; 3643 } 3644 } else if (inputDef->isInt64ToBigInt()) { 3645 inputDef = inputDef->toInt64ToBigInt()->input(); 3646 } else { 3647 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef); 3648 block()->insertBefore(this, truncate); 3649 inputDef = truncate; 3650 } 3651 3652 if (inputDef->type() == MIRType::IntPtr) { 3653 MOZ_ASSERT(canSignExtend); 3654 3655 // If |bits| is larger-or-equal to |BigInt::DigitBits|, return the input. 3656 if (size_t(bitsInt) >= BigInt::DigitBits) { 3657 auto* limited = MIntPtrLimitedTruncate::New(alloc, inputDef); 3658 block()->insertBefore(this, limited); 3659 inputDef = limited; 3660 } else { 3661 MOZ_ASSERT(bitsInt < 64); 3662 3663 // Otherwise extension is the way to go. 3664 MSignExtendIntPtr::Mode mode; 3665 switch (bitsInt) { 3666 case 8: 3667 mode = MSignExtendIntPtr::Byte; 3668 break; 3669 case 16: 3670 mode = MSignExtendIntPtr::Half; 3671 break; 3672 case 32: 3673 mode = MSignExtendIntPtr::Word; 3674 break; 3675 } 3676 3677 auto* extend = MSignExtendIntPtr::New(alloc, inputDef, mode); 3678 block()->insertBefore(this, extend); 3679 inputDef = extend; 3680 } 3681 3682 return MIntPtrToBigInt::New(alloc, inputDef); 3683 } 3684 MOZ_ASSERT(inputDef->type() == MIRType::Int64); 3685 3686 if (canSignExtend) { 3687 // If |bits| is equal to 64, return the input. 3688 if (bitsInt == 64) { 3689 auto* limited = MInt64LimitedTruncate::New(alloc, inputDef); 3690 block()->insertBefore(this, limited); 3691 inputDef = limited; 3692 } else { 3693 MOZ_ASSERT(bitsInt < 64); 3694 3695 // Otherwise extension is the way to go. 3696 MSignExtendInt64::Mode mode; 3697 switch (bitsInt) { 3698 case 8: 3699 mode = MSignExtendInt64::Byte; 3700 break; 3701 case 16: 3702 mode = MSignExtendInt64::Half; 3703 break; 3704 case 32: 3705 mode = MSignExtendInt64::Word; 3706 break; 3707 } 3708 3709 auto* extend = MSignExtendInt64::New(alloc, inputDef, mode); 3710 block()->insertBefore(this, extend); 3711 inputDef = extend; 3712 } 3713 } else { 3714 MOZ_ASSERT(bitsInt < 64); 3715 3716 uint64_t mask = 0; 3717 if (bitsInt > 0) { 3718 mask = uint64_t(-1) >> (64 - bitsInt); 3719 } 3720 3721 auto* cst = MConstant::NewInt64(alloc, int64_t(mask)); 3722 block()->insertBefore(this, cst); 3723 3724 // Mask off any excess bits. 3725 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64); 3726 block()->insertBefore(this, bitAnd); 3727 3728 auto* shift = MConstant::NewInt64(alloc, int64_t(64 - bitsInt)); 3729 block()->insertBefore(this, shift); 3730 3731 // Left-shift to make the sign-bit the left-most bit. 3732 auto* lsh = MLsh::New(alloc, bitAnd, shift, MIRType::Int64); 3733 block()->insertBefore(this, lsh); 3734 3735 // Right-shift to propagate the sign-bit. 3736 auto* rsh = MRsh::New(alloc, lsh, shift, MIRType::Int64); 3737 block()->insertBefore(this, rsh); 3738 3739 inputDef = rsh; 3740 } 3741 3742 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ true); 3743 } 3744 3745 MDefinition* MBigIntAsUintN::foldsTo(TempAllocator& alloc) { 3746 auto* bitsDef = bits(); 3747 if (!bitsDef->isConstant()) { 3748 return this; 3749 } 3750 3751 // Negative |bits| throw an error and too large |bits| don't fit into Int64. 3752 int32_t bitsInt = bitsDef->toConstant()->toInt32(); 3753 if (bitsInt < 0 || bitsInt > 64) { 3754 return this; 3755 } 3756 3757 // Ensure the input is Int64 typed. 3758 auto* inputDef = input(); 3759 if (inputDef->isIntPtrToBigInt()) { 3760 inputDef = inputDef->toIntPtrToBigInt()->input(); 3761 3762 auto* int64 = MIntPtrToInt64::New(alloc, inputDef); 3763 block()->insertBefore(this, int64); 3764 inputDef = int64; 3765 } else if (inputDef->isInt64ToBigInt()) { 3766 inputDef = inputDef->toInt64ToBigInt()->input(); 3767 } else { 3768 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef); 3769 block()->insertBefore(this, truncate); 3770 inputDef = truncate; 3771 } 3772 MOZ_ASSERT(inputDef->type() == MIRType::Int64); 3773 3774 if (bitsInt < 64) { 3775 uint64_t mask = 0; 3776 if (bitsInt > 0) { 3777 mask = uint64_t(-1) >> (64 - bitsInt); 3778 } 3779 3780 // Mask off any excess bits. 3781 auto* cst = MConstant::NewInt64(alloc, int64_t(mask)); 3782 block()->insertBefore(this, cst); 3783 3784 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64); 3785 block()->insertBefore(this, bitAnd); 3786 3787 inputDef = bitAnd; 3788 } 3789 3790 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ false); 3791 } 3792 3793 bool MBigIntPtrBinaryArithInstruction::isMaybeZero(MDefinition* ins) { 3794 MOZ_ASSERT(ins->type() == MIRType::IntPtr); 3795 if (ins->isBigIntToIntPtr()) { 3796 ins = ins->toBigIntToIntPtr()->input(); 3797 } 3798 if (ins->isConstant()) { 3799 if (ins->type() == MIRType::IntPtr) { 3800 return ins->toConstant()->toIntPtr() == 0; 3801 } 3802 MOZ_ASSERT(ins->type() == MIRType::BigInt); 3803 return ins->toConstant()->toBigInt()->isZero(); 3804 } 3805 return true; 3806 } 3807 3808 bool MBigIntPtrBinaryArithInstruction::isMaybeNegative(MDefinition* ins) { 3809 MOZ_ASSERT(ins->type() == MIRType::IntPtr); 3810 if (ins->isBigIntToIntPtr()) { 3811 ins = ins->toBigIntToIntPtr()->input(); 3812 } 3813 if (ins->isConstant()) { 3814 if (ins->type() == MIRType::IntPtr) { 3815 return ins->toConstant()->toIntPtr() < 0; 3816 } 3817 MOZ_ASSERT(ins->type() == MIRType::BigInt); 3818 return ins->toConstant()->toBigInt()->isNegative(); 3819 } 3820 return true; 3821 } 3822 3823 MDefinition* MBigIntPtrBinaryArithInstruction::foldsTo(TempAllocator& alloc) { 3824 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) { 3825 return folded; 3826 } 3827 return this; 3828 } 3829 3830 MDefinition* MBigIntPtrPow::foldsTo(TempAllocator& alloc) { 3831 // Follow MPow::foldsTo and fold if: 3832 // 1. Both operands are constants. 3833 // 2. The power operand is ≤ 4 and the operation can be expressed as a series 3834 // of multiplications. 3835 3836 if (!rhs()->isConstant()) { 3837 return this; 3838 } 3839 intptr_t pow = rhs()->toConstant()->toIntPtr(); 3840 3841 if (lhs()->isConstant()) { 3842 intptr_t base = lhs()->toConstant()->toIntPtr(); 3843 intptr_t result; 3844 if (!BigInt::powIntPtr(base, pow, &result)) { 3845 return this; 3846 } 3847 return MConstant::NewIntPtr(alloc, result); 3848 } 3849 3850 if (pow == 1) { 3851 return lhs(); 3852 } 3853 3854 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) { 3855 auto* mul = MBigIntPtrMul::New(alloc, lhs, rhs); 3856 mul->setBailoutKind(bailoutKind()); 3857 return mul; 3858 }; 3859 3860 // (x ** 2n) == x*x. 3861 if (pow == 2) { 3862 return multiply(lhs(), lhs()); 3863 } 3864 3865 // (x ** 3n) == x*x*x. 3866 if (pow == 3) { 3867 auto* mul1 = multiply(lhs(), lhs()); 3868 block()->insertBefore(this, mul1); 3869 return multiply(lhs(), mul1); 3870 } 3871 3872 // (x ** 4n) == y*y, where y = x*x. 3873 if (pow == 4) { 3874 auto* y = multiply(lhs(), lhs()); 3875 block()->insertBefore(this, y); 3876 return multiply(y, y); 3877 } 3878 3879 // No optimization 3880 return this; 3881 } 3882 3883 MDefinition* MBigIntPtrBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) { 3884 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) { 3885 return folded; 3886 } 3887 return this; 3888 } 3889 3890 MDefinition* MBigIntPtrBitNot::foldsTo(TempAllocator& alloc) { 3891 if (!input()->isConstant()) { 3892 return this; 3893 } 3894 return MConstant::NewIntPtr(alloc, ~input()->toConstant()->toIntPtr()); 3895 } 3896 3897 MDefinition* MInt32ToIntPtr::foldsTo(TempAllocator& alloc) { 3898 MDefinition* def = input(); 3899 if (def->isConstant()) { 3900 int32_t i = def->toConstant()->toInt32(); 3901 return MConstant::NewIntPtr(alloc, intptr_t(i)); 3902 } 3903 3904 if (def->isNonNegativeIntPtrToInt32()) { 3905 return def->toNonNegativeIntPtrToInt32()->input(); 3906 } 3907 3908 return this; 3909 } 3910 3911 bool MAbs::fallible() const { 3912 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds()); 3913 } 3914 3915 void MAbs::trySpecializeFloat32(TempAllocator& alloc) { 3916 // Do not use Float32 if we can use int32. 3917 if (input()->type() == MIRType::Int32) { 3918 return; 3919 } 3920 3921 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 3922 setResultType(MIRType::Float32); 3923 } 3924 } 3925 3926 MDefinition* MDiv::foldsTo(TempAllocator& alloc) { 3927 MOZ_ASSERT(IsNumberType(type())); 3928 MOZ_ASSERT(type() != MIRType::IntPtr, "not yet implemented"); 3929 3930 if (type() == MIRType::Int64) { 3931 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) { 3932 return folded; 3933 } 3934 return this; 3935 } 3936 3937 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) { 3938 return folded; 3939 } 3940 3941 if (MDefinition* folded = EvaluateExactReciprocal(alloc, this)) { 3942 return folded; 3943 } 3944 3945 return this; 3946 } 3947 3948 void MDiv::analyzeEdgeCasesForward() { 3949 // This is only meaningful when doing integer division. 3950 if (type() != MIRType::Int32) { 3951 return; 3952 } 3953 3954 MOZ_ASSERT(lhs()->type() == MIRType::Int32); 3955 MOZ_ASSERT(rhs()->type() == MIRType::Int32); 3956 3957 // Try removing divide by zero check 3958 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) { 3959 canBeDivideByZero_ = false; 3960 } 3961 3962 // If lhs is a constant int != INT32_MIN, then 3963 // negative overflow check can be skipped. 3964 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN)) { 3965 canBeNegativeOverflow_ = false; 3966 } 3967 3968 // If rhs is a constant int != -1, likewise. 3969 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) { 3970 canBeNegativeOverflow_ = false; 3971 } 3972 3973 // If lhs is != 0, then negative zero check can be skipped. 3974 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) { 3975 setCanBeNegativeZero(false); 3976 } 3977 3978 // If rhs is >= 0, likewise. 3979 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) { 3980 if (rhs()->toConstant()->toInt32() >= 0) { 3981 setCanBeNegativeZero(false); 3982 } 3983 } 3984 } 3985 3986 void MDiv::analyzeEdgeCasesBackward() { 3987 // In general, canBeNegativeZero_ is only valid for integer divides. 3988 // It's fine to access here because we're only using it to avoid 3989 // wasting effort to decide whether we can clear an already cleared 3990 // flag. 3991 if (canBeNegativeZero_ && !NeedNegativeZeroCheck(this)) { 3992 setCanBeNegativeZero(false); 3993 } 3994 } 3995 3996 bool MDiv::fallible() const { return !isTruncated(); } 3997 3998 MDefinition* MMod::foldsTo(TempAllocator& alloc) { 3999 MOZ_ASSERT(IsNumberType(type())); 4000 MOZ_ASSERT(type() != MIRType::IntPtr, "not yet implemented"); 4001 4002 if (type() == MIRType::Int64) { 4003 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) { 4004 return folded; 4005 } 4006 } else { 4007 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) { 4008 return folded; 4009 } 4010 } 4011 return this; 4012 } 4013 4014 void MMod::analyzeEdgeCasesForward() { 4015 // These optimizations make sense only for integer division 4016 if (type() != MIRType::Int32) { 4017 return; 4018 } 4019 4020 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) { 4021 canBeDivideByZero_ = false; 4022 } 4023 4024 if (rhs()->isConstant()) { 4025 int32_t n = rhs()->toConstant()->toInt32(); 4026 if (n > 0 && !IsPowerOfTwo(uint32_t(n))) { 4027 canBePowerOfTwoDivisor_ = false; 4028 } 4029 } 4030 } 4031 4032 bool MMod::fallible() const { 4033 return !isTruncated() && 4034 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend()); 4035 } 4036 4037 void MMathFunction::trySpecializeFloat32(TempAllocator& alloc) { 4038 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 4039 setResultType(MIRType::Float32); 4040 specialization_ = MIRType::Float32; 4041 } 4042 } 4043 4044 bool MMathFunction::isFloat32Commutative() const { 4045 switch (function_) { 4046 case UnaryMathFunction::Floor: 4047 case UnaryMathFunction::Ceil: 4048 case UnaryMathFunction::Round: 4049 case UnaryMathFunction::Trunc: 4050 return true; 4051 default: 4052 return false; 4053 } 4054 } 4055 4056 MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) { 4057 uint32_t length = vector.length(); 4058 MHypot* hypot = new (alloc) MHypot; 4059 if (!hypot->init(alloc, length)) { 4060 return nullptr; 4061 } 4062 4063 for (uint32_t i = 0; i < length; ++i) { 4064 hypot->initOperand(i, vector[i]); 4065 } 4066 return hypot; 4067 } 4068 4069 bool MAdd::fallible() const { 4070 // the add is fallible if range analysis does not say that it is finite, AND 4071 // either the truncation analysis shows that there are non-truncated uses. 4072 if (truncateKind() >= TruncateKind::IndirectTruncate) { 4073 return false; 4074 } 4075 if (range() && range()->hasInt32Bounds()) { 4076 return false; 4077 } 4078 return true; 4079 } 4080 4081 bool MSub::fallible() const { 4082 // see comment in MAdd::fallible() 4083 if (truncateKind() >= TruncateKind::IndirectTruncate) { 4084 return false; 4085 } 4086 if (range() && range()->hasInt32Bounds()) { 4087 return false; 4088 } 4089 return true; 4090 } 4091 4092 MDefinition* MSub::foldsTo(TempAllocator& alloc) { 4093 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc); 4094 if (out != this) { 4095 return out; 4096 } 4097 4098 // Optimize X - X to 0. This optimization is only valid for integer values. 4099 // Subtracting a floating point value from itself returns NaN when the operand 4100 // is either Infinity or NaN. 4101 if (lhs() == rhs()) { 4102 switch (type()) { 4103 case MIRType::Int32: 4104 // Ensure that any bailouts that we depend on to guarantee that X 4105 // is Int32 are not removed. 4106 lhs()->setGuardRangeBailoutsUnchecked(); 4107 return MConstant::NewInt32(alloc, 0); 4108 case MIRType::Int64: 4109 return MConstant::NewInt64(alloc, 0); 4110 case MIRType::IntPtr: 4111 return MConstant::NewIntPtr(alloc, 0); 4112 default: 4113 MOZ_ASSERT(IsFloatingPointType(type())); 4114 } 4115 } 4116 4117 return this; 4118 } 4119 4120 MDefinition* MMul::foldsTo(TempAllocator& alloc) { 4121 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc); 4122 if (out != this) { 4123 return out; 4124 } 4125 4126 if (type() != MIRType::Int32) { 4127 return this; 4128 } 4129 4130 if (lhs() == rhs()) { 4131 setCanBeNegativeZero(false); 4132 } 4133 4134 return this; 4135 } 4136 4137 void MMul::analyzeEdgeCasesForward() { 4138 // Try to remove the check for negative zero 4139 // This only makes sense when using the integer multiplication 4140 if (type() != MIRType::Int32) { 4141 return; 4142 } 4143 4144 // If lhs is > 0, no need for negative zero check. 4145 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) { 4146 if (lhs()->toConstant()->toInt32() > 0) { 4147 setCanBeNegativeZero(false); 4148 } 4149 } 4150 4151 // If rhs is > 0, likewise. 4152 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) { 4153 if (rhs()->toConstant()->toInt32() > 0) { 4154 setCanBeNegativeZero(false); 4155 } 4156 } 4157 } 4158 4159 void MMul::analyzeEdgeCasesBackward() { 4160 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) { 4161 setCanBeNegativeZero(false); 4162 } 4163 } 4164 4165 bool MMul::canOverflow() const { 4166 if (isTruncated()) { 4167 return false; 4168 } 4169 return !range() || !range()->hasInt32Bounds(); 4170 } 4171 4172 bool MUrsh::fallible() const { 4173 if (bailoutsDisabled()) { 4174 return false; 4175 } 4176 return !range() || !range()->hasInt32Bounds(); 4177 } 4178 4179 static inline bool MustBeUInt32(MDefinition* def, MDefinition** pwrapped) { 4180 if (def->isUrsh()) { 4181 *pwrapped = def->toUrsh()->lhs(); 4182 MDefinition* rhs = def->toUrsh()->rhs(); 4183 return def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() && 4184 rhs->maybeConstantValue()->isInt32(0); 4185 } 4186 4187 if (MConstant* defConst = def->maybeConstantValue()) { 4188 *pwrapped = defConst; 4189 return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0; 4190 } 4191 4192 *pwrapped = nullptr; // silence GCC warning 4193 return false; 4194 } 4195 4196 /* static */ 4197 bool MBinaryInstruction::unsignedOperands(MDefinition* left, 4198 MDefinition* right) { 4199 MDefinition* replace; 4200 if (!MustBeUInt32(left, &replace)) { 4201 return false; 4202 } 4203 if (replace->type() != MIRType::Int32) { 4204 return false; 4205 } 4206 if (!MustBeUInt32(right, &replace)) { 4207 return false; 4208 } 4209 if (replace->type() != MIRType::Int32) { 4210 return false; 4211 } 4212 return true; 4213 } 4214 4215 bool MBinaryInstruction::unsignedOperands() { 4216 return unsignedOperands(getOperand(0), getOperand(1)); 4217 } 4218 4219 void MBinaryInstruction::replaceWithUnsignedOperands() { 4220 MOZ_ASSERT(unsignedOperands()); 4221 4222 for (size_t i = 0; i < numOperands(); i++) { 4223 MDefinition* replace; 4224 MustBeUInt32(getOperand(i), &replace); 4225 if (replace == getOperand(i)) { 4226 continue; 4227 } 4228 4229 getOperand(i)->setImplicitlyUsedUnchecked(); 4230 replaceOperand(i, replace); 4231 } 4232 } 4233 4234 MDefinition* MBitNot::foldsTo(TempAllocator& alloc) { 4235 if (type() == MIRType::Int64) { 4236 return this; 4237 } 4238 MOZ_ASSERT(type() == MIRType::Int32); 4239 4240 MDefinition* input = getOperand(0); 4241 4242 if (input->isConstant()) { 4243 int32_t v = ~(input->toConstant()->toInt32()); 4244 return MConstant::NewInt32(alloc, v); 4245 } 4246 4247 if (input->isBitNot()) { 4248 MOZ_ASSERT(input->toBitNot()->type() == MIRType::Int32); 4249 MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType::Int32); 4250 return MTruncateToInt32::New(alloc, 4251 input->toBitNot()->input()); // ~~x => x | 0 4252 } 4253 4254 return this; 4255 } 4256 4257 static void AssertKnownClass(TempAllocator& alloc, MInstruction* ins, 4258 MDefinition* obj) { 4259 #ifdef DEBUG 4260 const JSClass* clasp = GetObjectKnownJSClass(obj); 4261 MOZ_ASSERT(clasp); 4262 4263 auto* assert = MAssertClass::New(alloc, obj, clasp); 4264 ins->block()->insertBefore(ins, assert); 4265 #endif 4266 } 4267 4268 MDefinition* MBoxNonStrictThis::foldsTo(TempAllocator& alloc) { 4269 MDefinition* in = input(); 4270 4271 if (!in->isBox()) { 4272 return this; 4273 } 4274 4275 MDefinition* unboxed = in->toBox()->input(); 4276 if (unboxed->type() == MIRType::Object) { 4277 return unboxed; 4278 } 4279 4280 if (unboxed->typeIsOneOf({MIRType::Undefined, MIRType::Null})) { 4281 return MConstant::NewObject(alloc, this->globalThis()); 4282 } 4283 4284 return this; 4285 } 4286 4287 AliasSet MLoadArgumentsObjectArg::getAliasSet() const { 4288 return AliasSet::Load(AliasSet::Any); 4289 } 4290 4291 AliasSet MLoadArgumentsObjectArgHole::getAliasSet() const { 4292 return AliasSet::Load(AliasSet::Any); 4293 } 4294 4295 AliasSet MInArgumentsObjectArg::getAliasSet() const { 4296 // Loads |arguments.length|, but not the actual element, so we can use the 4297 // same alias-set as MArgumentsObjectLength. 4298 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 4299 AliasSet::DynamicSlot); 4300 } 4301 4302 AliasSet MArgumentsObjectLength::getAliasSet() const { 4303 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 4304 AliasSet::DynamicSlot); 4305 } 4306 4307 bool MGuardArgumentsObjectFlags::congruentTo(const MDefinition* ins) const { 4308 if (!ins->isGuardArgumentsObjectFlags() || 4309 ins->toGuardArgumentsObjectFlags()->flags() != flags()) { 4310 return false; 4311 } 4312 return congruentIfOperandsEqual(ins); 4313 } 4314 4315 AliasSet MGuardArgumentsObjectFlags::getAliasSet() const { 4316 // The flags are packed with the length in a fixed private slot. 4317 return AliasSet::Load(AliasSet::FixedSlot); 4318 } 4319 4320 MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) { 4321 if (idVal()->isBox()) { 4322 auto* input = idVal()->toBox()->input(); 4323 MIRType idType = input->type(); 4324 if (idType == MIRType::String || idType == MIRType::Symbol) { 4325 return idVal(); 4326 } 4327 if (idType == MIRType::Int32) { 4328 auto* toString = 4329 MToString::New(alloc, input, MToString::SideEffectHandling::Bailout); 4330 block()->insertBefore(this, toString); 4331 4332 return MBox::New(alloc, toString); 4333 } 4334 } 4335 4336 return this; 4337 } 4338 4339 MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) { 4340 MDefinition* rval = value(); 4341 if (!rval->isBox()) { 4342 return this; 4343 } 4344 4345 MDefinition* unboxed = rval->toBox()->input(); 4346 if (unboxed->type() == MIRType::Object) { 4347 return unboxed; 4348 } 4349 4350 return object(); 4351 } 4352 4353 MDefinition* MTypeOf::foldsTo(TempAllocator& alloc) { 4354 MDefinition* unboxed = input(); 4355 if (unboxed->isBox()) { 4356 unboxed = unboxed->toBox()->input(); 4357 } 4358 4359 JSType type; 4360 switch (unboxed->type()) { 4361 case MIRType::Double: 4362 case MIRType::Float32: 4363 case MIRType::Int32: 4364 type = JSTYPE_NUMBER; 4365 break; 4366 case MIRType::String: 4367 type = JSTYPE_STRING; 4368 break; 4369 case MIRType::Symbol: 4370 type = JSTYPE_SYMBOL; 4371 break; 4372 case MIRType::BigInt: 4373 type = JSTYPE_BIGINT; 4374 break; 4375 case MIRType::Null: 4376 type = JSTYPE_OBJECT; 4377 break; 4378 case MIRType::Undefined: 4379 type = JSTYPE_UNDEFINED; 4380 break; 4381 case MIRType::Boolean: 4382 type = JSTYPE_BOOLEAN; 4383 break; 4384 case MIRType::Object: { 4385 KnownClass known = GetObjectKnownClass(unboxed); 4386 if (known != KnownClass::None) { 4387 if (known == KnownClass::Function) { 4388 type = JSTYPE_FUNCTION; 4389 } else { 4390 type = JSTYPE_OBJECT; 4391 } 4392 4393 AssertKnownClass(alloc, this, unboxed); 4394 break; 4395 } 4396 [[fallthrough]]; 4397 } 4398 default: 4399 return this; 4400 } 4401 4402 return MConstant::NewInt32(alloc, static_cast<int32_t>(type)); 4403 } 4404 4405 MDefinition* MTypeOfName::foldsTo(TempAllocator& alloc) { 4406 MOZ_ASSERT(input()->type() == MIRType::Int32); 4407 4408 if (!input()->isConstant()) { 4409 return this; 4410 } 4411 4412 static_assert(JSTYPE_UNDEFINED == 0); 4413 4414 int32_t type = input()->toConstant()->toInt32(); 4415 MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT); 4416 4417 JSString* name = 4418 TypeName(static_cast<JSType>(type), GetJitContext()->runtime->names()); 4419 return MConstant::NewString(alloc, name); 4420 } 4421 4422 MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left, 4423 MDefinition* right, MIRType type) { 4424 MUrsh* ins = new (alloc) MUrsh(left, right, type); 4425 4426 // Since Ion has no UInt32 type, we use Int32 and we have a special 4427 // exception to the type rules: we can return values in 4428 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type 4429 // without bailing out. This is necessary because Ion has no UInt32 4430 // type and we can't have bailouts in wasm code. 4431 ins->bailoutsDisabled_ = true; 4432 4433 return ins; 4434 } 4435 4436 MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block, 4437 jsbytecode* pc, ResumeMode mode) { 4438 MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode); 4439 if (!resume->init(alloc)) { 4440 block->discardPreAllocatedResumePoint(resume); 4441 return nullptr; 4442 } 4443 resume->inherit(block); 4444 return resume; 4445 } 4446 4447 MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode) 4448 : MNode(block, Kind::ResumePoint), 4449 pc_(pc), 4450 instruction_(nullptr), 4451 mode_(mode) { 4452 block->addResumePoint(this); 4453 } 4454 4455 bool MResumePoint::init(TempAllocator& alloc) { 4456 return operands_.init(alloc, block()->stackDepth()); 4457 } 4458 4459 MResumePoint* MResumePoint::caller() const { 4460 return block()->callerResumePoint(); 4461 } 4462 4463 void MResumePoint::inherit(MBasicBlock* block) { 4464 // FixedList doesn't initialize its elements, so do unchecked inits. 4465 for (size_t i = 0; i < stackDepth(); i++) { 4466 initOperand(i, block->getSlot(i)); 4467 } 4468 } 4469 4470 void MResumePoint::addStore(TempAllocator& alloc, MDefinition* store, 4471 const MResumePoint* cache) { 4472 MOZ_ASSERT(block()->outerResumePoint() != this); 4473 MOZ_ASSERT_IF(cache, !cache->stores_.empty()); 4474 4475 if (cache && cache->stores_.begin()->operand == store) { 4476 // If the last resume point had the same side-effect stack, then we can 4477 // reuse the current side effect without cloning it. This is a simple 4478 // way to share common context by making a spaghetti stack. 4479 if (++cache->stores_.begin() == stores_.begin()) { 4480 stores_.copy(cache->stores_); 4481 return; 4482 } 4483 } 4484 4485 // Ensure that the store would not be deleted by DCE. 4486 MOZ_ASSERT(store->isEffectful()); 4487 4488 MStoreToRecover* top = new (alloc) MStoreToRecover(store); 4489 stores_.push(top); 4490 } 4491 4492 #ifdef JS_JITSPEW 4493 void MResumePoint::dump(GenericPrinter& out) const { 4494 out.printf("resumepoint mode="); 4495 4496 switch (mode()) { 4497 case ResumeMode::ResumeAt: 4498 if (instruction_) { 4499 out.printf("ResumeAt(%u)", instruction_->id()); 4500 } else { 4501 out.printf("ResumeAt"); 4502 } 4503 break; 4504 default: 4505 out.put(ResumeModeToString(mode())); 4506 break; 4507 } 4508 4509 if (MResumePoint* c = caller()) { 4510 out.printf(" (caller in block%u)", c->block()->id()); 4511 } 4512 4513 for (size_t i = 0; i < numOperands(); i++) { 4514 out.printf(" "); 4515 if (operands_[i].hasProducer()) { 4516 getOperand(i)->printName(out); 4517 } else { 4518 out.printf("(null)"); 4519 } 4520 } 4521 out.printf("\n"); 4522 } 4523 4524 void MResumePoint::dump() const { 4525 Fprinter out(stderr); 4526 dump(out); 4527 out.finish(); 4528 } 4529 #endif 4530 4531 bool MResumePoint::isObservableOperand(MUse* u) const { 4532 return isObservableOperand(indexOf(u)); 4533 } 4534 4535 bool MResumePoint::isObservableOperand(size_t index) const { 4536 return block()->info().isObservableSlot(index); 4537 } 4538 4539 bool MResumePoint::isRecoverableOperand(MUse* u) const { 4540 return block()->info().isRecoverableOperand(indexOf(u)); 4541 } 4542 4543 MDefinition* MBigIntToIntPtr::foldsTo(TempAllocator& alloc) { 4544 MDefinition* def = input(); 4545 4546 // If the operand converts an IntPtr to BigInt, drop both conversions. 4547 if (def->isIntPtrToBigInt()) { 4548 return def->toIntPtrToBigInt()->input(); 4549 } 4550 4551 // Fold this operation if the input operand is constant. 4552 if (def->isConstant()) { 4553 BigInt* bigInt = def->toConstant()->toBigInt(); 4554 intptr_t i; 4555 if (BigInt::isIntPtr(bigInt, &i)) { 4556 return MConstant::NewIntPtr(alloc, i); 4557 } 4558 } 4559 4560 // Fold BigIntToIntPtr(Int64ToBigInt(int64)) to Int64ToIntPtr(int64) 4561 if (def->isInt64ToBigInt()) { 4562 auto* toBigInt = def->toInt64ToBigInt(); 4563 return MInt64ToIntPtr::New(alloc, toBigInt->input(), toBigInt->isSigned()); 4564 } 4565 4566 return this; 4567 } 4568 4569 MDefinition* MIntPtrToBigInt::foldsTo(TempAllocator& alloc) { 4570 MDefinition* def = input(); 4571 4572 // If the operand converts a BigInt to IntPtr, drop both conversions. 4573 if (def->isBigIntToIntPtr()) { 4574 return def->toBigIntToIntPtr()->input(); 4575 } 4576 4577 return this; 4578 } 4579 4580 MDefinition* MTruncateBigIntToInt64::foldsTo(TempAllocator& alloc) { 4581 MDefinition* input = this->input(); 4582 MOZ_ASSERT(input->type() == MIRType::BigInt); 4583 4584 // If the operand converts an I64 to BigInt, drop both conversions. 4585 if (input->isInt64ToBigInt()) { 4586 return input->toInt64ToBigInt()->input(); 4587 } 4588 4589 // If the operand is an IntPtr, extend the IntPtr to I64. 4590 if (input->isIntPtrToBigInt()) { 4591 auto* intPtr = input->toIntPtrToBigInt()->input(); 4592 if (intPtr->isConstant()) { 4593 intptr_t c = intPtr->toConstant()->toIntPtr(); 4594 return MConstant::NewInt64(alloc, int64_t(c)); 4595 } 4596 return MIntPtrToInt64::New(alloc, intPtr); 4597 } 4598 4599 // Fold this operation if the input operand is constant. 4600 if (input->isConstant()) { 4601 return MConstant::NewInt64( 4602 alloc, BigInt::toInt64(input->toConstant()->toBigInt())); 4603 } 4604 4605 return this; 4606 } 4607 4608 MDefinition* MToInt64::foldsTo(TempAllocator& alloc) { 4609 MDefinition* input = getOperand(0); 4610 4611 if (input->isBox()) { 4612 input = input->getOperand(0); 4613 } 4614 4615 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64. 4616 if (input->isInt64ToBigInt()) { 4617 return input->getOperand(0); 4618 } 4619 4620 // Unwrap IntPtrToBigInt: 4621 // MToInt64(MIntPtrToBigInt(intptr)) = MIntPtrToInt64(intptr). 4622 if (input->isIntPtrToBigInt()) { 4623 auto* intPtr = input->toIntPtrToBigInt()->input(); 4624 if (intPtr->isConstant()) { 4625 intptr_t c = intPtr->toConstant()->toIntPtr(); 4626 return MConstant::NewInt64(alloc, int64_t(c)); 4627 } 4628 return MIntPtrToInt64::New(alloc, intPtr); 4629 } 4630 4631 // When the input is an Int64 already, just return it. 4632 if (input->type() == MIRType::Int64) { 4633 return input; 4634 } 4635 4636 // Fold this operation if the input operand is constant. 4637 if (input->isConstant()) { 4638 switch (input->type()) { 4639 case MIRType::Boolean: 4640 return MConstant::NewInt64(alloc, input->toConstant()->toBoolean()); 4641 default: 4642 break; 4643 } 4644 } 4645 4646 return this; 4647 } 4648 4649 MDefinition* MToNumberInt32::foldsTo(TempAllocator& alloc) { 4650 // Fold this operation if the input operand is constant. 4651 if (MConstant* cst = input()->maybeConstantValue()) { 4652 switch (cst->type()) { 4653 case MIRType::Null: 4654 if (conversion() == IntConversionInputKind::Any) { 4655 return MConstant::NewInt32(alloc, 0); 4656 } 4657 break; 4658 case MIRType::Boolean: 4659 if (conversion() == IntConversionInputKind::Any) { 4660 return MConstant::NewInt32(alloc, cst->toBoolean()); 4661 } 4662 break; 4663 case MIRType::Int32: 4664 return MConstant::NewInt32(alloc, cst->toInt32()); 4665 case MIRType::Float32: 4666 case MIRType::Double: 4667 int32_t ival; 4668 // Only the value within the range of Int32 can be substituted as 4669 // constant. 4670 if (mozilla::NumberIsInt32(cst->numberToDouble(), &ival)) { 4671 return MConstant::NewInt32(alloc, ival); 4672 } 4673 break; 4674 default: 4675 break; 4676 } 4677 } 4678 4679 MDefinition* input = getOperand(0); 4680 if (input->isBox()) { 4681 input = input->toBox()->input(); 4682 } 4683 4684 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh 4685 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js, 4686 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator 4687 // is folded to a MTruncateToInt32 node, which will result in this MIR: 4688 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that 4689 // the MUrsh node's type is int32 (since uint32 is not implemented), and 4690 // that would fold the MTruncateToInt32 node. This will make the modulo 4691 // unsigned, while is should have been signed. 4692 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) { 4693 return input; 4694 } 4695 4696 return this; 4697 } 4698 4699 MDefinition* MBooleanToInt32::foldsTo(TempAllocator& alloc) { 4700 MDefinition* input = getOperand(0); 4701 MOZ_ASSERT(input->type() == MIRType::Boolean); 4702 4703 if (input->isConstant()) { 4704 return MConstant::NewInt32(alloc, input->toConstant()->toBoolean()); 4705 } 4706 4707 return this; 4708 } 4709 4710 void MToNumberInt32::analyzeEdgeCasesBackward() { 4711 if (!NeedNegativeZeroCheck(this)) { 4712 setNeedsNegativeZeroCheck(false); 4713 } 4714 } 4715 4716 MDefinition* MTruncateToInt32::foldsTo(TempAllocator& alloc) { 4717 MDefinition* input = getOperand(0); 4718 if (input->isBox()) { 4719 input = input->getOperand(0); 4720 } 4721 4722 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh 4723 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js, 4724 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator 4725 // is folded to a MTruncateToInt32 node, which will result in this MIR: 4726 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that 4727 // the MUrsh node's type is int32 (since uint32 is not implemented), and 4728 // that would fold the MTruncateToInt32 node. This will make the modulo 4729 // unsigned, while is should have been signed. 4730 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) { 4731 return input; 4732 } 4733 4734 if (input->type() == MIRType::Double && input->isConstant()) { 4735 int32_t ret = ToInt32(input->toConstant()->toDouble()); 4736 return MConstant::NewInt32(alloc, ret); 4737 } 4738 4739 return this; 4740 } 4741 4742 MDefinition* MWrapInt64ToInt32::foldsTo(TempAllocator& alloc) { 4743 MDefinition* input = this->input(); 4744 if (input->isConstant()) { 4745 uint64_t c = input->toConstant()->toInt64(); 4746 int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32); 4747 return MConstant::NewInt32(alloc, output); 4748 } 4749 4750 return this; 4751 } 4752 4753 MDefinition* MExtendInt32ToInt64::foldsTo(TempAllocator& alloc) { 4754 MDefinition* input = this->input(); 4755 if (input->isConstant()) { 4756 int32_t c = input->toConstant()->toInt32(); 4757 int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c); 4758 return MConstant::NewInt64(alloc, res); 4759 } 4760 4761 return this; 4762 } 4763 4764 MDefinition* MSignExtendInt32::foldsTo(TempAllocator& alloc) { 4765 MDefinition* input = this->input(); 4766 if (input->isConstant()) { 4767 int32_t c = input->toConstant()->toInt32(); 4768 int32_t res; 4769 switch (mode_) { 4770 case Byte: 4771 res = int32_t(int8_t(c & 0xFF)); 4772 break; 4773 case Half: 4774 res = int32_t(int16_t(c & 0xFFFF)); 4775 break; 4776 } 4777 return MConstant::NewInt32(alloc, res); 4778 } 4779 4780 return this; 4781 } 4782 4783 MDefinition* MSignExtendInt64::foldsTo(TempAllocator& alloc) { 4784 MDefinition* input = this->input(); 4785 if (input->isConstant()) { 4786 int64_t c = input->toConstant()->toInt64(); 4787 int64_t res; 4788 switch (mode_) { 4789 case Byte: 4790 res = int64_t(int8_t(c & 0xFF)); 4791 break; 4792 case Half: 4793 res = int64_t(int16_t(c & 0xFFFF)); 4794 break; 4795 case Word: 4796 res = int64_t(int32_t(c & 0xFFFFFFFFU)); 4797 break; 4798 } 4799 return MConstant::NewInt64(alloc, res); 4800 } 4801 4802 return this; 4803 } 4804 4805 MDefinition* MSignExtendIntPtr::foldsTo(TempAllocator& alloc) { 4806 MDefinition* input = this->input(); 4807 if (input->isConstant()) { 4808 intptr_t c = input->toConstant()->toIntPtr(); 4809 intptr_t res; 4810 switch (mode_) { 4811 case Byte: 4812 res = intptr_t(int8_t(c & 0xFF)); 4813 break; 4814 case Half: 4815 res = intptr_t(int16_t(c & 0xFFFF)); 4816 break; 4817 case Word: 4818 res = intptr_t(int32_t(c & 0xFFFFFFFFU)); 4819 break; 4820 } 4821 return MConstant::NewIntPtr(alloc, res); 4822 } 4823 4824 return this; 4825 } 4826 4827 MDefinition* MToDouble::foldsTo(TempAllocator& alloc) { 4828 MDefinition* input = getOperand(0); 4829 if (input->isBox()) { 4830 input = input->getOperand(0); 4831 } 4832 4833 if (input->type() == MIRType::Double) { 4834 return input; 4835 } 4836 4837 if (input->isConstant() && 4838 input->toConstant()->isTypeRepresentableAsDouble()) { 4839 return MConstant::NewDouble(alloc, input->toConstant()->numberToDouble()); 4840 } 4841 4842 return this; 4843 } 4844 4845 MDefinition* MToFloat32::foldsTo(TempAllocator& alloc) { 4846 MDefinition* input = getOperand(0); 4847 if (input->isBox()) { 4848 input = input->getOperand(0); 4849 } 4850 4851 if (input->type() == MIRType::Float32) { 4852 return input; 4853 } 4854 4855 // If x is a Float32, Float32(Double(x)) == x 4856 if (!mustPreserveNaN_ && input->isToDouble() && 4857 input->toToDouble()->input()->type() == MIRType::Float32) { 4858 return input->toToDouble()->input(); 4859 } 4860 4861 if (input->isConstant() && 4862 input->toConstant()->isTypeRepresentableAsDouble()) { 4863 return MConstant::NewFloat32(alloc, 4864 float(input->toConstant()->numberToDouble())); 4865 } 4866 4867 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32). 4868 if (input->isToDouble() && 4869 input->toToDouble()->input()->type() == MIRType::Int32) { 4870 return MToFloat32::New(alloc, input->toToDouble()->input()); 4871 } 4872 4873 return this; 4874 } 4875 4876 MDefinition* MToFloat16::foldsTo(TempAllocator& alloc) { 4877 MDefinition* in = input(); 4878 if (in->isBox()) { 4879 in = in->toBox()->input(); 4880 } 4881 4882 if (in->isConstant()) { 4883 auto* cst = in->toConstant(); 4884 if (cst->isTypeRepresentableAsDouble()) { 4885 double num = cst->numberToDouble(); 4886 return MConstant::NewFloat32(alloc, static_cast<float>(js::float16{num})); 4887 } 4888 } 4889 4890 auto isFloat16 = [](auto* def) -> MDefinition* { 4891 // ToFloat16(ToDouble(float16)) => float16 4892 // ToFloat16(ToFloat32(float16)) => float16 4893 if (def->isToDouble()) { 4894 def = def->toToDouble()->input(); 4895 } else if (def->isToFloat32()) { 4896 def = def->toToFloat32()->input(); 4897 } 4898 4899 // ToFloat16(ToFloat16(x)) => ToFloat16(x) 4900 if (def->isToFloat16()) { 4901 return def; 4902 } 4903 4904 // ToFloat16(LoadFloat16(x)) => LoadFloat16(x) 4905 if (def->isLoadUnboxedScalar() && 4906 def->toLoadUnboxedScalar()->storageType() == Scalar::Float16) { 4907 return def; 4908 } 4909 if (def->isLoadDataViewElement() && 4910 def->toLoadDataViewElement()->storageType() == Scalar::Float16) { 4911 return def; 4912 } 4913 return nullptr; 4914 }; 4915 4916 // Fold loads which are guaranteed to return Float16. 4917 if (auto* f16 = isFloat16(in)) { 4918 return f16; 4919 } 4920 4921 // Fold ToFloat16(ToDouble(float32)) to ToFloat16(float32). 4922 // Fold ToFloat16(ToDouble(int32)) to ToFloat16(int32). 4923 if (in->isToDouble()) { 4924 auto* toDoubleInput = in->toToDouble()->input(); 4925 if (toDoubleInput->type() == MIRType::Float32 || 4926 toDoubleInput->type() == MIRType::Int32) { 4927 return MToFloat16::New(alloc, toDoubleInput); 4928 } 4929 } 4930 4931 return this; 4932 } 4933 4934 MDefinition* MToString::foldsTo(TempAllocator& alloc) { 4935 MDefinition* in = input(); 4936 if (in->isBox()) { 4937 in = in->getOperand(0); 4938 } 4939 4940 if (in->type() == MIRType::String) { 4941 return in; 4942 } 4943 return this; 4944 } 4945 4946 MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) { 4947 if (MConstant* inputConst = input()->maybeConstantValue()) { 4948 if (inputConst->isTypeRepresentableAsDouble()) { 4949 int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble()); 4950 return MConstant::NewInt32(alloc, clamped); 4951 } 4952 } 4953 return this; 4954 } 4955 4956 bool MCompare::tryFoldEqualOperands(bool* result) { 4957 if (lhs() != rhs()) { 4958 return false; 4959 } 4960 4961 // Intuitively somebody would think that if lhs === rhs, 4962 // then we can just return true. (Or false for !==) 4963 // However NaN !== NaN is true! So we spend some time trying 4964 // to eliminate this case. 4965 4966 if (!IsEqualityOp(jsop())) { 4967 return false; 4968 } 4969 4970 switch (compareType_) { 4971 case Compare_Int32: 4972 case Compare_UInt32: 4973 case Compare_Int64: 4974 case Compare_UInt64: 4975 case Compare_IntPtr: 4976 case Compare_UIntPtr: 4977 case Compare_Float32: 4978 case Compare_Double: 4979 case Compare_String: 4980 case Compare_Object: 4981 case Compare_Symbol: 4982 case Compare_BigInt: 4983 case Compare_WasmAnyRef: 4984 case Compare_Null: 4985 case Compare_Undefined: 4986 break; 4987 case Compare_BigInt_Int32: 4988 case Compare_BigInt_String: 4989 case Compare_BigInt_Double: 4990 MOZ_CRASH("Expecting different operands for lhs and rhs"); 4991 } 4992 4993 if (isDoubleComparison() || isFloat32Comparison()) { 4994 if (!operandsAreNeverNaN()) { 4995 return false; 4996 } 4997 } else { 4998 MOZ_ASSERT(!IsFloatingPointType(lhs()->type())); 4999 } 5000 5001 lhs()->setGuardRangeBailoutsUnchecked(); 5002 5003 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq); 5004 return true; 5005 } 5006 5007 static JSType TypeOfName(const JSOffThreadAtom* str) { 5008 static constexpr std::array types = { 5009 JSTYPE_UNDEFINED, JSTYPE_OBJECT, JSTYPE_FUNCTION, JSTYPE_STRING, 5010 JSTYPE_NUMBER, JSTYPE_BOOLEAN, JSTYPE_SYMBOL, JSTYPE_BIGINT, 5011 }; 5012 static_assert(types.size() == JSTYPE_LIMIT); 5013 5014 const JSAtomState& names = GetJitContext()->runtime->names(); 5015 for (auto type : types) { 5016 // Both sides are atoms, so we can simply compare pointer identity. 5017 if (TypeName(type, names) == str->unwrap()) { 5018 return type; 5019 } 5020 } 5021 return JSTYPE_LIMIT; 5022 } 5023 5024 struct TypeOfCompareInput { 5025 // The `typeof expr` side of the comparison. 5026 // MTypeOfName for JSOp::Typeof/JSOp::TypeofExpr, and 5027 // MTypeOf for JSOp::TypeofEq (same pointer as typeOf). 5028 MDefinition* typeOfSide; 5029 5030 // The actual `typeof` operation. 5031 MTypeOf* typeOf; 5032 5033 // The string side of the comparison. 5034 JSType type; 5035 5036 // True if the comparison uses raw JSType (Generated for JSOp::TypeofEq). 5037 bool isIntComparison; 5038 5039 TypeOfCompareInput(MDefinition* typeOfSide, MTypeOf* typeOf, JSType type, 5040 bool isIntComparison) 5041 : typeOfSide(typeOfSide), 5042 typeOf(typeOf), 5043 type(type), 5044 isIntComparison(isIntComparison) {} 5045 }; 5046 5047 static mozilla::Maybe<TypeOfCompareInput> IsTypeOfCompare(MCompare* ins) { 5048 if (!IsEqualityOp(ins->jsop())) { 5049 return mozilla::Nothing(); 5050 } 5051 5052 if (ins->compareType() == MCompare::Compare_Int32) { 5053 auto* lhs = ins->lhs(); 5054 auto* rhs = ins->rhs(); 5055 5056 // NOTE: The comparison is generated inside JIT, and typeof should always 5057 // be in the LHS. 5058 if (!lhs->isTypeOf() || !rhs->isConstant()) { 5059 return mozilla::Nothing(); 5060 } 5061 5062 MOZ_ASSERT(ins->type() == MIRType::Boolean); 5063 MOZ_ASSERT(lhs->type() == MIRType::Int32); 5064 MOZ_ASSERT(rhs->type() == MIRType::Int32); 5065 5066 auto* typeOf = lhs->toTypeOf(); 5067 auto* constant = rhs->toConstant(); 5068 5069 JSType type = JSType(constant->toInt32()); 5070 return mozilla::Some(TypeOfCompareInput(typeOf, typeOf, type, true)); 5071 } 5072 5073 if (ins->compareType() != MCompare::Compare_String) { 5074 return mozilla::Nothing(); 5075 } 5076 5077 auto* lhs = ins->lhs(); 5078 auto* rhs = ins->rhs(); 5079 5080 MOZ_ASSERT(ins->type() == MIRType::Boolean); 5081 MOZ_ASSERT(lhs->type() == MIRType::String); 5082 MOZ_ASSERT(rhs->type() == MIRType::String); 5083 5084 if (!lhs->isTypeOfName() && !rhs->isTypeOfName()) { 5085 return mozilla::Nothing(); 5086 } 5087 if (!lhs->isConstant() && !rhs->isConstant()) { 5088 return mozilla::Nothing(); 5089 } 5090 5091 auto* typeOfName = 5092 lhs->isTypeOfName() ? lhs->toTypeOfName() : rhs->toTypeOfName(); 5093 auto* typeOf = typeOfName->input()->toTypeOf(); 5094 5095 auto* constant = lhs->isConstant() ? lhs->toConstant() : rhs->toConstant(); 5096 5097 JSType type = TypeOfName(constant->toString()); 5098 return mozilla::Some(TypeOfCompareInput(typeOfName, typeOf, type, false)); 5099 } 5100 5101 bool MCompare::tryFoldTypeOf(bool* result) { 5102 auto typeOfCompare = IsTypeOfCompare(this); 5103 if (!typeOfCompare) { 5104 return false; 5105 } 5106 auto* typeOf = typeOfCompare->typeOf; 5107 JSType type = typeOfCompare->type; 5108 5109 // Can't fold if the input is boxed. (Unless the typeof string is bogus.) 5110 MIRType inputType = typeOf->input()->type(); 5111 if (inputType == MIRType::Value && type != JSTYPE_LIMIT) { 5112 return false; 5113 } 5114 5115 bool matchesInputType; 5116 switch (type) { 5117 case JSTYPE_BOOLEAN: 5118 matchesInputType = (inputType == MIRType::Boolean); 5119 break; 5120 case JSTYPE_NUMBER: 5121 matchesInputType = IsTypeRepresentableAsDouble(inputType); 5122 break; 5123 case JSTYPE_STRING: 5124 matchesInputType = (inputType == MIRType::String); 5125 break; 5126 case JSTYPE_SYMBOL: 5127 matchesInputType = (inputType == MIRType::Symbol); 5128 break; 5129 case JSTYPE_BIGINT: 5130 matchesInputType = (inputType == MIRType::BigInt); 5131 break; 5132 case JSTYPE_OBJECT: 5133 // Watch out for `object-emulating-undefined` and callable objects. 5134 if (inputType == MIRType::Object) { 5135 return false; 5136 } 5137 matchesInputType = (inputType == MIRType::Null); 5138 break; 5139 case JSTYPE_UNDEFINED: 5140 // Watch out for `object-emulating-undefined`. 5141 if (inputType == MIRType::Object) { 5142 return false; 5143 } 5144 matchesInputType = (inputType == MIRType::Undefined); 5145 break; 5146 case JSTYPE_FUNCTION: 5147 // Can't decide at compile-time if an object is callable. 5148 if (inputType == MIRType::Object) { 5149 return false; 5150 } 5151 matchesInputType = false; 5152 break; 5153 case JSTYPE_LIMIT: 5154 matchesInputType = false; 5155 break; 5156 } 5157 5158 if (matchesInputType) { 5159 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq); 5160 } else { 5161 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne); 5162 } 5163 return true; 5164 } 5165 5166 bool MCompare::tryFold(bool* result) { 5167 JSOp op = jsop(); 5168 5169 if (tryFoldEqualOperands(result)) { 5170 return true; 5171 } 5172 5173 if (tryFoldTypeOf(result)) { 5174 return true; 5175 } 5176 5177 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) { 5178 // The LHS is the value we want to test against null or undefined. 5179 if (IsStrictEqualityOp(op)) { 5180 MIRType expectedType = 5181 compareType_ == Compare_Null ? MIRType::Null : MIRType::Undefined; 5182 if (lhs()->type() == expectedType) { 5183 *result = (op == JSOp::StrictEq); 5184 return true; 5185 } 5186 if (lhs()->type() != MIRType::Value) { 5187 *result = (op == JSOp::StrictNe); 5188 return true; 5189 } 5190 } else { 5191 MOZ_ASSERT(IsLooseEqualityOp(op)); 5192 if (IsNullOrUndefined(lhs()->type())) { 5193 *result = (op == JSOp::Eq); 5194 return true; 5195 } 5196 if (lhs()->type() != MIRType::Object && lhs()->type() != MIRType::Value) { 5197 *result = (op == JSOp::Ne); 5198 return true; 5199 } 5200 } 5201 return false; 5202 } 5203 5204 return false; 5205 } 5206 5207 template <typename T> 5208 static bool FoldComparison(JSOp op, T left, T right) { 5209 switch (op) { 5210 case JSOp::Lt: 5211 return left < right; 5212 case JSOp::Le: 5213 return left <= right; 5214 case JSOp::Gt: 5215 return left > right; 5216 case JSOp::Ge: 5217 return left >= right; 5218 case JSOp::StrictEq: 5219 case JSOp::Eq: 5220 return left == right; 5221 case JSOp::StrictNe: 5222 case JSOp::Ne: 5223 return left != right; 5224 default: 5225 MOZ_CRASH("Unexpected op."); 5226 } 5227 } 5228 5229 static bool FoldBigIntComparison(JSOp op, const BigInt* left, double right) { 5230 switch (op) { 5231 case JSOp::Lt: 5232 return BigInt::lessThan(left, right).valueOr(false); 5233 case JSOp::Le: 5234 return !BigInt::lessThan(right, left).valueOr(true); 5235 case JSOp::Gt: 5236 return BigInt::lessThan(right, left).valueOr(false); 5237 case JSOp::Ge: 5238 return !BigInt::lessThan(left, right).valueOr(true); 5239 case JSOp::StrictEq: 5240 case JSOp::Eq: 5241 return BigInt::equal(left, right); 5242 case JSOp::StrictNe: 5243 case JSOp::Ne: 5244 return !BigInt::equal(left, right); 5245 default: 5246 MOZ_CRASH("Unexpected op."); 5247 } 5248 } 5249 5250 bool MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) { 5251 if (type() != MIRType::Boolean && type() != MIRType::Int32) { 5252 return false; 5253 } 5254 5255 MDefinition* left = getOperand(0); 5256 MDefinition* right = getOperand(1); 5257 5258 if (compareType() == Compare_Double) { 5259 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range). 5260 // In most cases the MToDouble was added, because the constant is 5261 // a double. 5262 // e.g. v < 9007199254740991, where v is an int32 is always true. 5263 if (!lhs()->isConstant() && !rhs()->isConstant()) { 5264 return false; 5265 } 5266 5267 MDefinition* operand = left->isConstant() ? right : left; 5268 MConstant* constant = 5269 left->isConstant() ? left->toConstant() : right->toConstant(); 5270 MOZ_ASSERT(constant->type() == MIRType::Double); 5271 double cte = constant->toDouble(); 5272 5273 if (operand->isToDouble() && 5274 operand->getOperand(0)->type() == MIRType::Int32) { 5275 bool replaced = false; 5276 switch (jsop_) { 5277 case JSOp::Lt: 5278 if (cte > INT32_MAX || cte < INT32_MIN) { 5279 *result = !((constant == lhs()) ^ (cte < INT32_MIN)); 5280 replaced = true; 5281 } 5282 break; 5283 case JSOp::Le: 5284 if (constant == lhs()) { 5285 if (cte > INT32_MAX || cte <= INT32_MIN) { 5286 *result = (cte <= INT32_MIN); 5287 replaced = true; 5288 } 5289 } else { 5290 if (cte >= INT32_MAX || cte < INT32_MIN) { 5291 *result = (cte >= INT32_MIN); 5292 replaced = true; 5293 } 5294 } 5295 break; 5296 case JSOp::Gt: 5297 if (cte > INT32_MAX || cte < INT32_MIN) { 5298 *result = !((constant == rhs()) ^ (cte < INT32_MIN)); 5299 replaced = true; 5300 } 5301 break; 5302 case JSOp::Ge: 5303 if (constant == lhs()) { 5304 if (cte >= INT32_MAX || cte < INT32_MIN) { 5305 *result = (cte >= INT32_MAX); 5306 replaced = true; 5307 } 5308 } else { 5309 if (cte > INT32_MAX || cte <= INT32_MIN) { 5310 *result = (cte <= INT32_MIN); 5311 replaced = true; 5312 } 5313 } 5314 break; 5315 case JSOp::StrictEq: // Fall through. 5316 case JSOp::Eq: 5317 if (cte > INT32_MAX || cte < INT32_MIN) { 5318 *result = false; 5319 replaced = true; 5320 } 5321 break; 5322 case JSOp::StrictNe: // Fall through. 5323 case JSOp::Ne: 5324 if (cte > INT32_MAX || cte < INT32_MIN) { 5325 *result = true; 5326 replaced = true; 5327 } 5328 break; 5329 default: 5330 MOZ_CRASH("Unexpected op."); 5331 } 5332 if (replaced) { 5333 MLimitedTruncate* limit = MLimitedTruncate::New( 5334 alloc, operand->getOperand(0), TruncateKind::NoTruncate); 5335 limit->setGuardUnchecked(); 5336 block()->insertBefore(this, limit); 5337 return true; 5338 } 5339 } 5340 5341 // Optimize comparison against NaN. 5342 if (std::isnan(cte)) { 5343 switch (jsop_) { 5344 case JSOp::Lt: 5345 case JSOp::Le: 5346 case JSOp::Gt: 5347 case JSOp::Ge: 5348 case JSOp::Eq: 5349 case JSOp::StrictEq: 5350 *result = false; 5351 break; 5352 case JSOp::Ne: 5353 case JSOp::StrictNe: 5354 *result = true; 5355 break; 5356 default: 5357 MOZ_CRASH("Unexpected op."); 5358 } 5359 return true; 5360 } 5361 } 5362 5363 if (!left->isConstant() || !right->isConstant()) { 5364 return false; 5365 } 5366 5367 MConstant* lhs = left->toConstant(); 5368 MConstant* rhs = right->toConstant(); 5369 5370 switch (compareType()) { 5371 case Compare_Int32: 5372 case Compare_Double: 5373 case Compare_Float32: { 5374 *result = 5375 FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble()); 5376 return true; 5377 } 5378 case Compare_UInt32: { 5379 *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()), 5380 uint32_t(rhs->toInt32())); 5381 return true; 5382 } 5383 case Compare_Int64: { 5384 *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64()); 5385 return true; 5386 } 5387 case Compare_UInt64: { 5388 *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()), 5389 uint64_t(rhs->toInt64())); 5390 return true; 5391 } 5392 case Compare_IntPtr: { 5393 *result = FoldComparison(jsop_, lhs->toIntPtr(), rhs->toIntPtr()); 5394 return true; 5395 } 5396 case Compare_UIntPtr: { 5397 *result = FoldComparison(jsop_, uintptr_t(lhs->toIntPtr()), 5398 uintptr_t(rhs->toIntPtr())); 5399 return true; 5400 } 5401 case Compare_String: { 5402 int32_t comp = CompareStrings(lhs->toString(), rhs->toString()); 5403 *result = FoldComparison(jsop_, comp, 0); 5404 return true; 5405 } 5406 case Compare_BigInt: { 5407 int32_t comp = BigInt::compare(lhs->toBigInt(), rhs->toBigInt()); 5408 *result = FoldComparison(jsop_, comp, 0); 5409 return true; 5410 } 5411 case Compare_BigInt_Int32: 5412 case Compare_BigInt_Double: { 5413 *result = 5414 FoldBigIntComparison(jsop_, lhs->toBigInt(), rhs->numberToDouble()); 5415 return true; 5416 } 5417 case Compare_BigInt_String: { 5418 JSOffThreadAtom* str = rhs->toString(); 5419 if (!str->hasIndexValue()) { 5420 return false; 5421 } 5422 *result = 5423 FoldBigIntComparison(jsop_, lhs->toBigInt(), str->getIndexValue()); 5424 return true; 5425 } 5426 5427 case Compare_Undefined: 5428 case Compare_Null: 5429 case Compare_Symbol: 5430 case Compare_Object: 5431 case Compare_WasmAnyRef: 5432 return false; 5433 } 5434 5435 MOZ_CRASH("unexpected compare type"); 5436 } 5437 5438 MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) { 5439 auto typeOfCompare = IsTypeOfCompare(this); 5440 if (!typeOfCompare) { 5441 return this; 5442 } 5443 auto* typeOf = typeOfCompare->typeOf; 5444 JSType type = typeOfCompare->type; 5445 5446 auto* input = typeOf->input(); 5447 MOZ_ASSERT(input->type() == MIRType::Value || 5448 input->type() == MIRType::Object); 5449 5450 // Constant typeof folding handles the other cases. 5451 MOZ_ASSERT_IF(input->type() == MIRType::Object, type == JSTYPE_UNDEFINED || 5452 type == JSTYPE_OBJECT || 5453 type == JSTYPE_FUNCTION); 5454 5455 MOZ_ASSERT(type != JSTYPE_LIMIT, "unknown typeof strings folded earlier"); 5456 5457 // If there's only a single use, assume this |typeof| is used in a simple 5458 // comparison context. 5459 // 5460 // if (typeof thing === "number") { ... } 5461 // 5462 // It'll be compiled into something similar to: 5463 // 5464 // if (IsNumber(thing)) { ... } 5465 // 5466 // This heuristic can go wrong when repeated |typeof| are used in consecutive 5467 // if-statements. 5468 // 5469 // if (typeof thing === "number") { ... } 5470 // else if (typeof thing === "string") { ... } 5471 // ... repeated for all possible types 5472 // 5473 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We 5474 // don't yet handle that case, because it'd require a separate optimization 5475 // pass to correctly detect it. 5476 if (typeOfCompare->typeOfSide->hasOneUse()) { 5477 return MTypeOfIs::New(alloc, input, jsop(), type); 5478 } 5479 5480 if (typeOfCompare->isIntComparison) { 5481 // Already optimized. 5482 return this; 5483 } 5484 5485 MConstant* cst = MConstant::NewInt32(alloc, type); 5486 block()->insertBefore(this, cst); 5487 5488 return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32); 5489 } 5490 5491 MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) { 5492 if (compareType() != Compare_String) { 5493 return this; 5494 } 5495 5496 MDefinition* left = lhs(); 5497 MOZ_ASSERT(left->type() == MIRType::String); 5498 5499 MDefinition* right = rhs(); 5500 MOZ_ASSERT(right->type() == MIRType::String); 5501 5502 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|. 5503 // Out-of-bounds access is compiled as 5504 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|. 5505 auto isCharAccess = [](MDefinition* ins) { 5506 if (ins->isFromCharCode()) { 5507 return ins->toFromCharCode()->code()->isCharCodeAt(); 5508 } 5509 if (ins->isFromCharCodeEmptyIfNegative()) { 5510 auto* fromCharCode = ins->toFromCharCodeEmptyIfNegative(); 5511 return fromCharCode->code()->isCharCodeAtOrNegative(); 5512 } 5513 return false; 5514 }; 5515 5516 auto charAccessCode = [](MDefinition* ins) { 5517 if (ins->isFromCharCode()) { 5518 return ins->toFromCharCode()->code(); 5519 } 5520 return ins->toFromCharCodeEmptyIfNegative()->code(); 5521 }; 5522 5523 if (left->isConstant() || right->isConstant()) { 5524 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)| 5525 // as |MConstant(charcode) <compare> MCharCodeAt|. 5526 MConstant* constant; 5527 MDefinition* operand; 5528 if (left->isConstant()) { 5529 constant = left->toConstant(); 5530 operand = right; 5531 } else { 5532 constant = right->toConstant(); 5533 operand = left; 5534 } 5535 5536 if (constant->toString()->length() != 1 || !isCharAccess(operand)) { 5537 return this; 5538 } 5539 5540 char16_t charCode = constant->toString()->latin1OrTwoByteChar(0); 5541 MConstant* charCodeConst = MConstant::NewInt32(alloc, charCode); 5542 block()->insertBefore(this, charCodeConst); 5543 5544 MDefinition* charCodeAt = charAccessCode(operand); 5545 5546 if (left->isConstant()) { 5547 left = charCodeConst; 5548 right = charCodeAt; 5549 } else { 5550 left = charCodeAt; 5551 right = charCodeConst; 5552 } 5553 } else if (isCharAccess(left) && isCharAccess(right)) { 5554 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode 5555 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|. 5556 5557 left = charAccessCode(left); 5558 right = charAccessCode(right); 5559 } else { 5560 return this; 5561 } 5562 5563 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32); 5564 } 5565 5566 MDefinition* MCompare::tryFoldStringCompare(TempAllocator& alloc) { 5567 if (compareType() != Compare_String) { 5568 return this; 5569 } 5570 5571 MDefinition* left = lhs(); 5572 MOZ_ASSERT(left->type() == MIRType::String); 5573 5574 MDefinition* right = rhs(); 5575 MOZ_ASSERT(right->type() == MIRType::String); 5576 5577 if (!left->isConstant() && !right->isConstant()) { 5578 return this; 5579 } 5580 5581 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string) 5582 // <compare> MConstant(0)|. 5583 5584 MConstant* constant = 5585 left->isConstant() ? left->toConstant() : right->toConstant(); 5586 if (!constant->toString()->empty()) { 5587 return this; 5588 } 5589 5590 MDefinition* operand = left->isConstant() ? right : left; 5591 5592 auto* strLength = MStringLength::New(alloc, operand); 5593 block()->insertBefore(this, strLength); 5594 5595 auto* zero = MConstant::NewInt32(alloc, 0); 5596 block()->insertBefore(this, zero); 5597 5598 if (left->isConstant()) { 5599 left = zero; 5600 right = strLength; 5601 } else { 5602 left = strLength; 5603 right = zero; 5604 } 5605 5606 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32); 5607 } 5608 5609 MDefinition* MCompare::tryFoldStringSubstring(TempAllocator& alloc) { 5610 if (compareType() != Compare_String) { 5611 return this; 5612 } 5613 if (!IsEqualityOp(jsop())) { 5614 return this; 5615 } 5616 5617 auto* left = lhs(); 5618 MOZ_ASSERT(left->type() == MIRType::String); 5619 5620 auto* right = rhs(); 5621 MOZ_ASSERT(right->type() == MIRType::String); 5622 5623 // One operand must be a constant string. 5624 if (!left->isConstant() && !right->isConstant()) { 5625 return this; 5626 } 5627 5628 // The constant string must be non-empty. 5629 auto* constant = 5630 left->isConstant() ? left->toConstant() : right->toConstant(); 5631 if (constant->toString()->empty()) { 5632 return this; 5633 } 5634 5635 // The other operand must be a substring operation. 5636 auto* operand = left->isConstant() ? right : left; 5637 if (!operand->isSubstr()) { 5638 return this; 5639 } 5640 auto* substr = operand->toSubstr(); 5641 5642 static_assert(JSString::MAX_LENGTH < INT32_MAX, 5643 "string length can be casted to int32_t"); 5644 5645 int32_t stringLength = int32_t(constant->toString()->length()); 5646 5647 MInstruction* replacement; 5648 if (IsSubstrTo(substr, stringLength)) { 5649 // Fold |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|. 5650 replacement = MStringStartsWith::New(alloc, substr->string(), constant); 5651 } else if (IsSubstrLast(substr, -stringLength)) { 5652 // Fold |str.slice(-2) == "aa"| to |str.endsWith("aa")|. 5653 replacement = MStringEndsWith::New(alloc, substr->string(), constant); 5654 } else { 5655 return this; 5656 } 5657 5658 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) { 5659 return replacement; 5660 } 5661 5662 // Invert for inequality. 5663 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe); 5664 5665 block()->insertBefore(this, replacement); 5666 return MNot::New(alloc, replacement); 5667 } 5668 5669 MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) { 5670 if (compareType() != Compare_Int32) { 5671 return this; 5672 } 5673 if (!IsEqualityOp(jsop())) { 5674 return this; 5675 } 5676 5677 auto* left = lhs(); 5678 MOZ_ASSERT(left->type() == MIRType::Int32); 5679 5680 auto* right = rhs(); 5681 MOZ_ASSERT(right->type() == MIRType::Int32); 5682 5683 // One operand must be a constant integer. 5684 if (!left->isConstant() && !right->isConstant()) { 5685 return this; 5686 } 5687 5688 // The constant must be zero. 5689 auto* constant = 5690 left->isConstant() ? left->toConstant() : right->toConstant(); 5691 if (!constant->isInt32(0)) { 5692 return this; 5693 } 5694 5695 // The other operand must be an indexOf operation. 5696 auto* operand = left->isConstant() ? right : left; 5697 if (!operand->isStringIndexOf()) { 5698 return this; 5699 } 5700 5701 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|. 5702 5703 auto* indexOf = operand->toStringIndexOf(); 5704 auto* startsWith = 5705 MStringStartsWith::New(alloc, indexOf->string(), indexOf->searchString()); 5706 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) { 5707 return startsWith; 5708 } 5709 5710 // Invert for inequality. 5711 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe); 5712 5713 block()->insertBefore(this, startsWith); 5714 return MNot::New(alloc, startsWith); 5715 } 5716 5717 MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) { 5718 if (compareType() == Compare_BigInt) { 5719 auto* left = lhs(); 5720 MOZ_ASSERT(left->type() == MIRType::BigInt); 5721 5722 auto* right = rhs(); 5723 MOZ_ASSERT(right->type() == MIRType::BigInt); 5724 5725 // At least one operand must be MInt64ToBigInt. 5726 if (!left->isInt64ToBigInt() && !right->isInt64ToBigInt()) { 5727 return this; 5728 } 5729 5730 // Unwrap MInt64ToBigInt on both sides and perform a Int64 comparison. 5731 if (left->isInt64ToBigInt() && right->isInt64ToBigInt()) { 5732 auto* lhsInt64 = left->toInt64ToBigInt(); 5733 auto* rhsInt64 = right->toInt64ToBigInt(); 5734 5735 // Don't optimize if Int64 against Uint64 comparison. 5736 if (lhsInt64->isSigned() != rhsInt64->isSigned()) { 5737 return this; 5738 } 5739 5740 bool isSigned = lhsInt64->isSigned(); 5741 auto compareType = 5742 isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64; 5743 return MCompare::New(alloc, lhsInt64->input(), rhsInt64->input(), jsop_, 5744 compareType); 5745 } 5746 5747 // Optimize IntPtr x Int64 comparison to Int64 x Int64 comparison. 5748 if (left->isIntPtrToBigInt() || right->isIntPtrToBigInt()) { 5749 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt() 5750 : right->toInt64ToBigInt(); 5751 5752 // Can't optimize when comparing Uint64 against IntPtr. 5753 if (!int64ToBigInt->isSigned()) { 5754 return this; 5755 } 5756 5757 auto* intPtrToBigInt = left->isIntPtrToBigInt() 5758 ? left->toIntPtrToBigInt() 5759 : right->toIntPtrToBigInt(); 5760 5761 auto* intPtrToInt64 = MIntPtrToInt64::New(alloc, intPtrToBigInt->input()); 5762 block()->insertBefore(this, intPtrToInt64); 5763 5764 if (left == int64ToBigInt) { 5765 left = int64ToBigInt->input(); 5766 right = intPtrToInt64; 5767 } else { 5768 left = intPtrToInt64; 5769 right = int64ToBigInt->input(); 5770 } 5771 return MCompare::New(alloc, left, right, jsop_, MCompare::Compare_Int64); 5772 } 5773 5774 // The other operand must be a constant. 5775 if (!left->isConstant() && !right->isConstant()) { 5776 return this; 5777 } 5778 5779 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt() 5780 : right->toInt64ToBigInt(); 5781 bool isSigned = int64ToBigInt->isSigned(); 5782 5783 auto* constant = 5784 left->isConstant() ? left->toConstant() : right->toConstant(); 5785 auto* bigInt = constant->toBigInt(); 5786 5787 // Extract the BigInt value if representable as Int64/Uint64. 5788 mozilla::Maybe<int64_t> value; 5789 if (isSigned) { 5790 int64_t x; 5791 if (BigInt::isInt64(bigInt, &x)) { 5792 value = mozilla::Some(x); 5793 } 5794 } else { 5795 uint64_t x; 5796 if (BigInt::isUint64(bigInt, &x)) { 5797 value = mozilla::Some(static_cast<int64_t>(x)); 5798 } 5799 } 5800 5801 // The comparison is a constant if the BigInt has too many digits. 5802 if (!value) { 5803 int32_t repr = bigInt->isNegative() ? -1 : 1; 5804 5805 bool result; 5806 if (left == int64ToBigInt) { 5807 result = FoldComparison(jsop_, 0, repr); 5808 } else { 5809 result = FoldComparison(jsop_, repr, 0); 5810 } 5811 return MConstant::NewBoolean(alloc, result); 5812 } 5813 5814 auto* cst = MConstant::NewInt64(alloc, *value); 5815 block()->insertBefore(this, cst); 5816 5817 auto compareType = 5818 isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64; 5819 if (left == int64ToBigInt) { 5820 return MCompare::New(alloc, int64ToBigInt->input(), cst, jsop_, 5821 compareType); 5822 } 5823 return MCompare::New(alloc, cst, int64ToBigInt->input(), jsop_, 5824 compareType); 5825 } 5826 5827 if (compareType() == Compare_BigInt_Int32) { 5828 auto* left = lhs(); 5829 MOZ_ASSERT(left->type() == MIRType::BigInt); 5830 5831 auto* right = rhs(); 5832 MOZ_ASSERT(right->type() == MIRType::Int32); 5833 5834 // Optimize MInt64ToBigInt against a constant int32. 5835 if (!left->isInt64ToBigInt() || !right->isConstant()) { 5836 return this; 5837 } 5838 5839 auto* int64ToBigInt = left->toInt64ToBigInt(); 5840 bool isSigned = int64ToBigInt->isSigned(); 5841 5842 int32_t constInt32 = right->toConstant()->toInt32(); 5843 5844 // The unsigned comparison against a negative operand is a constant. 5845 if (!isSigned && constInt32 < 0) { 5846 bool result = FoldComparison(jsop_, 0, constInt32); 5847 return MConstant::NewBoolean(alloc, result); 5848 } 5849 5850 auto* cst = MConstant::NewInt64(alloc, int64_t(constInt32)); 5851 block()->insertBefore(this, cst); 5852 5853 auto compareType = 5854 isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64; 5855 return MCompare::New(alloc, int64ToBigInt->input(), cst, jsop_, 5856 compareType); 5857 } 5858 5859 return this; 5860 } 5861 5862 MDefinition* MCompare::tryFoldBigIntPtr(TempAllocator& alloc) { 5863 if (compareType() == Compare_BigInt) { 5864 auto* left = lhs(); 5865 MOZ_ASSERT(left->type() == MIRType::BigInt); 5866 5867 auto* right = rhs(); 5868 MOZ_ASSERT(right->type() == MIRType::BigInt); 5869 5870 // At least one operand must be MIntPtrToBigInt. 5871 if (!left->isIntPtrToBigInt() && !right->isIntPtrToBigInt()) { 5872 return this; 5873 } 5874 5875 // Unwrap MIntPtrToBigInt on both sides and perform an IntPtr comparison. 5876 if (left->isIntPtrToBigInt() && right->isIntPtrToBigInt()) { 5877 auto* lhsIntPtr = left->toIntPtrToBigInt(); 5878 auto* rhsIntPtr = right->toIntPtrToBigInt(); 5879 5880 return MCompare::New(alloc, lhsIntPtr->input(), rhsIntPtr->input(), jsop_, 5881 MCompare::Compare_IntPtr); 5882 } 5883 5884 // The other operand must be a constant. 5885 if (!left->isConstant() && !right->isConstant()) { 5886 return this; 5887 } 5888 5889 auto* intPtrToBigInt = left->isIntPtrToBigInt() ? left->toIntPtrToBigInt() 5890 : right->toIntPtrToBigInt(); 5891 5892 auto* constant = 5893 left->isConstant() ? left->toConstant() : right->toConstant(); 5894 auto* bigInt = constant->toBigInt(); 5895 5896 // Extract the BigInt value if representable as intptr_t. 5897 intptr_t value; 5898 if (!BigInt::isIntPtr(bigInt, &value)) { 5899 // The comparison is a constant if the BigInt has too many digits. 5900 int32_t repr = bigInt->isNegative() ? -1 : 1; 5901 5902 bool result; 5903 if (left == intPtrToBigInt) { 5904 result = FoldComparison(jsop_, 0, repr); 5905 } else { 5906 result = FoldComparison(jsop_, repr, 0); 5907 } 5908 return MConstant::NewBoolean(alloc, result); 5909 } 5910 5911 auto* cst = MConstant::NewIntPtr(alloc, value); 5912 block()->insertBefore(this, cst); 5913 5914 if (left == intPtrToBigInt) { 5915 left = intPtrToBigInt->input(); 5916 right = cst; 5917 } else { 5918 left = cst; 5919 right = intPtrToBigInt->input(); 5920 } 5921 return MCompare::New(alloc, left, right, jsop_, MCompare::Compare_IntPtr); 5922 } 5923 5924 if (compareType() == Compare_BigInt_Int32) { 5925 auto* left = lhs(); 5926 MOZ_ASSERT(left->type() == MIRType::BigInt); 5927 5928 auto* right = rhs(); 5929 MOZ_ASSERT(right->type() == MIRType::Int32); 5930 5931 // Optimize MIntPtrToBigInt against a constant int32. 5932 if (!left->isIntPtrToBigInt() || !right->isConstant()) { 5933 return this; 5934 } 5935 5936 auto* cst = 5937 MConstant::NewIntPtr(alloc, intptr_t(right->toConstant()->toInt32())); 5938 block()->insertBefore(this, cst); 5939 5940 return MCompare::New(alloc, left->toIntPtrToBigInt()->input(), cst, jsop_, 5941 MCompare::Compare_IntPtr); 5942 } 5943 5944 return this; 5945 } 5946 5947 MDefinition* MCompare::tryFoldBigInt(TempAllocator& alloc) { 5948 if (compareType() != Compare_BigInt) { 5949 return this; 5950 } 5951 5952 auto* left = lhs(); 5953 MOZ_ASSERT(left->type() == MIRType::BigInt); 5954 5955 auto* right = rhs(); 5956 MOZ_ASSERT(right->type() == MIRType::BigInt); 5957 5958 // One operand must be a constant. 5959 if (!left->isConstant() && !right->isConstant()) { 5960 return this; 5961 } 5962 5963 auto* constant = 5964 left->isConstant() ? left->toConstant() : right->toConstant(); 5965 auto* operand = left->isConstant() ? right : left; 5966 5967 // The constant must be representable as an Int32. 5968 int32_t x; 5969 if (!BigInt::isInt32(constant->toBigInt(), &x)) { 5970 return this; 5971 } 5972 5973 MConstant* int32Const = MConstant::NewInt32(alloc, x); 5974 block()->insertBefore(this, int32Const); 5975 5976 auto op = jsop(); 5977 if (IsStrictEqualityOp(op)) { 5978 // Compare_BigInt_Int32 is only valid for loose comparison. 5979 op = op == JSOp::StrictEq ? JSOp::Eq : JSOp::Ne; 5980 } else if (operand == right) { 5981 // Reverse the comparison operator if the operands were reordered. 5982 op = ReverseCompareOp(op); 5983 } 5984 5985 return MCompare::New(alloc, operand, int32Const, op, 5986 MCompare::Compare_BigInt_Int32); 5987 } 5988 5989 MDefinition* MCompare::foldsTo(TempAllocator& alloc) { 5990 bool result; 5991 5992 if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) { 5993 if (type() == MIRType::Int32) { 5994 return MConstant::NewInt32(alloc, result); 5995 } 5996 5997 MOZ_ASSERT(type() == MIRType::Boolean); 5998 return MConstant::NewBoolean(alloc, result); 5999 } 6000 6001 if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) { 6002 return folded; 6003 } 6004 6005 if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) { 6006 return folded; 6007 } 6008 6009 if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) { 6010 return folded; 6011 } 6012 6013 if (MDefinition* folded = tryFoldStringSubstring(alloc); folded != this) { 6014 return folded; 6015 } 6016 6017 if (MDefinition* folded = tryFoldStringIndexOf(alloc); folded != this) { 6018 return folded; 6019 } 6020 6021 if (MDefinition* folded = tryFoldBigInt64(alloc); folded != this) { 6022 return folded; 6023 } 6024 6025 if (MDefinition* folded = tryFoldBigIntPtr(alloc); folded != this) { 6026 return folded; 6027 } 6028 6029 if (MDefinition* folded = tryFoldBigInt(alloc); folded != this) { 6030 return folded; 6031 } 6032 6033 return this; 6034 } 6035 6036 void MCompare::trySpecializeFloat32(TempAllocator& alloc) { 6037 if (AllOperandsCanProduceFloat32(this) && compareType_ == Compare_Double) { 6038 compareType_ = Compare_Float32; 6039 } else { 6040 ConvertOperandsToDouble(this, alloc); 6041 } 6042 } 6043 6044 MDefinition* MStrictConstantCompareInt32::foldsTo(TempAllocator& alloc) { 6045 if (!value()->isBox()) { 6046 return this; 6047 } 6048 MDefinition* unboxed = value()->toBox()->input(); 6049 6050 if (unboxed->type() == MIRType::Int32) { 6051 if (unboxed->isConstant()) { 6052 bool result = 6053 FoldComparison(jsop(), unboxed->toConstant()->toInt32(), constant()); 6054 return MConstant::NewBoolean(alloc, result); 6055 } 6056 6057 auto* cst = MConstant::NewInt32(alloc, constant()); 6058 block()->insertBefore(this, cst); 6059 6060 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Int32); 6061 } 6062 6063 if (unboxed->type() == MIRType::Double) { 6064 if (unboxed->isConstant()) { 6065 bool result = FoldComparison(jsop(), unboxed->toConstant()->toDouble(), 6066 double(constant())); 6067 return MConstant::NewBoolean(alloc, result); 6068 } 6069 6070 auto* cst = MConstant::NewDouble(alloc, constant()); 6071 block()->insertBefore(this, cst); 6072 6073 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Double); 6074 } 6075 6076 MOZ_ASSERT(!IsNumberType(unboxed->type())); 6077 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe); 6078 } 6079 6080 MDefinition* MStrictConstantCompareBoolean::foldsTo(TempAllocator& alloc) { 6081 if (!value()->isBox()) { 6082 return this; 6083 } 6084 MDefinition* unboxed = value()->toBox()->input(); 6085 6086 if (unboxed->type() == MIRType::Boolean) { 6087 if (unboxed->isConstant()) { 6088 bool result = (jsop() == JSOp::StrictEq) == 6089 (unboxed->toConstant()->toBoolean() == constant()); 6090 return MConstant::NewBoolean(alloc, result); 6091 } 6092 6093 auto* inputI32 = MBooleanToInt32::New(alloc, unboxed); 6094 block()->insertBefore(this, inputI32); 6095 6096 auto* cst = MConstant::NewInt32(alloc, int32_t(constant())); 6097 block()->insertBefore(this, cst); 6098 6099 return MCompare::New(alloc, inputI32, cst, jsop(), MCompare::Compare_Int32); 6100 } 6101 6102 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe); 6103 } 6104 6105 MDefinition* MSameValue::foldsTo(TempAllocator& alloc) { 6106 MDefinition* lhs = left(); 6107 if (lhs->isBox()) { 6108 lhs = lhs->toBox()->input(); 6109 } 6110 6111 MDefinition* rhs = right(); 6112 if (rhs->isBox()) { 6113 rhs = rhs->toBox()->input(); 6114 } 6115 6116 // Trivially true if both operands are the same. 6117 if (lhs == rhs) { 6118 return MConstant::NewBoolean(alloc, true); 6119 } 6120 6121 // CacheIR optimizes the following cases, so don't bother to handle them here: 6122 // 1. Both inputs are numbers (int32 or double). 6123 // 2. Both inputs are strictly different types. 6124 // 3. Both inputs are the same type. 6125 6126 // Optimize when one operand is guaranteed to be |null|. 6127 if (lhs->type() == MIRType::Null || rhs->type() == MIRType::Null) { 6128 // The `null` value must be the right-hand side operand. 6129 auto* input = lhs->type() == MIRType::Null ? rhs : lhs; 6130 auto* cst = lhs->type() == MIRType::Null ? lhs : rhs; 6131 return MCompare::New(alloc, input, cst, JSOp::StrictEq, 6132 MCompare::Compare_Null); 6133 } 6134 6135 // Optimize when one operand is guaranteed to be |undefined|. 6136 if (lhs->type() == MIRType::Undefined || rhs->type() == MIRType::Undefined) { 6137 // The `undefined` value must be the right-hand side operand. 6138 auto* input = lhs->type() == MIRType::Undefined ? rhs : lhs; 6139 auto* cst = lhs->type() == MIRType::Undefined ? lhs : rhs; 6140 return MCompare::New(alloc, input, cst, JSOp::StrictEq, 6141 MCompare::Compare_Undefined); 6142 } 6143 6144 return this; 6145 } 6146 6147 MDefinition* MSameValueDouble::foldsTo(TempAllocator& alloc) { 6148 // Trivially true if both operands are the same. 6149 if (left() == right()) { 6150 return MConstant::NewBoolean(alloc, true); 6151 } 6152 6153 // At least one operand must be a constant. 6154 if (!left()->isConstant() && !right()->isConstant()) { 6155 return this; 6156 } 6157 6158 auto* input = left()->isConstant() ? right() : left(); 6159 auto* cst = left()->isConstant() ? left() : right(); 6160 double dbl = cst->toConstant()->toDouble(); 6161 6162 // Use bitwise comparison for +/-0. 6163 if (dbl == 0.0) { 6164 auto* reinterp = MReinterpretCast::New(alloc, input, MIRType::Int64); 6165 block()->insertBefore(this, reinterp); 6166 6167 auto* zeroBitsCst = 6168 MConstant::NewInt64(alloc, mozilla::BitwiseCast<int64_t>(dbl)); 6169 block()->insertBefore(this, zeroBitsCst); 6170 6171 return MCompare::New(alloc, reinterp, zeroBitsCst, JSOp::StrictEq, 6172 MCompare::Compare_Int64); 6173 } 6174 6175 // Fold `Object.is(d, NaN)` to `d !== d`. 6176 if (std::isnan(dbl)) { 6177 return MCompare::New(alloc, input, input, JSOp::StrictNe, 6178 MCompare::Compare_Double); 6179 } 6180 6181 // Otherwise fold to MCompare. 6182 return MCompare::New(alloc, left(), right(), JSOp::StrictEq, 6183 MCompare::Compare_Double); 6184 } 6185 6186 MDefinition* MNot::foldsTo(TempAllocator& alloc) { 6187 auto foldConstant = [&alloc](MDefinition* input, MIRType type) -> MConstant* { 6188 MConstant* inputConst = input->maybeConstantValue(); 6189 if (!inputConst) { 6190 return nullptr; 6191 } 6192 bool b; 6193 if (!inputConst->valueToBoolean(&b)) { 6194 return nullptr; 6195 } 6196 if (type == MIRType::Int32) { 6197 return MConstant::NewInt32(alloc, !b); 6198 } 6199 MOZ_ASSERT(type == MIRType::Boolean); 6200 return MConstant::NewBoolean(alloc, !b); 6201 }; 6202 6203 // Fold if the input is constant. 6204 if (MConstant* folded = foldConstant(input(), type())) { 6205 return folded; 6206 } 6207 6208 // If the operand of the Not is itself a Not, they cancel out. But we can't 6209 // always convert Not(Not(x)) to x because that may loose the conversion to 6210 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though. 6211 MDefinition* op = getOperand(0); 6212 if (op->isNot()) { 6213 MDefinition* opop = op->getOperand(0); 6214 if (opop->isNot()) { 6215 return opop; 6216 } 6217 } 6218 6219 // Not of an undefined or null value is always true 6220 if (input()->type() == MIRType::Undefined || 6221 input()->type() == MIRType::Null) { 6222 return MConstant::NewBoolean(alloc, true); 6223 } 6224 6225 // Not of a symbol is always false. 6226 if (input()->type() == MIRType::Symbol) { 6227 return MConstant::NewBoolean(alloc, false); 6228 } 6229 6230 // Drop the conversion in `Not(Int64ToBigInt(int64))` to `Not(int64)`. 6231 if (input()->isInt64ToBigInt()) { 6232 MDefinition* int64 = input()->toInt64ToBigInt()->input(); 6233 if (MConstant* folded = foldConstant(int64, type())) { 6234 return folded; 6235 } 6236 return MNot::New(alloc, int64); 6237 } 6238 6239 // Drop the conversion in `Not(IntPtrToBigInt(intptr))` to `Not(intptr)`. 6240 if (input()->isIntPtrToBigInt()) { 6241 MDefinition* intPtr = input()->toIntPtrToBigInt()->input(); 6242 if (MConstant* folded = foldConstant(intPtr, type())) { 6243 return folded; 6244 } 6245 return MNot::New(alloc, intPtr); 6246 } 6247 6248 return this; 6249 } 6250 6251 void MNot::trySpecializeFloat32(TempAllocator& alloc) { 6252 (void)EnsureFloatInputOrConvert(this, alloc); 6253 } 6254 6255 #ifdef JS_JITSPEW 6256 void MBeta::printOpcode(GenericPrinter& out) const { 6257 MDefinition::printOpcode(out); 6258 6259 out.printf(" "); 6260 comparison_->dump(out); 6261 } 6262 #endif 6263 6264 AliasSet MCreateThis::getAliasSet() const { 6265 return AliasSet::Load(AliasSet::Any); 6266 } 6267 6268 bool MGetArgumentsObjectArg::congruentTo(const MDefinition* ins) const { 6269 if (!ins->isGetArgumentsObjectArg()) { 6270 return false; 6271 } 6272 if (ins->toGetArgumentsObjectArg()->argno() != argno()) { 6273 return false; 6274 } 6275 return congruentIfOperandsEqual(ins); 6276 } 6277 6278 AliasSet MGetArgumentsObjectArg::getAliasSet() const { 6279 return AliasSet::Load(AliasSet::Any); 6280 } 6281 6282 AliasSet MSetArgumentsObjectArg::getAliasSet() const { 6283 return AliasSet::Store(AliasSet::Any); 6284 } 6285 6286 MObjectState::MObjectState(MObjectState* state) 6287 : MVariadicInstruction(classOpcode), 6288 numSlots_(state->numSlots_), 6289 numFixedSlots_(state->numFixedSlots_) { 6290 // This instruction is only used as a summary for bailout paths. 6291 setResultType(MIRType::Object); 6292 setRecoveredOnBailout(); 6293 } 6294 6295 MObjectState::MObjectState(JSObject* templateObject) 6296 : MObjectState(templateObject->as<NativeObject>().shape()) {} 6297 6298 MObjectState::MObjectState(const Shape* shape) 6299 : MVariadicInstruction(classOpcode) { 6300 // This instruction is only used as a summary for bailout paths. 6301 setResultType(MIRType::Object); 6302 setRecoveredOnBailout(); 6303 6304 numSlots_ = shape->asShared().slotSpan(); 6305 numFixedSlots_ = shape->asShared().numFixedSlots(); 6306 } 6307 6308 /* static */ 6309 JSObject* MObjectState::templateObjectOf(MDefinition* obj) { 6310 // MNewPlainObject uses a shape constant, not an object. 6311 MOZ_ASSERT(!obj->isNewPlainObject()); 6312 6313 if (obj->isNewObject()) { 6314 return obj->toNewObject()->templateObject(); 6315 } else if (obj->isNewCallObject()) { 6316 return obj->toNewCallObject()->templateObject(); 6317 } else if (obj->isNewIterator()) { 6318 return obj->toNewIterator()->templateObject(); 6319 } 6320 6321 MOZ_CRASH("unreachable"); 6322 } 6323 6324 bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) { 6325 if (!MVariadicInstruction::init(alloc, numSlots() + 1)) { 6326 return false; 6327 } 6328 // +1, for the Object. 6329 initOperand(0, obj); 6330 return true; 6331 } 6332 6333 void MObjectState::initFromTemplateObject(TempAllocator& alloc, 6334 MDefinition* undefinedVal) { 6335 if (object()->isNewPlainObject()) { 6336 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() == 6337 numSlots()); 6338 for (size_t i = 0; i < numSlots(); i++) { 6339 initSlot(i, undefinedVal); 6340 } 6341 return; 6342 } 6343 6344 JSObject* templateObject = templateObjectOf(object()); 6345 6346 // Initialize all the slots of the object state with the value contained in 6347 // the template object. This is needed to account values which are baked in 6348 // the template objects and not visible in IonMonkey, such as the 6349 // uninitialized-lexical magic value of call objects. 6350 6351 MOZ_ASSERT(templateObject->is<NativeObject>()); 6352 NativeObject& nativeObject = templateObject->as<NativeObject>(); 6353 MOZ_ASSERT(nativeObject.slotSpan() == numSlots()); 6354 6355 for (size_t i = 0; i < numSlots(); i++) { 6356 Value val = nativeObject.getSlot(i); 6357 MDefinition* def = undefinedVal; 6358 if (!val.isUndefined()) { 6359 MConstant* ins = MConstant::New(alloc, val); 6360 block()->insertBefore(this, ins); 6361 def = ins; 6362 } 6363 initSlot(i, def); 6364 } 6365 } 6366 6367 MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) { 6368 MObjectState* res; 6369 if (obj->isNewPlainObject()) { 6370 const Shape* shape = obj->toNewPlainObject()->shape(); 6371 res = new (alloc) MObjectState(shape); 6372 } else { 6373 JSObject* templateObject = templateObjectOf(obj); 6374 MOZ_ASSERT(templateObject, "Unexpected object creation."); 6375 res = new (alloc) MObjectState(templateObject); 6376 } 6377 6378 if (!res || !res->init(alloc, obj)) { 6379 return nullptr; 6380 } 6381 return res; 6382 } 6383 6384 MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) { 6385 MObjectState* res = new (alloc) MObjectState(state); 6386 if (!res || !res->init(alloc, state->object())) { 6387 return nullptr; 6388 } 6389 for (size_t i = 0; i < res->numSlots(); i++) { 6390 res->initSlot(i, state->getSlot(i)); 6391 } 6392 return res; 6393 } 6394 6395 MArrayState::MArrayState(MDefinition* arr) : MVariadicInstruction(classOpcode) { 6396 // This instruction is only used as a summary for bailout paths. 6397 setResultType(MIRType::Object); 6398 setRecoveredOnBailout(); 6399 if (arr->isNewArrayObject()) { 6400 numElements_ = arr->toNewArrayObject()->length(); 6401 } else { 6402 numElements_ = arr->toNewArray()->length(); 6403 } 6404 } 6405 6406 bool MArrayState::init(TempAllocator& alloc, MDefinition* obj, 6407 MDefinition* len) { 6408 if (!MVariadicInstruction::init(alloc, numElements() + 2)) { 6409 return false; 6410 } 6411 // +1, for the Array object. 6412 initOperand(0, obj); 6413 // +1, for the length value of the array. 6414 initOperand(1, len); 6415 return true; 6416 } 6417 6418 void MArrayState::initFromTemplateObject(TempAllocator& alloc, 6419 MDefinition* undefinedVal) { 6420 for (size_t i = 0; i < numElements(); i++) { 6421 initElement(i, undefinedVal); 6422 } 6423 } 6424 6425 MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr, 6426 MDefinition* initLength) { 6427 MArrayState* res = new (alloc) MArrayState(arr); 6428 if (!res || !res->init(alloc, arr, initLength)) { 6429 return nullptr; 6430 } 6431 return res; 6432 } 6433 6434 MArrayState* MArrayState::Copy(TempAllocator& alloc, MArrayState* state) { 6435 MDefinition* arr = state->array(); 6436 MDefinition* len = state->initializedLength(); 6437 MArrayState* res = new (alloc) MArrayState(arr); 6438 if (!res || !res->init(alloc, arr, len)) { 6439 return nullptr; 6440 } 6441 for (size_t i = 0; i < res->numElements(); i++) { 6442 res->initElement(i, state->getElement(i)); 6443 } 6444 return res; 6445 } 6446 6447 MNewArray::MNewArray(uint32_t length, MConstant* templateConst, 6448 gc::Heap initialHeap, bool vmCall) 6449 : MUnaryInstruction(classOpcode, templateConst), 6450 length_(length), 6451 initialHeap_(initialHeap), 6452 vmCall_(vmCall) { 6453 setResultType(MIRType::Object); 6454 } 6455 6456 MDefinition::AliasType MLoadFixedSlot::mightAlias( 6457 const MDefinition* def) const { 6458 if (def->isStoreFixedSlot()) { 6459 const MStoreFixedSlot* store = def->toStoreFixedSlot(); 6460 if (store->slot() != slot()) { 6461 return AliasType::NoAlias; 6462 } 6463 if (store->object() != object()) { 6464 return AliasType::MayAlias; 6465 } 6466 return AliasType::MustAlias; 6467 } 6468 return AliasType::MayAlias; 6469 } 6470 6471 HashNumber MLoadFixedSlot::valueHash() const { 6472 HashNumber hash = MUnaryInstruction::valueHash(); 6473 hash = addU32ToHash(hash, slot()); 6474 return hash; 6475 } 6476 6477 MDefinition* MLoadFixedSlot::foldsTo(TempAllocator& alloc) { 6478 if (MDefinition* def = foldsToStore(alloc)) { 6479 return def; 6480 } 6481 6482 return this; 6483 } 6484 6485 MDefinition::AliasType MLoadFixedSlotAndUnbox::mightAlias( 6486 const MDefinition* def) const { 6487 if (def->isStoreFixedSlot()) { 6488 const MStoreFixedSlot* store = def->toStoreFixedSlot(); 6489 if (store->slot() != slot()) { 6490 return AliasType::NoAlias; 6491 } 6492 if (store->object() != object()) { 6493 return AliasType::MayAlias; 6494 } 6495 return AliasType::MustAlias; 6496 } 6497 return AliasType::MayAlias; 6498 } 6499 6500 MDefinition* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc) { 6501 if (MDefinition* def = foldsToStore(alloc)) { 6502 return def; 6503 } 6504 6505 return this; 6506 } 6507 6508 MDefinition::AliasType MLoadDynamicSlot::mightAlias( 6509 const MDefinition* def) const { 6510 if (def->isStoreDynamicSlot()) { 6511 const MStoreDynamicSlot* store = def->toStoreDynamicSlot(); 6512 if (store->slot() != slot()) { 6513 return AliasType::NoAlias; 6514 } 6515 6516 if (store->slots() != slots()) { 6517 return AliasType::MayAlias; 6518 } 6519 6520 return AliasType::MustAlias; 6521 } 6522 return AliasType::MayAlias; 6523 } 6524 6525 HashNumber MLoadDynamicSlot::valueHash() const { 6526 HashNumber hash = MUnaryInstruction::valueHash(); 6527 hash = addU32ToHash(hash, slot_); 6528 return hash; 6529 } 6530 6531 MDefinition* MLoadDynamicSlot::foldsTo(TempAllocator& alloc) { 6532 if (MDefinition* def = foldsToStore(alloc)) { 6533 return def; 6534 } 6535 6536 return this; 6537 } 6538 6539 #ifdef JS_JITSPEW 6540 void MLoadDynamicSlot::printOpcode(GenericPrinter& out) const { 6541 MDefinition::printOpcode(out); 6542 out.printf(" (slot %u)", slot()); 6543 } 6544 6545 void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter& out) const { 6546 MDefinition::printOpcode(out); 6547 out.printf(" (slot %zu)", slot()); 6548 } 6549 6550 void MStoreDynamicSlot::printOpcode(GenericPrinter& out) const { 6551 MDefinition::printOpcode(out); 6552 out.printf(" (slot %u)", slot()); 6553 } 6554 6555 void MLoadFixedSlot::printOpcode(GenericPrinter& out) const { 6556 MDefinition::printOpcode(out); 6557 out.printf(" (slot %zu)", slot()); 6558 } 6559 6560 void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter& out) const { 6561 MDefinition::printOpcode(out); 6562 out.printf(" (slot %zu)", slot()); 6563 } 6564 6565 void MStoreFixedSlot::printOpcode(GenericPrinter& out) const { 6566 MDefinition::printOpcode(out); 6567 out.printf(" (slot %zu)", slot()); 6568 } 6569 #endif 6570 6571 MDefinition* MGuardFunctionScript::foldsTo(TempAllocator& alloc) { 6572 MDefinition* in = input(); 6573 if (in->isLambda() && 6574 in->toLambda()->templateFunction()->baseScript() == expected()) { 6575 return in; 6576 } 6577 return this; 6578 } 6579 6580 MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) { 6581 if (input()->isLambda()) { 6582 return input()->toLambda()->environmentChain(); 6583 } 6584 if (input()->isFunctionWithProto()) { 6585 return input()->toFunctionWithProto()->environmentChain(); 6586 } 6587 return this; 6588 } 6589 6590 static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) { 6591 if (add->lhs() != ins && add->rhs() != ins) { 6592 return false; 6593 } 6594 MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs(); 6595 if (!IsTypeRepresentableAsDouble(other->type())) { 6596 return false; 6597 } 6598 if (!other->isConstant()) { 6599 return false; 6600 } 6601 if (other->toConstant()->numberToDouble() == 0) { 6602 return false; 6603 } 6604 return true; 6605 } 6606 6607 // Skip over instructions that usually appear between the actual index 6608 // value being used and the MLoadElement. 6609 // They don't modify the index value in a meaningful way. 6610 static MDefinition* SkipUninterestingInstructions(MDefinition* ins) { 6611 // Drop the MToNumberInt32 added by the TypePolicy for double and float 6612 // values. 6613 if (ins->isToNumberInt32()) { 6614 return SkipUninterestingInstructions(ins->toToNumberInt32()->input()); 6615 } 6616 6617 // Ignore the bounds check, which don't modify the index. 6618 if (ins->isBoundsCheck()) { 6619 return SkipUninterestingInstructions(ins->toBoundsCheck()->index()); 6620 } 6621 6622 // Masking the index for Spectre-mitigation is not observable. 6623 if (ins->isSpectreMaskIndex()) { 6624 return SkipUninterestingInstructions(ins->toSpectreMaskIndex()->index()); 6625 } 6626 6627 return ins; 6628 } 6629 6630 static bool DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2) { 6631 ins1 = SkipUninterestingInstructions(ins1); 6632 ins2 = SkipUninterestingInstructions(ins2); 6633 6634 if (ins1 == ins2) { 6635 return false; 6636 } 6637 6638 // For constants check they are not equal. 6639 if (ins1->isConstant() && ins2->isConstant()) { 6640 MConstant* cst1 = ins1->toConstant(); 6641 MConstant* cst2 = ins2->toConstant(); 6642 6643 if (!cst1->isTypeRepresentableAsDouble() || 6644 !cst2->isTypeRepresentableAsDouble()) { 6645 return false; 6646 } 6647 6648 // Be conservative and only allow values that fit into int32. 6649 int32_t n1, n2; 6650 if (!mozilla::NumberIsInt32(cst1->numberToDouble(), &n1) || 6651 !mozilla::NumberIsInt32(cst2->numberToDouble(), &n2)) { 6652 return false; 6653 } 6654 6655 return n1 != n2; 6656 } 6657 6658 // Check if "ins1 = ins2 + cte", which would make both instructions 6659 // have different values. 6660 if (ins1->isAdd()) { 6661 if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2)) { 6662 return true; 6663 } 6664 } 6665 if (ins2->isAdd()) { 6666 if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1)) { 6667 return true; 6668 } 6669 } 6670 6671 return false; 6672 } 6673 6674 MDefinition::AliasType MLoadElement::mightAlias(const MDefinition* def) const { 6675 if (def->isStoreElement()) { 6676 const MStoreElement* store = def->toStoreElement(); 6677 if (store->index() != index()) { 6678 if (DefinitelyDifferentValue(store->index(), index())) { 6679 return AliasType::NoAlias; 6680 } 6681 return AliasType::MayAlias; 6682 } 6683 6684 if (store->elements() != elements()) { 6685 return AliasType::MayAlias; 6686 } 6687 6688 return AliasType::MustAlias; 6689 } 6690 return AliasType::MayAlias; 6691 } 6692 6693 MDefinition* MLoadElement::foldsTo(TempAllocator& alloc) { 6694 if (MDefinition* def = foldsToStore(alloc)) { 6695 return def; 6696 } 6697 6698 return this; 6699 } 6700 6701 void MSqrt::trySpecializeFloat32(TempAllocator& alloc) { 6702 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) { 6703 setResultType(MIRType::Float32); 6704 specialization_ = MIRType::Float32; 6705 } 6706 } 6707 6708 MDefinition* MClz::foldsTo(TempAllocator& alloc) { 6709 if (num()->isConstant()) { 6710 MConstant* c = num()->toConstant(); 6711 if (type() == MIRType::Int32) { 6712 int32_t n = c->toInt32(); 6713 if (n == 0) { 6714 return MConstant::NewInt32(alloc, 32); 6715 } 6716 return MConstant::NewInt32(alloc, mozilla::CountLeadingZeroes32(n)); 6717 } 6718 int64_t n = c->toInt64(); 6719 if (n == 0) { 6720 return MConstant::NewInt64(alloc, int64_t(64)); 6721 } 6722 return MConstant::NewInt64(alloc, 6723 int64_t(mozilla::CountLeadingZeroes64(n))); 6724 } 6725 6726 return this; 6727 } 6728 6729 MDefinition* MCtz::foldsTo(TempAllocator& alloc) { 6730 if (num()->isConstant()) { 6731 MConstant* c = num()->toConstant(); 6732 if (type() == MIRType::Int32) { 6733 int32_t n = num()->toConstant()->toInt32(); 6734 if (n == 0) { 6735 return MConstant::NewInt32(alloc, 32); 6736 } 6737 return MConstant::NewInt32(alloc, mozilla::CountTrailingZeroes32(n)); 6738 } 6739 int64_t n = c->toInt64(); 6740 if (n == 0) { 6741 return MConstant::NewInt64(alloc, int64_t(64)); 6742 } 6743 return MConstant::NewInt64(alloc, 6744 int64_t(mozilla::CountTrailingZeroes64(n))); 6745 } 6746 6747 return this; 6748 } 6749 6750 MDefinition* MPopcnt::foldsTo(TempAllocator& alloc) { 6751 if (num()->isConstant()) { 6752 MConstant* c = num()->toConstant(); 6753 if (type() == MIRType::Int32) { 6754 int32_t n = num()->toConstant()->toInt32(); 6755 return MConstant::NewInt32(alloc, mozilla::CountPopulation32(n)); 6756 } 6757 int64_t n = c->toInt64(); 6758 return MConstant::NewInt64(alloc, int64_t(mozilla::CountPopulation64(n))); 6759 } 6760 6761 return this; 6762 } 6763 6764 MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) { 6765 if (type() == MIRType::Int32 && index()->isConstant() && 6766 length()->isConstant()) { 6767 uint32_t len = length()->toConstant()->toInt32(); 6768 uint32_t idx = index()->toConstant()->toInt32(); 6769 if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) { 6770 return index(); 6771 } 6772 } 6773 6774 return this; 6775 } 6776 6777 MDefinition* MTableSwitch::foldsTo(TempAllocator& alloc) { 6778 MDefinition* op = getOperand(0); 6779 6780 // If we only have one successor, convert to a plain goto to the only 6781 // successor. TableSwitch indices are numeric; other types will always go to 6782 // the only successor. 6783 if (numSuccessors() == 1 || 6784 (op->type() != MIRType::Value && !IsNumberType(op->type()))) { 6785 return MGoto::New(alloc, getDefault()); 6786 } 6787 6788 if (MConstant* opConst = op->maybeConstantValue()) { 6789 if (op->type() == MIRType::Int32) { 6790 int32_t i = opConst->toInt32() - low_; 6791 MBasicBlock* target; 6792 if (size_t(i) < numCases()) { 6793 target = getCase(size_t(i)); 6794 } else { 6795 target = getDefault(); 6796 } 6797 MOZ_ASSERT(target); 6798 return MGoto::New(alloc, target); 6799 } 6800 } 6801 6802 return this; 6803 } 6804 6805 MDefinition* MArrayJoin::foldsTo(TempAllocator& alloc) { 6806 MDefinition* arr = array(); 6807 6808 if (!arr->isStringSplit()) { 6809 return this; 6810 } 6811 6812 setRecoveredOnBailout(); 6813 if (arr->hasLiveDefUses()) { 6814 setNotRecoveredOnBailout(); 6815 return this; 6816 } 6817 6818 // The MStringSplit won't generate any code. 6819 arr->setRecoveredOnBailout(); 6820 6821 // We're replacing foo.split(bar).join(baz) by 6822 // foo.replace(bar, baz). MStringSplit could be recovered by 6823 // a bailout. As we are removing its last use, and its result 6824 // could be captured by a resume point, this MStringSplit will 6825 // be executed on the bailout path. 6826 MDefinition* string = arr->toStringSplit()->string(); 6827 MDefinition* pattern = arr->toStringSplit()->separator(); 6828 MDefinition* replacement = separator(); 6829 6830 MStringReplace* substr = 6831 MStringReplace::New(alloc, string, pattern, replacement); 6832 substr->setFlatReplacement(); 6833 return substr; 6834 } 6835 6836 MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) { 6837 MDefinition* strArg = str(); 6838 if (!strArg->isConstant()) { 6839 return this; 6840 } 6841 6842 JSOffThreadAtom* str = strArg->toConstant()->toString(); 6843 int32_t index = GetFirstDollarIndexRawFlat(str); 6844 return MConstant::NewInt32(alloc, index); 6845 } 6846 6847 AliasSet MThrowRuntimeLexicalError::getAliasSet() const { 6848 return AliasSet::Store(AliasSet::ExceptionState); 6849 } 6850 6851 AliasSet MSlots::getAliasSet() const { 6852 return AliasSet::Load(AliasSet::ObjectFields); 6853 } 6854 6855 MDefinition::AliasType MSlots::mightAlias(const MDefinition* store) const { 6856 // ArrayPush only modifies object elements, but not object slots. 6857 if (store->isArrayPush()) { 6858 return AliasType::NoAlias; 6859 } 6860 return MInstruction::mightAlias(store); 6861 } 6862 6863 AliasSet MElements::getAliasSet() const { 6864 return AliasSet::Load(AliasSet::ObjectFields); 6865 } 6866 6867 AliasSet MInitializedLength::getAliasSet() const { 6868 return AliasSet::Load(AliasSet::ObjectFields); 6869 } 6870 6871 AliasSet MSetInitializedLength::getAliasSet() const { 6872 return AliasSet::Store(AliasSet::ObjectFields); 6873 } 6874 6875 AliasSet MObjectKeysLength::getAliasSet() const { 6876 return AliasSet::Load(AliasSet::ObjectFields); 6877 } 6878 6879 AliasSet MArrayLength::getAliasSet() const { 6880 return AliasSet::Load(AliasSet::ObjectFields); 6881 } 6882 6883 AliasSet MSetArrayLength::getAliasSet() const { 6884 return AliasSet::Store(AliasSet::ObjectFields); 6885 } 6886 6887 AliasSet MFunctionLength::getAliasSet() const { 6888 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 6889 AliasSet::DynamicSlot); 6890 } 6891 6892 AliasSet MFunctionName::getAliasSet() const { 6893 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 6894 AliasSet::DynamicSlot); 6895 } 6896 6897 AliasSet MArrayBufferByteLength::getAliasSet() const { 6898 return AliasSet::Load(AliasSet::FixedSlot); 6899 } 6900 6901 AliasSet MArrayBufferViewLength::getAliasSet() const { 6902 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset); 6903 } 6904 6905 AliasSet MArrayBufferViewByteOffset::getAliasSet() const { 6906 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset); 6907 } 6908 6909 AliasSet MArrayBufferViewElements::getAliasSet() const { 6910 return AliasSet::Load(AliasSet::ObjectFields); 6911 } 6912 6913 AliasSet MArrayBufferViewElementsWithOffset::getAliasSet() const { 6914 return AliasSet::Load(AliasSet::ObjectFields); 6915 } 6916 6917 AliasSet MGuardHasAttachedArrayBuffer::getAliasSet() const { 6918 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot); 6919 } 6920 6921 AliasSet MResizableTypedArrayLength::getAliasSet() const { 6922 // Loads the length and byteOffset slots, the shared-elements flag, the 6923 // auto-length fixed slot, and the shared raw-buffer length. 6924 auto flags = AliasSet::ArrayBufferViewLengthOrOffset | 6925 AliasSet::ObjectFields | AliasSet::FixedSlot | 6926 AliasSet::SharedArrayRawBufferLength; 6927 6928 // When a barrier is needed make the instruction effectful by giving it a 6929 // "store" effect. Also prevent reordering LoadUnboxedScalar before this 6930 // instruction by including |UnboxedElement| in the alias set. 6931 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { 6932 return AliasSet::Store(flags | AliasSet::UnboxedElement); 6933 } 6934 return AliasSet::Load(flags); 6935 } 6936 6937 bool MResizableTypedArrayLength::congruentTo(const MDefinition* ins) const { 6938 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { 6939 return false; 6940 } 6941 return congruentIfOperandsEqual(ins); 6942 } 6943 6944 AliasSet MResizableDataViewByteLength::getAliasSet() const { 6945 // Loads the length and byteOffset slots, the shared-elements flag, the 6946 // auto-length fixed slot, and the shared raw-buffer length. 6947 auto flags = AliasSet::ArrayBufferViewLengthOrOffset | 6948 AliasSet::ObjectFields | AliasSet::FixedSlot | 6949 AliasSet::SharedArrayRawBufferLength; 6950 6951 // When a barrier is needed make the instruction effectful by giving it a 6952 // "store" effect. Also prevent reordering LoadUnboxedScalar before this 6953 // instruction by including |UnboxedElement| in the alias set. 6954 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { 6955 return AliasSet::Store(flags | AliasSet::UnboxedElement); 6956 } 6957 return AliasSet::Load(flags); 6958 } 6959 6960 bool MResizableDataViewByteLength::congruentTo(const MDefinition* ins) const { 6961 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) { 6962 return false; 6963 } 6964 return congruentIfOperandsEqual(ins); 6965 } 6966 6967 AliasSet MGrowableSharedArrayBufferByteLength::getAliasSet() const { 6968 // Requires a barrier, so make the instruction effectful by giving it a 6969 // "store" effect. Also prevent reordering LoadUnboxedScalar before this 6970 // instruction by including |UnboxedElement| in the alias set. 6971 return AliasSet::Store(AliasSet::FixedSlot | 6972 AliasSet::SharedArrayRawBufferLength | 6973 AliasSet::UnboxedElement); 6974 } 6975 6976 AliasSet MGuardResizableArrayBufferViewInBounds::getAliasSet() const { 6977 // Additionally reads the |initialLength| and |initialByteOffset| slots, but 6978 // since these can't change after construction, we don't need to track them. 6979 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset); 6980 } 6981 6982 AliasSet MGuardResizableArrayBufferViewInBoundsOrDetached::getAliasSet() const { 6983 // Loads the byteOffset and additionally checks for detached buffers, so the 6984 // alias set also has to include |ObjectFields| and |FixedSlot|. 6985 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | 6986 AliasSet::ObjectFields | AliasSet::FixedSlot); 6987 } 6988 6989 AliasSet MTypedArraySet::getAliasSet() const { 6990 // Loads typed array length and elements. 6991 constexpr auto load = 6992 AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | 6993 AliasSet::ObjectFields | AliasSet::UnboxedElement); 6994 6995 // Stores into typed array elements. 6996 constexpr auto store = AliasSet::Store(AliasSet::UnboxedElement); 6997 6998 return load | store; 6999 } 7000 7001 AliasSet MTypedArraySetFromSubarray::getAliasSet() const { 7002 // Loads typed array length and elements. 7003 constexpr auto load = 7004 AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | 7005 AliasSet::ObjectFields | AliasSet::UnboxedElement); 7006 7007 // Stores into typed array elements. 7008 constexpr auto store = AliasSet::Store(AliasSet::UnboxedElement); 7009 7010 return load | store; 7011 } 7012 7013 AliasSet MArrayPush::getAliasSet() const { 7014 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); 7015 } 7016 7017 MDefinition* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator& alloc) { 7018 MDefinition* input = this->input(); 7019 7020 if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) { 7021 return MInt32ToIntPtr::New(alloc, input->getOperand(0)); 7022 } 7023 7024 if (!input->isConstant()) { 7025 return this; 7026 } 7027 7028 // Fold constant double representable as intptr to intptr. 7029 int64_t ival; 7030 if (!mozilla::NumberEqualsInt64(input->toConstant()->toDouble(), &ival)) { 7031 // If not representable as an int64, this access is equal to an OOB access. 7032 // So replace it with a known int64/intptr value which also produces an OOB 7033 // access. If we don't support OOB accesses we have to bail out. 7034 if (!supportOOB()) { 7035 return this; 7036 } 7037 ival = -1; 7038 } 7039 7040 if (ival < INTPTR_MIN || ival > INTPTR_MAX) { 7041 return this; 7042 } 7043 7044 return MConstant::NewIntPtr(alloc, intptr_t(ival)); 7045 } 7046 7047 MDefinition* MIsObject::foldsTo(TempAllocator& alloc) { 7048 MDefinition* input = object(); 7049 if (!input->isBox()) { 7050 return this; 7051 } 7052 7053 MDefinition* unboxed = input->toBox()->input(); 7054 return MConstant::NewBoolean(alloc, unboxed->type() == MIRType::Object); 7055 } 7056 7057 MDefinition* MIsNullOrUndefined::foldsTo(TempAllocator& alloc) { 7058 // MIsNullOrUndefined doesn't have a type-policy, so the value can already be 7059 // unboxed. 7060 MDefinition* unboxed = value(); 7061 if (unboxed->type() == MIRType::Value) { 7062 if (!unboxed->isBox()) { 7063 return this; 7064 } 7065 unboxed = unboxed->toBox()->input(); 7066 } 7067 7068 return MConstant::NewBoolean(alloc, IsNullOrUndefined(unboxed->type())); 7069 } 7070 7071 AliasSet MHomeObjectSuperBase::getAliasSet() const { 7072 return AliasSet::Load(AliasSet::ObjectFields); 7073 } 7074 7075 MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) { 7076 if (MConstant* cst = value()->maybeConstantValue()) { 7077 if (expected().isValue() && cst->toJSValue() == expected().toValue()) { 7078 return value(); 7079 } 7080 } 7081 7082 return this; 7083 } 7084 7085 MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) { 7086 MDefinition* input = value(); 7087 if (!input->isBox()) { 7088 return this; 7089 } 7090 7091 MDefinition* unboxed = input->toBox()->input(); 7092 if (IsNullOrUndefined(unboxed->type())) { 7093 return input; 7094 } 7095 7096 return this; 7097 } 7098 7099 MDefinition* MGuardIsNotObject::foldsTo(TempAllocator& alloc) { 7100 MDefinition* input = value(); 7101 if (!input->isBox()) { 7102 return this; 7103 } 7104 7105 MDefinition* unboxed = input->toBox()->input(); 7106 if (unboxed->type() == MIRType::Object) { 7107 return this; 7108 } 7109 7110 return input; 7111 } 7112 7113 MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) { 7114 if (object()->isConstant() && expected()->isConstant()) { 7115 JSObject* obj = &object()->toConstant()->toObject(); 7116 JSObject* other = &expected()->toConstant()->toObject(); 7117 if (!bailOnEquality()) { 7118 if (obj == other) { 7119 return object(); 7120 } 7121 } else { 7122 if (obj != other) { 7123 return object(); 7124 } 7125 } 7126 } 7127 7128 if (!bailOnEquality() && object()->isNurseryObject() && 7129 expected()->isNurseryObject()) { 7130 uint32_t objIndex = object()->toNurseryObject()->nurseryObjectIndex(); 7131 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex(); 7132 if (objIndex == otherIndex) { 7133 return object(); 7134 } 7135 } 7136 7137 return this; 7138 } 7139 7140 MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) { 7141 if (function()->isConstant() && expected()->isConstant()) { 7142 JSObject* fun = &function()->toConstant()->toObject(); 7143 JSObject* other = &expected()->toConstant()->toObject(); 7144 if (fun == other) { 7145 return function(); 7146 } 7147 } 7148 7149 if (function()->isNurseryObject() && expected()->isNurseryObject()) { 7150 uint32_t funIndex = function()->toNurseryObject()->nurseryObjectIndex(); 7151 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex(); 7152 if (funIndex == otherIndex) { 7153 return function(); 7154 } 7155 } 7156 7157 return this; 7158 } 7159 7160 MDefinition* MGuardSpecificAtom::foldsTo(TempAllocator& alloc) { 7161 if (str()->isConstant()) { 7162 JSOffThreadAtom* s = str()->toConstant()->toString(); 7163 if (s == atom()) { 7164 return str(); 7165 } 7166 } 7167 7168 return this; 7169 } 7170 7171 MDefinition* MGuardSpecificSymbol::foldsTo(TempAllocator& alloc) { 7172 if (symbol()->isConstant()) { 7173 if (symbol()->toConstant()->toSymbol() == expected()) { 7174 return symbol(); 7175 } 7176 } 7177 7178 return this; 7179 } 7180 7181 MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) { 7182 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) { 7183 return num(); 7184 } 7185 return this; 7186 } 7187 7188 MDefinition* MGuardShape::foldsTo(TempAllocator& alloc) { 7189 if (object()->isGuardShape() && 7190 shape() == object()->toGuardShape()->shape() && dependency() && 7191 object()->dependency() == dependency()) { 7192 return object(); 7193 } 7194 return this; 7195 } 7196 7197 bool MGuardShape::congruentTo(const MDefinition* ins) const { 7198 return congruentIfOperandsEqual(ins) && 7199 shape() == ins->toGuardShape()->shape(); 7200 } 7201 7202 AliasSet MGuardShape::getAliasSet() const { 7203 return AliasSet::Load(AliasSet::ObjectFields); 7204 } 7205 7206 bool MGuardShapeList::congruentTo(const MDefinition* ins) const { 7207 if (!congruentIfOperandsEqual(ins)) { 7208 return false; 7209 } 7210 7211 // Returns true iff all non-nullptr shapes in |a| are also in |b|. 7212 auto hasAllShapes = [](const auto& a, const auto& b) { 7213 for (Shape* shape : a) { 7214 if (!shape) { 7215 continue; 7216 } 7217 auto isSameShape = [shape](Shape* other) { return shape == other; }; 7218 if (!std::any_of(b.begin(), b.end(), isSameShape)) { 7219 return false; 7220 } 7221 } 7222 return true; 7223 }; 7224 7225 // Return true if all shapes in |shapesA| are also in |shapesB| and vice 7226 // versa. 7227 const auto& shapesA = this->shapeList()->shapes(); 7228 const auto& shapesB = ins->toGuardShapeList()->shapeList()->shapes(); 7229 return hasAllShapes(shapesA, shapesB) && hasAllShapes(shapesB, shapesA); 7230 } 7231 7232 AliasSet MGuardShapeList::getAliasSet() const { 7233 return AliasSet::Load(AliasSet::ObjectFields); 7234 } 7235 7236 bool MGuardShapeListToOffset::congruentTo(const MDefinition* ins) const { 7237 if (!congruentIfOperandsEqual(ins)) { 7238 return false; 7239 } 7240 7241 const auto& shapesA = this->shapeList()->shapes(); 7242 const auto& shapesB = ins->toGuardShapeListToOffset()->shapeList()->shapes(); 7243 if (!std::equal(shapesA.begin(), shapesA.end(), shapesB.begin(), 7244 shapesB.end())) 7245 return false; 7246 7247 const auto& offsetsA = this->shapeList()->offsets(); 7248 const auto& offsetsB = 7249 ins->toGuardShapeListToOffset()->shapeList()->offsets(); 7250 return std::equal(offsetsA.begin(), offsetsA.end(), offsetsB.begin(), 7251 offsetsB.end()); 7252 } 7253 7254 AliasSet MGuardShapeListToOffset::getAliasSet() const { 7255 return AliasSet::Load(AliasSet::ObjectFields); 7256 } 7257 7258 bool MHasShape::congruentTo(const MDefinition* ins) const { 7259 if (!ins->isHasShape()) { 7260 return false; 7261 } 7262 if (shape() != ins->toHasShape()->shape()) { 7263 return false; 7264 } 7265 return congruentIfOperandsEqual(ins); 7266 } 7267 7268 AliasSet MHasShape::getAliasSet() const { 7269 return AliasSet::Load(AliasSet::ObjectFields); 7270 } 7271 7272 MDefinition::AliasType MGuardShape::mightAlias(const MDefinition* store) const { 7273 // These instructions only modify object elements, but not the shape. 7274 if (store->isStoreElementHole() || store->isArrayPush()) { 7275 return AliasType::NoAlias; 7276 } 7277 if (object()->isConstantProto()) { 7278 const MDefinition* receiverObject = 7279 object()->toConstantProto()->getReceiverObject(); 7280 switch (store->op()) { 7281 case MDefinition::Opcode::StoreFixedSlot: 7282 if (store->toStoreFixedSlot()->object()->skipObjectGuards() == 7283 receiverObject) { 7284 return AliasType::NoAlias; 7285 } 7286 break; 7287 case MDefinition::Opcode::StoreDynamicSlot: 7288 if (store->toStoreDynamicSlot() 7289 ->slots() 7290 ->toSlots() 7291 ->object() 7292 ->skipObjectGuards() == receiverObject) { 7293 return AliasType::NoAlias; 7294 } 7295 break; 7296 case MDefinition::Opcode::AddAndStoreSlot: 7297 if (store->toAddAndStoreSlot()->object()->skipObjectGuards() == 7298 receiverObject) { 7299 return AliasType::NoAlias; 7300 } 7301 break; 7302 case MDefinition::Opcode::AllocateAndStoreSlot: 7303 if (store->toAllocateAndStoreSlot()->object()->skipObjectGuards() == 7304 receiverObject) { 7305 return AliasType::NoAlias; 7306 } 7307 break; 7308 default: 7309 break; 7310 } 7311 } 7312 return MInstruction::mightAlias(store); 7313 } 7314 7315 bool MGuardFuse::congruentTo(const MDefinition* ins) const { 7316 if (!ins->isGuardFuse()) { 7317 return false; 7318 } 7319 if (fuseIndex() != ins->toGuardFuse()->fuseIndex()) { 7320 return false; 7321 } 7322 return congruentIfOperandsEqual(ins); 7323 } 7324 7325 AliasSet MGuardFuse::getAliasSet() const { 7326 // The alias set below reflects the set of operations which could cause a fuse 7327 // to be popped, and therefore MGuardFuse aliases with. 7328 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::DynamicSlot | 7329 AliasSet::FixedSlot | 7330 AliasSet::GlobalGenerationCounter); 7331 } 7332 7333 AliasSet MGuardMultipleShapes::getAliasSet() const { 7334 // Note: This instruction loads the elements of the ListObject used to 7335 // store the list of shapes, but that object is internal and not exposed 7336 // to script, so it doesn't have to be in the alias set. 7337 return AliasSet::Load(AliasSet::ObjectFields); 7338 } 7339 7340 AliasSet MGuardMultipleShapesToOffset::getAliasSet() const { 7341 return AliasSet::Load(AliasSet::ObjectFields); 7342 } 7343 7344 AliasSet MLoadFixedSlotFromOffset::getAliasSet() const { 7345 return AliasSet::Load(AliasSet::FixedSlot); 7346 } 7347 7348 AliasSet MLoadDynamicSlotFromOffset::getAliasSet() const { 7349 MOZ_ASSERT(slots()->type() == MIRType::Slots); 7350 return AliasSet::Load(AliasSet::DynamicSlot); 7351 } 7352 7353 AliasSet MGuardGlobalGeneration::getAliasSet() const { 7354 return AliasSet::Load(AliasSet::GlobalGenerationCounter); 7355 } 7356 7357 bool MGuardGlobalGeneration::congruentTo(const MDefinition* ins) const { 7358 return ins->isGuardGlobalGeneration() && 7359 ins->toGuardGlobalGeneration()->expected() == expected() && 7360 ins->toGuardGlobalGeneration()->generationAddr() == generationAddr(); 7361 } 7362 7363 MDefinition* MGuardIsNotProxy::foldsTo(TempAllocator& alloc) { 7364 KnownClass known = GetObjectKnownClass(object()); 7365 if (known == KnownClass::None) { 7366 return this; 7367 } 7368 7369 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject()); 7370 AssertKnownClass(alloc, this, object()); 7371 return object(); 7372 } 7373 7374 AliasSet MMegamorphicLoadSlotByValue::getAliasSet() const { 7375 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7376 AliasSet::DynamicSlot); 7377 } 7378 7379 static PropertyKey ToNonIntPropertyKey(MDefinition* idval) { 7380 MConstant* constant = idval->maybeConstantValue(); 7381 if (!constant) { 7382 return PropertyKey::Void(); 7383 } 7384 if (constant->type() == MIRType::String) { 7385 JSOffThreadAtom* str = constant->toString(); 7386 if (str->isIndex()) { 7387 return PropertyKey::Void(); 7388 } 7389 return PropertyKey::NonIntAtom(str->unwrap()); 7390 } 7391 if (constant->type() == MIRType::Symbol) { 7392 return PropertyKey::Symbol(constant->toSymbol()); 7393 } 7394 return PropertyKey::Void(); 7395 } 7396 7397 MDefinition* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator& alloc) { 7398 PropertyKey id = ToNonIntPropertyKey(idVal()); 7399 if (id.isVoid()) { 7400 return this; 7401 } 7402 7403 auto* result = MMegamorphicLoadSlot::New(alloc, object(), id); 7404 result->setDependency(dependency()); 7405 return result; 7406 } 7407 7408 MDefinition* MMegamorphicLoadSlotByValuePermissive::foldsTo( 7409 TempAllocator& alloc) { 7410 PropertyKey id = ToNonIntPropertyKey(idVal()); 7411 if (id.isVoid()) { 7412 return this; 7413 } 7414 7415 auto* result = MMegamorphicLoadSlotPermissive::New(alloc, object(), id); 7416 result->stealResumePoint(this); 7417 return result; 7418 } 7419 7420 bool MMegamorphicLoadSlot::congruentTo(const MDefinition* ins) const { 7421 if (!ins->isMegamorphicLoadSlot()) { 7422 return false; 7423 } 7424 if (ins->toMegamorphicLoadSlot()->name() != name()) { 7425 return false; 7426 } 7427 return congruentIfOperandsEqual(ins); 7428 } 7429 7430 AliasSet MMegamorphicLoadSlot::getAliasSet() const { 7431 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7432 AliasSet::DynamicSlot); 7433 } 7434 7435 bool MSmallObjectVariableKeyHasProp::congruentTo(const MDefinition* ins) const { 7436 if (!ins->isSmallObjectVariableKeyHasProp()) { 7437 return false; 7438 } 7439 if (ins->toSmallObjectVariableKeyHasProp()->shape() != shape()) { 7440 return false; 7441 } 7442 return congruentIfOperandsEqual(ins); 7443 } 7444 7445 AliasSet MSmallObjectVariableKeyHasProp::getAliasSet() const { 7446 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7447 AliasSet::DynamicSlot); 7448 } 7449 7450 bool MMegamorphicHasProp::congruentTo(const MDefinition* ins) const { 7451 if (!ins->isMegamorphicHasProp()) { 7452 return false; 7453 } 7454 if (ins->toMegamorphicHasProp()->hasOwn() != hasOwn()) { 7455 return false; 7456 } 7457 return congruentIfOperandsEqual(ins); 7458 } 7459 7460 AliasSet MMegamorphicHasProp::getAliasSet() const { 7461 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7462 AliasSet::DynamicSlot); 7463 } 7464 7465 HashNumber MNurseryObject::valueHash() const { 7466 HashNumber hash = MNullaryInstruction::valueHash(); 7467 hash = addU32ToHash(hash, nurseryObjectIndex()); 7468 return hash; 7469 } 7470 7471 bool MNurseryObject::congruentTo(const MDefinition* ins) const { 7472 if (!ins->isNurseryObject()) { 7473 return false; 7474 } 7475 return nurseryObjectIndex() == ins->toNurseryObject()->nurseryObjectIndex(); 7476 } 7477 7478 AliasSet MGuardFunctionIsNonBuiltinCtor::getAliasSet() const { 7479 return AliasSet::Load(AliasSet::ObjectFields); 7480 } 7481 7482 bool MGuardFunctionKind::congruentTo(const MDefinition* ins) const { 7483 if (!ins->isGuardFunctionKind()) { 7484 return false; 7485 } 7486 if (expected() != ins->toGuardFunctionKind()->expected()) { 7487 return false; 7488 } 7489 if (bailOnEquality() != ins->toGuardFunctionKind()->bailOnEquality()) { 7490 return false; 7491 } 7492 return congruentIfOperandsEqual(ins); 7493 } 7494 7495 AliasSet MGuardFunctionKind::getAliasSet() const { 7496 return AliasSet::Load(AliasSet::ObjectFields); 7497 } 7498 7499 bool MGuardFunctionScript::congruentTo(const MDefinition* ins) const { 7500 if (!ins->isGuardFunctionScript()) { 7501 return false; 7502 } 7503 if (expected() != ins->toGuardFunctionScript()->expected()) { 7504 return false; 7505 } 7506 return congruentIfOperandsEqual(ins); 7507 } 7508 7509 AliasSet MGuardFunctionScript::getAliasSet() const { 7510 // A JSFunction's BaseScript pointer is immutable. Relazification of 7511 // top-level/named self-hosted functions is an exception to this, but we don't 7512 // use this guard for those self-hosted functions. 7513 // See IRGenerator::emitCalleeGuard. 7514 MOZ_ASSERT_IF(flags_.isSelfHostedOrIntrinsic(), flags_.isLambda()); 7515 return AliasSet::None(); 7516 } 7517 7518 bool MGuardSpecificAtom::congruentTo(const MDefinition* ins) const { 7519 if (!ins->isGuardSpecificAtom()) { 7520 return false; 7521 } 7522 if (atom() != ins->toGuardSpecificAtom()->atom()) { 7523 return false; 7524 } 7525 return congruentIfOperandsEqual(ins); 7526 } 7527 7528 MDefinition* MGuardStringToIndex::foldsTo(TempAllocator& alloc) { 7529 if (!string()->isConstant()) { 7530 return this; 7531 } 7532 7533 JSOffThreadAtom* str = string()->toConstant()->toString(); 7534 7535 uint32_t index = UINT32_MAX; 7536 if (!str->isIndex(&index) || index > INT32_MAX) { 7537 return this; 7538 } 7539 7540 return MConstant::NewInt32(alloc, index); 7541 } 7542 7543 MDefinition* MGuardStringToInt32::foldsTo(TempAllocator& alloc) { 7544 if (!string()->isConstant()) { 7545 return this; 7546 } 7547 7548 JSOffThreadAtom* str = string()->toConstant()->toString(); 7549 double number = OffThreadAtomToNumber(str); 7550 7551 int32_t n; 7552 if (!mozilla::NumberIsInt32(number, &n)) { 7553 return this; 7554 } 7555 7556 return MConstant::NewInt32(alloc, n); 7557 } 7558 7559 MDefinition* MGuardStringToDouble::foldsTo(TempAllocator& alloc) { 7560 if (!string()->isConstant()) { 7561 return this; 7562 } 7563 7564 JSOffThreadAtom* str = string()->toConstant()->toString(); 7565 double number = OffThreadAtomToNumber(str); 7566 return MConstant::NewDouble(alloc, number); 7567 } 7568 7569 AliasSet MGuardNoDenseElements::getAliasSet() const { 7570 return AliasSet::Load(AliasSet::ObjectFields); 7571 } 7572 7573 AliasSet MIteratorHasIndices::getAliasSet() const { 7574 return AliasSet::Load(AliasSet::ObjectFields); 7575 } 7576 7577 AliasSet MIteratorsMatchAndHaveIndices::getAliasSet() const { 7578 return AliasSet::Load(AliasSet::ObjectFields); 7579 } 7580 7581 AliasSet MAllocateAndStoreSlot::getAliasSet() const { 7582 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::DynamicSlot); 7583 } 7584 7585 AliasSet MLoadDOMExpandoValue::getAliasSet() const { 7586 return AliasSet::Load(AliasSet::DOMProxyExpando); 7587 } 7588 7589 AliasSet MLoadDOMExpandoValueIgnoreGeneration::getAliasSet() const { 7590 return AliasSet::Load(AliasSet::DOMProxyExpando); 7591 } 7592 7593 bool MGuardDOMExpandoMissingOrGuardShape::congruentTo( 7594 const MDefinition* ins) const { 7595 if (!ins->isGuardDOMExpandoMissingOrGuardShape()) { 7596 return false; 7597 } 7598 if (shape() != ins->toGuardDOMExpandoMissingOrGuardShape()->shape()) { 7599 return false; 7600 } 7601 return congruentIfOperandsEqual(ins); 7602 } 7603 7604 AliasSet MGuardDOMExpandoMissingOrGuardShape::getAliasSet() const { 7605 return AliasSet::Load(AliasSet::ObjectFields); 7606 } 7607 7608 MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) { 7609 const JSClass* clasp = GetObjectKnownJSClass(object()); 7610 if (!clasp || getClass() != clasp) { 7611 return this; 7612 } 7613 7614 AssertKnownClass(alloc, this, object()); 7615 return object(); 7616 } 7617 7618 MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) { 7619 if (GetObjectKnownClass(object()) != KnownClass::Function) { 7620 return this; 7621 } 7622 7623 AssertKnownClass(alloc, this, object()); 7624 return object(); 7625 } 7626 7627 MDefinition* MHasClass::foldsTo(TempAllocator& alloc) { 7628 const JSClass* clasp = GetObjectKnownJSClass(object()); 7629 if (!clasp) { 7630 return this; 7631 } 7632 7633 AssertKnownClass(alloc, this, object()); 7634 return MConstant::NewBoolean(alloc, getClass() == clasp); 7635 } 7636 7637 MDefinition* MIsCallable::foldsTo(TempAllocator& alloc) { 7638 if (input()->type() != MIRType::Object) { 7639 return this; 7640 } 7641 7642 KnownClass known = GetObjectKnownClass(input()); 7643 if (known == KnownClass::None) { 7644 return this; 7645 } 7646 7647 AssertKnownClass(alloc, this, input()); 7648 return MConstant::NewBoolean(alloc, known == KnownClass::Function); 7649 } 7650 7651 MDefinition* MIsArray::foldsTo(TempAllocator& alloc) { 7652 if (input()->type() != MIRType::Object) { 7653 return this; 7654 } 7655 7656 KnownClass known = GetObjectKnownClass(input()); 7657 if (known == KnownClass::None) { 7658 return this; 7659 } 7660 7661 AssertKnownClass(alloc, this, input()); 7662 return MConstant::NewBoolean(alloc, known == KnownClass::Array); 7663 } 7664 7665 AliasSet MObjectClassToString::getAliasSet() const { 7666 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7667 AliasSet::DynamicSlot); 7668 } 7669 7670 MDefinition* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator& alloc) { 7671 switch (GetObjectKnownClass(object())) { 7672 case KnownClass::PlainObject: 7673 case KnownClass::Array: 7674 case KnownClass::Function: 7675 case KnownClass::RegExp: 7676 case KnownClass::ArrayIterator: 7677 case KnownClass::StringIterator: 7678 case KnownClass::RegExpStringIterator: { 7679 AssertKnownClass(alloc, this, object()); 7680 return object(); 7681 } 7682 case KnownClass::None: 7683 break; 7684 } 7685 7686 return this; 7687 } 7688 7689 MDefinition* MCheckIsObj::foldsTo(TempAllocator& alloc) { 7690 if (!input()->isBox()) { 7691 return this; 7692 } 7693 7694 MDefinition* unboxed = input()->toBox()->input(); 7695 if (unboxed->type() == MIRType::Object) { 7696 return unboxed; 7697 } 7698 7699 return this; 7700 } 7701 7702 AliasSet MCheckIsObj::getAliasSet() const { 7703 return AliasSet::Store(AliasSet::ExceptionState); 7704 } 7705 7706 #ifdef JS_PUNBOX64 7707 AliasSet MCheckScriptedProxyGetResult::getAliasSet() const { 7708 return AliasSet::Store(AliasSet::ExceptionState); 7709 } 7710 #endif 7711 7712 static bool IsBoxedObject(MDefinition* def) { 7713 MOZ_ASSERT(def->type() == MIRType::Value); 7714 7715 if (def->isBox()) { 7716 return def->toBox()->input()->type() == MIRType::Object; 7717 } 7718 7719 // Construct calls are always returning a boxed object. 7720 // 7721 // TODO: We should consider encoding this directly in the graph instead of 7722 // having to special case it here. 7723 if (def->isCall()) { 7724 return def->toCall()->isConstructing(); 7725 } 7726 if (def->isConstructArray()) { 7727 return true; 7728 } 7729 if (def->isConstructArgs()) { 7730 return true; 7731 } 7732 7733 return false; 7734 } 7735 7736 MDefinition* MCheckReturn::foldsTo(TempAllocator& alloc) { 7737 auto* returnVal = returnValue(); 7738 if (!returnVal->isBox()) { 7739 return this; 7740 } 7741 7742 auto* unboxedReturnVal = returnVal->toBox()->input(); 7743 if (unboxedReturnVal->type() == MIRType::Object) { 7744 return returnVal; 7745 } 7746 7747 if (unboxedReturnVal->type() != MIRType::Undefined) { 7748 return this; 7749 } 7750 7751 auto* thisVal = thisValue(); 7752 if (IsBoxedObject(thisVal)) { 7753 return thisVal; 7754 } 7755 7756 return this; 7757 } 7758 7759 MDefinition* MCheckThis::foldsTo(TempAllocator& alloc) { 7760 MDefinition* input = thisValue(); 7761 if (!input->isBox()) { 7762 return this; 7763 } 7764 7765 MDefinition* unboxed = input->toBox()->input(); 7766 if (IsMagicType(unboxed->type())) { 7767 return this; 7768 } 7769 7770 return input; 7771 } 7772 7773 MDefinition* MCheckThisReinit::foldsTo(TempAllocator& alloc) { 7774 MDefinition* input = thisValue(); 7775 if (!input->isBox()) { 7776 return this; 7777 } 7778 7779 MDefinition* unboxed = input->toBox()->input(); 7780 if (unboxed->type() != MIRType::MagicUninitializedLexical) { 7781 return this; 7782 } 7783 7784 return input; 7785 } 7786 7787 MDefinition* MCheckObjCoercible::foldsTo(TempAllocator& alloc) { 7788 MDefinition* input = checkValue(); 7789 if (!input->isBox()) { 7790 return this; 7791 } 7792 7793 MDefinition* unboxed = input->toBox()->input(); 7794 if (IsNullOrUndefined(unboxed->type())) { 7795 return this; 7796 } 7797 7798 return input; 7799 } 7800 7801 AliasSet MCheckObjCoercible::getAliasSet() const { 7802 return AliasSet::Store(AliasSet::ExceptionState); 7803 } 7804 7805 AliasSet MCheckReturn::getAliasSet() const { 7806 return AliasSet::Store(AliasSet::ExceptionState); 7807 } 7808 7809 AliasSet MCheckThis::getAliasSet() const { 7810 return AliasSet::Store(AliasSet::ExceptionState); 7811 } 7812 7813 AliasSet MCheckThisReinit::getAliasSet() const { 7814 return AliasSet::Store(AliasSet::ExceptionState); 7815 } 7816 7817 AliasSet MIsPackedArray::getAliasSet() const { 7818 return AliasSet::Load(AliasSet::ObjectFields); 7819 } 7820 7821 AliasSet MGuardArrayIsPacked::getAliasSet() const { 7822 return AliasSet::Load(AliasSet::ObjectFields); 7823 } 7824 7825 AliasSet MGuardElementsArePacked::getAliasSet() const { 7826 return AliasSet::Load(AliasSet::ObjectFields); 7827 } 7828 7829 AliasSet MSuperFunction::getAliasSet() const { 7830 return AliasSet::Load(AliasSet::ObjectFields); 7831 } 7832 7833 AliasSet MSuperFunctionAndUnbox::getAliasSet() const { 7834 return AliasSet::Load(AliasSet::ObjectFields); 7835 } 7836 7837 AliasSet MInitHomeObject::getAliasSet() const { 7838 return AliasSet::Store(AliasSet::ObjectFields); 7839 } 7840 7841 AliasSet MLoadWrapperTarget::getAliasSet() const { 7842 return AliasSet::Load(AliasSet::Any); 7843 } 7844 7845 bool MLoadWrapperTarget::congruentTo(const MDefinition* ins) const { 7846 if (!ins->isLoadWrapperTarget()) { 7847 return false; 7848 } 7849 if (ins->toLoadWrapperTarget()->fallible() != fallible()) { 7850 return false; 7851 } 7852 return congruentIfOperandsEqual(ins); 7853 } 7854 7855 AliasSet MGuardHasGetterSetter::getAliasSet() const { 7856 return AliasSet::Load(AliasSet::ObjectFields); 7857 } 7858 7859 bool MGuardHasGetterSetter::congruentTo(const MDefinition* ins) const { 7860 if (!ins->isGuardHasGetterSetter()) { 7861 return false; 7862 } 7863 if (ins->toGuardHasGetterSetter()->propId() != propId()) { 7864 return false; 7865 } 7866 if (ins->toGuardHasGetterSetter()->getterSetterValue() != 7867 getterSetterValue()) { 7868 return false; 7869 } 7870 return congruentIfOperandsEqual(ins); 7871 } 7872 7873 AliasSet MGuardIsExtensible::getAliasSet() const { 7874 return AliasSet::Load(AliasSet::ObjectFields); 7875 } 7876 7877 AliasSet MGuardIndexIsNotDenseElement::getAliasSet() const { 7878 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element); 7879 } 7880 7881 AliasSet MGuardIndexIsValidUpdateOrAdd::getAliasSet() const { 7882 return AliasSet::Load(AliasSet::ObjectFields); 7883 } 7884 7885 AliasSet MCallObjectHasSparseElement::getAliasSet() const { 7886 return AliasSet::Load(AliasSet::Element | AliasSet::ObjectFields | 7887 AliasSet::FixedSlot | AliasSet::DynamicSlot); 7888 } 7889 7890 AliasSet MLoadSlotByIteratorIndex::getAliasSet() const { 7891 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7892 AliasSet::DynamicSlot | AliasSet::Element); 7893 } 7894 7895 AliasSet MStoreSlotByIteratorIndex::getAliasSet() const { 7896 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | 7897 AliasSet::DynamicSlot | AliasSet::Element); 7898 } 7899 7900 AliasSet MLoadSlotByIteratorIndexIndexed::getAliasSet() const { 7901 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 7902 AliasSet::DynamicSlot | AliasSet::Element); 7903 } 7904 7905 AliasSet MStoreSlotByIteratorIndexIndexed::getAliasSet() const { 7906 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | 7907 AliasSet::DynamicSlot | AliasSet::Element); 7908 } 7909 7910 MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) { 7911 MOZ_ASSERT(index()->type() == MIRType::Int32); 7912 7913 MDefinition* input = index(); 7914 if (!input->isConstant() || input->toConstant()->toInt32() < 0) { 7915 return this; 7916 } 7917 return input; 7918 } 7919 7920 MDefinition* MGuardIntPtrIsNonNegative::foldsTo(TempAllocator& alloc) { 7921 MOZ_ASSERT(index()->type() == MIRType::IntPtr); 7922 7923 MDefinition* input = index(); 7924 if (!input->isConstant() || input->toConstant()->toIntPtr() < 0) { 7925 return this; 7926 } 7927 return input; 7928 } 7929 7930 MDefinition* MGuardInt32Range::foldsTo(TempAllocator& alloc) { 7931 MOZ_ASSERT(input()->type() == MIRType::Int32); 7932 MOZ_ASSERT(minimum() <= maximum()); 7933 7934 MDefinition* in = input(); 7935 if (!in->isConstant()) { 7936 return this; 7937 } 7938 int32_t cst = in->toConstant()->toInt32(); 7939 if (cst < minimum() || cst > maximum()) { 7940 return this; 7941 } 7942 return in; 7943 } 7944 7945 MDefinition* MGuardNonGCThing::foldsTo(TempAllocator& alloc) { 7946 if (!input()->isBox()) { 7947 return this; 7948 } 7949 7950 MDefinition* unboxed = input()->toBox()->input(); 7951 if (!IsNonGCThing(unboxed->type())) { 7952 return this; 7953 } 7954 return input(); 7955 } 7956 7957 AliasSet MSetObjectHasNonBigInt::getAliasSet() const { 7958 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7959 } 7960 7961 AliasSet MSetObjectHasBigInt::getAliasSet() const { 7962 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7963 } 7964 7965 AliasSet MSetObjectHasValue::getAliasSet() const { 7966 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7967 } 7968 7969 AliasSet MSetObjectHasValueVMCall::getAliasSet() const { 7970 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7971 } 7972 7973 AliasSet MSetObjectSize::getAliasSet() const { 7974 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7975 } 7976 7977 AliasSet MMapObjectHasNonBigInt::getAliasSet() const { 7978 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7979 } 7980 7981 AliasSet MMapObjectHasBigInt::getAliasSet() const { 7982 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7983 } 7984 7985 AliasSet MMapObjectHasValue::getAliasSet() const { 7986 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7987 } 7988 7989 AliasSet MMapObjectHasValueVMCall::getAliasSet() const { 7990 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7991 } 7992 7993 AliasSet MMapObjectGetNonBigInt::getAliasSet() const { 7994 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7995 } 7996 7997 AliasSet MMapObjectGetBigInt::getAliasSet() const { 7998 return AliasSet::Load(AliasSet::MapOrSetHashTable); 7999 } 8000 8001 AliasSet MMapObjectGetValue::getAliasSet() const { 8002 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8003 } 8004 8005 AliasSet MMapObjectGetValueVMCall::getAliasSet() const { 8006 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8007 } 8008 8009 AliasSet MMapObjectSize::getAliasSet() const { 8010 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8011 } 8012 8013 AliasSet MWeakMapGetObject::getAliasSet() const { 8014 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8015 } 8016 8017 AliasSet MWeakMapHasObject::getAliasSet() const { 8018 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8019 } 8020 8021 AliasSet MWeakSetHasObject::getAliasSet() const { 8022 return AliasSet::Load(AliasSet::MapOrSetHashTable); 8023 } 8024 8025 AliasSet MDateFillLocalTimeSlots::getAliasSet() const { 8026 // Reads and stores fixed slots. Additional reads from DateTimeInfo don't need 8027 // to be tracked, because they don't interact with other alias set states. 8028 return AliasSet::Store(AliasSet::FixedSlot); 8029 } 8030 8031 MBindFunction* MBindFunction::New(TempAllocator& alloc, MDefinition* target, 8032 uint32_t argc, JSObject* templateObj) { 8033 auto* ins = new (alloc) MBindFunction(templateObj); 8034 if (!ins->init(alloc, NumNonArgumentOperands + argc)) { 8035 return nullptr; 8036 } 8037 ins->initOperand(0, target); 8038 return ins; 8039 } 8040 8041 MCreateInlinedArgumentsObject* MCreateInlinedArgumentsObject::New( 8042 TempAllocator& alloc, MDefinition* callObj, MDefinition* callee, 8043 MDefinitionVector& args, ArgumentsObject* templateObj) { 8044 MCreateInlinedArgumentsObject* ins = 8045 new (alloc) MCreateInlinedArgumentsObject(templateObj); 8046 8047 uint32_t argc = args.length(); 8048 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs); 8049 8050 if (!ins->init(alloc, argc + NumNonArgumentOperands)) { 8051 return nullptr; 8052 } 8053 8054 ins->initOperand(0, callObj); 8055 ins->initOperand(1, callee); 8056 for (uint32_t i = 0; i < argc; i++) { 8057 ins->initOperand(i + NumNonArgumentOperands, args[i]); 8058 } 8059 8060 return ins; 8061 } 8062 8063 MGetInlinedArgument* MGetInlinedArgument::New( 8064 TempAllocator& alloc, MDefinition* index, 8065 MCreateInlinedArgumentsObject* args) { 8066 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument(); 8067 8068 uint32_t argc = args->numActuals(); 8069 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs); 8070 8071 if (!ins->init(alloc, argc + NumNonArgumentOperands)) { 8072 return nullptr; 8073 } 8074 8075 ins->initOperand(0, index); 8076 for (uint32_t i = 0; i < argc; i++) { 8077 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i)); 8078 } 8079 8080 return ins; 8081 } 8082 8083 MGetInlinedArgument* MGetInlinedArgument::New(TempAllocator& alloc, 8084 MDefinition* index, 8085 const CallInfo& callInfo) { 8086 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument(); 8087 8088 uint32_t argc = callInfo.argc(); 8089 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs); 8090 8091 if (!ins->init(alloc, argc + NumNonArgumentOperands)) { 8092 return nullptr; 8093 } 8094 8095 ins->initOperand(0, index); 8096 for (uint32_t i = 0; i < argc; i++) { 8097 ins->initOperand(i + NumNonArgumentOperands, callInfo.getArg(i)); 8098 } 8099 8100 return ins; 8101 } 8102 8103 MDefinition* MGetInlinedArgument::foldsTo(TempAllocator& alloc) { 8104 MDefinition* indexDef = SkipUninterestingInstructions(index()); 8105 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) { 8106 return this; 8107 } 8108 8109 int32_t indexConst = indexDef->toConstant()->toInt32(); 8110 if (indexConst < 0 || uint32_t(indexConst) >= numActuals()) { 8111 return this; 8112 } 8113 8114 MDefinition* arg = getArg(indexConst); 8115 if (arg->type() != MIRType::Value) { 8116 arg = MBox::New(alloc, arg); 8117 } 8118 8119 return arg; 8120 } 8121 8122 MGetInlinedArgumentHole* MGetInlinedArgumentHole::New( 8123 TempAllocator& alloc, MDefinition* index, 8124 MCreateInlinedArgumentsObject* args) { 8125 auto* ins = new (alloc) MGetInlinedArgumentHole(); 8126 8127 uint32_t argc = args->numActuals(); 8128 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs); 8129 8130 if (!ins->init(alloc, argc + NumNonArgumentOperands)) { 8131 return nullptr; 8132 } 8133 8134 ins->initOperand(0, index); 8135 for (uint32_t i = 0; i < argc; i++) { 8136 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i)); 8137 } 8138 8139 return ins; 8140 } 8141 8142 MDefinition* MGetInlinedArgumentHole::foldsTo(TempAllocator& alloc) { 8143 MDefinition* indexDef = SkipUninterestingInstructions(index()); 8144 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) { 8145 return this; 8146 } 8147 8148 int32_t indexConst = indexDef->toConstant()->toInt32(); 8149 if (indexConst < 0) { 8150 return this; 8151 } 8152 8153 MDefinition* arg; 8154 if (uint32_t(indexConst) < numActuals()) { 8155 arg = getArg(indexConst); 8156 8157 if (arg->type() != MIRType::Value) { 8158 arg = MBox::New(alloc, arg); 8159 } 8160 } else { 8161 auto* undefined = MConstant::NewUndefined(alloc); 8162 block()->insertBefore(this, undefined); 8163 8164 arg = MBox::New(alloc, undefined); 8165 } 8166 8167 return arg; 8168 } 8169 8170 MInlineArgumentsSlice* MInlineArgumentsSlice::New( 8171 TempAllocator& alloc, MDefinition* begin, MDefinition* count, 8172 MCreateInlinedArgumentsObject* args, JSObject* templateObj, 8173 gc::Heap initialHeap) { 8174 auto* ins = new (alloc) MInlineArgumentsSlice(templateObj, initialHeap); 8175 8176 uint32_t argc = args->numActuals(); 8177 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs); 8178 8179 if (!ins->init(alloc, argc + NumNonArgumentOperands)) { 8180 return nullptr; 8181 } 8182 8183 ins->initOperand(0, begin); 8184 ins->initOperand(1, count); 8185 for (uint32_t i = 0; i < argc; i++) { 8186 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i)); 8187 } 8188 8189 return ins; 8190 } 8191 8192 MDefinition* MArrayLength::foldsTo(TempAllocator& alloc) { 8193 // Object.keys() is potentially effectful, in case of Proxies. Otherwise, when 8194 // it is only computed for its length property, there is no need to 8195 // materialize the Array which results from it and it can be marked as 8196 // recovered on bailout as long as no properties are added to / removed from 8197 // the object. 8198 MDefinition* elems = elements(); 8199 if (!elems->isElements()) { 8200 return this; 8201 } 8202 8203 MDefinition* guardshape = elems->toElements()->object(); 8204 if (!guardshape->isGuardShape()) { 8205 return this; 8206 } 8207 8208 // The Guard shape is guarding the shape of the object returned by 8209 // Object.keys, this guard can be removed as knowing the function is good 8210 // enough to infer that we are returning an array. 8211 MDefinition* keys = guardshape->toGuardShape()->object(); 8212 if (!keys->isObjectKeys()) { 8213 return this; 8214 } 8215 8216 // Object.keys() inline cache guards against proxies when creating the IC. We 8217 // rely on this here as we are looking to elide `Object.keys(...)` call, which 8218 // is only possible if we know for sure that no side-effect might have 8219 // happened. 8220 MDefinition* noproxy = keys->toObjectKeys()->object(); 8221 if (!noproxy->isGuardIsNotProxy()) { 8222 // The guard might have been replaced by an assertion, in case the class is 8223 // known at compile time. IF the guard has been removed check whether check 8224 // has been removed. 8225 MOZ_RELEASE_ASSERT(GetObjectKnownClass(noproxy) != KnownClass::None); 8226 MOZ_RELEASE_ASSERT(!GetObjectKnownJSClass(noproxy)->isProxyObject()); 8227 } 8228 8229 // Check if both the elements and the Object.keys() have a single use. We only 8230 // check for live uses, and are ok if a branch which was previously using the 8231 // keys array has been removed since. 8232 if (!elems->hasOneLiveDefUse() || !guardshape->hasOneLiveDefUse() || 8233 !keys->hasOneLiveDefUse()) { 8234 return this; 8235 } 8236 8237 // Check that the latest active resume point is the one from Object.keys(), in 8238 // order to steal it. If this is not the latest active resume point then some 8239 // side-effect might happen which updates the content of the object, making 8240 // any recovery of the keys exhibit a different behavior than expected. 8241 if (keys->toObjectKeys()->resumePoint() != block()->activeResumePoint(this)) { 8242 return this; 8243 } 8244 8245 // Verify whether any resume point captures the keys array after any aliasing 8246 // mutations. If this were to be the case the recovery of ObjectKeys on 8247 // bailout might compute a version which might not match with the elided 8248 // result. 8249 // 8250 // Iterate over the resume point uses of ObjectKeys, and check whether the 8251 // instructions they are attached to are aliasing Object fields. If so, skip 8252 // this optimization. 8253 AliasSet enumKeysAliasSet = AliasSet::Load(AliasSet::Flag::ObjectFields); 8254 for (auto* use : UsesIterator(keys)) { 8255 if (!use->consumer()->isResumePoint()) { 8256 // There is only a single use, and this is the length computation as 8257 // asserted with `hasOneLiveDefUse`. 8258 continue; 8259 } 8260 8261 MResumePoint* rp = use->consumer()->toResumePoint(); 8262 if (!rp->instruction()) { 8263 // If there is no instruction, this is a resume point which is attached to 8264 // the entry of a block. Thus no risk of mutating the object on which the 8265 // keys are queried. 8266 continue; 8267 } 8268 8269 MInstruction* ins = rp->instruction(); 8270 if (ins == keys) { 8271 continue; 8272 } 8273 8274 // Check whether the instruction can potentially alias the object fields of 8275 // the object from which we are querying the keys. 8276 AliasSet mightAlias = ins->getAliasSet() & enumKeysAliasSet; 8277 if (!mightAlias.isNone()) { 8278 return this; 8279 } 8280 } 8281 8282 // Flag every instructions since Object.keys(..) as recovered on bailout, and 8283 // make Object.keys(..) be the recovered value in-place of the shape guard. 8284 setRecoveredOnBailout(); 8285 elems->setRecoveredOnBailout(); 8286 guardshape->replaceAllUsesWith(keys); 8287 guardshape->block()->discard(guardshape->toGuardShape()); 8288 keys->setRecoveredOnBailout(); 8289 8290 // Steal the resume point from Object.keys, which is ok as we confirmed that 8291 // there is no other resume point in-between. 8292 MObjectKeysLength* keysLength = MObjectKeysLength::New(alloc, noproxy); 8293 keysLength->stealResumePoint(keys->toObjectKeys()); 8294 8295 // Set the dependency of the newly created instruction. Unfortunately 8296 // MObjectKeys (keys) is an instruction with a Store(Any) alias set, as it 8297 // could be used with proxies which can re-enter JavaScript. 8298 // 8299 // Thus, the loadDependency field of MObjectKeys is null. On the other hand 8300 // MObjectKeysLength has a Load alias set. Thus, instead of reconstructing the 8301 // Alias Analysis by updating every instructions which depends on MObjectKeys 8302 // and finding the matching store instruction, we reuse the MObjectKeys as any 8303 // store instruction, despite it being marked as recovered-on-bailout. 8304 keysLength->setDependency(keys); 8305 8306 return keysLength; 8307 } 8308 8309 MDefinition* MNormalizeSliceTerm::foldsTo(TempAllocator& alloc) { 8310 auto* length = this->length(); 8311 if (!length->isConstant() && !length->isArgumentsLength()) { 8312 return this; 8313 } 8314 8315 if (length->isConstant()) { 8316 int32_t lengthConst = length->toConstant()->toInt32(); 8317 MOZ_ASSERT(lengthConst >= 0); 8318 8319 // Result is always zero when |length| is zero. 8320 if (lengthConst == 0) { 8321 return length; 8322 } 8323 8324 auto* value = this->value(); 8325 if (value->isConstant()) { 8326 int32_t valueConst = value->toConstant()->toInt32(); 8327 8328 int32_t normalized; 8329 if (valueConst < 0) { 8330 normalized = std::max(valueConst + lengthConst, 0); 8331 } else { 8332 normalized = std::min(valueConst, lengthConst); 8333 } 8334 8335 if (normalized == valueConst) { 8336 return value; 8337 } 8338 if (normalized == lengthConst) { 8339 return length; 8340 } 8341 return MConstant::NewInt32(alloc, normalized); 8342 } 8343 8344 return this; 8345 } 8346 8347 auto* value = this->value(); 8348 if (value->isConstant()) { 8349 int32_t valueConst = value->toConstant()->toInt32(); 8350 8351 // Minimum of |value| and |length|. 8352 if (valueConst > 0) { 8353 return MMinMax::NewMin(alloc, value, length, MIRType::Int32); 8354 } 8355 8356 // Maximum of |value + length| and zero. 8357 if (valueConst < 0) { 8358 // Safe to truncate because |length| is never negative. 8359 auto* add = MAdd::New(alloc, value, length, TruncateKind::Truncate); 8360 block()->insertBefore(this, add); 8361 8362 auto* zero = MConstant::NewInt32(alloc, 0); 8363 block()->insertBefore(this, zero); 8364 8365 return MMinMax::NewMax(alloc, add, zero, MIRType::Int32); 8366 } 8367 8368 // Directly return the value when it's zero. 8369 return value; 8370 } 8371 8372 // Normalizing MArgumentsLength is a no-op. 8373 if (value->isArgumentsLength()) { 8374 return value; 8375 } 8376 8377 return this; 8378 } 8379 8380 MDefinition* MToIntegerIndex::foldsTo(TempAllocator& alloc) { 8381 // |length| is guaranteed to be a non-negative value. 8382 8383 auto* index = this->index(); 8384 auto* length = this->length(); 8385 8386 if (index == length) { 8387 return index; 8388 } 8389 8390 if (index->isConstant()) { 8391 intptr_t indexConst = index->toConstant()->toIntPtr(); 8392 8393 // Minimum of |index| and |length|. 8394 if (indexConst > 0) { 8395 return MMinMax::NewMin(alloc, index, length, MIRType::IntPtr); 8396 } 8397 8398 // Maximum of |value + length| and zero. 8399 if (indexConst < 0) { 8400 auto* add = MAdd::New(alloc, index, length, MIRType::IntPtr); 8401 block()->insertBefore(this, add); 8402 8403 auto* zero = MConstant::NewIntPtr(alloc, 0); 8404 block()->insertBefore(this, zero); 8405 8406 return MMinMax::NewMax(alloc, add, zero, MIRType::IntPtr); 8407 } 8408 8409 // Directly return the index when it's zero. 8410 return index; 8411 } 8412 8413 return this; 8414 } 8415 8416 bool MInt32ToStringWithBase::congruentTo(const MDefinition* ins) const { 8417 if (!ins->isInt32ToStringWithBase()) { 8418 return false; 8419 } 8420 if (ins->toInt32ToStringWithBase()->lowerCase() != lowerCase()) { 8421 return false; 8422 } 8423 return congruentIfOperandsEqual(ins); 8424 } 8425 8426 // Returns `false` if it can be proven that (1) both `mtyA` and `mtyB` are 8427 // struct types and (2) they are not related by inheritance. Returns `true` in 8428 // all other cases. `true` is the safe-but-possibly-suboptimal return value. 8429 static bool StructTypesMightBeRelatedByInheritance(wasm::MaybeRefType mtyA, 8430 wasm::MaybeRefType mtyB) { 8431 if (!mtyA.isSome() || !mtyB.isSome()) { 8432 // The "Track Wasm ref types" pass couldn't establish that both `mtyA` and 8433 // `mtyB` are ref types. Give up. 8434 return true; 8435 } 8436 8437 wasm::RefType tyA = mtyA.value(); 8438 wasm::RefType tyB = mtyB.value(); 8439 if (!tyA.isTypeRef() || !tyA.typeDef()->isStructType() || !tyB.isTypeRef() || 8440 !tyB.typeDef()->isStructType()) { 8441 // They aren't both struct types. Give up. 8442 return true; 8443 } 8444 8445 // They are both struct types. So they are related by inheritance if one is 8446 // a subtype of the other. (Which is also the case if they are the same 8447 // type.) 8448 return wasm::RefType::valuesMightAlias(tyA, tyB); 8449 } 8450 8451 MDefinition::AliasType MWasmLoadField::mightAlias( 8452 const MDefinition* ins) const { 8453 if (!(getAliasSet().flags() & ins->getAliasSet().flags())) { 8454 return AliasType::NoAlias; 8455 } 8456 MOZ_ASSERT(!isEffectful() && ins->isEffectful()); 8457 8458 // Pick off cases where we can easily prove non-aliasing. The idea is that 8459 // two struct field accesses can't alias if either they are at different 8460 // offsets, or the struct types are unrelated (which implies that the struct 8461 // base pointer for one of the accesses could not validly be handed to the 8462 // other access). 8463 if (ins->isWasmStoreField()) { 8464 const MWasmStoreField* store = ins->toWasmStoreField(); 8465 if (offset() != store->offset() || 8466 !StructTypesMightBeRelatedByInheritance(base()->wasmRefType(), 8467 store->base()->wasmRefType())) { 8468 return AliasType::NoAlias; 8469 } 8470 } else if (ins->isWasmStoreFieldRef()) { 8471 const MWasmStoreFieldRef* store = ins->toWasmStoreFieldRef(); 8472 if (offset() != store->offset() || 8473 !StructTypesMightBeRelatedByInheritance(base()->wasmRefType(), 8474 store->base()->wasmRefType())) { 8475 return AliasType::NoAlias; 8476 } 8477 } 8478 8479 return AliasType::MayAlias; 8480 }