tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CacheIR.cpp (566641B)


      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/CacheIR.h"
      8 
      9 #include "mozilla/CheckedInt.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/FloatingPoint.h"
     12 
     13 #include "jsapi.h"
     14 #include "jsdate.h"
     15 #include "jsmath.h"
     16 #include "jsnum.h"
     17 
     18 #include "builtin/DataViewObject.h"
     19 #include "builtin/MapObject.h"
     20 #include "builtin/ModuleObject.h"
     21 #include "builtin/Object.h"
     22 #include "builtin/WeakMapObject.h"
     23 #include "builtin/WeakSetObject.h"
     24 #include "gc/GC.h"
     25 #include "jit/BaselineIC.h"
     26 #include "jit/CacheIRCloner.h"
     27 #include "jit/CacheIRCompiler.h"
     28 #include "jit/CacheIRGenerator.h"
     29 #include "jit/CacheIRSpewer.h"
     30 #include "jit/CacheIRWriter.h"
     31 #include "jit/InlinableNatives.h"
     32 #include "jit/JitContext.h"
     33 #include "jit/JitZone.h"
     34 #include "js/experimental/JitInfo.h"  // JSJitInfo
     35 #include "js/friend/DOMProxy.h"       // JS::ExpandoAndGeneration
     36 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy, js::ToWindowIfWindowProxy
     37 #include "js/friend/XrayJitInfo.h"  // js::jit::GetXrayJitInfo, JS::XrayJitInfo
     38 #include "js/GCAPI.h"               // JS::AutoSuppressGCAnalysis
     39 #include "js/Prefs.h"               // JS::Prefs
     40 #include "js/RegExpFlags.h"         // JS::RegExpFlags
     41 #include "js/ScalarType.h"          // js::Scalar::Type
     42 #include "js/Utility.h"             // JS::AutoEnterOOMUnsafeRegion
     43 #include "js/Wrapper.h"
     44 #include "proxy/DOMProxy.h"  // js::GetDOMProxyHandlerFamily
     45 #include "proxy/ScriptedProxyHandler.h"
     46 #include "util/DifferentialTesting.h"
     47 #include "util/Unicode.h"
     48 #include "vm/ArrayBufferObject.h"
     49 #include "vm/BoundFunctionObject.h"
     50 #include "vm/BytecodeUtil.h"
     51 #include "vm/Compartment.h"
     52 #include "vm/DateObject.h"
     53 #include "vm/Iteration.h"
     54 #include "vm/PlainObject.h"  // js::PlainObject
     55 #include "vm/ProxyObject.h"
     56 #include "vm/RegExpObject.h"
     57 #include "vm/SelfHosting.h"
     58 #include "vm/ThrowMsgKind.h"     // ThrowCondition
     59 #include "vm/TypeofEqOperand.h"  // TypeofEqOperand
     60 #include "vm/Watchtower.h"
     61 #include "wasm/WasmInstance.h"
     62 
     63 #include "jit/BaselineFrame-inl.h"
     64 #include "jit/MacroAssembler-inl.h"
     65 #include "vm/ArrayBufferObject-inl.h"
     66 #include "vm/BytecodeUtil-inl.h"
     67 #include "vm/EnvironmentObject-inl.h"
     68 #include "vm/JSContext-inl.h"
     69 #include "vm/JSFunction-inl.h"
     70 #include "vm/JSObject-inl.h"
     71 #include "vm/JSScript-inl.h"
     72 #include "vm/List-inl.h"
     73 #include "vm/NativeObject-inl.h"
     74 #include "vm/PlainObject-inl.h"
     75 #include "vm/StringObject-inl.h"
     76 #include "wasm/WasmInstance-inl.h"
     77 
     78 using namespace js;
     79 using namespace js::jit;
     80 
     81 using mozilla::DebugOnly;
     82 using mozilla::Maybe;
     83 
     84 using JS::DOMProxyShadowsResult;
     85 using JS::ExpandoAndGeneration;
     86 
     87 const char* const js::jit::CacheKindNames[] = {
     88 #define DEFINE_KIND(kind) #kind,
     89    CACHE_IR_KINDS(DEFINE_KIND)
     90 #undef DEFINE_KIND
     91 };
     92 
     93 const char* const js::jit::CacheIROpNames[] = {
     94 #define OPNAME(op, ...) #op,
     95    CACHE_IR_OPS(OPNAME)
     96 #undef OPNAME
     97 };
     98 
     99 const CacheIROpInfo js::jit::CacheIROpInfos[] = {
    100 #define OPINFO(op, len, transpile, ...) {len, transpile},
    101    CACHE_IR_OPS(OPINFO)
    102 #undef OPINFO
    103 };
    104 
    105 const uint32_t js::jit::CacheIROpHealth[] = {
    106 #define OPHEALTH(op, len, transpile, health) health,
    107    CACHE_IR_OPS(OPHEALTH)
    108 #undef OPHEALTH
    109 };
    110 
    111 size_t js::jit::NumInputsForCacheKind(CacheKind kind) {
    112  switch (kind) {
    113    case CacheKind::NewArray:
    114    case CacheKind::NewObject:
    115    case CacheKind::Lambda:
    116    case CacheKind::LazyConstant:
    117    case CacheKind::GetImport:
    118      return 0;
    119    case CacheKind::GetProp:
    120    case CacheKind::TypeOf:
    121    case CacheKind::TypeOfEq:
    122    case CacheKind::ToPropertyKey:
    123    case CacheKind::GetIterator:
    124    case CacheKind::ToBool:
    125    case CacheKind::UnaryArith:
    126    case CacheKind::GetName:
    127    case CacheKind::BindName:
    128    case CacheKind::Call:
    129    case CacheKind::OptimizeSpreadCall:
    130    case CacheKind::CloseIter:
    131    case CacheKind::OptimizeGetIterator:
    132      return 1;
    133    case CacheKind::Compare:
    134    case CacheKind::GetElem:
    135    case CacheKind::GetPropSuper:
    136    case CacheKind::SetProp:
    137    case CacheKind::In:
    138    case CacheKind::HasOwn:
    139    case CacheKind::CheckPrivateField:
    140    case CacheKind::InstanceOf:
    141    case CacheKind::BinaryArith:
    142      return 2;
    143    case CacheKind::GetElemSuper:
    144    case CacheKind::SetElem:
    145      return 3;
    146  }
    147  MOZ_CRASH("Invalid kind");
    148 }
    149 
    150 #ifdef DEBUG
    151 void CacheIRWriter::assertSameCompartment(JSObject* obj) {
    152  MOZ_ASSERT(cx_->compartment() == obj->compartment());
    153 }
    154 void CacheIRWriter::assertSameZone(Shape* shape) {
    155  MOZ_ASSERT(cx_->zone() == shape->zone());
    156 }
    157 #endif
    158 
    159 StubField CacheIRWriter::readStubField(uint32_t offset,
    160                                       StubField::Type type) const {
    161  size_t index = 0;
    162  size_t currentOffset = 0;
    163 
    164  // If we've seen an offset earlier than this before, we know we can start the
    165  // search there at least, otherwise, we start the search from the beginning.
    166  if (lastOffset_ < offset) {
    167    currentOffset = lastOffset_;
    168    index = lastIndex_;
    169  }
    170 
    171  while (currentOffset != offset) {
    172    currentOffset += StubField::sizeInBytes(stubFields_[index].type());
    173    index++;
    174    MOZ_ASSERT(index < stubFields_.length());
    175  }
    176 
    177  MOZ_ASSERT(stubFields_[index].type() == type);
    178 
    179  lastOffset_ = currentOffset;
    180  lastIndex_ = index;
    181 
    182  return stubFields_[index];
    183 }
    184 
    185 CacheIRCloner::CacheIRCloner(ICCacheIRStub* stub)
    186    : stubInfo_(stub->stubInfo()), stubData_(stub->stubDataStart()) {}
    187 
    188 void CacheIRCloner::cloneOp(CacheOp op, CacheIRReader& reader,
    189                            CacheIRWriter& writer) {
    190  switch (op) {
    191 #define DEFINE_OP(op, ...)     \
    192  case CacheOp::op:            \
    193    clone##op(reader, writer); \
    194    break;
    195    CACHE_IR_OPS(DEFINE_OP)
    196 #undef DEFINE_OP
    197    default:
    198      MOZ_CRASH("Invalid op");
    199  }
    200 }
    201 
    202 uintptr_t CacheIRCloner::readStubWord(uint32_t offset) {
    203  return stubInfo_->getStubRawWord(stubData_, offset);
    204 }
    205 int64_t CacheIRCloner::readStubInt64(uint32_t offset) {
    206  return stubInfo_->getStubRawInt64(stubData_, offset);
    207 }
    208 
    209 Shape* CacheIRCloner::getShapeField(uint32_t stubOffset) {
    210  return reinterpret_cast<Shape*>(readStubWord(stubOffset));
    211 }
    212 Shape* CacheIRCloner::getWeakShapeField(uint32_t stubOffset) {
    213  // No barrier is required to clone a weak pointer.
    214  return reinterpret_cast<Shape*>(readStubWord(stubOffset));
    215 }
    216 JSObject* CacheIRCloner::getObjectField(uint32_t stubOffset) {
    217  return reinterpret_cast<JSObject*>(readStubWord(stubOffset));
    218 }
    219 JSObject* CacheIRCloner::getWeakObjectField(uint32_t stubOffset) {
    220  // No barrier is required to clone a weak pointer.
    221  return reinterpret_cast<JSObject*>(readStubWord(stubOffset));
    222 }
    223 JSString* CacheIRCloner::getStringField(uint32_t stubOffset) {
    224  return reinterpret_cast<JSString*>(readStubWord(stubOffset));
    225 }
    226 JSAtom* CacheIRCloner::getAtomField(uint32_t stubOffset) {
    227  return reinterpret_cast<JSAtom*>(readStubWord(stubOffset));
    228 }
    229 JS::Symbol* CacheIRCloner::getSymbolField(uint32_t stubOffset) {
    230  return reinterpret_cast<JS::Symbol*>(readStubWord(stubOffset));
    231 }
    232 BaseScript* CacheIRCloner::getWeakBaseScriptField(uint32_t stubOffset) {
    233  // No barrier is required to clone a weak pointer.
    234  return reinterpret_cast<BaseScript*>(readStubWord(stubOffset));
    235 }
    236 JitCode* CacheIRCloner::getJitCodeField(uint32_t stubOffset) {
    237  return reinterpret_cast<JitCode*>(readStubWord(stubOffset));
    238 }
    239 uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset) {
    240  return uint32_t(reinterpret_cast<uintptr_t>(readStubWord(stubOffset)));
    241 }
    242 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset) {
    243  return reinterpret_cast<const void*>(readStubWord(stubOffset));
    244 }
    245 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset) {
    246  return static_cast<uint64_t>(readStubInt64(stubOffset));
    247 }
    248 gc::AllocSite* CacheIRCloner::getAllocSiteField(uint32_t stubOffset) {
    249  return reinterpret_cast<gc::AllocSite*>(readStubWord(stubOffset));
    250 }
    251 
    252 jsid CacheIRCloner::getIdField(uint32_t stubOffset) {
    253  return jsid::fromRawBits(readStubWord(stubOffset));
    254 }
    255 Value CacheIRCloner::getValueField(uint32_t stubOffset) {
    256  return Value::fromRawBits(uint64_t(readStubInt64(stubOffset)));
    257 }
    258 Value CacheIRCloner::getWeakValueField(uint32_t stubOffset) {
    259  // No barrier is required to clone a weak pointer.
    260  return Value::fromRawBits(uint64_t(readStubInt64(stubOffset)));
    261 }
    262 double CacheIRCloner::getDoubleField(uint32_t stubOffset) {
    263  uint64_t bits = uint64_t(readStubInt64(stubOffset));
    264  return mozilla::BitwiseCast<double>(bits);
    265 }
    266 
    267 IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    268                         CacheKind cacheKind, ICState state,
    269                         BaselineFrame* maybeFrame)
    270    : writer(cx),
    271      cx_(cx),
    272      script_(script),
    273      pc_(pc),
    274      maybeFrame_(maybeFrame),
    275      cacheKind_(cacheKind),
    276      mode_(state.mode()),
    277      isFirstStub_(state.newStubIsFirstStub()),
    278      numOptimizedStubs_(state.numOptimizedStubs()) {}
    279 
    280 // Allocation sites are usually created during baseline compilation, but we also
    281 // need to create them when an IC stub is added to a baseline compiled script
    282 // and when trial inlining.
    283 gc::AllocSite* IRGenerator::maybeCreateAllocSite() {
    284  MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc_)));
    285 
    286  BaselineFrame* frame = maybeFrame_;
    287  MOZ_ASSERT(frame);
    288 
    289  JSScript* outerScript = frame->outerScript();
    290  bool hasBaselineScript = outerScript->hasBaselineScript();
    291  bool isInlined = frame->icScript()->isInlined();
    292  if (!hasBaselineScript && !isInlined) {
    293    MOZ_ASSERT(frame->runningInInterpreter());
    294    return outerScript->zone()->unknownAllocSite(JS::TraceKind::Object);
    295  }
    296 
    297  uint32_t pcOffset = frame->script()->pcToOffset(pc_);
    298  return frame->icScript()->getOrCreateAllocSite(outerScript, pcOffset);
    299 }
    300 
    301 GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script,
    302                                       jsbytecode* pc, ICState state,
    303                                       CacheKind cacheKind, HandleValue val,
    304                                       HandleValue idVal,
    305                                       HandleValue receiverVal)
    306    : IRGenerator(cx, script, pc, cacheKind, state),
    307      val_(val),
    308      idVal_(idVal),
    309      receiverVal_(receiverVal) {}
    310 
    311 static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderId,
    312                               NativeObject* holder, PropertyInfo prop) {
    313  if (holder->isFixedSlot(prop.slot())) {
    314    writer.loadFixedSlotResult(holderId,
    315                               NativeObject::getFixedSlotOffset(prop.slot()));
    316  } else {
    317    size_t dynamicSlotOffset =
    318        holder->dynamicSlotIndex(prop.slot()) * sizeof(Value);
    319    writer.loadDynamicSlotResult(holderId, dynamicSlotOffset);
    320  }
    321 }
    322 
    323 // DOM proxies
    324 // -----------
    325 //
    326 // DOM proxies are proxies that are used to implement various DOM objects like
    327 // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
    328 // object that stores extra properties added to the object. The following
    329 // CacheIR instructions are only used with DOM proxies:
    330 //
    331 // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
    332 //   returns either an UndefinedValue (no expando), ObjectValue (the expando
    333 //   object), or PrivateValue(ExpandoAndGeneration*).
    334 //
    335 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
    336 //   slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
    337 //   generation, then returns expandoAndGeneration->expando. This Value is
    338 //   either an UndefinedValue or ObjectValue.
    339 //
    340 // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
    341 //   expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
    342 //   returns the expandoAndGeneration->expando Value.
    343 //
    344 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
    345 //   guards it's either UndefinedValue or an object with the expected shape.
    346 
    347 enum class ProxyStubType {
    348  None,
    349  DOMExpando,
    350  DOMShadowed,
    351  DOMUnshadowed,
    352  Generic
    353 };
    354 
    355 static bool IsCacheableDOMProxy(ProxyObject* obj) {
    356  const BaseProxyHandler* handler = obj->handler();
    357  if (handler->family() != GetDOMProxyHandlerFamily()) {
    358    return false;
    359  }
    360 
    361  // Some DOM proxies have dynamic prototypes.  We can't really cache those very
    362  // well.
    363  return obj->hasStaticPrototype();
    364 }
    365 
    366 static ProxyStubType GetProxyStubType(JSContext* cx, HandleObject obj,
    367                                      HandleId id) {
    368  if (!obj->is<ProxyObject>()) {
    369    return ProxyStubType::None;
    370  }
    371  auto proxy = obj.as<ProxyObject>();
    372 
    373  if (!IsCacheableDOMProxy(proxy)) {
    374    return ProxyStubType::Generic;
    375  }
    376 
    377  // Private fields are defined on a separate expando object.
    378  if (id.isPrivateName()) {
    379    return ProxyStubType::Generic;
    380  }
    381 
    382  DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, proxy, id);
    383  if (shadows == DOMProxyShadowsResult::ShadowCheckFailed) {
    384    cx->clearPendingException();
    385    return ProxyStubType::None;
    386  }
    387 
    388  if (DOMProxyIsShadowing(shadows)) {
    389    if (shadows == DOMProxyShadowsResult::ShadowsViaDirectExpando ||
    390        shadows == DOMProxyShadowsResult::ShadowsViaIndirectExpando) {
    391      return ProxyStubType::DOMExpando;
    392    }
    393    return ProxyStubType::DOMShadowed;
    394  }
    395 
    396  MOZ_ASSERT(shadows == DOMProxyShadowsResult::DoesntShadow ||
    397             shadows == DOMProxyShadowsResult::DoesntShadowUnique);
    398  return ProxyStubType::DOMUnshadowed;
    399 }
    400 
    401 static bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idVal,
    402                                  MutableHandleId id, bool* nameOrSymbol) {
    403  *nameOrSymbol = false;
    404 
    405  if (idVal.isObject() || idVal.isBigInt()) {
    406    return true;
    407  }
    408 
    409  MOZ_ASSERT(idVal.isString() || idVal.isSymbol() || idVal.isBoolean() ||
    410             idVal.isUndefined() || idVal.isNull() || idVal.isNumber());
    411 
    412  if (IsNumberIndex(idVal)) {
    413    return true;
    414  }
    415 
    416  if (!PrimitiveValueToId<CanGC>(cx, idVal, id)) {
    417    return false;
    418  }
    419 
    420  if (!id.isAtom() && !id.isSymbol()) {
    421    id.set(JS::PropertyKey::Void());
    422    return true;
    423  }
    424 
    425  if (id.isAtom() && id.toAtom()->isIndex()) {
    426    id.set(JS::PropertyKey::Void());
    427    return true;
    428  }
    429 
    430  *nameOrSymbol = true;
    431  return true;
    432 }
    433 
    434 AttachDecision GetPropIRGenerator::tryAttachStub() {
    435  AutoAssertNoPendingException aanpe(cx_);
    436 
    437  ValOperandId valId(writer.setInputOperandId(0));
    438  if (cacheKind_ != CacheKind::GetProp) {
    439    MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper,
    440                  getSuperReceiverValueId().id() == 1);
    441    MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper,
    442                  getElemKeyValueId().id() == 1);
    443    writer.setInputOperandId(1);
    444  }
    445  if (cacheKind_ == CacheKind::GetElemSuper) {
    446    MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
    447    writer.setInputOperandId(2);
    448  }
    449 
    450  RootedId id(cx_);
    451  bool nameOrSymbol;
    452  if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    453    cx_->clearPendingException();
    454    return AttachDecision::NoAction;
    455  }
    456 
    457  // |super.prop| getter calls use a |this| value that differs from lookup
    458  // object.
    459  ValOperandId receiverId = isSuper() ? getSuperReceiverValueId() : valId;
    460 
    461  if (val_.isObject()) {
    462    RootedObject obj(cx_, &val_.toObject());
    463    ObjOperandId objId = writer.guardToObject(valId);
    464 
    465    TRY_ATTACH(tryAttachTypedArrayElement(obj, objId));
    466 
    467    if (nameOrSymbol) {
    468      TRY_ATTACH(tryAttachObjectLength(obj, objId, id));
    469      TRY_ATTACH(tryAttachNative(obj, objId, id, receiverId));
    470      TRY_ATTACH(tryAttachModuleNamespace(obj, objId, id));
    471      TRY_ATTACH(tryAttachWindowProxy(obj, objId, id));
    472      TRY_ATTACH(tryAttachCrossCompartmentWrapper(obj, objId, id));
    473      TRY_ATTACH(
    474          tryAttachXrayCrossCompartmentWrapper(obj, objId, id, receiverId));
    475      TRY_ATTACH(tryAttachFunction(obj, objId, id));
    476      TRY_ATTACH(tryAttachArgumentsObjectIterator(obj, objId, id));
    477      TRY_ATTACH(tryAttachArgumentsObjectCallee(obj, objId, id));
    478      TRY_ATTACH(tryAttachProxy(obj, objId, id, receiverId));
    479 
    480      if (!isSuper() && mode_ == ICState::Mode::Megamorphic &&
    481          JSOp(*pc_) != JSOp::GetBoundName) {
    482        attachMegamorphicNativeSlotPermissive(objId, id);
    483        return AttachDecision::Attach;
    484      }
    485 
    486      trackAttached(IRGenerator::NotAttached);
    487      return AttachDecision::NoAction;
    488    }
    489 
    490    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
    491               cacheKind_ == CacheKind::GetElemSuper);
    492 
    493    TRY_ATTACH(tryAttachProxyElement(obj, objId));
    494 
    495    uint32_t index;
    496    Int32OperandId indexId;
    497    if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
    498      TRY_ATTACH(tryAttachDenseElement(obj, objId, index, indexId));
    499      TRY_ATTACH(tryAttachDenseElementHole(obj, objId, index, indexId));
    500      TRY_ATTACH(tryAttachSparseElement(obj, objId, index, indexId));
    501      TRY_ATTACH(tryAttachArgumentsObjectArg(obj, objId, index, indexId));
    502      TRY_ATTACH(tryAttachArgumentsObjectArgHole(obj, objId, index, indexId));
    503      TRY_ATTACH(
    504          tryAttachGenericElement(obj, objId, index, indexId, receiverId));
    505 
    506      trackAttached(IRGenerator::NotAttached);
    507      return AttachDecision::NoAction;
    508    }
    509 
    510    trackAttached(IRGenerator::NotAttached);
    511    return AttachDecision::NoAction;
    512  }
    513 
    514  if (nameOrSymbol) {
    515    TRY_ATTACH(tryAttachPrimitive(valId, id));
    516    TRY_ATTACH(tryAttachStringLength(valId, id));
    517 
    518    trackAttached(IRGenerator::NotAttached);
    519    return AttachDecision::NoAction;
    520  }
    521 
    522  if (idVal_.isInt32()) {
    523    ValOperandId indexId = getElemKeyValueId();
    524    TRY_ATTACH(tryAttachStringChar(valId, indexId));
    525 
    526    trackAttached(IRGenerator::NotAttached);
    527    return AttachDecision::NoAction;
    528  }
    529 
    530  trackAttached(IRGenerator::NotAttached);
    531  return AttachDecision::NoAction;
    532 }
    533 
    534 #ifdef DEBUG
    535 // Any property lookups performed when trying to attach ICs must be pure, i.e.
    536 // must use LookupPropertyPure() or similar functions. Pure lookups are
    537 // guaranteed to never modify the prototype chain. This ensures that the holder
    538 // object can always be found on the prototype chain.
    539 static bool IsCacheableProtoChain(NativeObject* obj, NativeObject* holder) {
    540  while (obj != holder) {
    541    JSObject* proto = obj->staticPrototype();
    542    if (!proto || !proto->is<NativeObject>()) {
    543      return false;
    544    }
    545    obj = &proto->as<NativeObject>();
    546  }
    547  return true;
    548 }
    549 #endif
    550 
    551 static bool IsCacheableGetPropSlot(NativeObject* obj, NativeObject* holder,
    552                                   PropertyInfo prop) {
    553  MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
    554 
    555  return prop.isDataProperty();
    556 }
    557 
    558 static NativeGetPropKind IsCacheableGetPropCall(NativeObject* obj,
    559                                                NativeObject* holder,
    560                                                PropertyInfo prop,
    561                                                jsbytecode* pc = nullptr) {
    562  MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
    563 
    564  if (pc && JSOp(*pc) == JSOp::GetBoundName) {
    565    return NativeGetPropKind::None;
    566  }
    567 
    568  if (!prop.isAccessorProperty()) {
    569    return NativeGetPropKind::None;
    570  }
    571 
    572  JSObject* getterObject = holder->getGetter(prop);
    573  if (!getterObject || !getterObject->is<JSFunction>()) {
    574    return NativeGetPropKind::None;
    575  }
    576 
    577  JSFunction& getter = getterObject->as<JSFunction>();
    578 
    579  if (getter.isClassConstructor()) {
    580    return NativeGetPropKind::None;
    581  }
    582 
    583  // Scripted functions and natives with JIT entry can use the scripted path.
    584  if (getter.hasJitEntry()) {
    585    return NativeGetPropKind::ScriptedGetter;
    586  }
    587 
    588  MOZ_ASSERT(getter.isNativeWithoutJitEntry());
    589  return NativeGetPropKind::NativeGetter;
    590 }
    591 
    592 static bool CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id) {
    593  if (!obj->is<NativeObject>()) {
    594    return false;
    595  }
    596  // Don't handle objects with resolve hooks.
    597  if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
    598    return false;
    599  }
    600  if (obj->as<NativeObject>().contains(cx, id)) {
    601    return false;
    602  }
    603  if (obj->is<TypedArrayObject>() && ToTypedArrayIndex(id).isSome()) {
    604    return false;
    605  }
    606  return true;
    607 }
    608 
    609 static bool CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id) {
    610  JSObject* curObj = obj;
    611  do {
    612    if (!CheckHasNoSuchOwnProperty(cx, curObj, id)) {
    613      return false;
    614    }
    615 
    616    curObj = curObj->staticPrototype();
    617  } while (curObj);
    618 
    619  return true;
    620 }
    621 
    622 static bool IsCacheableNoProperty(JSContext* cx, NativeObject* obj,
    623                                  NativeObject* holder, jsid id,
    624                                  jsbytecode* pc) {
    625  MOZ_ASSERT(!holder);
    626 
    627  // If we're doing a name lookup, we have to throw a ReferenceError.
    628  if (JSOp(*pc) == JSOp::GetBoundName) {
    629    return false;
    630  }
    631 
    632  return CheckHasNoSuchProperty(cx, obj, id);
    633 }
    634 
    635 static NativeGetPropKind CanAttachNativeGetProp(JSContext* cx, JSObject* obj,
    636                                                PropertyKey id,
    637                                                NativeObject** holder,
    638                                                Maybe<PropertyInfo>* propInfo,
    639                                                jsbytecode* pc) {
    640  MOZ_ASSERT(id.isString() || id.isSymbol());
    641  MOZ_ASSERT(!*holder);
    642 
    643  // The lookup needs to be universally pure, otherwise we risk calling hooks
    644  // out of turn. We don't mind doing this even when purity isn't required,
    645  // because we only miss out on shape hashification, which is only a temporary
    646  // perf cost. The limits were arbitrarily set, anyways.
    647  NativeObject* baseHolder = nullptr;
    648  PropertyResult prop;
    649  if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop)) {
    650    return NativeGetPropKind::None;
    651  }
    652  auto* nobj = &obj->as<NativeObject>();
    653 
    654  if (prop.isNativeProperty()) {
    655    MOZ_ASSERT(baseHolder);
    656    *holder = baseHolder;
    657    *propInfo = mozilla::Some(prop.propertyInfo());
    658 
    659    if (IsCacheableGetPropSlot(nobj, *holder, propInfo->ref())) {
    660      return NativeGetPropKind::Slot;
    661    }
    662 
    663    return IsCacheableGetPropCall(nobj, *holder, propInfo->ref(), pc);
    664  }
    665 
    666  if (!prop.isFound()) {
    667    if (IsCacheableNoProperty(cx, nobj, *holder, id, pc)) {
    668      return NativeGetPropKind::Missing;
    669    }
    670  }
    671 
    672  return NativeGetPropKind::None;
    673 }
    674 
    675 static void GuardReceiverProto(CacheIRWriter& writer, NativeObject* obj,
    676                               ObjOperandId objId) {
    677  // Note: we guard on the actual prototype and not on the shape because this is
    678  // used for sparse elements where we expect shape changes.
    679 
    680  if (JSObject* proto = obj->staticPrototype()) {
    681    writer.guardProto(objId, proto);
    682  } else {
    683    writer.guardNullProto(objId);
    684  }
    685 }
    686 
    687 // Guard that a given object has same class and same OwnProperties (excluding
    688 // dense elements and dynamic properties).
    689 static void TestMatchingNativeReceiver(CacheIRWriter& writer, NativeObject* obj,
    690                                       ObjOperandId objId) {
    691  writer.guardShapeForOwnProperties(objId, obj->shape());
    692 }
    693 
    694 // Similar to |TestMatchingNativeReceiver|, but specialized for ProxyObject.
    695 static void TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj,
    696                                      ObjOperandId objId) {
    697  writer.guardShapeForClass(objId, obj->shape());
    698 }
    699 
    700 static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
    701                                    NativeObject* holder, ObjOperandId objId) {
    702  // Assuming target property is on |holder|, generate appropriate guards to
    703  // ensure |holder| is still on the prototype chain of |obj| and we haven't
    704  // introduced any shadowing definitions.
    705  //
    706  // For each item in the proto chain before holder, we must ensure that
    707  // [[GetPrototypeOf]] still has the expected result, and that
    708  // [[GetOwnProperty]] has no definition of the target property.
    709  //
    710  //
    711  // [SMDOC] Shape Teleporting Optimization
    712  // --------------------------------------
    713  //
    714  // Starting with the assumption (and guideline to developers) that mutating
    715  // prototypes is an uncommon and fair-to-penalize operation we move cost
    716  // from the access side to the mutation side.
    717  //
    718  // Consider the following proto chain, with B defining a property 'x':
    719  //
    720  //      D  ->  C  ->  B{x: 3}  ->  A  -> null
    721  //
    722  // When accessing |D.x| we refer to D as the "receiver", and B as the
    723  // "holder". To optimize this access we need to ensure that neither D nor C
    724  // has since defined a shadowing property 'x'. Since C is a prototype that
    725  // we assume is rarely mutated we would like to avoid checking each time if
    726  // new properties are added. To do this we require that whenever C starts
    727  // shadowing a property on its proto chain, we invalidate (and opt out of) the
    728  // teleporting optimization by setting the InvalidatedTeleporting flag on the
    729  // object we're shadowing, triggering a shape change of that object. As a
    730  // result, checking the shape of D and B is sufficient. Note that we do not
    731  // care if the shape or properties of A change since the lookup of 'x' will
    732  // stop at B.
    733  //
    734  // The second condition we must verify is that the prototype chain was not
    735  // mutated. The same mechanism as above is used. When the prototype link is
    736  // changed, we generate a new shape for the object. If the object whose
    737  // link we are mutating is itself a prototype, we regenerate shapes down
    738  // the chain by setting the InvalidatedTeleporting flag on them. This means
    739  // the same two shape checks as above are sufficient.
    740  //
    741  // Once the InvalidatedTeleporting flag is set, it means the shape will no
    742  // longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
    743  // In this case we can no longer apply the optimization.
    744  //
    745  // See:
    746  //  - ReshapeForProtoMutation
    747  //  - ReshapeForShadowedProp
    748 
    749  MOZ_ASSERT(holder);
    750  MOZ_ASSERT(obj != holder);
    751 
    752  // Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
    753  // unchanged so peel off the receiver.
    754  JSObject* pobj = obj->staticPrototype();
    755  MOZ_ASSERT(pobj->isUsedAsPrototype());
    756 
    757  // If teleporting is supported for this holder, we are done.
    758  if (!holder->hasInvalidatedTeleporting()) {
    759    return;
    760  }
    761 
    762  // If already at the holder, no further proto checks are needed.
    763  if (pobj == holder) {
    764    return;
    765  }
    766 
    767  // Synchronize pobj and protoId.
    768  MOZ_ASSERT(pobj == obj->staticPrototype());
    769  ObjOperandId protoId = writer.loadProto(objId);
    770 
    771  // Shape guard each prototype object between receiver and holder. This guards
    772  // against both proto changes and shadowing properties.
    773  while (pobj != holder) {
    774    writer.guardShape(protoId, pobj->shape());
    775 
    776    pobj = pobj->staticPrototype();
    777    protoId = writer.loadProto(protoId);
    778  }
    779 }
    780 
    781 static void GeneratePrototypeHoleGuards(CacheIRWriter& writer,
    782                                        NativeObject* obj, ObjOperandId objId,
    783                                        bool alwaysGuardFirstProto) {
    784  if (alwaysGuardFirstProto) {
    785    GuardReceiverProto(writer, obj, objId);
    786  }
    787 
    788  JSObject* pobj = obj->staticPrototype();
    789  while (pobj) {
    790    ObjOperandId protoId = writer.loadObject(pobj);
    791 
    792    // Make sure the shape matches, to ensure the proto is unchanged and to
    793    // avoid non-dense elements or anything else that is being checked by
    794    // CanAttachDenseElementHole.
    795    MOZ_ASSERT(pobj->is<NativeObject>());
    796    writer.guardShape(protoId, pobj->shape());
    797 
    798    // Also make sure there are no dense elements.
    799    writer.guardNoDenseElements(protoId);
    800 
    801    pobj = pobj->staticPrototype();
    802  }
    803 }
    804 
    805 // Similar to |TestMatchingReceiver|, but for the holder object (when it
    806 // differs from the receiver). The holder may also be the expando of the
    807 // receiver if it exists.
    808 static void TestMatchingHolder(CacheIRWriter& writer, NativeObject* obj,
    809                               ObjOperandId objId) {
    810  // The GeneratePrototypeGuards + TestMatchingHolder checks only support
    811  // prototype chains composed of NativeObject (excluding the receiver
    812  // itself).
    813  writer.guardShapeForOwnProperties(objId, obj->shape());
    814 }
    815 
    816 enum class IsCrossCompartment { No, Yes };
    817 
    818 // Emit a shape guard for all objects on the proto chain. This does NOT include
    819 // the receiver; callers must ensure the receiver's proto is the first proto by
    820 // either emitting a shape guard or a prototype guard for |objId|.
    821 //
    822 // Note: this relies on shape implying proto.
    823 template <IsCrossCompartment MaybeCrossCompartment = IsCrossCompartment::No>
    824 static void ShapeGuardProtoChain(CacheIRWriter& writer, NativeObject* obj,
    825                                 ObjOperandId objId) {
    826  uint32_t depth = 0;
    827  static const uint32_t MAX_CACHED_LOADS = 4;
    828  ObjOperandId receiverObjId = objId;
    829 
    830  while (true) {
    831    JSObject* proto = obj->staticPrototype();
    832    if (!proto) {
    833      return;
    834    }
    835 
    836    obj = &proto->as<NativeObject>();
    837 
    838    // After guarding the shape of an object, we can safely bake that
    839    // object's proto into the stub data. Compared to LoadProto, this
    840    // takes one load instead of three (object -> shape -> baseshape
    841    // -> proto). We cap the depth to avoid bloating the size of the
    842    // stub data. To avoid compartment mismatch, we skip this optimization
    843    // in the cross-compartment case.
    844    if (depth < MAX_CACHED_LOADS &&
    845        MaybeCrossCompartment == IsCrossCompartment::No) {
    846      objId = writer.loadProtoObject(obj, receiverObjId);
    847    } else {
    848      objId = writer.loadProto(objId);
    849    }
    850    depth++;
    851 
    852    writer.guardShape(objId, obj->shape());
    853  }
    854 }
    855 
    856 // For cross compartment guards we shape-guard the prototype chain to avoid
    857 // referencing the holder object.
    858 //
    859 // This peels off the first layer because it's guarded against obj == holder.
    860 //
    861 // Returns the holder's OperandId.
    862 static ObjOperandId ShapeGuardProtoChainForCrossCompartmentHolder(
    863    CacheIRWriter& writer, NativeObject* obj, ObjOperandId objId,
    864    NativeObject* holder) {
    865  MOZ_ASSERT(obj != holder);
    866  MOZ_ASSERT(holder);
    867  while (true) {
    868    MOZ_ASSERT(obj->staticPrototype());
    869    obj = &obj->staticPrototype()->as<NativeObject>();
    870 
    871    objId = writer.loadProto(objId);
    872    if (obj == holder) {
    873      TestMatchingHolder(writer, obj, objId);
    874      return objId;
    875    }
    876    writer.guardShapeForOwnProperties(objId, obj->shape());
    877  }
    878 }
    879 
    880 // Emit guards for reading a data property on |holder|. Returns the holder's
    881 // OperandId.
    882 template <IsCrossCompartment MaybeCrossCompartment = IsCrossCompartment::No>
    883 static ObjOperandId EmitReadSlotGuard(CacheIRWriter& writer, NativeObject* obj,
    884                                      NativeObject* holder,
    885                                      ObjOperandId objId) {
    886  MOZ_ASSERT(holder);
    887  TestMatchingNativeReceiver(writer, obj, objId);
    888 
    889  if (obj == holder) {
    890    return objId;
    891  }
    892 
    893  if (MaybeCrossCompartment == IsCrossCompartment::Yes) {
    894    // Guard proto chain integrity.
    895    // We use a variant of guards that avoid baking in any cross-compartment
    896    // object pointers.
    897    return ShapeGuardProtoChainForCrossCompartmentHolder(writer, obj, objId,
    898                                                         holder);
    899  }
    900 
    901  // Guard proto chain integrity.
    902  GeneratePrototypeGuards(writer, obj, holder, objId);
    903 
    904  // Guard on the holder's shape.
    905  ObjOperandId holderId = writer.loadObject(holder);
    906  TestMatchingHolder(writer, holder, holderId);
    907  return holderId;
    908 }
    909 
    910 template <IsCrossCompartment MaybeCrossCompartment = IsCrossCompartment::No>
    911 static void EmitMissingPropGuard(CacheIRWriter& writer, NativeObject* obj,
    912                                 ObjOperandId objId) {
    913  TestMatchingNativeReceiver(writer, obj, objId);
    914 
    915  // The property does not exist. Guard on everything in the prototype
    916  // chain. This is guaranteed to see only Native objects because of
    917  // CanAttachNativeGetProp().
    918  ShapeGuardProtoChain<MaybeCrossCompartment>(writer, obj, objId);
    919 }
    920 
    921 template <IsCrossCompartment MaybeCrossCompartment = IsCrossCompartment::No>
    922 static void EmitReadSlotResult(CacheIRWriter& writer, NativeObject* obj,
    923                               NativeObject* holder, PropertyInfo prop,
    924                               ObjOperandId objId) {
    925  MOZ_ASSERT(holder);
    926 
    927  ObjOperandId holderId =
    928      EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId);
    929 
    930  MOZ_ASSERT(holderId.valid());
    931  EmitLoadSlotResult(writer, holderId, holder, prop);
    932 }
    933 
    934 template <IsCrossCompartment MaybeCrossCompartment = IsCrossCompartment::No>
    935 static void EmitMissingPropResult(CacheIRWriter& writer, NativeObject* obj,
    936                                  ObjOperandId objId) {
    937  EmitMissingPropGuard<MaybeCrossCompartment>(writer, obj, objId);
    938  writer.loadUndefinedResult();
    939 }
    940 
    941 static ValOperandId EmitLoadSlot(CacheIRWriter& writer, NativeObject* holder,
    942                                 ObjOperandId holderId, uint32_t slot) {
    943  if (holder->isFixedSlot(slot)) {
    944    return writer.loadFixedSlot(holderId,
    945                                NativeObject::getFixedSlotOffset(slot));
    946  }
    947  size_t dynamicSlotIndex = holder->dynamicSlotIndex(slot);
    948  return writer.loadDynamicSlot(holderId, dynamicSlotIndex);
    949 }
    950 
    951 void IRGenerator::emitCallGetterResultNoGuards(NativeGetPropKind kind,
    952                                               NativeObject* obj,
    953                                               NativeObject* holder,
    954                                               PropertyInfo prop,
    955                                               ValOperandId receiverId) {
    956  MOZ_ASSERT(IsCacheableGetPropCall(obj, holder, prop) == kind);
    957 
    958  JSFunction* target = &holder->getGetter(prop)->as<JSFunction>();
    959  bool sameRealm = cx_->realm() == target->realm();
    960 
    961  switch (kind) {
    962    case NativeGetPropKind::NativeGetter: {
    963      writer.callNativeGetterResult(receiverId, target, sameRealm);
    964      writer.returnFromIC();
    965      break;
    966    }
    967    case NativeGetPropKind::ScriptedGetter: {
    968      writer.callScriptedGetterResult(receiverId, target, sameRealm);
    969      writer.returnFromIC();
    970      break;
    971    }
    972    default:
    973      // CanAttachNativeGetProp guarantees that the getter is either a native or
    974      // a scripted function.
    975      MOZ_ASSERT_UNREACHABLE("Can't attach getter");
    976      break;
    977  }
    978 }
    979 
    980 static bool FunctionHasStableBaseScript(JSFunction* fun) {
    981  // When guarding a callee, guarding on the JSFunction* is most efficient,
    982  // but doesn't work well for lambda clones (multiple functions with the
    983  // same BaseScript). We can instead guard on the BaseScript itself.
    984  if (!fun->hasBaseScript()) {
    985    return false;
    986  }
    987  // Self-hosted functions are more complicated: top-level functions can be
    988  // relazified using SelfHostedLazyScript and this means they don't have a
    989  // stable BaseScript pointer. These functions are never lambda clones, though,
    990  // so we can just always guard on the JSFunction*. Self-hosted lambdas are
    991  // never relazified so there we use the normal heuristics.
    992  if (fun->isSelfHostedBuiltin() && !fun->isLambda()) {
    993    return false;
    994  }
    995  return true;
    996 }
    997 
    998 // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
    999 // properties
   1000 void IRGenerator::emitGuardGetterSetterSlot(NativeObject* holder,
   1001                                            PropertyInfo prop,
   1002                                            ObjOperandId holderId,
   1003                                            AccessorKind kind,
   1004                                            bool holderIsConstant) {
   1005  // If the holder is guaranteed to be the same object, and it never had a
   1006  // slot holding a GetterSetter mutated or deleted, its Shape will change when
   1007  // that does happen so we don't need to guard on the GetterSetter.
   1008  if (holderIsConstant && !holder->hadGetterSetterChange()) {
   1009    return;
   1010  }
   1011 
   1012  size_t slot = prop.slot();
   1013 
   1014  // For the same reasons as emitCalleeGuard, we guard on the BaseScript
   1015  // instead of the GetterSetter if the callee is scripted and this isn't
   1016  // the first IC stub.
   1017  if (!isFirstStub_) {
   1018    bool isGetter = kind == AccessorKind::Getter;
   1019    JSObject* accessor =
   1020        isGetter ? holder->getGetter(prop) : holder->getSetter(prop);
   1021    JSFunction* fun = &accessor->as<JSFunction>();
   1022    if (FunctionHasStableBaseScript(fun)) {
   1023      bool needsClassGuard = holder->hasNonFunctionAccessor();
   1024      ValOperandId getterSetterId =
   1025          EmitLoadSlot(writer, holder, holderId, slot);
   1026      ObjOperandId functionId = writer.loadGetterSetterFunction(
   1027          getterSetterId, isGetter, needsClassGuard);
   1028      writer.saveScriptedGetterSetterCallee(functionId);
   1029      writer.guardFunctionScript(functionId, fun->baseScript());
   1030      return;
   1031    }
   1032  }
   1033 
   1034  Value slotVal = holder->getSlot(slot);
   1035  MOZ_ASSERT(slotVal.isPrivateGCThing());
   1036 
   1037  if (holder->isFixedSlot(slot)) {
   1038    size_t offset = NativeObject::getFixedSlotOffset(slot);
   1039    writer.guardFixedSlotValue(holderId, offset, slotVal);
   1040  } else {
   1041    size_t offset = holder->dynamicSlotIndex(slot) * sizeof(Value);
   1042    writer.guardDynamicSlotValue(holderId, offset, slotVal);
   1043  }
   1044 }
   1045 
   1046 static ObjOperandId EmitGuardObjectFuseHolder(CacheIRWriter& writer,
   1047                                              NativeObject* obj,
   1048                                              NativeObject* holder,
   1049                                              ObjOperandId objId) {
   1050  if (obj == holder) {
   1051    // Guard the object is the current |holder| object because each ObjectFuse
   1052    // applies to a single object.
   1053    writer.guardSpecificObject(objId, obj);
   1054    return objId;
   1055  }
   1056 
   1057  // Note: we don't need to call GeneratePrototypeGuards here because the
   1058  // ObjectFuse's generation will be updated when the proto chain is
   1059  // mutated.
   1060  TestMatchingNativeReceiver(writer, obj, objId);
   1061  return writer.loadObject(holder);
   1062 }
   1063 
   1064 void IRGenerator::emitCallAccessorGuards(NativeObject* obj,
   1065                                         NativeObject* holder, HandleId id,
   1066                                         PropertyInfo prop, ObjOperandId objId,
   1067                                         AccessorKind accessorKind) {
   1068  // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
   1069  // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
   1070  // require outerizing).
   1071 
   1072  MOZ_ASSERT(holder->containsPure(id, prop));
   1073 
   1074  if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
   1075    // Fast path for constant properties of objects with an ObjectFuse.
   1076    ObjectFuse* objFuse = nullptr;
   1077    if (canOptimizeConstantAccessorProperty(holder, prop, &objFuse)) {
   1078      ObjOperandId holderId =
   1079          EmitGuardObjectFuseHolder(writer, obj, holder, objId);
   1080      emitGuardConstantAccessorProperty(holder, holderId, id, prop, objFuse);
   1081      return;
   1082    }
   1083 
   1084    TestMatchingNativeReceiver(writer, obj, objId);
   1085 
   1086    if (obj != holder) {
   1087      GeneratePrototypeGuards(writer, obj, holder, objId);
   1088 
   1089      // Guard on the holder's shape.
   1090      ObjOperandId holderId = writer.loadObject(holder);
   1091      TestMatchingHolder(writer, holder, holderId);
   1092 
   1093      emitGuardGetterSetterSlot(holder, prop, holderId, accessorKind,
   1094                                /* holderIsConstant = */ true);
   1095    } else {
   1096      emitGuardGetterSetterSlot(holder, prop, objId, accessorKind);
   1097    }
   1098  } else {
   1099    Value val = holder->getSlot(prop.slot());
   1100    MOZ_ASSERT(val.isPrivateGCThing());
   1101    MOZ_ASSERT(val.toGCThing()->is<GetterSetter>());
   1102    writer.guardHasGetterSetter(objId, id, val);
   1103  }
   1104 }
   1105 
   1106 void GetPropIRGenerator::emitCallGetterResultGuards(NativeObject* obj,
   1107                                                    NativeObject* holder,
   1108                                                    HandleId id,
   1109                                                    PropertyInfo prop,
   1110                                                    ObjOperandId objId) {
   1111  emitCallAccessorGuards(obj, holder, id, prop, objId, AccessorKind::Getter);
   1112 }
   1113 
   1114 void GetPropIRGenerator::emitCallGetterResult(NativeGetPropKind kind,
   1115                                              Handle<NativeObject*> obj,
   1116                                              Handle<NativeObject*> holder,
   1117                                              HandleId id, PropertyInfo prop,
   1118                                              ObjOperandId objId,
   1119                                              ValOperandId receiverId) {
   1120  emitCallGetterResultGuards(obj, holder, id, prop, objId);
   1121 
   1122  if (kind == NativeGetPropKind::NativeGetter &&
   1123      mode_ == ICState::Mode::Specialized) {
   1124    auto attached = tryAttachInlinableNativeGetter(holder, prop, receiverId);
   1125    if (attached != AttachDecision::NoAction) {
   1126      MOZ_ASSERT(attached == AttachDecision::Attach);
   1127      return;
   1128    }
   1129  }
   1130 
   1131  emitCallGetterResultNoGuards(kind, obj, holder, prop, receiverId);
   1132 }
   1133 
   1134 static bool CanAttachDOMCall(JSContext* cx, JSJitInfo::OpType type,
   1135                             JSObject* obj, JSFunction* fun,
   1136                             ICState::Mode mode) {
   1137  MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter ||
   1138             type == JSJitInfo::Method);
   1139 
   1140  if (mode != ICState::Mode::Specialized) {
   1141    return false;
   1142  }
   1143 
   1144  if (!fun->hasJitInfo()) {
   1145    return false;
   1146  }
   1147 
   1148  if (cx->realm() != fun->realm()) {
   1149    return false;
   1150  }
   1151 
   1152  const JSJitInfo* jitInfo = fun->jitInfo();
   1153  if (jitInfo->type() != type) {
   1154    return false;
   1155  }
   1156 
   1157  MOZ_ASSERT_IF(IsWindow(obj), !jitInfo->needsOuterizedThisObject());
   1158 
   1159  const JSClass* clasp = obj->getClass();
   1160  if (!clasp->isDOMClass()) {
   1161    return false;
   1162  }
   1163 
   1164  if (type != JSJitInfo::Method && clasp->isProxyObject()) {
   1165    return false;
   1166  }
   1167 
   1168  // Ion codegen expects DOM_OBJECT_SLOT to be a fixed slot in LoadDOMPrivate.
   1169  // It can be a dynamic slot if we transplanted this reflector object with a
   1170  // proxy.
   1171  if (obj->is<NativeObject>() && obj->as<NativeObject>().numFixedSlots() == 0) {
   1172    return false;
   1173  }
   1174 
   1175  // Tell the analysis the |DOMInstanceClassHasProtoAtDepth| hook can't GC.
   1176  JS::AutoSuppressGCAnalysis nogc;
   1177 
   1178  DOMInstanceClassHasProtoAtDepth instanceChecker =
   1179      cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
   1180  return instanceChecker(clasp, jitInfo->protoID, jitInfo->depth);
   1181 }
   1182 
   1183 static bool CanAttachDOMGetterSetter(JSContext* cx, JSJitInfo::OpType type,
   1184                                     NativeObject* obj, NativeObject* holder,
   1185                                     PropertyInfo prop, ICState::Mode mode) {
   1186  MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter);
   1187 
   1188  JSObject* accessor = type == JSJitInfo::Getter ? holder->getGetter(prop)
   1189                                                 : holder->getSetter(prop);
   1190  JSFunction* fun = &accessor->as<JSFunction>();
   1191 
   1192  return CanAttachDOMCall(cx, type, obj, fun, mode);
   1193 }
   1194 
   1195 void IRGenerator::emitCallDOMGetterResultNoGuards(NativeObject* holder,
   1196                                                  PropertyInfo prop,
   1197                                                  ObjOperandId objId) {
   1198  JSFunction* getter = &holder->getGetter(prop)->as<JSFunction>();
   1199  writer.callDOMGetterResult(objId, getter->jitInfo());
   1200  writer.returnFromIC();
   1201 }
   1202 
   1203 void GetPropIRGenerator::emitCallDOMGetterResult(NativeObject* obj,
   1204                                                 NativeObject* holder,
   1205                                                 HandleId id, PropertyInfo prop,
   1206                                                 ObjOperandId objId) {
   1207  // Note: this relies on emitCallGetterResultGuards emitting a shape guard
   1208  // for specialized stubs.
   1209  // The shape guard ensures the receiver's Class is valid for this DOM getter.
   1210  emitCallGetterResultGuards(obj, holder, id, prop, objId);
   1211  emitCallDOMGetterResultNoGuards(holder, prop, objId);
   1212 }
   1213 
   1214 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId,
   1215                                                     jsid id) {
   1216  MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
   1217 
   1218  // We don't support GetBoundName because environment objects have
   1219  // lookupProperty hooks and GetBoundName is usually not megamorphic.
   1220  MOZ_ASSERT(JSOp(*pc_) != JSOp::GetBoundName);
   1221 
   1222  if (cacheKind_ == CacheKind::GetProp ||
   1223      cacheKind_ == CacheKind::GetPropSuper) {
   1224    writer.megamorphicLoadSlotResult(objId, id);
   1225  } else {
   1226    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
   1227               cacheKind_ == CacheKind::GetElemSuper);
   1228    writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId());
   1229  }
   1230  writer.returnFromIC();
   1231 
   1232  trackAttached("GetProp.MegamorphicNativeSlot");
   1233 }
   1234 
   1235 void GetPropIRGenerator::attachMegamorphicNativeSlotPermissive(
   1236    ObjOperandId objId, jsid id) {
   1237  MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
   1238 
   1239  // We don't support GetBoundName because environment objects have
   1240  // lookupProperty hooks and GetBoundName is usually not megamorphic.
   1241  MOZ_ASSERT(JSOp(*pc_) != JSOp::GetBoundName);
   1242  // It is not worth the complexity to support super here because we'd have
   1243  // to plumb the receiver through everywhere, so we just skip it.
   1244  MOZ_ASSERT(!isSuper());
   1245 
   1246  if (cacheKind_ == CacheKind::GetProp) {
   1247    writer.megamorphicLoadSlotPermissiveResult(objId, id);
   1248  } else {
   1249    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
   1250    writer.megamorphicLoadSlotByValuePermissiveResult(objId,
   1251                                                      getElemKeyValueId());
   1252  }
   1253  writer.returnFromIC();
   1254 
   1255  trackAttached("GetProp.MegamorphicNativeSlotPermissive");
   1256 }
   1257 
   1258 AttachDecision GetPropIRGenerator::tryAttachNative(HandleObject obj,
   1259                                                   ObjOperandId objId,
   1260                                                   HandleId id,
   1261                                                   ValOperandId receiverId) {
   1262  Maybe<PropertyInfo> prop;
   1263  Rooted<NativeObject*> holder(cx_);
   1264 
   1265  NativeGetPropKind kind =
   1266      CanAttachNativeGetProp(cx_, obj, id, holder.address(), &prop, pc_);
   1267  switch (kind) {
   1268    case NativeGetPropKind::None:
   1269      return AttachDecision::NoAction;
   1270    case NativeGetPropKind::Missing:
   1271    case NativeGetPropKind::Slot: {
   1272      auto* nobj = &obj->as<NativeObject>();
   1273 
   1274      if (mode_ == ICState::Mode::Megamorphic &&
   1275          JSOp(*pc_) != JSOp::GetBoundName) {
   1276        attachMegamorphicNativeSlot(objId, id);
   1277        return AttachDecision::Attach;
   1278      }
   1279 
   1280      maybeEmitIdGuard(id);
   1281      if (kind == NativeGetPropKind::Slot) {
   1282        emitLoadDataPropertyResult(nobj, holder, id, *prop, objId);
   1283        writer.returnFromIC();
   1284        trackAttached("GetProp.NativeSlot");
   1285      } else {
   1286        EmitMissingPropResult(writer, nobj, objId);
   1287        writer.returnFromIC();
   1288        trackAttached("GetProp.Missing");
   1289      }
   1290      return AttachDecision::Attach;
   1291    }
   1292    case NativeGetPropKind::ScriptedGetter:
   1293    case NativeGetPropKind::NativeGetter: {
   1294      auto nobj = obj.as<NativeObject>();
   1295      MOZ_ASSERT(!IsWindow(nobj));
   1296 
   1297      // If we're in megamorphic mode, we assume that a specialized
   1298      // getter call is just going to end up failing later, so we let this
   1299      // get handled further down the chain by
   1300      // attachMegamorphicNativeSlotPermissive
   1301      if (!isSuper() && mode_ == ICState::Mode::Megamorphic) {
   1302        return AttachDecision::NoAction;
   1303      }
   1304 
   1305      maybeEmitIdGuard(id);
   1306 
   1307      if (!isSuper() && CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, nobj,
   1308                                                 holder, *prop, mode_)) {
   1309        emitCallDOMGetterResult(nobj, holder, id, *prop, objId);
   1310 
   1311        trackAttached("GetProp.DOMGetter");
   1312        return AttachDecision::Attach;
   1313      }
   1314 
   1315      emitCallGetterResult(kind, nobj, holder, id, *prop, objId, receiverId);
   1316 
   1317      trackAttached("GetProp.NativeGetter");
   1318      return AttachDecision::Attach;
   1319    }
   1320  }
   1321 
   1322  MOZ_CRASH("Bad NativeGetPropKind");
   1323 }
   1324 
   1325 // Returns whether obj is a WindowProxy wrapping the script's global.
   1326 static bool IsWindowProxyForScriptGlobal(JSScript* script, JSObject* obj) {
   1327  if (!IsWindowProxy(obj)) {
   1328    return false;
   1329  }
   1330 
   1331  MOZ_ASSERT(obj->getClass() ==
   1332             script->runtimeFromMainThread()->maybeWindowProxyClass());
   1333 
   1334  JSObject* window = ToWindowIfWindowProxy(obj);
   1335 
   1336  // Ion relies on the WindowProxy's group changing (and the group getting
   1337  // marked as having unknown properties) on navigation. If we ever stop
   1338  // transplanting same-compartment WindowProxies, this assert will fail and we
   1339  // need to fix that code.
   1340  MOZ_ASSERT(window == &obj->nonCCWGlobal());
   1341 
   1342  // This must be a WindowProxy for a global in this compartment. Else it would
   1343  // be a cross-compartment wrapper and IsWindowProxy returns false for
   1344  // those.
   1345  MOZ_ASSERT(script->compartment() == obj->compartment());
   1346 
   1347  // Only optimize lookups on the WindowProxy for the current global. Other
   1348  // WindowProxies in the compartment may require security checks (based on
   1349  // mutable document.domain). See bug 1516775.
   1350  return window == &script->global();
   1351 }
   1352 
   1353 // Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
   1354 static ObjOperandId GuardAndLoadWindowProxyWindow(CacheIRWriter& writer,
   1355                                                  ObjOperandId objId,
   1356                                                  GlobalObject* windowObj) {
   1357  writer.guardClass(objId, GuardClassKind::WindowProxy);
   1358  ObjOperandId windowObjId = writer.loadWrapperTarget(objId,
   1359                                                      /*fallible = */ false);
   1360  writer.guardSpecificObject(windowObjId, windowObj);
   1361  return windowObjId;
   1362 }
   1363 
   1364 // Whether a getter/setter on the global should have the WindowProxy as |this|
   1365 // value instead of the Window (the global object). This always returns true for
   1366 // scripted functions.
   1367 static bool GetterNeedsWindowProxyThis(NativeObject* holder,
   1368                                       PropertyInfo prop) {
   1369  JSFunction* callee = &holder->getGetter(prop)->as<JSFunction>();
   1370  return !callee->hasJitInfo() || callee->jitInfo()->needsOuterizedThisObject();
   1371 }
   1372 static bool SetterNeedsWindowProxyThis(NativeObject* holder,
   1373                                       PropertyInfo prop) {
   1374  JSFunction* callee = &holder->getSetter(prop)->as<JSFunction>();
   1375  return !callee->hasJitInfo() || callee->jitInfo()->needsOuterizedThisObject();
   1376 }
   1377 
   1378 AttachDecision GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
   1379                                                        ObjOperandId objId,
   1380                                                        HandleId id) {
   1381  // Attach a stub when the receiver is a WindowProxy and we can do the lookup
   1382  // on the Window (the global object).
   1383 
   1384  if (!IsWindowProxyForScriptGlobal(script_, obj)) {
   1385    return AttachDecision::NoAction;
   1386  }
   1387 
   1388  // If we're megamorphic prefer a generic proxy stub that handles a lot more
   1389  // cases.
   1390  if (mode_ == ICState::Mode::Megamorphic) {
   1391    return AttachDecision::NoAction;
   1392  }
   1393 
   1394  // Now try to do the lookup on the Window (the current global).
   1395  Handle<GlobalObject*> windowObj = cx_->global();
   1396  Rooted<NativeObject*> holder(cx_);
   1397  Maybe<PropertyInfo> prop;
   1398  NativeGetPropKind kind =
   1399      CanAttachNativeGetProp(cx_, windowObj, id, holder.address(), &prop, pc_);
   1400  switch (kind) {
   1401    case NativeGetPropKind::None:
   1402      return AttachDecision::NoAction;
   1403 
   1404    case NativeGetPropKind::Slot: {
   1405      maybeEmitIdGuard(id);
   1406      ObjOperandId windowObjId =
   1407          GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
   1408      emitLoadDataPropertyResult(windowObj, holder, id, *prop, windowObjId);
   1409      writer.returnFromIC();
   1410 
   1411      trackAttached("GetProp.WindowProxySlot");
   1412      return AttachDecision::Attach;
   1413    }
   1414 
   1415    case NativeGetPropKind::Missing: {
   1416      maybeEmitIdGuard(id);
   1417      ObjOperandId windowObjId =
   1418          GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
   1419      EmitMissingPropResult(writer, windowObj, windowObjId);
   1420      writer.returnFromIC();
   1421 
   1422      trackAttached("GetProp.WindowProxyMissing");
   1423      return AttachDecision::Attach;
   1424    }
   1425 
   1426    case NativeGetPropKind::NativeGetter:
   1427    case NativeGetPropKind::ScriptedGetter: {
   1428      // If a |super| access, it is not worth the complexity to attach an IC.
   1429      if (isSuper()) {
   1430        return AttachDecision::NoAction;
   1431      }
   1432 
   1433      bool needsWindowProxy = GetterNeedsWindowProxyThis(holder, *prop);
   1434 
   1435      // Guard the incoming object is a WindowProxy and inline a getter call
   1436      // based on the Window object.
   1437      maybeEmitIdGuard(id);
   1438      ObjOperandId windowObjId =
   1439          GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
   1440 
   1441      if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, windowObj, holder,
   1442                                   *prop, mode_)) {
   1443        MOZ_ASSERT(!needsWindowProxy);
   1444        emitCallDOMGetterResult(windowObj, holder, id, *prop, windowObjId);
   1445        trackAttached("GetProp.WindowProxyDOMGetter");
   1446      } else {
   1447        ValOperandId receiverId =
   1448            writer.boxObject(needsWindowProxy ? objId : windowObjId);
   1449        emitCallGetterResult(kind, windowObj, holder, id, *prop, windowObjId,
   1450                             receiverId);
   1451        trackAttached("GetProp.WindowProxyGetter");
   1452      }
   1453 
   1454      return AttachDecision::Attach;
   1455    }
   1456  }
   1457 
   1458  MOZ_CRASH("Unreachable");
   1459 }
   1460 
   1461 AttachDecision GetPropIRGenerator::tryAttachCrossCompartmentWrapper(
   1462    HandleObject obj, ObjOperandId objId, HandleId id) {
   1463  // We can only optimize this very wrapper-handler, because others might
   1464  // have a security policy.
   1465  if (!IsWrapper(obj) ||
   1466      Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton) {
   1467    return AttachDecision::NoAction;
   1468  }
   1469 
   1470  // If we're megamorphic prefer a generic proxy stub that handles a lot more
   1471  // cases.
   1472  if (mode_ == ICState::Mode::Megamorphic) {
   1473    return AttachDecision::NoAction;
   1474  }
   1475 
   1476  RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
   1477  MOZ_ASSERT(unwrapped == UnwrapOneCheckedStatic(obj));
   1478  MOZ_ASSERT(!IsCrossCompartmentWrapper(unwrapped),
   1479             "CCWs must not wrap other CCWs");
   1480 
   1481  // If we allowed different zones we would have to wrap strings.
   1482  if (unwrapped->compartment()->zone() != cx_->compartment()->zone()) {
   1483    return AttachDecision::NoAction;
   1484  }
   1485 
   1486  // Take the unwrapped object's global, and wrap in a
   1487  // this-compartment wrapper. This is what will be stored in the IC
   1488  // keep the compartment alive.
   1489  RootedObject wrappedTargetGlobal(cx_, &unwrapped->nonCCWGlobal());
   1490  if (!cx_->compartment()->wrap(cx_, &wrappedTargetGlobal)) {
   1491    cx_->clearPendingException();
   1492    return AttachDecision::NoAction;
   1493  }
   1494 
   1495  NativeObject* holder = nullptr;
   1496  Maybe<PropertyInfo> prop;
   1497 
   1498  // Enter realm of target to prevent failing compartment assertions when doing
   1499  // the lookup.
   1500  {
   1501    AutoRealm ar(cx_, unwrapped);
   1502 
   1503    NativeGetPropKind kind =
   1504        CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &prop, pc_);
   1505    if (kind != NativeGetPropKind::Slot && kind != NativeGetPropKind::Missing) {
   1506      return AttachDecision::NoAction;
   1507    }
   1508  }
   1509  auto* unwrappedNative = &unwrapped->as<NativeObject>();
   1510 
   1511  maybeEmitIdGuard(id);
   1512  writer.guardIsProxy(objId);
   1513  writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj));
   1514 
   1515  // Load the object wrapped by the CCW
   1516  ObjOperandId wrapperTargetId =
   1517      writer.loadWrapperTarget(objId, /*fallible = */ false);
   1518 
   1519  // If the compartment of the wrapped object is different we should fail.
   1520  writer.guardCompartment(wrapperTargetId, wrappedTargetGlobal,
   1521                          unwrappedNative->compartment());
   1522 
   1523  ObjOperandId unwrappedId = wrapperTargetId;
   1524  if (holder) {
   1525    EmitReadSlotResult<IsCrossCompartment::Yes>(writer, unwrappedNative, holder,
   1526                                                *prop, unwrappedId);
   1527    writer.wrapResult();
   1528    writer.returnFromIC();
   1529    trackAttached("GetProp.CCWSlot");
   1530  } else {
   1531    EmitMissingPropResult<IsCrossCompartment::Yes>(writer, unwrappedNative,
   1532                                                   unwrappedId);
   1533    writer.returnFromIC();
   1534    trackAttached("GetProp.CCWMissing");
   1535  }
   1536  return AttachDecision::Attach;
   1537 }
   1538 
   1539 static JSObject* NewWrapperWithObjectShape(JSContext* cx,
   1540                                           Handle<NativeObject*> obj);
   1541 
   1542 static bool GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray,
   1543                                       MutableHandleObject wrapper) {
   1544  Value v = GetProxyReservedSlot(xray, GetXrayJitInfo()->xrayHolderSlot);
   1545  if (v.isObject()) {
   1546    NativeObject* holder = &v.toObject().as<NativeObject>();
   1547    v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot);
   1548    if (v.isObject()) {
   1549      Rooted<NativeObject*> expando(
   1550          cx, &UncheckedUnwrap(&v.toObject())->as<NativeObject>());
   1551      wrapper.set(NewWrapperWithObjectShape(cx, expando));
   1552      return wrapper != nullptr;
   1553    }
   1554  }
   1555  wrapper.set(nullptr);
   1556  return true;
   1557 }
   1558 
   1559 AttachDecision GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(
   1560    HandleObject obj, ObjOperandId objId, HandleId id,
   1561    ValOperandId receiverId) {
   1562  if (!obj->is<ProxyObject>()) {
   1563    return AttachDecision::NoAction;
   1564  }
   1565 
   1566  JS::XrayJitInfo* info = GetXrayJitInfo();
   1567  if (!info || !info->isCrossCompartmentXray(GetProxyHandler(obj))) {
   1568    return AttachDecision::NoAction;
   1569  }
   1570 
   1571  if (!info->compartmentHasExclusiveExpandos(obj)) {
   1572    return AttachDecision::NoAction;
   1573  }
   1574 
   1575  RootedObject target(cx_, UncheckedUnwrap(obj));
   1576 
   1577  RootedObject expandoShapeWrapper(cx_);
   1578  if (!GetXrayExpandoShapeWrapper(cx_, obj, &expandoShapeWrapper)) {
   1579    cx_->recoverFromOutOfMemory();
   1580    return AttachDecision::NoAction;
   1581  }
   1582 
   1583  // Look for a getter we can call on the xray or its prototype chain.
   1584  Rooted<Maybe<PropertyDescriptor>> desc(cx_);
   1585  RootedObject holder(cx_, obj);
   1586  RootedObjectVector prototypes(cx_);
   1587  RootedObjectVector prototypeExpandoShapeWrappers(cx_);
   1588  while (true) {
   1589    if (!GetOwnPropertyDescriptor(cx_, holder, id, &desc)) {
   1590      cx_->clearPendingException();
   1591      return AttachDecision::NoAction;
   1592    }
   1593    if (desc.isSome()) {
   1594      break;
   1595    }
   1596    if (!GetPrototype(cx_, holder, &holder)) {
   1597      cx_->clearPendingException();
   1598      return AttachDecision::NoAction;
   1599    }
   1600    if (!holder || !holder->is<ProxyObject>() ||
   1601        !info->isCrossCompartmentXray(GetProxyHandler(holder))) {
   1602      return AttachDecision::NoAction;
   1603    }
   1604    RootedObject prototypeExpandoShapeWrapper(cx_);
   1605    if (!GetXrayExpandoShapeWrapper(cx_, holder,
   1606                                    &prototypeExpandoShapeWrapper) ||
   1607        !prototypes.append(holder) ||
   1608        !prototypeExpandoShapeWrappers.append(prototypeExpandoShapeWrapper)) {
   1609      cx_->recoverFromOutOfMemory();
   1610      return AttachDecision::NoAction;
   1611    }
   1612  }
   1613  if (!desc->isAccessorDescriptor()) {
   1614    return AttachDecision::NoAction;
   1615  }
   1616 
   1617  RootedObject getter(cx_, desc->getter());
   1618  if (!getter || !getter->is<JSFunction>() ||
   1619      !getter->as<JSFunction>().isNativeWithoutJitEntry()) {
   1620    return AttachDecision::NoAction;
   1621  }
   1622 
   1623  maybeEmitIdGuard(id);
   1624  writer.guardIsProxy(objId);
   1625  writer.guardHasProxyHandler(objId, GetProxyHandler(obj));
   1626 
   1627  // Load the object wrapped by the CCW
   1628  ObjOperandId wrapperTargetId =
   1629      writer.loadWrapperTarget(objId, /*fallible = */ false);
   1630 
   1631  // Test the wrapped object's class. The properties held by xrays or their
   1632  // prototypes will be invariant for objects of a given class, except for
   1633  // changes due to xray expandos or xray prototype mutations.
   1634  writer.guardAnyClass(wrapperTargetId, target->getClass());
   1635 
   1636  // Make sure the expandos on the xray and its prototype chain match up with
   1637  // what we expect. The expando shape needs to be consistent, to ensure it
   1638  // has not had any shadowing properties added, and the expando cannot have
   1639  // any custom prototype (xray prototypes are stable otherwise).
   1640  //
   1641  // We can only do this for xrays with exclusive access to their expandos
   1642  // (as we checked earlier), which store a pointer to their expando
   1643  // directly. Xrays in other compartments may share their expandos with each
   1644  // other and a VM call is needed just to find the expando.
   1645  if (expandoShapeWrapper) {
   1646    writer.guardXrayExpandoShapeAndDefaultProto(objId, expandoShapeWrapper);
   1647  } else {
   1648    writer.guardXrayNoExpando(objId);
   1649  }
   1650  for (size_t i = 0; i < prototypes.length(); i++) {
   1651    JSObject* proto = prototypes[i];
   1652    ObjOperandId protoId = writer.loadObject(proto);
   1653    if (JSObject* protoShapeWrapper = prototypeExpandoShapeWrappers[i]) {
   1654      writer.guardXrayExpandoShapeAndDefaultProto(protoId, protoShapeWrapper);
   1655    } else {
   1656      writer.guardXrayNoExpando(protoId);
   1657    }
   1658  }
   1659 
   1660  bool sameRealm = cx_->realm() == getter->as<JSFunction>().realm();
   1661  writer.callNativeGetterResult(receiverId, &getter->as<JSFunction>(),
   1662                                sameRealm);
   1663  writer.returnFromIC();
   1664 
   1665  trackAttached("GetProp.XrayCCW");
   1666  return AttachDecision::Attach;
   1667 }
   1668 
   1669 #ifdef JS_PUNBOX64
   1670 AttachDecision GetPropIRGenerator::tryAttachScriptedProxy(
   1671    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id) {
   1672  if (cacheKind_ != CacheKind::GetProp && cacheKind_ != CacheKind::GetElem) {
   1673    return AttachDecision::NoAction;
   1674  }
   1675  if (cacheKind_ == CacheKind::GetElem) {
   1676    if (!idVal_.isString() && !idVal_.isInt32() && !idVal_.isSymbol()) {
   1677      return AttachDecision::NoAction;
   1678    }
   1679  }
   1680 
   1681  // Private fields of proxies are stored on the expando, and don't fire traps.
   1682  // Note that we don't need to guard against this in CacheIR, because there's
   1683  // no way to generate bytecode that will *sometimes* load a private field;
   1684  // either it's accessing a private field, or it isn't. We assert in the
   1685  // CacheIR implementation of callScriptedProxy(GetResult|GetByValueResult)
   1686  // that we don't see any private field symbols.
   1687  if (idVal_.isSymbol() && idVal_.toSymbol()->isPrivateName()) {
   1688    return AttachDecision::NoAction;
   1689  }
   1690 
   1691  JSObject* handlerObj = ScriptedProxyHandler::handlerObject(obj);
   1692  if (!handlerObj) {
   1693    return AttachDecision::NoAction;
   1694  }
   1695 
   1696  NativeObject* trapHolder = nullptr;
   1697  Maybe<PropertyInfo> trapProp;
   1698  // We call with pc_ even though that's not the actual corresponding pc. It
   1699  // should, however, be fine, because it's just used to check if this is a
   1700  // GetBoundName, which it's not.
   1701  NativeGetPropKind trapKind = CanAttachNativeGetProp(
   1702      cx_, handlerObj, NameToId(cx_->names().get), &trapHolder, &trapProp, pc_);
   1703 
   1704  if (trapKind != NativeGetPropKind::Missing &&
   1705      trapKind != NativeGetPropKind::Slot) {
   1706    return AttachDecision::NoAction;
   1707  }
   1708 
   1709  if (trapKind != NativeGetPropKind::Missing) {
   1710    uint32_t trapSlot = trapProp->slot();
   1711    const Value& trapVal = trapHolder->getSlot(trapSlot);
   1712    if (!trapVal.isObject()) {
   1713      return AttachDecision::NoAction;
   1714    }
   1715 
   1716    JSObject* trapObj = &trapVal.toObject();
   1717    if (!trapObj->is<JSFunction>()) {
   1718      return AttachDecision::NoAction;
   1719    }
   1720 
   1721    JSFunction* trapFn = &trapObj->as<JSFunction>();
   1722    if (trapFn->isClassConstructor()) {
   1723      return AttachDecision::NoAction;
   1724    }
   1725 
   1726    if (!trapFn->hasJitEntry()) {
   1727      return AttachDecision::NoAction;
   1728    }
   1729 
   1730    if (cx_->realm() != trapFn->realm()) {
   1731      return AttachDecision::NoAction;
   1732    }
   1733  }
   1734 
   1735  NativeObject* nHandlerObj = &handlerObj->as<NativeObject>();
   1736  JSObject* targetObj = obj->target();
   1737  MOZ_ASSERT(targetObj, "Guaranteed by the scripted Proxy constructor");
   1738 
   1739  // We just require that the target is a NativeObject to make our lives
   1740  // easier. There's too much nonsense we might have to handle otherwise and
   1741  // we're not set up to recursively call GetPropIRGenerator::tryAttachStub
   1742  // for the target object.
   1743  if (!targetObj->is<NativeObject>()) {
   1744    return AttachDecision::NoAction;
   1745  }
   1746 
   1747  writer.guardIsProxy(objId);
   1748  writer.guardHasProxyHandler(objId, &ScriptedProxyHandler::singleton);
   1749  ObjOperandId handlerObjId = writer.loadScriptedProxyHandler(objId);
   1750  ObjOperandId targetObjId =
   1751      writer.loadWrapperTarget(objId, /*fallible =*/true);
   1752 
   1753  writer.guardIsNativeObject(targetObjId);
   1754 
   1755  if (trapKind == NativeGetPropKind::Missing) {
   1756    EmitMissingPropGuard(writer, nHandlerObj, handlerObjId);
   1757    if (cacheKind_ == CacheKind::GetProp) {
   1758      writer.megamorphicLoadSlotResult(targetObjId, id);
   1759    } else {
   1760      writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId());
   1761    }
   1762  } else {
   1763    uint32_t trapSlot = trapProp->slot();
   1764    const Value& trapVal = trapHolder->getSlot(trapSlot);
   1765    JSObject* trapObj = &trapVal.toObject();
   1766    JSFunction* trapFn = &trapObj->as<JSFunction>();
   1767    ObjOperandId trapHolderId =
   1768        EmitReadSlotGuard(writer, nHandlerObj, trapHolder, handlerObjId);
   1769 
   1770    ValOperandId fnValId =
   1771        EmitLoadSlot(writer, trapHolder, trapHolderId, trapSlot);
   1772    ObjOperandId fnObjId = writer.guardToObject(fnValId);
   1773    emitCalleeGuard(fnObjId, trapFn);
   1774    ValOperandId targetValId = writer.boxObject(targetObjId);
   1775    if (cacheKind_ == CacheKind::GetProp) {
   1776      writer.callScriptedProxyGetResult(targetValId, objId, handlerObjId,
   1777                                        fnObjId, trapFn, id);
   1778    } else {
   1779      ValOperandId idId = getElemKeyValueId();
   1780      ValOperandId stringIdId = writer.idToStringOrSymbol(idId);
   1781      writer.callScriptedProxyGetByValueResult(targetValId, objId, handlerObjId,
   1782                                               stringIdId, fnObjId, trapFn);
   1783    }
   1784  }
   1785  writer.returnFromIC();
   1786 
   1787  trackAttached("GetScriptedProxy");
   1788  return AttachDecision::Attach;
   1789 }
   1790 #endif
   1791 
   1792 AttachDecision GetPropIRGenerator::tryAttachGenericProxy(
   1793    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   1794    bool handleDOMProxies) {
   1795  writer.guardIsProxy(objId);
   1796 
   1797  if (!handleDOMProxies) {
   1798    // Ensure that the incoming object is not a DOM proxy, so that we can get to
   1799    // the specialized stubs
   1800    writer.guardIsNotDOMProxy(objId);
   1801  }
   1802 
   1803  if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
   1804    MOZ_ASSERT(!isSuper());
   1805    maybeEmitIdGuard(id);
   1806    writer.proxyGetResult(objId, id);
   1807  } else {
   1808    // Attach a stub that handles every id.
   1809    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
   1810    MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
   1811    MOZ_ASSERT(!isSuper());
   1812    writer.proxyGetByValueResult(objId, getElemKeyValueId());
   1813  }
   1814 
   1815  writer.returnFromIC();
   1816 
   1817  trackAttached("GetProp.GenericProxy");
   1818  return AttachDecision::Attach;
   1819 }
   1820 
   1821 static bool ValueIsInt64Index(const Value& val, int64_t* index) {
   1822  // Try to convert the Value to a TypedArray index or DataView offset.
   1823 
   1824  if (val.isInt32()) {
   1825    *index = val.toInt32();
   1826    return true;
   1827  }
   1828 
   1829  if (val.isDouble()) {
   1830    // Use NumberEqualsInt64 because ToPropertyKey(-0) is 0.
   1831    return mozilla::NumberEqualsInt64(val.toDouble(), index);
   1832  }
   1833 
   1834  return false;
   1835 }
   1836 
   1837 IntPtrOperandId IRGenerator::guardToIntPtrIndex(const Value& index,
   1838                                                ValOperandId indexId,
   1839                                                bool supportOOB) {
   1840 #ifdef DEBUG
   1841  int64_t indexInt64;
   1842  MOZ_ASSERT_IF(!supportOOB, ValueIsInt64Index(index, &indexInt64));
   1843 #endif
   1844 
   1845  if (index.isInt32()) {
   1846    Int32OperandId int32IndexId = writer.guardToInt32(indexId);
   1847    return writer.int32ToIntPtr(int32IndexId);
   1848  }
   1849 
   1850  MOZ_ASSERT(index.isNumber());
   1851  NumberOperandId numberIndexId = writer.guardIsNumber(indexId);
   1852  return writer.guardNumberToIntPtrIndex(numberIndexId, supportOOB);
   1853 }
   1854 
   1855 ObjOperandId IRGenerator::guardDOMProxyExpandoObjectAndShape(
   1856    ProxyObject* obj, ObjOperandId objId, const Value& expandoVal,
   1857    NativeObject* expandoObj) {
   1858  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   1859 
   1860  TestMatchingProxyReceiver(writer, obj, objId);
   1861 
   1862  // Shape determines Class, so now it must be a DOM proxy.
   1863  ValOperandId expandoValId;
   1864  if (expandoVal.isObject()) {
   1865    expandoValId = writer.loadDOMExpandoValue(objId);
   1866  } else {
   1867    expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
   1868  }
   1869 
   1870  // Guard the expando is an object and shape guard.
   1871  ObjOperandId expandoObjId = writer.guardToObject(expandoValId);
   1872  TestMatchingHolder(writer, expandoObj, expandoObjId);
   1873  return expandoObjId;
   1874 }
   1875 
   1876 AttachDecision GetPropIRGenerator::tryAttachDOMProxyExpando(
   1877    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   1878    ValOperandId receiverId) {
   1879  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   1880 
   1881  Value expandoVal = GetProxyPrivate(obj);
   1882  JSObject* expandoObj;
   1883  if (expandoVal.isObject()) {
   1884    expandoObj = &expandoVal.toObject();
   1885  } else {
   1886    MOZ_ASSERT(!expandoVal.isUndefined(),
   1887               "How did a missing expando manage to shadow things?");
   1888    auto expandoAndGeneration =
   1889        static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
   1890    MOZ_ASSERT(expandoAndGeneration);
   1891    expandoObj = &expandoAndGeneration->expando.toObject();
   1892  }
   1893 
   1894  // Try to do the lookup on the expando object.
   1895  NativeObject* holder = nullptr;
   1896  Maybe<PropertyInfo> prop;
   1897  NativeGetPropKind kind =
   1898      CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &prop, pc_);
   1899  if (kind == NativeGetPropKind::None) {
   1900    return AttachDecision::NoAction;
   1901  }
   1902  if (!holder) {
   1903    return AttachDecision::NoAction;
   1904  }
   1905  auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
   1906 
   1907  MOZ_ASSERT(holder == nativeExpandoObj);
   1908 
   1909  maybeEmitIdGuard(id);
   1910  ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
   1911      obj, objId, expandoVal, nativeExpandoObj);
   1912 
   1913  if (kind == NativeGetPropKind::Slot) {
   1914    // Load from the expando's slots.
   1915    EmitLoadSlotResult(writer, expandoObjId, nativeExpandoObj, *prop);
   1916    writer.returnFromIC();
   1917  } else {
   1918    // Call the getter. Note that we pass objId, the DOM proxy, as |this|
   1919    // and not the expando object.
   1920    MOZ_ASSERT(kind == NativeGetPropKind::NativeGetter ||
   1921               kind == NativeGetPropKind::ScriptedGetter);
   1922    emitGuardGetterSetterSlot(nativeExpandoObj, *prop, expandoObjId,
   1923                              AccessorKind::Getter);
   1924    emitCallGetterResultNoGuards(kind, nativeExpandoObj, nativeExpandoObj,
   1925                                 *prop, receiverId);
   1926  }
   1927 
   1928  trackAttached("GetProp.DOMProxyExpando");
   1929  return AttachDecision::Attach;
   1930 }
   1931 
   1932 AttachDecision GetPropIRGenerator::tryAttachDOMProxyShadowed(
   1933    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id) {
   1934  MOZ_ASSERT(!isSuper());
   1935  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   1936 
   1937  maybeEmitIdGuard(id);
   1938  TestMatchingProxyReceiver(writer, obj, objId);
   1939  writer.proxyGetResult(objId, id);
   1940  writer.returnFromIC();
   1941 
   1942  trackAttached("GetProp.DOMProxyShadowed");
   1943  return AttachDecision::Attach;
   1944 }
   1945 
   1946 // Emit CacheIR to guard the DOM proxy doesn't shadow |id|. There are two types
   1947 // of DOM proxies:
   1948 //
   1949 // (a) DOM proxies marked LegacyOverrideBuiltIns in WebIDL, for example
   1950 //     HTMLDocument or HTMLFormElement. These proxies look up properties in this
   1951 //     order:
   1952 //
   1953 //       (1) The expando object.
   1954 //       (2) The proxy's named-property handler.
   1955 //       (3) The prototype chain.
   1956 //
   1957 //     To optimize properties on the prototype chain, we have to guard that (1)
   1958 //     and (2) don't shadow (3). We handle (1) by either emitting a shape guard
   1959 //     for the expando object or by guarding the proxy has no expando object. To
   1960 //     efficiently handle (2), the proxy must have an ExpandoAndGeneration*
   1961 //     stored as PrivateValue. We guard on its generation field to ensure the
   1962 //     set of names hasn't changed.
   1963 //
   1964 //     Missing properties can be optimized in a similar way by emitting shape
   1965 //     guards for the prototype chain.
   1966 //
   1967 // (b) Other DOM proxies. These proxies look up properties in this
   1968 //     order:
   1969 //
   1970 //       (1) The expando object.
   1971 //       (2) The prototype chain.
   1972 //       (3) The proxy's named-property handler.
   1973 //
   1974 //     To optimize properties on the prototype chain, we only have to guard the
   1975 //     expando object doesn't shadow it.
   1976 //
   1977 //     Missing properties can't be optimized in this case because we don't have
   1978 //     an efficient way to guard against the proxy handler shadowing the
   1979 //     property (there's no ExpandoAndGeneration*).
   1980 //
   1981 // See also:
   1982 // * DOMProxyShadows in DOMJSProxyHandler.cpp
   1983 // * https://webidl.spec.whatwg.org/#dfn-named-property-visibility (the Note at
   1984 //   the end)
   1985 //
   1986 // Callers are expected to have already guarded on the shape of the
   1987 // object, which guarantees the object is a DOM proxy.
   1988 static void CheckDOMProxyDoesNotShadow(CacheIRWriter& writer, ProxyObject* obj,
   1989                                       jsid id, ObjOperandId objId,
   1990                                       bool* canOptimizeMissing) {
   1991  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   1992 
   1993  Value expandoVal = GetProxyPrivate(obj);
   1994 
   1995  ValOperandId expandoId;
   1996  if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
   1997    // Case (a).
   1998    auto expandoAndGeneration =
   1999        static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
   2000    uint64_t generation = expandoAndGeneration->generation;
   2001    expandoId = writer.loadDOMExpandoValueGuardGeneration(
   2002        objId, expandoAndGeneration, generation);
   2003    expandoVal = expandoAndGeneration->expando;
   2004    *canOptimizeMissing = true;
   2005  } else {
   2006    // Case (b).
   2007    expandoId = writer.loadDOMExpandoValue(objId);
   2008    *canOptimizeMissing = false;
   2009  }
   2010 
   2011  if (expandoVal.isUndefined()) {
   2012    // Guard there's no expando object.
   2013    writer.guardNonDoubleType(expandoId, ValueType::Undefined);
   2014  } else if (expandoVal.isObject()) {
   2015    // Guard the proxy either has no expando object or, if it has one, that
   2016    // the shape matches the current expando object.
   2017    NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
   2018    MOZ_ASSERT(!expandoObj.containsPure(id));
   2019    writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.shape());
   2020  } else {
   2021    MOZ_CRASH("Invalid expando value");
   2022  }
   2023 }
   2024 
   2025 AttachDecision GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
   2026    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   2027    ValOperandId receiverId) {
   2028  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   2029 
   2030  JSObject* protoObj = obj->staticPrototype();
   2031  if (!protoObj) {
   2032    return AttachDecision::NoAction;
   2033  }
   2034 
   2035  NativeObject* holder = nullptr;
   2036  Maybe<PropertyInfo> prop;
   2037  NativeGetPropKind kind =
   2038      CanAttachNativeGetProp(cx_, protoObj, id, &holder, &prop, pc_);
   2039  if (kind == NativeGetPropKind::None) {
   2040    return AttachDecision::NoAction;
   2041  }
   2042  auto* nativeProtoObj = &protoObj->as<NativeObject>();
   2043 
   2044  maybeEmitIdGuard(id);
   2045 
   2046  // Guard that our proxy (expando) object hasn't started shadowing this
   2047  // property.
   2048  TestMatchingProxyReceiver(writer, obj, objId);
   2049  bool canOptimizeMissing = false;
   2050  CheckDOMProxyDoesNotShadow(writer, obj, id, objId, &canOptimizeMissing);
   2051 
   2052  if (holder) {
   2053    // Found the property on the prototype chain. Treat it like a native
   2054    // getprop.
   2055    GeneratePrototypeGuards(writer, obj, holder, objId);
   2056 
   2057    // Guard on the holder of the property.
   2058    ObjOperandId holderId = writer.loadObject(holder);
   2059    TestMatchingHolder(writer, holder, holderId);
   2060 
   2061    if (kind == NativeGetPropKind::Slot) {
   2062      EmitLoadSlotResult(writer, holderId, holder, *prop);
   2063      writer.returnFromIC();
   2064    } else {
   2065      // EmitCallGetterResultNoGuards expects |obj| to be the object the
   2066      // property is on to do some checks. Since we actually looked at
   2067      // checkObj, and no extra guards will be generated, we can just
   2068      // pass that instead.
   2069      MOZ_ASSERT(kind == NativeGetPropKind::NativeGetter ||
   2070                 kind == NativeGetPropKind::ScriptedGetter);
   2071      MOZ_ASSERT(!isSuper());
   2072      emitGuardGetterSetterSlot(holder, *prop, holderId, AccessorKind::Getter,
   2073                                /* holderIsConstant = */ true);
   2074      emitCallGetterResultNoGuards(kind, nativeProtoObj, holder, *prop,
   2075                                   receiverId);
   2076    }
   2077  } else {
   2078    // Property was not found on the prototype chain.
   2079    MOZ_ASSERT(kind == NativeGetPropKind::Missing);
   2080    if (canOptimizeMissing) {
   2081      // We already guarded on the proxy's shape, so now shape guard the proto
   2082      // chain.
   2083      ObjOperandId protoId = writer.loadObject(nativeProtoObj);
   2084      EmitMissingPropResult(writer, nativeProtoObj, protoId);
   2085    } else {
   2086      MOZ_ASSERT(!isSuper());
   2087      writer.proxyGetResult(objId, id);
   2088    }
   2089    writer.returnFromIC();
   2090  }
   2091 
   2092  trackAttached("GetProp.DOMProxyUnshadowed");
   2093  return AttachDecision::Attach;
   2094 }
   2095 
   2096 AttachDecision GetPropIRGenerator::tryAttachProxy(HandleObject obj,
   2097                                                  ObjOperandId objId,
   2098                                                  HandleId id,
   2099                                                  ValOperandId receiverId) {
   2100  // The proxy stubs don't currently support |super| access.
   2101  if (isSuper()) {
   2102    return AttachDecision::NoAction;
   2103  }
   2104 
   2105  // Always try to attach scripted proxy get even if we're megamorphic.
   2106  // In Speedometer 3 we'll often run into cases where we're megamorphic
   2107  // overall, but monomorphic for the proxy case. This is because there
   2108  // are functions which lazily turn various differently-shaped objects
   2109  // into proxies. So the un-proxified objects are megamorphic, but the
   2110  // proxy handlers are actually monomorphic. There is room for a bit
   2111  // more sophistication here, but this should do for now.
   2112  if (!obj->is<ProxyObject>()) {
   2113    return AttachDecision::NoAction;
   2114  }
   2115  auto proxy = obj.as<ProxyObject>();
   2116 #ifdef JS_PUNBOX64
   2117  if (proxy->handler()->isScripted()) {
   2118    TRY_ATTACH(tryAttachScriptedProxy(proxy, objId, id));
   2119  }
   2120 #endif
   2121 
   2122  ProxyStubType type = GetProxyStubType(cx_, obj, id);
   2123  if (type == ProxyStubType::None) {
   2124    return AttachDecision::NoAction;
   2125  }
   2126 
   2127  if (mode_ == ICState::Mode::Megamorphic) {
   2128    return tryAttachGenericProxy(proxy, objId, id,
   2129                                 /* handleDOMProxies = */ true);
   2130  }
   2131 
   2132  switch (type) {
   2133    case ProxyStubType::None:
   2134      break;
   2135    case ProxyStubType::DOMExpando:
   2136      TRY_ATTACH(tryAttachDOMProxyExpando(proxy, objId, id, receiverId));
   2137      [[fallthrough]];  // Fall through to the generic shadowed case.
   2138    case ProxyStubType::DOMShadowed:
   2139      return tryAttachDOMProxyShadowed(proxy, objId, id);
   2140    case ProxyStubType::DOMUnshadowed:
   2141      TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy, objId, id, receiverId));
   2142      return tryAttachGenericProxy(proxy, objId, id,
   2143                                   /* handleDOMProxies = */ true);
   2144    case ProxyStubType::Generic:
   2145      return tryAttachGenericProxy(proxy, objId, id,
   2146                                   /* handleDOMProxies = */ false);
   2147  }
   2148 
   2149  MOZ_CRASH("Unexpected ProxyStubType");
   2150 }
   2151 
   2152 const JSClass* js::jit::ClassFor(GuardClassKind kind) {
   2153  switch (kind) {
   2154    case GuardClassKind::Array:
   2155      return &ArrayObject::class_;
   2156    case GuardClassKind::PlainObject:
   2157      return &PlainObject::class_;
   2158    case GuardClassKind::FixedLengthArrayBuffer:
   2159      return &FixedLengthArrayBufferObject::class_;
   2160    case GuardClassKind::ImmutableArrayBuffer:
   2161      return &ImmutableArrayBufferObject::class_;
   2162    case GuardClassKind::ResizableArrayBuffer:
   2163      return &ResizableArrayBufferObject::class_;
   2164    case GuardClassKind::FixedLengthSharedArrayBuffer:
   2165      return &FixedLengthSharedArrayBufferObject::class_;
   2166    case GuardClassKind::GrowableSharedArrayBuffer:
   2167      return &GrowableSharedArrayBufferObject::class_;
   2168    case GuardClassKind::FixedLengthDataView:
   2169      return &FixedLengthDataViewObject::class_;
   2170    case GuardClassKind::ImmutableDataView:
   2171      return &ImmutableDataViewObject::class_;
   2172    case GuardClassKind::ResizableDataView:
   2173      return &ResizableDataViewObject::class_;
   2174    case GuardClassKind::MappedArguments:
   2175      return &MappedArgumentsObject::class_;
   2176    case GuardClassKind::UnmappedArguments:
   2177      return &UnmappedArgumentsObject::class_;
   2178    case GuardClassKind::WindowProxy:
   2179      // Caller needs to handle this case, see
   2180      // JSRuntime::maybeWindowProxyClass().
   2181      break;
   2182    case GuardClassKind::JSFunction:
   2183      // Caller needs to handle this case. Can be either |js::FunctionClass| or
   2184      // |js::ExtendedFunctionClass|.
   2185      break;
   2186    case GuardClassKind::BoundFunction:
   2187      return &BoundFunctionObject::class_;
   2188    case GuardClassKind::Set:
   2189      return &SetObject::class_;
   2190    case GuardClassKind::Map:
   2191      return &MapObject::class_;
   2192    case GuardClassKind::Date:
   2193      return &DateObject::class_;
   2194    case GuardClassKind::WeakMap:
   2195      return &WeakMapObject::class_;
   2196    case GuardClassKind::WeakSet:
   2197      return &WeakSetObject::class_;
   2198  }
   2199  MOZ_CRASH("unexpected kind");
   2200 }
   2201 
   2202 // Guards the class of an object. Because shape implies class, and a shape guard
   2203 // is faster than a class guard, if this is our first time attaching a stub, we
   2204 // instead generate a shape guard.
   2205 void IRGenerator::emitOptimisticClassGuard(ObjOperandId objId, JSObject* obj,
   2206                                           GuardClassKind kind) {
   2207 #ifdef DEBUG
   2208  switch (kind) {
   2209    case GuardClassKind::Array:
   2210    case GuardClassKind::PlainObject:
   2211    case GuardClassKind::FixedLengthArrayBuffer:
   2212    case GuardClassKind::ImmutableArrayBuffer:
   2213    case GuardClassKind::ResizableArrayBuffer:
   2214    case GuardClassKind::FixedLengthSharedArrayBuffer:
   2215    case GuardClassKind::GrowableSharedArrayBuffer:
   2216    case GuardClassKind::FixedLengthDataView:
   2217    case GuardClassKind::ImmutableDataView:
   2218    case GuardClassKind::ResizableDataView:
   2219    case GuardClassKind::Set:
   2220    case GuardClassKind::Map:
   2221    case GuardClassKind::Date:
   2222    case GuardClassKind::WeakMap:
   2223    case GuardClassKind::WeakSet:
   2224      MOZ_ASSERT(obj->hasClass(ClassFor(kind)));
   2225      break;
   2226 
   2227    case GuardClassKind::MappedArguments:
   2228    case GuardClassKind::UnmappedArguments:
   2229    case GuardClassKind::JSFunction:
   2230    case GuardClassKind::BoundFunction:
   2231    case GuardClassKind::WindowProxy:
   2232      // Arguments, functions, and the global object have
   2233      // less consistent shapes.
   2234      MOZ_CRASH("GuardClassKind not supported");
   2235  }
   2236 #endif
   2237 
   2238  if (isFirstStub_) {
   2239    writer.guardShapeForClass(objId, obj->shape());
   2240  } else {
   2241    writer.guardClass(objId, kind);
   2242  }
   2243 }
   2244 
   2245 bool IRGenerator::canOptimizeConstantDataProperty(NativeObject* holder,
   2246                                                  PropertyInfo prop,
   2247                                                  ObjectFuse** objFuse) {
   2248  MOZ_ASSERT(prop.isDataProperty());
   2249 
   2250  if (mode_ != ICState::Mode::Specialized || !holder->hasObjectFuse()) {
   2251    return false;
   2252  }
   2253 
   2254  *objFuse = cx_->zone()->objectFuses.getOrCreate(cx_, holder);
   2255  if (!*objFuse) {
   2256    cx_->recoverFromOutOfMemory();
   2257    return false;
   2258  }
   2259 
   2260  if (!(*objFuse)->tryOptimizeConstantProperty(prop)) {
   2261    return false;
   2262  }
   2263 
   2264  // Warp supports nursery objects and strings. Strings are atomized below,
   2265  // and if that were not the case, WarpOracle would handle it. Warp does not
   2266  // support nursery BigInts, a very uncommon case, so we currently don't
   2267  // optimize such properties.
   2268  Value result = holder->getSlot(prop.slot());
   2269  if (result.isGCThing() && !result.toGCThing()->isTenured() &&
   2270      !result.isObject() && !result.isString()) {
   2271    MOZ_ASSERT(result.isBigInt());
   2272    return false;
   2273  }
   2274 
   2275  // Atomize strings to ensure the slot's value won't be mutated by the
   2276  // LLoadFixedSlotAndAtomize or LLoadDynamicSlotAndAtomize instructions in Ion.
   2277  // This matters because, for GC reasons, the Value that will be stored in the
   2278  // IC stub in a WeakValueField must match the Value stored in the object slot.
   2279  // In emitConstantDataPropertyResult we emit CacheIR instructions to assert
   2280  // this.
   2281  //
   2282  // We don't optimize the constant property if the string is very long because
   2283  // atomizing such strings can be slow.
   2284  if (result.isString() && !result.toString()->isAtom()) {
   2285    static constexpr size_t MaxLengthForAtomize = 1000;
   2286    if (result.toString()->length() > MaxLengthForAtomize) {
   2287      return false;
   2288    }
   2289    JSAtom* atom = AtomizeString(cx_, result.toString());
   2290    if (!atom) {
   2291      cx_->recoverFromOutOfMemory();
   2292      return false;
   2293    }
   2294    result.setString(atom);
   2295    holder->setSlot(prop.slot(), result);
   2296  }
   2297 
   2298  return true;
   2299 }
   2300 
   2301 void IRGenerator::emitConstantDataPropertyResult(NativeObject* holder,
   2302                                                 ObjOperandId holderId,
   2303                                                 PropertyKey key,
   2304                                                 PropertyInfo prop,
   2305                                                 ObjectFuse* objFuse) {
   2306  MOZ_ASSERT(prop.isDataProperty());
   2307 
   2308  auto data = objFuse->getConstantPropertyGuardData(prop);
   2309  bool canUseFastPath = !objFuse->hasInvalidatedConstantProperty();
   2310  writer.guardObjectFuseProperty(holderId, holder, objFuse, data.generation,
   2311                                 data.propIndex, data.propMask, canUseFastPath);
   2312 #ifdef DEBUG
   2313  writer.assertPropertyLookup(holderId, key, prop.slot());
   2314 #endif
   2315 
   2316  Value result = holder->getSlot(prop.slot());
   2317  MOZ_RELEASE_ASSERT(!result.isMagic());
   2318  MOZ_ASSERT_IF(result.isString(), result.toString()->isAtom());
   2319 
   2320  if (result.isGCThing()) {
   2321    // We store the property Value in the IC stub in a WeakValueField (or
   2322    // WeakObjectField) to ensure the stub doesn't keep GC things alive after
   2323    // the property is mutated. Weak references normally require a read barrier
   2324    // but not using a barrier here should be safe, because this property Value
   2325    // is also stored in the holder object's slot and Watchtower should pop the
   2326    // fuse when that slot is mutated.
   2327    //
   2328    // To make sure a Watchtower correctness bug doesn't become a GC security
   2329    // bug, we check the slot Value still matches the current value before we
   2330    // use the weak reference. This only affects Baseline IC code because we
   2331    // don't use weak references in the transpiler or for Ion ICs.
   2332    if (holder->isFixedSlot(prop.slot())) {
   2333      size_t offset = NativeObject::getFixedSlotOffset(prop.slot());
   2334      writer.checkWeakValueResultForFixedSlot(holderId, offset, result);
   2335    } else {
   2336      size_t offset = holder->dynamicSlotIndex(prop.slot()) * sizeof(Value);
   2337      writer.checkWeakValueResultForDynamicSlot(holderId, offset, result);
   2338    }
   2339 
   2340    // Use a different CacheIR instruction for objects, to support nursery
   2341    // objects in Warp.
   2342    if (result.isObject()) {
   2343      writer.uncheckedLoadWeakObjectResult(&result.toObject());
   2344    } else {
   2345      writer.uncheckedLoadWeakValueResult(result);
   2346    }
   2347  } else {
   2348    writer.loadValueResult(result);
   2349  }
   2350 }
   2351 
   2352 void IRGenerator::emitLoadDataPropertyResult(NativeObject* obj,
   2353                                             NativeObject* holder,
   2354                                             PropertyKey key, PropertyInfo prop,
   2355                                             ObjOperandId objId) {
   2356  ObjectFuse* objFuse = nullptr;
   2357  if (canOptimizeConstantDataProperty(holder, prop, &objFuse)) {
   2358    ObjOperandId holderId =
   2359        EmitGuardObjectFuseHolder(writer, obj, holder, objId);
   2360    emitConstantDataPropertyResult(holder, holderId, key, prop, objFuse);
   2361    return;
   2362  }
   2363 
   2364  EmitReadSlotResult(writer, obj, holder, prop, objId);
   2365 }
   2366 
   2367 bool IRGenerator::canOptimizeConstantAccessorProperty(NativeObject* holder,
   2368                                                      PropertyInfo prop,
   2369                                                      ObjectFuse** objFuse) {
   2370  MOZ_ASSERT(prop.isAccessorProperty());
   2371  MOZ_ASSERT(holder->getSlot(prop.slot()).toGCThing()->is<GetterSetter>());
   2372 
   2373  if (mode_ != ICState::Mode::Specialized || !holder->hasObjectFuse()) {
   2374    return false;
   2375  }
   2376 
   2377  *objFuse = cx_->zone()->objectFuses.getOrCreate(cx_, holder);
   2378  if (!*objFuse) {
   2379    cx_->recoverFromOutOfMemory();
   2380    return false;
   2381  }
   2382 
   2383  return (*objFuse)->tryOptimizeConstantProperty(prop);
   2384 }
   2385 
   2386 void IRGenerator::emitGuardConstantAccessorProperty(NativeObject* holder,
   2387                                                    ObjOperandId holderId,
   2388                                                    PropertyKey key,
   2389                                                    PropertyInfo prop,
   2390                                                    ObjectFuse* objFuse) {
   2391  MOZ_ASSERT(prop.isAccessorProperty());
   2392 
   2393  auto data = objFuse->getConstantPropertyGuardData(prop);
   2394  bool canUseFastPath = !objFuse->hasInvalidatedConstantProperty();
   2395  writer.guardObjectFuseProperty(holderId, holder, objFuse, data.generation,
   2396                                 data.propIndex, data.propMask, canUseFastPath);
   2397 #ifdef DEBUG
   2398  writer.assertPropertyLookup(holderId, key, prop.slot());
   2399 #endif
   2400 }
   2401 
   2402 static void AssertArgumentsCustomDataProp(ArgumentsObject* obj,
   2403                                          PropertyKey key) {
   2404 #ifdef DEBUG
   2405  // The property must still be a custom data property if it has been resolved.
   2406  // If this assertion fails, we're probably missing a call to mark this
   2407  // property overridden.
   2408  Maybe<PropertyInfo> prop = obj->lookupPure(key);
   2409  MOZ_ASSERT_IF(prop, prop->isCustomDataProperty());
   2410 #endif
   2411 }
   2412 
   2413 AttachDecision GetPropIRGenerator::tryAttachObjectLength(HandleObject obj,
   2414                                                         ObjOperandId objId,
   2415                                                         HandleId id) {
   2416  if (!id.isAtom(cx_->names().length)) {
   2417    return AttachDecision::NoAction;
   2418  }
   2419 
   2420  if (obj->is<ArrayObject>()) {
   2421    if (obj->as<ArrayObject>().length() > INT32_MAX) {
   2422      return AttachDecision::NoAction;
   2423    }
   2424 
   2425    maybeEmitIdGuard(id);
   2426    emitOptimisticClassGuard(objId, obj, GuardClassKind::Array);
   2427    writer.loadInt32ArrayLengthResult(objId);
   2428    writer.returnFromIC();
   2429 
   2430    trackAttached("GetProp.ArrayLength");
   2431    return AttachDecision::Attach;
   2432  }
   2433 
   2434  if (obj->is<ArgumentsObject>() &&
   2435      !obj->as<ArgumentsObject>().hasOverriddenLength()) {
   2436    AssertArgumentsCustomDataProp(&obj->as<ArgumentsObject>(), id);
   2437    maybeEmitIdGuard(id);
   2438    if (obj->is<MappedArgumentsObject>()) {
   2439      writer.guardClass(objId, GuardClassKind::MappedArguments);
   2440    } else {
   2441      MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
   2442      writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   2443    }
   2444    writer.loadArgumentsObjectLengthResult(objId);
   2445    writer.returnFromIC();
   2446 
   2447    trackAttached("GetProp.ArgumentsObjectLength");
   2448    return AttachDecision::Attach;
   2449  }
   2450 
   2451  return AttachDecision::NoAction;
   2452 }
   2453 
   2454 AttachDecision GetPropIRGenerator::tryAttachInlinableNativeGetter(
   2455    Handle<NativeObject*> holder, PropertyInfo prop, ValOperandId receiverId) {
   2456  MOZ_ASSERT(mode_ == ICState::Mode::Specialized);
   2457 
   2458  // Suppress GC because |CacheIRWriter::trace()| doesn't yet support stub
   2459  // field tracing and stub fields were already added for shape and prototype
   2460  // guards.
   2461  gc::AutoSuppressGC suppressGC(cx_);
   2462 
   2463  Rooted<JSFunction*> target(cx_, &holder->getGetter(prop)->as<JSFunction>());
   2464  MOZ_ASSERT(target->isNativeWithoutJitEntry());
   2465 
   2466  Handle<Value> thisValue = receiverVal_;
   2467 
   2468  bool isSpread = false;
   2469  bool isSameRealm = cx_->realm() == target->realm();
   2470  bool isConstructing = false;
   2471  CallFlags flags(isConstructing, isSpread, isSameRealm);
   2472 
   2473  // Check for specific native-function optimizations.
   2474  InlinableNativeIRGenerator nativeGen(*this, target, thisValue, flags,
   2475                                       receiverId);
   2476  return nativeGen.tryAttachStub();
   2477 }
   2478 
   2479 AttachDecision GetPropIRGenerator::tryAttachFunction(HandleObject obj,
   2480                                                     ObjOperandId objId,
   2481                                                     HandleId id) {
   2482  // Function properties are lazily resolved so they might not be defined yet.
   2483  // And we might end up in a situation where we always have a fresh function
   2484  // object during the IC generation.
   2485  if (!obj->is<JSFunction>()) {
   2486    return AttachDecision::NoAction;
   2487  }
   2488 
   2489  bool isLength = id.isAtom(cx_->names().length);
   2490  if (!isLength && !id.isAtom(cx_->names().name)) {
   2491    return AttachDecision::NoAction;
   2492  }
   2493 
   2494  NativeObject* holder = nullptr;
   2495  PropertyResult prop;
   2496  // If this property exists already, don't attach the stub.
   2497  if (LookupPropertyPure(cx_, obj, id, &holder, &prop)) {
   2498    return AttachDecision::NoAction;
   2499  }
   2500 
   2501  JSFunction* fun = &obj->as<JSFunction>();
   2502 
   2503  if (isLength) {
   2504    // length was probably deleted from the function.
   2505    if (fun->hasResolvedLength()) {
   2506      return AttachDecision::NoAction;
   2507    }
   2508 
   2509    // Lazy functions don't store the length.
   2510    if (!fun->hasBytecode()) {
   2511      return AttachDecision::NoAction;
   2512    }
   2513  } else {
   2514    // name was probably deleted from the function.
   2515    if (fun->hasResolvedName()) {
   2516      return AttachDecision::NoAction;
   2517    }
   2518  }
   2519 
   2520  maybeEmitIdGuard(id);
   2521  writer.guardClass(objId, GuardClassKind::JSFunction);
   2522  if (isLength) {
   2523    writer.loadFunctionLengthResult(objId);
   2524    writer.returnFromIC();
   2525    trackAttached("GetProp.FunctionLength");
   2526  } else {
   2527    writer.loadFunctionNameResult(objId);
   2528    writer.returnFromIC();
   2529    trackAttached("GetProp.FunctionName");
   2530  }
   2531  return AttachDecision::Attach;
   2532 }
   2533 
   2534 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectIterator(
   2535    HandleObject obj, ObjOperandId objId, HandleId id) {
   2536  if (!obj->is<ArgumentsObject>()) {
   2537    return AttachDecision::NoAction;
   2538  }
   2539 
   2540  if (!id.isWellKnownSymbol(JS::SymbolCode::iterator)) {
   2541    return AttachDecision::NoAction;
   2542  }
   2543 
   2544  Handle<ArgumentsObject*> args = obj.as<ArgumentsObject>();
   2545  if (args->hasOverriddenIterator()) {
   2546    return AttachDecision::NoAction;
   2547  }
   2548  if (cx_->realm() != args->realm()) {
   2549    return AttachDecision::NoAction;
   2550  }
   2551 
   2552  AssertArgumentsCustomDataProp(args, id);
   2553 
   2554  RootedValue iterator(cx_);
   2555  if (!ArgumentsObject::getArgumentsIterator(cx_, &iterator)) {
   2556    cx_->recoverFromOutOfMemory();
   2557    return AttachDecision::NoAction;
   2558  }
   2559  MOZ_ASSERT(iterator.isObject());
   2560 
   2561  maybeEmitIdGuard(id);
   2562  if (args->is<MappedArgumentsObject>()) {
   2563    writer.guardClass(objId, GuardClassKind::MappedArguments);
   2564  } else {
   2565    MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   2566    writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   2567  }
   2568  uint32_t flags = ArgumentsObject::ITERATOR_OVERRIDDEN_BIT;
   2569  writer.guardArgumentsObjectFlags(objId, flags);
   2570  writer.guardObjectHasSameRealm(objId);
   2571 
   2572  ObjOperandId iterId = writer.loadObject(&iterator.toObject());
   2573  writer.loadObjectResult(iterId);
   2574  writer.returnFromIC();
   2575 
   2576  trackAttached("GetProp.ArgumentsObjectIterator");
   2577  return AttachDecision::Attach;
   2578 }
   2579 
   2580 AttachDecision GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj,
   2581                                                            ObjOperandId objId,
   2582                                                            HandleId id) {
   2583  if (!obj->is<ModuleNamespaceObject>()) {
   2584    return AttachDecision::NoAction;
   2585  }
   2586 
   2587  auto* ns = &obj->as<ModuleNamespaceObject>();
   2588  ModuleEnvironmentObject* env = nullptr;
   2589  Maybe<PropertyInfo> prop;
   2590  if (!ns->bindings().lookup(id, &env, &prop)) {
   2591    return AttachDecision::NoAction;
   2592  }
   2593 
   2594  // Don't emit a stub until the target binding has been initialized.
   2595  if (env->getSlot(prop->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
   2596    return AttachDecision::NoAction;
   2597  }
   2598 
   2599  // Check for the specific namespace object.
   2600  maybeEmitIdGuard(id);
   2601  writer.guardSpecificObject(objId, ns);
   2602 
   2603  ObjOperandId envId = writer.loadObject(env);
   2604  EmitLoadSlotResult(writer, envId, env, *prop);
   2605  writer.returnFromIC();
   2606 
   2607  trackAttached("GetProp.ModuleNamespace");
   2608  return AttachDecision::Attach;
   2609 }
   2610 
   2611 AttachDecision GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId,
   2612                                                      HandleId id) {
   2613  MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
   2614 
   2615  JSProtoKey protoKey;
   2616  switch (val_.type()) {
   2617    case ValueType::String:
   2618      if (id.isAtom(cx_->names().length)) {
   2619        // String length is special-cased, see js::GetProperty.
   2620        return AttachDecision::NoAction;
   2621      }
   2622      protoKey = JSProto_String;
   2623      break;
   2624    case ValueType::Int32:
   2625    case ValueType::Double:
   2626      protoKey = JSProto_Number;
   2627      break;
   2628    case ValueType::Boolean:
   2629      protoKey = JSProto_Boolean;
   2630      break;
   2631    case ValueType::Symbol:
   2632      protoKey = JSProto_Symbol;
   2633      break;
   2634    case ValueType::BigInt:
   2635      protoKey = JSProto_BigInt;
   2636      break;
   2637    case ValueType::Null:
   2638    case ValueType::Undefined:
   2639    case ValueType::Magic:
   2640      return AttachDecision::NoAction;
   2641    case ValueType::Object:
   2642    case ValueType::PrivateGCThing:
   2643      MOZ_CRASH("unexpected type");
   2644  }
   2645 
   2646  JSObject* proto = GlobalObject::getOrCreatePrototype(cx_, protoKey);
   2647  if (!proto) {
   2648    cx_->recoverFromOutOfMemory();
   2649    return AttachDecision::NoAction;
   2650  }
   2651 
   2652  Rooted<NativeObject*> holder(cx_);
   2653  Maybe<PropertyInfo> prop;
   2654  NativeGetPropKind kind =
   2655      CanAttachNativeGetProp(cx_, proto, id, holder.address(), &prop, pc_);
   2656  if (kind == NativeGetPropKind::None) {
   2657    return AttachDecision::NoAction;
   2658  }
   2659 
   2660  if (val_.isNumber()) {
   2661    writer.guardIsNumber(valId);
   2662  } else {
   2663    writer.guardNonDoubleType(valId, val_.type());
   2664  }
   2665  maybeEmitIdGuard(id);
   2666 
   2667  Rooted<NativeObject*> nproto(cx_, &proto->as<NativeObject>());
   2668  ObjOperandId protoId = writer.loadObject(nproto);
   2669 
   2670  switch (kind) {
   2671    case NativeGetPropKind::Missing: {
   2672      EmitMissingPropResult(writer, nproto, protoId);
   2673      writer.returnFromIC();
   2674 
   2675      trackAttached("GetProp.PrimitiveMissing");
   2676      return AttachDecision::Attach;
   2677    }
   2678    case NativeGetPropKind::Slot: {
   2679      emitLoadDataPropertyResult(nproto, holder, id, *prop, protoId);
   2680      writer.returnFromIC();
   2681 
   2682      trackAttached("GetProp.PrimitiveSlot");
   2683      return AttachDecision::Attach;
   2684    }
   2685    case NativeGetPropKind::ScriptedGetter:
   2686    case NativeGetPropKind::NativeGetter: {
   2687      emitCallGetterResult(kind, nproto, holder, id, *prop, protoId, valId);
   2688 
   2689      trackAttached("GetProp.PrimitiveGetter");
   2690      return AttachDecision::Attach;
   2691    }
   2692    case NativeGetPropKind::None:
   2693      break;
   2694  }
   2695 
   2696  MOZ_CRASH("Bad NativeGetPropKind");
   2697 }
   2698 
   2699 AttachDecision GetPropIRGenerator::tryAttachStringLength(ValOperandId valId,
   2700                                                         HandleId id) {
   2701  if (!val_.isString() || !id.isAtom(cx_->names().length)) {
   2702    return AttachDecision::NoAction;
   2703  }
   2704 
   2705  StringOperandId strId = writer.guardToString(valId);
   2706  maybeEmitIdGuard(id);
   2707  writer.loadStringLengthResult(strId);
   2708  writer.returnFromIC();
   2709 
   2710  trackAttached("GetProp.StringLength");
   2711  return AttachDecision::Attach;
   2712 }
   2713 
   2714 enum class AttachStringChar { No, Yes, Linearize, OutOfBounds };
   2715 
   2716 static AttachStringChar CanAttachStringChar(const Value& val,
   2717                                            const Value& idVal,
   2718                                            StringChar kind) {
   2719  if (!val.isString() || !idVal.isInt32()) {
   2720    return AttachStringChar::No;
   2721  }
   2722 
   2723  JSString* str = val.toString();
   2724  int32_t index = idVal.toInt32();
   2725 
   2726  if (index < 0 && kind == StringChar::At) {
   2727    static_assert(JSString::MAX_LENGTH <= INT32_MAX,
   2728                  "string length fits in int32");
   2729    index += int32_t(str->length());
   2730  }
   2731 
   2732  if (index < 0 || size_t(index) >= str->length()) {
   2733    return AttachStringChar::OutOfBounds;
   2734  }
   2735 
   2736  // This follows JSString::getChar and MacroAssembler::loadStringChar.
   2737  if (str->isRope()) {
   2738    JSRope* rope = &str->asRope();
   2739    if (size_t(index) < rope->leftChild()->length()) {
   2740      str = rope->leftChild();
   2741 
   2742      // MacroAssembler::loadStringChar doesn't support surrogate pairs which
   2743      // are split between the left and right child of a rope.
   2744      if (kind == StringChar::CodePointAt &&
   2745          size_t(index) + 1 == str->length() && str->isLinear()) {
   2746        // Linearize the string when the last character of the left child is a
   2747        // a lead surrogate.
   2748        char16_t ch = str->asLinear().latin1OrTwoByteChar(index);
   2749        if (unicode::IsLeadSurrogate(ch)) {
   2750          return AttachStringChar::Linearize;
   2751        }
   2752      }
   2753    } else {
   2754      str = rope->rightChild();
   2755    }
   2756  }
   2757 
   2758  if (!str->isLinear()) {
   2759    return AttachStringChar::Linearize;
   2760  }
   2761 
   2762  return AttachStringChar::Yes;
   2763 }
   2764 
   2765 static Int32OperandId EmitGuardToInt32Index(CacheIRWriter& writer,
   2766                                            const Value& index,
   2767                                            ValOperandId indexId) {
   2768  if (index.isInt32()) {
   2769    return writer.guardToInt32(indexId);
   2770  }
   2771  MOZ_ASSERT(index.isDouble());
   2772  return writer.guardToInt32Index(indexId);
   2773 }
   2774 
   2775 AttachDecision GetPropIRGenerator::tryAttachStringChar(ValOperandId valId,
   2776                                                       ValOperandId indexId) {
   2777  MOZ_ASSERT(idVal_.isInt32());
   2778 
   2779  auto attach = CanAttachStringChar(val_, idVal_, StringChar::CharAt);
   2780  if (attach == AttachStringChar::No) {
   2781    return AttachDecision::NoAction;
   2782  }
   2783 
   2784  // Can't attach for out-of-bounds access without guarding that indexed
   2785  // properties aren't present along the prototype chain of |String.prototype|.
   2786  if (attach == AttachStringChar::OutOfBounds) {
   2787    return AttachDecision::NoAction;
   2788  }
   2789 
   2790  StringOperandId strId = writer.guardToString(valId);
   2791  Int32OperandId int32IndexId = EmitGuardToInt32Index(writer, idVal_, indexId);
   2792  if (attach == AttachStringChar::Linearize) {
   2793    strId = writer.linearizeForCharAccess(strId, int32IndexId);
   2794  }
   2795  writer.loadStringCharResult(strId, int32IndexId, /* handleOOB = */ false);
   2796  writer.returnFromIC();
   2797 
   2798  trackAttached("GetProp.StringChar");
   2799  return AttachDecision::Attach;
   2800 }
   2801 
   2802 static bool ClassCanHaveExtraProperties(const JSClass* clasp) {
   2803  return clasp->getResolve() || clasp->getOpsLookupProperty() ||
   2804         clasp->getOpsGetProperty() || IsTypedArrayClass(clasp);
   2805 }
   2806 
   2807 enum class OwnProperty : bool { No, Yes };
   2808 enum class AllowIndexedReceiver : bool { No, Yes };
   2809 enum class AllowExtraReceiverProperties : bool { No, Yes };
   2810 
   2811 static bool CanAttachDenseElementHole(
   2812    NativeObject* obj, OwnProperty ownProp,
   2813    AllowIndexedReceiver allowIndexedReceiver = AllowIndexedReceiver::No,
   2814    AllowExtraReceiverProperties allowExtraReceiverProperties =
   2815        AllowExtraReceiverProperties::No) {
   2816  // Make sure the objects on the prototype don't have any indexed properties
   2817  // or that such properties can't appear without a shape change.
   2818  // Otherwise returning undefined for holes would obviously be incorrect,
   2819  // because we would have to lookup a property on the prototype instead.
   2820  do {
   2821    // The first two checks are also relevant to the receiver object.
   2822    if (allowIndexedReceiver == AllowIndexedReceiver::No && obj->isIndexed()) {
   2823      return false;
   2824    }
   2825    allowIndexedReceiver = AllowIndexedReceiver::No;
   2826 
   2827    if (allowExtraReceiverProperties == AllowExtraReceiverProperties::No &&
   2828        ClassCanHaveExtraProperties(obj->getClass())) {
   2829      return false;
   2830    }
   2831    allowExtraReceiverProperties = AllowExtraReceiverProperties::No;
   2832 
   2833    // Don't need to check prototype for OwnProperty checks
   2834    if (ownProp == OwnProperty::Yes) {
   2835      return true;
   2836    }
   2837 
   2838    JSObject* proto = obj->staticPrototype();
   2839    if (!proto) {
   2840      break;
   2841    }
   2842 
   2843    if (!proto->is<NativeObject>()) {
   2844      return false;
   2845    }
   2846 
   2847    // Make sure objects on the prototype don't have dense elements.
   2848    if (proto->as<NativeObject>().getDenseInitializedLength() != 0) {
   2849      return false;
   2850    }
   2851 
   2852    obj = &proto->as<NativeObject>();
   2853  } while (true);
   2854 
   2855  return true;
   2856 }
   2857 
   2858 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectArg(
   2859    HandleObject obj, ObjOperandId objId, uint32_t index,
   2860    Int32OperandId indexId) {
   2861  if (!obj->is<ArgumentsObject>()) {
   2862    return AttachDecision::NoAction;
   2863  }
   2864  auto* args = &obj->as<ArgumentsObject>();
   2865 
   2866  // No elements must have been overridden or deleted.
   2867  if (args->hasOverriddenElement()) {
   2868    return AttachDecision::NoAction;
   2869  }
   2870 
   2871  // Check bounds.
   2872  if (index >= args->initialLength()) {
   2873    return AttachDecision::NoAction;
   2874  }
   2875 
   2876  AssertArgumentsCustomDataProp(args, PropertyKey::Int(index));
   2877 
   2878  // And finally also check that the argument isn't forwarded.
   2879  if (args->argIsForwarded(index)) {
   2880    return AttachDecision::NoAction;
   2881  }
   2882 
   2883  if (args->is<MappedArgumentsObject>()) {
   2884    writer.guardClass(objId, GuardClassKind::MappedArguments);
   2885  } else {
   2886    MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   2887    writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   2888  }
   2889 
   2890  writer.loadArgumentsObjectArgResult(objId, indexId);
   2891  writer.returnFromIC();
   2892 
   2893  trackAttached("GetProp.ArgumentsObjectArg");
   2894  return AttachDecision::Attach;
   2895 }
   2896 
   2897 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
   2898    HandleObject obj, ObjOperandId objId, uint32_t index,
   2899    Int32OperandId indexId) {
   2900  if (!obj->is<ArgumentsObject>()) {
   2901    return AttachDecision::NoAction;
   2902  }
   2903  auto* args = &obj->as<ArgumentsObject>();
   2904 
   2905  // No elements must have been overridden or deleted.
   2906  if (args->hasOverriddenElement()) {
   2907    return AttachDecision::NoAction;
   2908  }
   2909 
   2910  // And also check that the argument isn't forwarded.
   2911  if (index < args->initialLength() && args->argIsForwarded(index)) {
   2912    return AttachDecision::NoAction;
   2913  }
   2914 
   2915  if (!CanAttachDenseElementHole(args, OwnProperty::No,
   2916                                 AllowIndexedReceiver::Yes,
   2917                                 AllowExtraReceiverProperties::Yes)) {
   2918    return AttachDecision::NoAction;
   2919  }
   2920 
   2921  // We don't need to guard on the shape, because we check if any element is
   2922  // overridden. Elements are marked as overridden iff any element is defined,
   2923  // irrespective of whether the element is in-bounds or out-of-bounds. So when
   2924  // that flag isn't set, we can guarantee that the arguments object doesn't
   2925  // have any additional own elements.
   2926 
   2927  if (args->is<MappedArgumentsObject>()) {
   2928    writer.guardClass(objId, GuardClassKind::MappedArguments);
   2929  } else {
   2930    MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   2931    writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   2932  }
   2933 
   2934  GeneratePrototypeHoleGuards(writer, args, objId,
   2935                              /* alwaysGuardFirstProto = */ true);
   2936 
   2937  writer.loadArgumentsObjectArgHoleResult(objId, indexId);
   2938  writer.returnFromIC();
   2939 
   2940  trackAttached("GetProp.ArgumentsObjectArgHole");
   2941  return AttachDecision::Attach;
   2942 }
   2943 
   2944 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectCallee(
   2945    HandleObject obj, ObjOperandId objId, HandleId id) {
   2946  // Only mapped arguments objects have a `callee` property.
   2947  if (!obj->is<MappedArgumentsObject>()) {
   2948    return AttachDecision::NoAction;
   2949  }
   2950 
   2951  if (!id.isAtom(cx_->names().callee)) {
   2952    return AttachDecision::NoAction;
   2953  }
   2954 
   2955  // The callee must not have been overridden or deleted.
   2956  MappedArgumentsObject* args = &obj->as<MappedArgumentsObject>();
   2957  if (args->hasOverriddenCallee()) {
   2958    return AttachDecision::NoAction;
   2959  }
   2960 
   2961  AssertArgumentsCustomDataProp(args, id);
   2962 
   2963  maybeEmitIdGuard(id);
   2964  writer.guardClass(objId, GuardClassKind::MappedArguments);
   2965 
   2966  uint32_t flags = ArgumentsObject::CALLEE_OVERRIDDEN_BIT;
   2967  writer.guardArgumentsObjectFlags(objId, flags);
   2968 
   2969  writer.loadFixedSlotResult(objId,
   2970                             MappedArgumentsObject::getCalleeSlotOffset());
   2971  writer.returnFromIC();
   2972 
   2973  trackAttached("GetProp.ArgumentsObjectCallee");
   2974  return AttachDecision::Attach;
   2975 }
   2976 
   2977 AttachDecision GetPropIRGenerator::tryAttachDenseElement(
   2978    HandleObject obj, ObjOperandId objId, uint32_t index,
   2979    Int32OperandId indexId) {
   2980  if (!obj->is<NativeObject>()) {
   2981    return AttachDecision::NoAction;
   2982  }
   2983 
   2984  NativeObject* nobj = &obj->as<NativeObject>();
   2985  if (!nobj->containsDenseElement(index)) {
   2986    return AttachDecision::NoAction;
   2987  }
   2988 
   2989  if (mode_ == ICState::Mode::Megamorphic) {
   2990    writer.guardIsNativeObject(objId);
   2991  } else {
   2992    TestMatchingNativeReceiver(writer, nobj, objId);
   2993  }
   2994  bool expectPackedElements = nobj->denseElementsArePacked();
   2995  writer.loadDenseElementResult(objId, indexId, expectPackedElements);
   2996  writer.returnFromIC();
   2997 
   2998  trackAttached("GetProp.DenseElement");
   2999  return AttachDecision::Attach;
   3000 }
   3001 
   3002 AttachDecision GetPropIRGenerator::tryAttachDenseElementHole(
   3003    HandleObject obj, ObjOperandId objId, uint32_t index,
   3004    Int32OperandId indexId) {
   3005  if (!obj->is<NativeObject>()) {
   3006    return AttachDecision::NoAction;
   3007  }
   3008 
   3009  NativeObject* nobj = &obj->as<NativeObject>();
   3010  if (nobj->containsDenseElement(index)) {
   3011    return AttachDecision::NoAction;
   3012  }
   3013  if (!CanAttachDenseElementHole(nobj, OwnProperty::No)) {
   3014    return AttachDecision::NoAction;
   3015  }
   3016 
   3017  // Guard on the shape, to prevent non-dense elements from appearing.
   3018  TestMatchingNativeReceiver(writer, nobj, objId);
   3019  GeneratePrototypeHoleGuards(writer, nobj, objId,
   3020                              /* alwaysGuardFirstProto = */ false);
   3021  writer.loadDenseElementHoleResult(objId, indexId);
   3022  writer.returnFromIC();
   3023 
   3024  trackAttached("GetProp.DenseElementHole");
   3025  return AttachDecision::Attach;
   3026 }
   3027 
   3028 AttachDecision GetPropIRGenerator::tryAttachSparseElement(
   3029    HandleObject obj, ObjOperandId objId, uint32_t index,
   3030    Int32OperandId indexId) {
   3031  if (!obj->is<NativeObject>()) {
   3032    return AttachDecision::NoAction;
   3033  }
   3034  NativeObject* nobj = &obj->as<NativeObject>();
   3035 
   3036  // Stub doesn't handle negative indices.
   3037  if (index > INT32_MAX) {
   3038    return AttachDecision::NoAction;
   3039  }
   3040 
   3041  // The object must have sparse elements.
   3042  if (!nobj->isIndexed()) {
   3043    return AttachDecision::NoAction;
   3044  }
   3045 
   3046  // The index must not be for a dense element.
   3047  if (nobj->containsDenseElement(index)) {
   3048    return AttachDecision::NoAction;
   3049  }
   3050 
   3051  // Only handle ArrayObject and PlainObject in this stub.
   3052  if (!nobj->is<ArrayObject>() && !nobj->is<PlainObject>()) {
   3053    return AttachDecision::NoAction;
   3054  }
   3055 
   3056  // GetSparseElementHelper assumes that the target and the receiver
   3057  // are the same.
   3058  if (isSuper()) {
   3059    return AttachDecision::NoAction;
   3060  }
   3061 
   3062  // Here, we ensure that the prototype chain does not define any sparse
   3063  // indexed properties on the shape lineage. This allows us to guard on
   3064  // the shapes up the prototype chain to ensure that no indexed properties
   3065  // exist outside of the dense elements.
   3066  //
   3067  // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
   3068  // as well as ensure that no prototypes contain dense elements, allowing
   3069  // us to perform a pure shape-search for out-of-bounds integer-indexed
   3070  // properties on the receiver object.
   3071  if (PrototypeMayHaveIndexedProperties(nobj)) {
   3072    return AttachDecision::NoAction;
   3073  }
   3074 
   3075  // Ensure that obj is an ArrayObject or PlainObject.
   3076  if (nobj->is<ArrayObject>()) {
   3077    writer.guardClass(objId, GuardClassKind::Array);
   3078  } else {
   3079    MOZ_ASSERT(nobj->is<PlainObject>());
   3080    writer.guardClass(objId, GuardClassKind::PlainObject);
   3081  }
   3082 
   3083  // The helper we are going to call only applies to non-dense elements.
   3084  writer.guardIndexIsNotDenseElement(objId, indexId);
   3085 
   3086  // Ensures we are able to efficiently able to map to an integral jsid.
   3087  writer.guardInt32IsNonNegative(indexId);
   3088 
   3089  // Shape guard the prototype chain to avoid shadowing indexes from appearing.
   3090  // The helper function also ensures that the index does not appear within the
   3091  // dense element set of the prototypes.
   3092  GeneratePrototypeHoleGuards(writer, nobj, objId,
   3093                              /* alwaysGuardFirstProto = */ true);
   3094 
   3095  // At this point, we are guaranteed that the indexed property will not
   3096  // be found on one of the prototypes. We are assured that we only have
   3097  // to check that the receiving object has the property.
   3098 
   3099  writer.callGetSparseElementResult(objId, indexId);
   3100  writer.returnFromIC();
   3101 
   3102  trackAttached("GetProp.SparseElement");
   3103  return AttachDecision::Attach;
   3104 }
   3105 
   3106 // For Uint32Array we let the stub return an Int32 if we have not seen a
   3107 // double, to allow better codegen in Warp while avoiding bailout loops.
   3108 static bool ForceDoubleForUint32Array(TypedArrayObject* tarr, uint64_t index) {
   3109  MOZ_ASSERT(index < tarr->length().valueOr(0));
   3110 
   3111  if (tarr->type() != Scalar::Type::Uint32) {
   3112    // Return value is only relevant for Uint32Array.
   3113    return false;
   3114  }
   3115 
   3116  Value res;
   3117  MOZ_ALWAYS_TRUE(tarr->getElementPure(index, &res));
   3118  MOZ_ASSERT(res.isNumber());
   3119  return res.isDouble();
   3120 }
   3121 
   3122 static ArrayBufferViewKind ToArrayBufferViewKind(const TypedArrayObject* obj) {
   3123  if (obj->is<FixedLengthTypedArrayObject>()) {
   3124    return ArrayBufferViewKind::FixedLength;
   3125  }
   3126 
   3127  if (obj->is<ImmutableTypedArrayObject>()) {
   3128    return ArrayBufferViewKind::Immutable;
   3129  }
   3130 
   3131  MOZ_ASSERT(obj->is<ResizableTypedArrayObject>());
   3132  return ArrayBufferViewKind::Resizable;
   3133 }
   3134 
   3135 static ArrayBufferViewKind ToArrayBufferViewKind(const DataViewObject* obj) {
   3136  if (obj->is<FixedLengthDataViewObject>()) {
   3137    return ArrayBufferViewKind::FixedLength;
   3138  }
   3139 
   3140  if (obj->is<ImmutableDataViewObject>()) {
   3141    return ArrayBufferViewKind::Immutable;
   3142  }
   3143 
   3144  MOZ_ASSERT(obj->is<ResizableDataViewObject>());
   3145  return ArrayBufferViewKind::Resizable;
   3146 }
   3147 
   3148 AttachDecision GetPropIRGenerator::tryAttachTypedArrayElement(
   3149    HandleObject obj, ObjOperandId objId) {
   3150  if (!obj->is<TypedArrayObject>()) {
   3151    return AttachDecision::NoAction;
   3152  }
   3153 
   3154  if (!idVal_.isNumber()) {
   3155    return AttachDecision::NoAction;
   3156  }
   3157 
   3158  auto* tarr = &obj->as<TypedArrayObject>();
   3159 
   3160  bool handleOOB = false;
   3161  int64_t indexInt64;
   3162  if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 ||
   3163      uint64_t(indexInt64) >= tarr->length().valueOr(0)) {
   3164    handleOOB = true;
   3165  }
   3166 
   3167  // If the number is not representable as an integer the result will be
   3168  // |undefined| so we leave |forceDoubleForUint32| as false.
   3169  bool forceDoubleForUint32 = false;
   3170  if (!handleOOB) {
   3171    uint64_t index = uint64_t(indexInt64);
   3172    forceDoubleForUint32 = ForceDoubleForUint32Array(tarr, index);
   3173  }
   3174 
   3175  writer.guardShapeForClass(objId, tarr->shape());
   3176 
   3177  ValOperandId keyId = getElemKeyValueId();
   3178  IntPtrOperandId intPtrIndexId = guardToIntPtrIndex(idVal_, keyId, handleOOB);
   3179 
   3180  auto viewKind = ToArrayBufferViewKind(tarr);
   3181  writer.loadTypedArrayElementResult(objId, intPtrIndexId, tarr->type(),
   3182                                     handleOOB, forceDoubleForUint32, viewKind);
   3183  writer.returnFromIC();
   3184 
   3185  trackAttached("GetProp.TypedElement");
   3186  return AttachDecision::Attach;
   3187 }
   3188 
   3189 AttachDecision GetPropIRGenerator::tryAttachGenericElement(
   3190    HandleObject obj, ObjOperandId objId, uint32_t index,
   3191    Int32OperandId indexId, ValOperandId receiverId) {
   3192  if (!obj->is<NativeObject>()) {
   3193    return AttachDecision::NoAction;
   3194  }
   3195 
   3196 #ifdef JS_CODEGEN_X86
   3197  if (isSuper()) {
   3198    // There aren't enough registers available on x86.
   3199    return AttachDecision::NoAction;
   3200  }
   3201 #endif
   3202 
   3203  // To allow other types to attach in the non-megamorphic case we test the
   3204  // specific matching native receiver; however, once megamorphic we can attach
   3205  // for any native
   3206  if (mode_ == ICState::Mode::Megamorphic) {
   3207    writer.guardIsNativeObject(objId);
   3208  } else {
   3209    NativeObject* nobj = &obj->as<NativeObject>();
   3210    TestMatchingNativeReceiver(writer, nobj, objId);
   3211  }
   3212  writer.guardIndexIsNotDenseElement(objId, indexId);
   3213  if (isSuper()) {
   3214    writer.callNativeGetElementSuperResult(objId, indexId, receiverId);
   3215  } else {
   3216    writer.callNativeGetElementResult(objId, indexId);
   3217  }
   3218  writer.returnFromIC();
   3219 
   3220  trackAttached(mode_ == ICState::Mode::Megamorphic
   3221                    ? "GenericElementMegamorphic"
   3222                    : "GenericElement");
   3223  return AttachDecision::Attach;
   3224 }
   3225 
   3226 AttachDecision GetPropIRGenerator::tryAttachProxyElement(HandleObject obj,
   3227                                                         ObjOperandId objId) {
   3228  if (!obj->is<ProxyObject>()) {
   3229    return AttachDecision::NoAction;
   3230  }
   3231 
   3232  // The proxy stubs don't currently support |super| access.
   3233  if (isSuper()) {
   3234    return AttachDecision::NoAction;
   3235  }
   3236 
   3237 #ifdef JS_PUNBOX64
   3238  auto proxy = obj.as<ProxyObject>();
   3239  if (proxy->handler()->isScripted()) {
   3240    TRY_ATTACH(tryAttachScriptedProxy(proxy, objId, JS::VoidHandlePropertyKey));
   3241  }
   3242 #endif
   3243 
   3244  writer.guardIsProxy(objId);
   3245 
   3246  // We are not guarding against DOM proxies here, because there is no other
   3247  // specialized DOM IC we could attach.
   3248  // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
   3249  // but for GetElem we prefer to attach a stub that can handle any Value
   3250  // so we don't attach a new stub for every id.
   3251  MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
   3252  MOZ_ASSERT(!isSuper());
   3253  writer.proxyGetByValueResult(objId, getElemKeyValueId());
   3254  writer.returnFromIC();
   3255 
   3256  trackAttached("GetProp.ProxyElement");
   3257  return AttachDecision::Attach;
   3258 }
   3259 
   3260 void GetPropIRGenerator::trackAttached(const char* name) {
   3261  stubName_ = name ? name : "NotAttached";
   3262 #ifdef JS_CACHEIR_SPEW
   3263  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   3264    sp.valueProperty("base", val_);
   3265    sp.valueProperty("property", idVal_);
   3266  }
   3267 #endif
   3268 }
   3269 
   3270 void IRGenerator::emitIdGuard(ValOperandId valId, const Value& idVal, jsid id) {
   3271  if (id.isSymbol()) {
   3272    MOZ_ASSERT(idVal.toSymbol() == id.toSymbol());
   3273    SymbolOperandId symId = writer.guardToSymbol(valId);
   3274    writer.guardSpecificSymbol(symId, id.toSymbol());
   3275    return;
   3276  }
   3277 
   3278  MOZ_ASSERT(id.isAtom());
   3279  switch (idVal.type()) {
   3280    case ValueType::String: {
   3281      StringOperandId strId = writer.guardToString(valId);
   3282      writer.guardSpecificAtom(strId, id.toAtom());
   3283      break;
   3284    }
   3285    case ValueType::Null:
   3286      MOZ_ASSERT(id.isAtom(cx_->names().null));
   3287      writer.guardIsNull(valId);
   3288      break;
   3289    case ValueType::Undefined:
   3290      MOZ_ASSERT(id.isAtom(cx_->names().undefined));
   3291      writer.guardIsUndefined(valId);
   3292      break;
   3293    case ValueType::Boolean:
   3294      MOZ_ASSERT(id.isAtom(cx_->names().true_) ||
   3295                 id.isAtom(cx_->names().false_));
   3296      writer.guardSpecificValue(valId, idVal);
   3297      break;
   3298    case ValueType::Int32:
   3299    case ValueType::Double:
   3300      MOZ_ASSERT(!IsNumberIndex(idVal));
   3301      writer.guardSpecificValue(valId, idVal);
   3302      break;
   3303    default:
   3304      MOZ_CRASH("Unexpected type in emitIdGuard");
   3305  }
   3306 }
   3307 
   3308 void GetPropIRGenerator::maybeEmitIdGuard(jsid id) {
   3309  if (cacheKind_ == CacheKind::GetProp ||
   3310      cacheKind_ == CacheKind::GetPropSuper) {
   3311    // Constant PropertyName, no guards necessary.
   3312    MOZ_ASSERT(&idVal_.toString()->asAtom() == id.toAtom());
   3313    return;
   3314  }
   3315 
   3316  MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
   3317             cacheKind_ == CacheKind::GetElemSuper);
   3318  emitIdGuard(getElemKeyValueId(), idVal_, id);
   3319 }
   3320 
   3321 void SetPropIRGenerator::maybeEmitIdGuard(jsid id) {
   3322  if (cacheKind_ == CacheKind::SetProp) {
   3323    // Constant PropertyName, no guards necessary.
   3324    MOZ_ASSERT(&idVal_.toString()->asAtom() == id.toAtom());
   3325    return;
   3326  }
   3327 
   3328  MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   3329  emitIdGuard(setElemKeyValueId(), idVal_, id);
   3330 }
   3331 
   3332 GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script,
   3333                                       jsbytecode* pc, ICState state,
   3334                                       HandleObject env,
   3335                                       Handle<PropertyName*> name)
   3336    : IRGenerator(cx, script, pc, CacheKind::GetName, state),
   3337      env_(env),
   3338      name_(name) {}
   3339 
   3340 AttachDecision GetNameIRGenerator::tryAttachStub() {
   3341  MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
   3342 
   3343  AutoAssertNoPendingException aanpe(cx_);
   3344 
   3345  ObjOperandId envId(writer.setInputOperandId(0));
   3346  RootedId id(cx_, NameToId(name_));
   3347 
   3348  TRY_ATTACH(tryAttachGlobalNameValue(envId, id));
   3349  TRY_ATTACH(tryAttachGlobalNameGetter(envId, id));
   3350  TRY_ATTACH(tryAttachEnvironmentName(envId, id));
   3351 
   3352  trackAttached(IRGenerator::NotAttached);
   3353  return AttachDecision::NoAction;
   3354 }
   3355 
   3356 static bool CanAttachGlobalName(JSContext* cx,
   3357                                GlobalLexicalEnvironmentObject* globalLexical,
   3358                                PropertyKey id, NativeObject** holder,
   3359                                Maybe<PropertyInfo>* prop) {
   3360  // The property must be found, and it must be found as a normal data property.
   3361  NativeObject* current = globalLexical;
   3362  while (true) {
   3363    *prop = current->lookup(cx, id);
   3364    if (prop->isSome()) {
   3365      break;
   3366    }
   3367 
   3368    if (current == globalLexical) {
   3369      current = &globalLexical->global();
   3370    } else {
   3371      // In the browser the global prototype chain should be immutable.
   3372      if (!current->staticPrototypeIsImmutable()) {
   3373        return false;
   3374      }
   3375 
   3376      JSObject* proto = current->staticPrototype();
   3377      if (!proto || !proto->is<NativeObject>()) {
   3378        return false;
   3379      }
   3380 
   3381      current = &proto->as<NativeObject>();
   3382    }
   3383  }
   3384 
   3385  *holder = current;
   3386  return true;
   3387 }
   3388 
   3389 AttachDecision GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId,
   3390                                                            HandleId id) {
   3391  if (!IsGlobalOp(JSOp(*pc_))) {
   3392    return AttachDecision::NoAction;
   3393  }
   3394  MOZ_ASSERT(!script_->hasNonSyntacticScope());
   3395 
   3396  auto* globalLexical = &env_->as<GlobalLexicalEnvironmentObject>();
   3397 
   3398  NativeObject* holder = nullptr;
   3399  Maybe<PropertyInfo> prop;
   3400  if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) {
   3401    return AttachDecision::NoAction;
   3402  }
   3403 
   3404  // The property must be found, and it must be found as a normal data property.
   3405  if (!prop->isDataProperty()) {
   3406    return AttachDecision::NoAction;
   3407  }
   3408 
   3409  // This might still be an uninitialized lexical.
   3410  if (holder->getSlot(prop->slot()).isMagic()) {
   3411    return AttachDecision::NoAction;
   3412  }
   3413 
   3414  if (holder == globalLexical) {
   3415    // There is no need to guard on the shape. Lexical bindings are
   3416    // non-configurable, and this stub cannot be shared across globals.
   3417    ObjectFuse* objFuse = nullptr;
   3418    if (canOptimizeConstantDataProperty(holder, *prop, &objFuse)) {
   3419      emitConstantDataPropertyResult(holder, objId, id, *prop, objFuse);
   3420    } else {
   3421      size_t dynamicSlotOffset =
   3422          holder->dynamicSlotIndex(prop->slot()) * sizeof(Value);
   3423      writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
   3424    }
   3425  } else if (holder == &globalLexical->global()) {
   3426    MOZ_ASSERT(globalLexical->global().isGenerationCountedGlobal());
   3427    ObjectFuse* objFuse = nullptr;
   3428    if (canOptimizeConstantDataProperty(holder, *prop, &objFuse)) {
   3429      ObjOperandId holderId = writer.loadObject(holder);
   3430      emitConstantDataPropertyResult(holder, holderId, id, *prop, objFuse);
   3431    } else {
   3432      writer.guardGlobalGeneration(
   3433          globalLexical->global().generationCount(),
   3434          globalLexical->global().addressOfGenerationCount());
   3435      ObjOperandId holderId = writer.loadObject(holder);
   3436 #ifdef DEBUG
   3437      writer.assertPropertyLookup(holderId, id, prop->slot());
   3438 #endif
   3439      EmitLoadSlotResult(writer, holderId, holder, *prop);
   3440    }
   3441  } else {
   3442    // Check the prototype chain from the global to the holder
   3443    // prototype. Ignore the global lexical scope as it doesn't figure
   3444    // into the prototype chain. We guard on the global lexical
   3445    // scope's shape independently.
   3446    if (!IsCacheableGetPropSlot(&globalLexical->global(), holder, *prop)) {
   3447      return AttachDecision::NoAction;
   3448    }
   3449 
   3450    // Shape guard for global lexical.
   3451    writer.guardShape(objId, globalLexical->shape());
   3452 
   3453    // Guard on the shape of the GlobalObject.
   3454    ObjOperandId globalId = writer.loadObject(&globalLexical->global());
   3455    writer.guardShape(globalId, globalLexical->global().shape());
   3456 
   3457    // Shape guard holder.
   3458    ObjOperandId holderId = writer.loadObject(holder);
   3459    writer.guardShape(holderId, holder->shape());
   3460 
   3461    EmitLoadSlotResult(writer, holderId, holder, *prop);
   3462  }
   3463 
   3464  writer.returnFromIC();
   3465 
   3466  trackAttached("GetName.GlobalNameValue");
   3467  return AttachDecision::Attach;
   3468 }
   3469 
   3470 AttachDecision GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId,
   3471                                                             HandleId id) {
   3472  if (!IsGlobalOp(JSOp(*pc_))) {
   3473    return AttachDecision::NoAction;
   3474  }
   3475  MOZ_ASSERT(!script_->hasNonSyntacticScope());
   3476 
   3477  Handle<GlobalLexicalEnvironmentObject*> globalLexical =
   3478      env_.as<GlobalLexicalEnvironmentObject>();
   3479  MOZ_ASSERT(globalLexical->isGlobal());
   3480 
   3481  NativeObject* holder = nullptr;
   3482  Maybe<PropertyInfo> prop;
   3483  if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) {
   3484    return AttachDecision::NoAction;
   3485  }
   3486 
   3487  if (holder == globalLexical) {
   3488    return AttachDecision::NoAction;
   3489  }
   3490 
   3491  GlobalObject* global = &globalLexical->global();
   3492 
   3493  NativeGetPropKind kind = IsCacheableGetPropCall(global, holder, *prop, pc_);
   3494  if (kind != NativeGetPropKind::NativeGetter &&
   3495      kind != NativeGetPropKind::ScriptedGetter) {
   3496    return AttachDecision::NoAction;
   3497  }
   3498 
   3499  bool needsWindowProxy =
   3500      IsWindow(global) && GetterNeedsWindowProxyThis(holder, *prop);
   3501 
   3502  ObjOperandId globalId;
   3503  ObjectFuse* objFuse = nullptr;
   3504  if (holder == global &&
   3505      canOptimizeConstantAccessorProperty(global, *prop, &objFuse)) {
   3506    globalId = writer.loadObject(global);
   3507    emitGuardConstantAccessorProperty(global, globalId, id, *prop, objFuse);
   3508  } else {
   3509    // Shape guard for global lexical.
   3510    writer.guardShape(objId, globalLexical->shape());
   3511 
   3512    // Guard on the shape of the GlobalObject.
   3513    globalId = writer.loadEnclosingEnvironment(objId);
   3514    writer.guardShape(globalId, global->shape());
   3515 
   3516    if (holder != global) {
   3517      // Shape guard holder.
   3518      ObjOperandId holderId = writer.loadObject(holder);
   3519      writer.guardShape(holderId, holder->shape());
   3520      emitGuardGetterSetterSlot(holder, *prop, holderId, AccessorKind::Getter,
   3521                                /* holderIsConstant = */ true);
   3522    } else {
   3523      // Note: pass true for |holderIsConstant| because the holder must be the
   3524      // current global object.
   3525      emitGuardGetterSetterSlot(holder, *prop, globalId, AccessorKind::Getter,
   3526                                /* holderIsConstant = */ true);
   3527    }
   3528  }
   3529 
   3530  if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, global, holder, *prop,
   3531                               mode_)) {
   3532    // The global shape guard above ensures the instance JSClass is correct.
   3533    MOZ_ASSERT(!needsWindowProxy);
   3534    emitCallDOMGetterResultNoGuards(holder, *prop, globalId);
   3535    trackAttached("GetName.GlobalNameDOMGetter");
   3536  } else {
   3537    ObjOperandId receiverObjId;
   3538    if (needsWindowProxy) {
   3539      MOZ_ASSERT(cx_->global()->maybeWindowProxy());
   3540      receiverObjId = writer.loadObject(cx_->global()->maybeWindowProxy());
   3541    } else {
   3542      receiverObjId = globalId;
   3543    }
   3544    ValOperandId receiverId = writer.boxObject(receiverObjId);
   3545    emitCallGetterResultNoGuards(kind, global, holder, *prop, receiverId);
   3546    trackAttached("GetName.GlobalNameGetter");
   3547  }
   3548 
   3549  return AttachDecision::Attach;
   3550 }
   3551 
   3552 static bool NeedEnvironmentShapeGuard(JSContext* cx, JSObject* envObj) {
   3553  // We can skip a guard on the call object if the script's bindings are
   3554  // guaranteed to be immutable (and thus cannot introduce shadowing variables).
   3555  // If the function is a relazified self-hosted function it has no BaseScript
   3556  // and we pessimistically create the guard.
   3557  if (envObj->is<CallObject>()) {
   3558    auto* callObj = &envObj->as<CallObject>();
   3559    JSFunction* fun = &callObj->callee();
   3560    return !fun->hasBaseScript() ||
   3561           fun->baseScript()->funHasExtensibleScope() ||
   3562           DebugEnvironments::hasDebugEnvironment(cx, *callObj);
   3563  }
   3564 
   3565  // Similar to the call object case, we can also skip a guard if the lexical
   3566  // environment's bindings are immutable.
   3567  if (envObj->is<LexicalEnvironmentObject>()) {
   3568    return envObj->as<LexicalEnvironmentObject>().isExtensible();
   3569  }
   3570 
   3571  // Use a shape guard for all other environment objects.
   3572  return true;
   3573 }
   3574 
   3575 AttachDecision GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId,
   3576                                                            HandleId id) {
   3577  if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) {
   3578    return AttachDecision::NoAction;
   3579  }
   3580 
   3581  JSObject* env = env_;
   3582  Maybe<PropertyInfo> prop;
   3583  NativeObject* holder = nullptr;
   3584 
   3585  while (env) {
   3586    if (env->is<GlobalObject>()) {
   3587      prop = env->as<GlobalObject>().lookup(cx_, id);
   3588      if (prop.isSome()) {
   3589        break;
   3590      }
   3591      return AttachDecision::NoAction;
   3592    }
   3593 
   3594    if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>()) {
   3595      return AttachDecision::NoAction;
   3596    }
   3597 
   3598    // Check for an 'own' property on the env. There is no need to
   3599    // check the prototype as non-with scopes do not inherit properties
   3600    // from any prototype.
   3601    prop = env->as<NativeObject>().lookup(cx_, id);
   3602    if (prop.isSome()) {
   3603      break;
   3604    }
   3605 
   3606    env = env->enclosingEnvironment();
   3607  }
   3608 
   3609  holder = &env->as<NativeObject>();
   3610  if (!IsCacheableGetPropSlot(holder, holder, *prop)) {
   3611    return AttachDecision::NoAction;
   3612  }
   3613  if (holder->getSlot(prop->slot()).isMagic()) {
   3614    MOZ_ASSERT(holder->is<EnvironmentObject>());
   3615    return AttachDecision::NoAction;
   3616  }
   3617 
   3618  ObjOperandId lastObjId = objId;
   3619  env = env_;
   3620  while (env) {
   3621    if (NeedEnvironmentShapeGuard(cx_, env)) {
   3622      writer.guardShape(lastObjId, env->shape());
   3623    }
   3624 
   3625    if (env == holder) {
   3626      break;
   3627    }
   3628 
   3629    lastObjId = writer.loadEnclosingEnvironment(lastObjId);
   3630    env = env->enclosingEnvironment();
   3631  }
   3632 
   3633  ValOperandId resId = EmitLoadSlot(writer, holder, lastObjId, prop->slot());
   3634  if (holder->is<EnvironmentObject>()) {
   3635    writer.guardIsNotUninitializedLexical(resId);
   3636  }
   3637  writer.loadOperandResult(resId);
   3638  writer.returnFromIC();
   3639 
   3640  trackAttached("GetName.EnvironmentName");
   3641  return AttachDecision::Attach;
   3642 }
   3643 
   3644 void GetNameIRGenerator::trackAttached(const char* name) {
   3645  stubName_ = name ? name : "NotAttached";
   3646 #ifdef JS_CACHEIR_SPEW
   3647  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   3648    sp.valueProperty("base", ObjectValue(*env_));
   3649    sp.valueProperty("property", StringValue(name_));
   3650  }
   3651 #endif
   3652 }
   3653 
   3654 BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script,
   3655                                         jsbytecode* pc, ICState state,
   3656                                         HandleObject env,
   3657                                         Handle<PropertyName*> name)
   3658    : IRGenerator(cx, script, pc, CacheKind::BindName, state),
   3659      env_(env),
   3660      name_(name) {}
   3661 
   3662 AttachDecision BindNameIRGenerator::tryAttachStub() {
   3663  MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
   3664 
   3665  AutoAssertNoPendingException aanpe(cx_);
   3666 
   3667  ObjOperandId envId(writer.setInputOperandId(0));
   3668  RootedId id(cx_, NameToId(name_));
   3669 
   3670  TRY_ATTACH(tryAttachGlobalName(envId, id));
   3671  TRY_ATTACH(tryAttachEnvironmentName(envId, id));
   3672 
   3673  trackAttached(IRGenerator::NotAttached);
   3674  return AttachDecision::NoAction;
   3675 }
   3676 
   3677 AttachDecision BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId,
   3678                                                        HandleId id) {
   3679  if (!IsGlobalOp(JSOp(*pc_))) {
   3680    return AttachDecision::NoAction;
   3681  }
   3682  MOZ_ASSERT(!script_->hasNonSyntacticScope());
   3683 
   3684  Handle<GlobalLexicalEnvironmentObject*> globalLexical =
   3685      env_.as<GlobalLexicalEnvironmentObject>();
   3686  MOZ_ASSERT(globalLexical->isGlobal());
   3687 
   3688  JSObject* result = nullptr;
   3689  if (Maybe<PropertyInfo> prop = globalLexical->lookup(cx_, id)) {
   3690    // If this is an uninitialized lexical or a const, we need to return a
   3691    // RuntimeLexicalErrorObject.
   3692    if (globalLexical->getSlot(prop->slot()).isMagic() || !prop->writable()) {
   3693      return AttachDecision::NoAction;
   3694    }
   3695    result = globalLexical;
   3696  } else {
   3697    result = &globalLexical->global();
   3698  }
   3699 
   3700  if (result == globalLexical) {
   3701    // Lexical bindings are non-configurable so we can just return the
   3702    // global lexical.
   3703    writer.loadObjectResult(objId);
   3704  } else {
   3705    // If the property exists on the global and is non-configurable, it cannot
   3706    // be shadowed by the lexical scope so we can just return the global without
   3707    // a shape guard.
   3708    Maybe<PropertyInfo> prop = result->as<GlobalObject>().lookup(cx_, id);
   3709    if (prop.isNothing() || prop->configurable()) {
   3710      writer.guardShape(objId, globalLexical->shape());
   3711    }
   3712    ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
   3713    writer.loadObjectResult(globalId);
   3714  }
   3715  writer.returnFromIC();
   3716 
   3717  trackAttached("BindName.GlobalName");
   3718  return AttachDecision::Attach;
   3719 }
   3720 
   3721 AttachDecision BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId,
   3722                                                             HandleId id) {
   3723  if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) {
   3724    return AttachDecision::NoAction;
   3725  }
   3726 
   3727  // JSOp::BindUnqualifiedName when writing to a dynamic environment binding.
   3728  // JSOp::BindName when reading from a dynamic environment binding.
   3729  bool unqualifiedLookup = JSOp(*pc_) == JSOp::BindUnqualifiedName;
   3730 
   3731  JSObject* env = env_;
   3732  Maybe<PropertyInfo> prop;
   3733  while (true) {
   3734    // Stop when we've reached the global object.
   3735    if (env->is<GlobalObject>()) {
   3736      break;
   3737    }
   3738 
   3739    if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>()) {
   3740      return AttachDecision::NoAction;
   3741    }
   3742 
   3743    // When we reach an unqualified variables object (like the global) we
   3744    // have to stop looking and return that object.
   3745    if (unqualifiedLookup && env->isUnqualifiedVarObj()) {
   3746      break;
   3747    }
   3748 
   3749    // Check for an 'own' property on the env. There is no need to
   3750    // check the prototype as non-with scopes do not inherit properties
   3751    // from any prototype.
   3752    prop = env->as<NativeObject>().lookup(cx_, id);
   3753    if (prop.isSome()) {
   3754      break;
   3755    }
   3756 
   3757    env = env->enclosingEnvironment();
   3758  }
   3759 
   3760  // If this is an uninitialized lexical or a const, we need to return a
   3761  // RuntimeLexicalErrorObject.
   3762  auto* holder = &env->as<NativeObject>();
   3763  if (prop.isSome() && holder->is<EnvironmentObject>()) {
   3764    // Uninitialized lexical binding.
   3765    if (holder->getSlot(prop->slot()).isMagic()) {
   3766      return AttachDecision::NoAction;
   3767    }
   3768 
   3769    // Attempt to write to a const binding.
   3770    if (unqualifiedLookup && !prop->writable()) {
   3771      return AttachDecision::NoAction;
   3772    }
   3773  }
   3774 
   3775  ObjOperandId lastObjId = objId;
   3776  env = env_;
   3777  while (env) {
   3778    if (NeedEnvironmentShapeGuard(cx_, env) && !env->is<GlobalObject>()) {
   3779      writer.guardShape(lastObjId, env->shape());
   3780    }
   3781 
   3782    if (env == holder) {
   3783      break;
   3784    }
   3785 
   3786    lastObjId = writer.loadEnclosingEnvironment(lastObjId);
   3787    env = env->enclosingEnvironment();
   3788  }
   3789 
   3790  if (prop.isSome() && holder->is<EnvironmentObject>()) {
   3791    ValOperandId valId = EmitLoadSlot(writer, holder, lastObjId, prop->slot());
   3792    writer.guardIsNotUninitializedLexical(valId);
   3793  }
   3794 
   3795  writer.loadObjectResult(lastObjId);
   3796  writer.returnFromIC();
   3797 
   3798  trackAttached("BindName.EnvironmentName");
   3799  return AttachDecision::Attach;
   3800 }
   3801 
   3802 void BindNameIRGenerator::trackAttached(const char* name) {
   3803  stubName_ = name ? name : "NotAttached";
   3804 #ifdef JS_CACHEIR_SPEW
   3805  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   3806    sp.valueProperty("base", ObjectValue(*env_));
   3807    sp.valueProperty("property", StringValue(name_));
   3808  }
   3809 #endif
   3810 }
   3811 
   3812 HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script,
   3813                                       jsbytecode* pc, ICState state,
   3814                                       CacheKind cacheKind, HandleValue idVal,
   3815                                       HandleValue val)
   3816    : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {}
   3817 
   3818 AttachDecision HasPropIRGenerator::tryAttachDense(HandleObject obj,
   3819                                                  ObjOperandId objId,
   3820                                                  uint32_t index,
   3821                                                  Int32OperandId indexId) {
   3822  if (!obj->is<NativeObject>()) {
   3823    return AttachDecision::NoAction;
   3824  }
   3825 
   3826  NativeObject* nobj = &obj->as<NativeObject>();
   3827  if (!nobj->containsDenseElement(index)) {
   3828    return AttachDecision::NoAction;
   3829  }
   3830 
   3831  if (mode_ == ICState::Mode::Megamorphic) {
   3832    writer.guardIsNativeObject(objId);
   3833  } else {
   3834    // Guard shape to ensure object class is NativeObject.
   3835    TestMatchingNativeReceiver(writer, nobj, objId);
   3836  }
   3837  writer.loadDenseElementExistsResult(objId, indexId);
   3838  writer.returnFromIC();
   3839 
   3840  trackAttached("HasProp.Dense");
   3841  return AttachDecision::Attach;
   3842 }
   3843 
   3844 AttachDecision HasPropIRGenerator::tryAttachDenseHole(HandleObject obj,
   3845                                                      ObjOperandId objId,
   3846                                                      uint32_t index,
   3847                                                      Int32OperandId indexId) {
   3848  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   3849  OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
   3850 
   3851  if (!obj->is<NativeObject>()) {
   3852    return AttachDecision::NoAction;
   3853  }
   3854 
   3855  NativeObject* nobj = &obj->as<NativeObject>();
   3856  if (nobj->containsDenseElement(index)) {
   3857    return AttachDecision::NoAction;
   3858  }
   3859  if (!CanAttachDenseElementHole(nobj, ownProp)) {
   3860    return AttachDecision::NoAction;
   3861  }
   3862 
   3863  // Guard shape to ensure class is NativeObject and to prevent non-dense
   3864  // elements being added. Also ensures prototype doesn't change if dynamic
   3865  // checks aren't emitted.
   3866  TestMatchingNativeReceiver(writer, nobj, objId);
   3867 
   3868  // Generate prototype guards if needed. This includes monitoring that
   3869  // properties were not added in the chain.
   3870  if (!hasOwn) {
   3871    GeneratePrototypeHoleGuards(writer, nobj, objId,
   3872                                /* alwaysGuardFirstProto = */ false);
   3873  }
   3874 
   3875  writer.loadDenseElementHoleExistsResult(objId, indexId);
   3876  writer.returnFromIC();
   3877 
   3878  trackAttached("HasProp.DenseHole");
   3879  return AttachDecision::Attach;
   3880 }
   3881 
   3882 AttachDecision HasPropIRGenerator::tryAttachSparse(HandleObject obj,
   3883                                                   ObjOperandId objId,
   3884                                                   Int32OperandId indexId) {
   3885  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   3886  OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
   3887 
   3888  if (!obj->is<NativeObject>()) {
   3889    return AttachDecision::NoAction;
   3890  }
   3891  auto* nobj = &obj->as<NativeObject>();
   3892 
   3893  if (!nobj->isIndexed()) {
   3894    return AttachDecision::NoAction;
   3895  }
   3896  if (!CanAttachDenseElementHole(nobj, ownProp, AllowIndexedReceiver::Yes)) {
   3897    return AttachDecision::NoAction;
   3898  }
   3899 
   3900  // Guard that this is a native object.
   3901  writer.guardIsNativeObject(objId);
   3902 
   3903  // Generate prototype guards if needed. This includes monitoring that
   3904  // properties were not added in the chain.
   3905  if (!hasOwn) {
   3906    GeneratePrototypeHoleGuards(writer, nobj, objId,
   3907                                /* alwaysGuardFirstProto = */ true);
   3908  }
   3909 
   3910  // Because of the prototype guard we know that the prototype chain
   3911  // does not include any dense or sparse (i.e indexed) properties.
   3912  writer.callObjectHasSparseElementResult(objId, indexId);
   3913  writer.returnFromIC();
   3914 
   3915  trackAttached("HasProp.Sparse");
   3916  return AttachDecision::Attach;
   3917 }
   3918 
   3919 AttachDecision HasPropIRGenerator::tryAttachArgumentsObjectArg(
   3920    HandleObject obj, ObjOperandId objId, Int32OperandId indexId) {
   3921  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   3922  OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
   3923 
   3924  if (!obj->is<ArgumentsObject>()) {
   3925    return AttachDecision::NoAction;
   3926  }
   3927  auto* args = &obj->as<ArgumentsObject>();
   3928 
   3929  // No elements must have been overridden or deleted.
   3930  if (args->hasOverriddenElement()) {
   3931    return AttachDecision::NoAction;
   3932  }
   3933 
   3934  if (!CanAttachDenseElementHole(args, ownProp, AllowIndexedReceiver::Yes,
   3935                                 AllowExtraReceiverProperties::Yes)) {
   3936    return AttachDecision::NoAction;
   3937  }
   3938 
   3939  if (args->is<MappedArgumentsObject>()) {
   3940    writer.guardClass(objId, GuardClassKind::MappedArguments);
   3941  } else {
   3942    MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   3943    writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   3944  }
   3945 
   3946  if (!hasOwn) {
   3947    GeneratePrototypeHoleGuards(writer, args, objId,
   3948                                /* alwaysGuardFirstProto = */ true);
   3949  }
   3950 
   3951  writer.loadArgumentsObjectArgExistsResult(objId, indexId);
   3952  writer.returnFromIC();
   3953 
   3954  trackAttached("HasProp.ArgumentsObjectArg");
   3955  return AttachDecision::Attach;
   3956 }
   3957 
   3958 AttachDecision HasPropIRGenerator::tryAttachNamedProp(HandleObject obj,
   3959                                                      ObjOperandId objId,
   3960                                                      HandleId key,
   3961                                                      ValOperandId keyId) {
   3962  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   3963 
   3964  Rooted<NativeObject*> holder(cx_);
   3965  PropertyResult prop;
   3966 
   3967  if (hasOwn) {
   3968    if (!LookupOwnPropertyPure(cx_, obj, key, &prop)) {
   3969      return AttachDecision::NoAction;
   3970    }
   3971 
   3972    holder.set(&obj->as<NativeObject>());
   3973  } else {
   3974    NativeObject* nHolder = nullptr;
   3975    if (!LookupPropertyPure(cx_, obj, key, &nHolder, &prop)) {
   3976      return AttachDecision::NoAction;
   3977    }
   3978    holder.set(nHolder);
   3979  }
   3980  if (prop.isNotFound()) {
   3981    return AttachDecision::NoAction;
   3982  }
   3983 
   3984  TRY_ATTACH(tryAttachSmallObjectVariableKey(obj, objId, key, keyId));
   3985  TRY_ATTACH(tryAttachMegamorphic(objId, keyId));
   3986  TRY_ATTACH(tryAttachNative(&obj->as<NativeObject>(), objId, key, keyId, prop,
   3987                             holder.get()));
   3988 
   3989  return AttachDecision::NoAction;
   3990 }
   3991 
   3992 AttachDecision HasPropIRGenerator::tryAttachSmallObjectVariableKey(
   3993    HandleObject obj, ObjOperandId objId, jsid key, ValOperandId keyId) {
   3994  MOZ_ASSERT(obj->is<NativeObject>());
   3995 
   3996  if (cacheKind_ != CacheKind::HasOwn) {
   3997    return AttachDecision::NoAction;
   3998  }
   3999 
   4000  if (mode_ != ICState::Mode::Megamorphic) {
   4001    return AttachDecision::NoAction;
   4002  }
   4003 
   4004  if (numOptimizedStubs_ != 0) {
   4005    return AttachDecision::NoAction;
   4006  }
   4007 
   4008  if (!idVal_.isString()) {
   4009    return AttachDecision::NoAction;
   4010  }
   4011 
   4012  if (!obj->as<NativeObject>().hasEmptyElements()) {
   4013    return AttachDecision::NoAction;
   4014  }
   4015 
   4016  if (ClassCanHaveExtraProperties(obj->getClass())) {
   4017    return AttachDecision::NoAction;
   4018  }
   4019 
   4020  if (!obj->shape()->isShared()) {
   4021    return AttachDecision::NoAction;
   4022  }
   4023 
   4024  static constexpr size_t SMALL_OBJECT_SIZE = 5;
   4025 
   4026  if (obj->shape()->asShared().slotSpan() > SMALL_OBJECT_SIZE) {
   4027    return AttachDecision::NoAction;
   4028  }
   4029 
   4030  Rooted<ListObject*> keyListObj(cx_, ListObject::create(cx_));
   4031  if (!keyListObj) {
   4032    cx_->recoverFromOutOfMemory();
   4033    return AttachDecision::NoAction;
   4034  }
   4035 
   4036  for (SharedShapePropertyIter<CanGC> iter(cx_, &obj->shape()->asShared());
   4037       !iter.done(); iter++) {
   4038    if (!iter->key().isAtom()) {
   4039      return AttachDecision::NoAction;
   4040    }
   4041 
   4042    if (keyListObj->length() == SMALL_OBJECT_SIZE) {
   4043      return AttachDecision::NoAction;
   4044    }
   4045 
   4046    if (!keyListObj->append(cx_, StringValue(iter->key().toAtom()))) {
   4047      cx_->recoverFromOutOfMemory();
   4048      return AttachDecision::NoAction;
   4049    }
   4050  }
   4051 
   4052  writer.guardShape(objId, obj->shape());
   4053  writer.guardNoDenseElements(objId);
   4054  StringOperandId keyStrId = writer.guardToString(keyId);
   4055  StringOperandId keyAtomId = writer.stringToAtom(keyStrId);
   4056  writer.smallObjectVariableKeyHasOwnResult(keyAtomId, keyListObj,
   4057                                            obj->shape());
   4058  writer.returnFromIC();
   4059  trackAttached("HasProp.SmallObjectVariableKey");
   4060  return AttachDecision::Attach;
   4061 }
   4062 
   4063 AttachDecision HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId,
   4064                                                        ValOperandId keyId) {
   4065  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   4066 
   4067  if (mode_ != ICState::Mode::Megamorphic) {
   4068    return AttachDecision::NoAction;
   4069  }
   4070 
   4071  writer.megamorphicHasPropResult(objId, keyId, hasOwn);
   4072  writer.returnFromIC();
   4073  trackAttached("HasProp.Megamorphic");
   4074  return AttachDecision::Attach;
   4075 }
   4076 
   4077 AttachDecision HasPropIRGenerator::tryAttachNative(NativeObject* obj,
   4078                                                   ObjOperandId objId, jsid key,
   4079                                                   ValOperandId keyId,
   4080                                                   PropertyResult prop,
   4081                                                   NativeObject* holder) {
   4082  MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
   4083 
   4084  if (!prop.isNativeProperty()) {
   4085    return AttachDecision::NoAction;
   4086  }
   4087 
   4088  emitIdGuard(keyId, idVal_, key);
   4089  EmitReadSlotGuard(writer, obj, holder, objId);
   4090  writer.loadBooleanResult(true);
   4091  writer.returnFromIC();
   4092 
   4093  trackAttached("HasProp.Native");
   4094  return AttachDecision::Attach;
   4095 }
   4096 
   4097 static void EmitGuardTypedArray(CacheIRWriter& writer, TypedArrayObject* obj,
   4098                                ObjOperandId objId) {
   4099  if (!obj->is<ResizableTypedArrayObject>()) {
   4100    writer.guardIsNonResizableTypedArray(objId);
   4101  } else {
   4102    writer.guardIsResizableTypedArray(objId);
   4103  }
   4104 }
   4105 
   4106 AttachDecision HasPropIRGenerator::tryAttachTypedArray(HandleObject obj,
   4107                                                       ObjOperandId objId,
   4108                                                       ValOperandId keyId) {
   4109  if (!obj->is<TypedArrayObject>()) {
   4110    return AttachDecision::NoAction;
   4111  }
   4112 
   4113  if (!idVal_.isNumber()) {
   4114    return AttachDecision::NoAction;
   4115  }
   4116 
   4117  auto* tarr = &obj->as<TypedArrayObject>();
   4118  EmitGuardTypedArray(writer, tarr, objId);
   4119 
   4120  IntPtrOperandId intPtrIndexId =
   4121      guardToIntPtrIndex(idVal_, keyId, /* supportOOB = */ true);
   4122 
   4123  auto viewKind = ToArrayBufferViewKind(tarr);
   4124  writer.loadTypedArrayElementExistsResult(objId, intPtrIndexId, viewKind);
   4125  writer.returnFromIC();
   4126 
   4127  trackAttached("HasProp.TypedArrayObject");
   4128  return AttachDecision::Attach;
   4129 }
   4130 
   4131 AttachDecision HasPropIRGenerator::tryAttachSlotDoesNotExist(
   4132    NativeObject* obj, ObjOperandId objId, jsid key, ValOperandId keyId) {
   4133  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   4134 
   4135  emitIdGuard(keyId, idVal_, key);
   4136  if (hasOwn) {
   4137    TestMatchingNativeReceiver(writer, obj, objId);
   4138  } else {
   4139    EmitMissingPropGuard(writer, obj, objId);
   4140  }
   4141  writer.loadBooleanResult(false);
   4142  writer.returnFromIC();
   4143 
   4144  trackAttached("HasProp.DoesNotExist");
   4145  return AttachDecision::Attach;
   4146 }
   4147 
   4148 AttachDecision HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj,
   4149                                                         ObjOperandId objId,
   4150                                                         HandleId key,
   4151                                                         ValOperandId keyId) {
   4152  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   4153 
   4154  // Check that property doesn't exist on |obj| or it's prototype chain. These
   4155  // checks allow NativeObjects with a NativeObject prototype chain. They return
   4156  // NoAction if unknown such as resolve hooks or proxies.
   4157  if (hasOwn) {
   4158    if (!CheckHasNoSuchOwnProperty(cx_, obj, key)) {
   4159      return AttachDecision::NoAction;
   4160    }
   4161  } else {
   4162    if (!CheckHasNoSuchProperty(cx_, obj, key)) {
   4163      return AttachDecision::NoAction;
   4164    }
   4165  }
   4166 
   4167  TRY_ATTACH(tryAttachSmallObjectVariableKey(obj, objId, key, keyId));
   4168  TRY_ATTACH(tryAttachMegamorphic(objId, keyId));
   4169  TRY_ATTACH(
   4170      tryAttachSlotDoesNotExist(&obj->as<NativeObject>(), objId, key, keyId));
   4171 
   4172  return AttachDecision::NoAction;
   4173 }
   4174 
   4175 AttachDecision HasPropIRGenerator::tryAttachProxyElement(HandleObject obj,
   4176                                                         ObjOperandId objId,
   4177                                                         ValOperandId keyId) {
   4178  bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
   4179 
   4180  if (!obj->is<ProxyObject>()) {
   4181    return AttachDecision::NoAction;
   4182  }
   4183 
   4184  writer.guardIsProxy(objId);
   4185  writer.proxyHasPropResult(objId, keyId, hasOwn);
   4186  writer.returnFromIC();
   4187 
   4188  trackAttached("HasProp.ProxyElement");
   4189  return AttachDecision::Attach;
   4190 }
   4191 
   4192 AttachDecision HasPropIRGenerator::tryAttachStub() {
   4193  MOZ_ASSERT(cacheKind_ == CacheKind::In || cacheKind_ == CacheKind::HasOwn);
   4194 
   4195  AutoAssertNoPendingException aanpe(cx_);
   4196 
   4197  // NOTE: Argument order is PROPERTY, OBJECT
   4198  ValOperandId keyId(writer.setInputOperandId(0));
   4199  ValOperandId valId(writer.setInputOperandId(1));
   4200 
   4201  if (!val_.isObject()) {
   4202    trackAttached(IRGenerator::NotAttached);
   4203    return AttachDecision::NoAction;
   4204  }
   4205  RootedObject obj(cx_, &val_.toObject());
   4206  ObjOperandId objId = writer.guardToObject(valId);
   4207 
   4208  // Optimize Proxies
   4209  TRY_ATTACH(tryAttachProxyElement(obj, objId, keyId));
   4210 
   4211  RootedId id(cx_);
   4212  bool nameOrSymbol;
   4213  if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
   4214    cx_->clearPendingException();
   4215    return AttachDecision::NoAction;
   4216  }
   4217 
   4218  TRY_ATTACH(tryAttachTypedArray(obj, objId, keyId));
   4219 
   4220  if (nameOrSymbol) {
   4221    TRY_ATTACH(tryAttachNamedProp(obj, objId, id, keyId));
   4222    TRY_ATTACH(tryAttachDoesNotExist(obj, objId, id, keyId));
   4223 
   4224    trackAttached(IRGenerator::NotAttached);
   4225    return AttachDecision::NoAction;
   4226  }
   4227 
   4228  uint32_t index;
   4229  Int32OperandId indexId;
   4230  if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
   4231    TRY_ATTACH(tryAttachDense(obj, objId, index, indexId));
   4232    TRY_ATTACH(tryAttachDenseHole(obj, objId, index, indexId));
   4233    TRY_ATTACH(tryAttachSparse(obj, objId, indexId));
   4234    TRY_ATTACH(tryAttachArgumentsObjectArg(obj, objId, indexId));
   4235 
   4236    trackAttached(IRGenerator::NotAttached);
   4237    return AttachDecision::NoAction;
   4238  }
   4239 
   4240  trackAttached(IRGenerator::NotAttached);
   4241  return AttachDecision::NoAction;
   4242 }
   4243 
   4244 void HasPropIRGenerator::trackAttached(const char* name) {
   4245  stubName_ = name ? name : "NotAttached";
   4246 #ifdef JS_CACHEIR_SPEW
   4247  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   4248    sp.valueProperty("base", val_);
   4249    sp.valueProperty("property", idVal_);
   4250  }
   4251 #endif
   4252 }
   4253 
   4254 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
   4255    JSContext* cx, HandleScript script, jsbytecode* pc, ICState state,
   4256    CacheKind cacheKind, HandleValue idVal, HandleValue val)
   4257    : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {
   4258  MOZ_ASSERT(idVal.isSymbol() && idVal.toSymbol()->isPrivateName());
   4259 }
   4260 
   4261 AttachDecision CheckPrivateFieldIRGenerator::tryAttachStub() {
   4262  AutoAssertNoPendingException aanpe(cx_);
   4263 
   4264  ValOperandId valId(writer.setInputOperandId(0));
   4265  ValOperandId keyId(writer.setInputOperandId(1));
   4266 
   4267  if (!val_.isObject()) {
   4268    trackAttached(IRGenerator::NotAttached);
   4269    return AttachDecision::NoAction;
   4270  }
   4271  JSObject* obj = &val_.toObject();
   4272  ObjOperandId objId = writer.guardToObject(valId);
   4273  PropertyKey key = PropertyKey::Symbol(idVal_.toSymbol());
   4274 
   4275  ThrowCondition condition;
   4276  ThrowMsgKind msgKind;
   4277  GetCheckPrivateFieldOperands(pc_, &condition, &msgKind);
   4278 
   4279  PropertyResult prop;
   4280  if (!LookupOwnPropertyPure(cx_, obj, key, &prop)) {
   4281    return AttachDecision::NoAction;
   4282  }
   4283 
   4284  if (CheckPrivateFieldWillThrow(condition, prop.isFound())) {
   4285    // Don't attach a stub if the operation will throw.
   4286    return AttachDecision::NoAction;
   4287  }
   4288 
   4289  auto* nobj = &obj->as<NativeObject>();
   4290 
   4291  TRY_ATTACH(tryAttachNative(nobj, objId, key, keyId, prop));
   4292 
   4293  return AttachDecision::NoAction;
   4294 }
   4295 
   4296 AttachDecision CheckPrivateFieldIRGenerator::tryAttachNative(
   4297    NativeObject* obj, ObjOperandId objId, jsid key, ValOperandId keyId,
   4298    PropertyResult prop) {
   4299  MOZ_ASSERT(prop.isNativeProperty() || prop.isNotFound());
   4300 
   4301  emitIdGuard(keyId, idVal_, key);
   4302  TestMatchingNativeReceiver(writer, obj, objId);
   4303  writer.loadBooleanResult(prop.isFound());
   4304  writer.returnFromIC();
   4305 
   4306  trackAttached("CheckPrivateField.Native");
   4307  return AttachDecision::Attach;
   4308 }
   4309 
   4310 void CheckPrivateFieldIRGenerator::trackAttached(const char* name) {
   4311  stubName_ = name ? name : "NotAttached";
   4312 #ifdef JS_CACHEIR_SPEW
   4313  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   4314    sp.valueProperty("base", val_);
   4315    sp.valueProperty("property", idVal_);
   4316  }
   4317 #endif
   4318 }
   4319 
   4320 bool IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
   4321                                       uint32_t* int32Index,
   4322                                       Int32OperandId* int32IndexId) {
   4323  if (index.isNumber()) {
   4324    int32_t indexSigned;
   4325    if (index.isInt32()) {
   4326      indexSigned = index.toInt32();
   4327    } else {
   4328      // We allow negative zero here.
   4329      if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned)) {
   4330        return false;
   4331      }
   4332    }
   4333 
   4334    if (indexSigned < 0) {
   4335      return false;
   4336    }
   4337 
   4338    *int32Index = uint32_t(indexSigned);
   4339    *int32IndexId = EmitGuardToInt32Index(writer, index, indexId);
   4340    return true;
   4341  }
   4342 
   4343  if (index.isString()) {
   4344    int32_t indexSigned = GetIndexFromString(index.toString());
   4345    if (indexSigned < 0) {
   4346      return false;
   4347    }
   4348 
   4349    StringOperandId strId = writer.guardToString(indexId);
   4350    *int32Index = uint32_t(indexSigned);
   4351    *int32IndexId = writer.guardStringToIndex(strId);
   4352    return true;
   4353  }
   4354 
   4355  return false;
   4356 }
   4357 
   4358 SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script,
   4359                                       jsbytecode* pc, CacheKind cacheKind,
   4360                                       ICState state, HandleValue lhsVal,
   4361                                       HandleValue idVal, HandleValue rhsVal)
   4362    : IRGenerator(cx, script, pc, cacheKind, state),
   4363      lhsVal_(lhsVal),
   4364      idVal_(idVal),
   4365      rhsVal_(rhsVal) {}
   4366 
   4367 AttachDecision SetPropIRGenerator::tryAttachStub() {
   4368  AutoAssertNoPendingException aanpe(cx_);
   4369 
   4370  ValOperandId objValId(writer.setInputOperandId(0));
   4371  ValOperandId rhsValId;
   4372  if (cacheKind_ == CacheKind::SetProp) {
   4373    rhsValId = ValOperandId(writer.setInputOperandId(1));
   4374  } else {
   4375    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   4376    MOZ_ASSERT(setElemKeyValueId().id() == 1);
   4377    writer.setInputOperandId(1);
   4378    rhsValId = ValOperandId(writer.setInputOperandId(2));
   4379  }
   4380 
   4381  RootedId id(cx_);
   4382  bool nameOrSymbol;
   4383  if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
   4384    cx_->clearPendingException();
   4385    return AttachDecision::NoAction;
   4386  }
   4387 
   4388  if (lhsVal_.isObject()) {
   4389    RootedObject obj(cx_, &lhsVal_.toObject());
   4390 
   4391    ObjOperandId objId = writer.guardToObject(objValId);
   4392    TRY_ATTACH(tryAttachSetTypedArrayElement(obj, objId, rhsValId));
   4393    if (IsPropertySetOp(JSOp(*pc_))) {
   4394      TRY_ATTACH(tryAttachMegamorphicSetElement(obj, objId, rhsValId));
   4395    }
   4396    if (nameOrSymbol) {
   4397      TRY_ATTACH(tryAttachNativeSetSlot(obj, objId, id, rhsValId));
   4398      if (IsPropertySetOp(JSOp(*pc_))) {
   4399        TRY_ATTACH(tryAttachSetArrayLength(obj, objId, id, rhsValId));
   4400        TRY_ATTACH(tryAttachSetter(obj, objId, id, rhsValId));
   4401        TRY_ATTACH(tryAttachWindowProxy(obj, objId, id, rhsValId));
   4402        TRY_ATTACH(tryAttachProxy(obj, objId, id, rhsValId));
   4403        TRY_ATTACH(tryAttachMegamorphicSetSlot(obj, objId, id, rhsValId));
   4404      }
   4405      if (canAttachAddSlotStub(obj, id)) {
   4406        deferType_ = DeferType::AddSlot;
   4407        return AttachDecision::Deferred;
   4408      }
   4409      return AttachDecision::NoAction;
   4410    }
   4411 
   4412    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   4413 
   4414    if (IsPropertySetOp(JSOp(*pc_))) {
   4415      TRY_ATTACH(tryAttachProxyElement(obj, objId, rhsValId));
   4416    }
   4417 
   4418    uint32_t index;
   4419    Int32OperandId indexId;
   4420    if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
   4421      TRY_ATTACH(
   4422          tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId));
   4423      TRY_ATTACH(
   4424          tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId));
   4425      TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj, objId, index, indexId,
   4426                                                   rhsValId));
   4427      return AttachDecision::NoAction;
   4428    }
   4429  }
   4430  return AttachDecision::NoAction;
   4431 }
   4432 
   4433 static void EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId,
   4434                                   NativeObject* nobj, PropertyInfo prop,
   4435                                   ValOperandId rhsId) {
   4436  if (nobj->isFixedSlot(prop.slot())) {
   4437    size_t offset = NativeObject::getFixedSlotOffset(prop.slot());
   4438    writer.storeFixedSlot(objId, offset, rhsId);
   4439  } else {
   4440    size_t offset = nobj->dynamicSlotIndex(prop.slot()) * sizeof(Value);
   4441    writer.storeDynamicSlot(objId, offset, rhsId);
   4442  }
   4443  writer.returnFromIC();
   4444 }
   4445 
   4446 static Maybe<PropertyInfo> LookupShapeForSetSlot(JSOp op, NativeObject* obj,
   4447                                                 jsid id) {
   4448  Maybe<PropertyInfo> prop = obj->lookupPure(id);
   4449  if (prop.isNothing() || !prop->isDataProperty() || !prop->writable()) {
   4450    return mozilla::Nothing();
   4451  }
   4452 
   4453  // If this is a property init operation, the property's attributes may have to
   4454  // be changed too, so make sure the current flags match.
   4455  if (IsPropertyInitOp(op)) {
   4456    // Don't support locked init operations.
   4457    if (IsLockedInitOp(op)) {
   4458      return mozilla::Nothing();
   4459    }
   4460 
   4461    // Can't redefine a non-configurable property.
   4462    if (!prop->configurable()) {
   4463      return mozilla::Nothing();
   4464    }
   4465 
   4466    // Make sure the enumerable flag matches the init operation.
   4467    if (IsHiddenInitOp(op) == prop->enumerable()) {
   4468      return mozilla::Nothing();
   4469    }
   4470  }
   4471 
   4472  return prop;
   4473 }
   4474 
   4475 SetSlotOptimizable SetPropIRGenerator::canAttachNativeSetSlot(
   4476    JSObject* obj, PropertyKey id, Maybe<PropertyInfo>* prop) {
   4477  if (!obj->is<NativeObject>()) {
   4478    return SetSlotOptimizable::No;
   4479  }
   4480 
   4481  *prop = LookupShapeForSetSlot(JSOp(*pc_), &obj->as<NativeObject>(), id);
   4482  if (!prop->isSome()) {
   4483    return SetSlotOptimizable::No;
   4484  }
   4485 
   4486  return Watchtower::canOptimizeSetSlot(cx_, &obj->as<NativeObject>(), **prop);
   4487 }
   4488 
   4489 // There is no need to guard on the shape. Global lexical bindings are
   4490 // non-configurable and can not be shadowed.
   4491 static bool IsGlobalLexicalSetGName(JSOp op, NativeObject* obj,
   4492                                    PropertyInfo prop) {
   4493  // Ensure that the env can't change.
   4494  if (op != JSOp::SetGName && op != JSOp::StrictSetGName) {
   4495    return false;
   4496  }
   4497 
   4498  if (!obj->is<GlobalLexicalEnvironmentObject>()) {
   4499    return false;
   4500  }
   4501 
   4502  // Uninitialized let bindings use a RuntimeLexicalErrorObject.
   4503  MOZ_ASSERT(!obj->getSlot(prop.slot()).isMagic());
   4504  MOZ_ASSERT(prop.writable());
   4505  MOZ_ASSERT(!prop.configurable());
   4506  return true;
   4507 }
   4508 
   4509 AttachDecision SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj,
   4510                                                          ObjOperandId objId,
   4511                                                          HandleId id,
   4512                                                          ValOperandId rhsId) {
   4513  Maybe<PropertyInfo> prop;
   4514  SetSlotOptimizable optimizable = canAttachNativeSetSlot(obj, id, &prop);
   4515  switch (optimizable) {
   4516    case SetSlotOptimizable::No:
   4517      return AttachDecision::NoAction;
   4518    case SetSlotOptimizable::NotYet:
   4519      return AttachDecision::TemporarilyUnoptimizable;
   4520    case SetSlotOptimizable::Yes:
   4521      break;
   4522  }
   4523 
   4524  if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp &&
   4525      IsPropertySetOp(JSOp(*pc_))) {
   4526    return AttachDecision::NoAction;
   4527  }
   4528 
   4529  maybeEmitIdGuard(id);
   4530 
   4531  NativeObject* nobj = &obj->as<NativeObject>();
   4532  if (!IsGlobalLexicalSetGName(JSOp(*pc_), nobj, *prop)) {
   4533    // If the object has an ObjectFuse, we can only optimize for this specific
   4534    // object so we have to emit GuardSpecificObject. We don't need to do this
   4535    // for the global object because there's only one object with that shape.
   4536    if (nobj->hasObjectFuse() && !nobj->is<GlobalObject>()) {
   4537      writer.guardSpecificObject(objId, nobj);
   4538    }
   4539    TestMatchingNativeReceiver(writer, nobj, objId);
   4540  }
   4541  EmitStoreSlotAndReturn(writer, objId, nobj, *prop, rhsId);
   4542 
   4543  trackAttached("SetProp.NativeSlot");
   4544  return AttachDecision::Attach;
   4545 }
   4546 
   4547 static bool ValueCanConvertToNumeric(Scalar::Type type, const Value& val) {
   4548  if (Scalar::isBigIntType(type)) {
   4549    return val.isBigInt();
   4550  }
   4551  return val.isNumber() || val.isNullOrUndefined() || val.isBoolean() ||
   4552         val.isString();
   4553 }
   4554 
   4555 OperandId IRGenerator::emitNumericGuard(ValOperandId valId, const Value& v,
   4556                                        Scalar::Type type) {
   4557  MOZ_ASSERT(ValueCanConvertToNumeric(type, v));
   4558  switch (type) {
   4559    case Scalar::Int8:
   4560    case Scalar::Uint8:
   4561    case Scalar::Int16:
   4562    case Scalar::Uint16:
   4563    case Scalar::Int32:
   4564    case Scalar::Uint32: {
   4565      if (v.isNumber()) {
   4566        return writer.guardToInt32ModUint32(valId);
   4567      }
   4568      if (v.isNullOrUndefined()) {
   4569        writer.guardIsNullOrUndefined(valId);
   4570        return writer.loadInt32Constant(0);
   4571      }
   4572      if (v.isBoolean()) {
   4573        return writer.guardBooleanToInt32(valId);
   4574      }
   4575      MOZ_ASSERT(v.isString());
   4576      StringOperandId strId = writer.guardToString(valId);
   4577      NumberOperandId numId = writer.guardStringToNumber(strId);
   4578      return writer.truncateDoubleToUInt32(numId);
   4579    }
   4580 
   4581    case Scalar::Float16:
   4582    case Scalar::Float32:
   4583    case Scalar::Float64: {
   4584      if (v.isNumber()) {
   4585        return writer.guardIsNumber(valId);
   4586      }
   4587      if (v.isNull()) {
   4588        writer.guardIsNull(valId);
   4589        return writer.loadDoubleConstant(0.0);
   4590      }
   4591      if (v.isUndefined()) {
   4592        writer.guardIsUndefined(valId);
   4593        return writer.loadDoubleConstant(JS::GenericNaN());
   4594      }
   4595      if (v.isBoolean()) {
   4596        BooleanOperandId boolId = writer.guardToBoolean(valId);
   4597        return writer.booleanToNumber(boolId);
   4598      }
   4599      MOZ_ASSERT(v.isString());
   4600      StringOperandId strId = writer.guardToString(valId);
   4601      return writer.guardStringToNumber(strId);
   4602    }
   4603 
   4604    case Scalar::Uint8Clamped: {
   4605      if (v.isNumber()) {
   4606        return writer.guardToUint8Clamped(valId);
   4607      }
   4608      if (v.isNullOrUndefined()) {
   4609        writer.guardIsNullOrUndefined(valId);
   4610        return writer.loadInt32Constant(0);
   4611      }
   4612      if (v.isBoolean()) {
   4613        return writer.guardBooleanToInt32(valId);
   4614      }
   4615      MOZ_ASSERT(v.isString());
   4616      StringOperandId strId = writer.guardToString(valId);
   4617      NumberOperandId numId = writer.guardStringToNumber(strId);
   4618      return writer.doubleToUint8Clamped(numId);
   4619    }
   4620 
   4621    case Scalar::BigInt64:
   4622    case Scalar::BigUint64:
   4623      MOZ_ASSERT(v.isBigInt());
   4624      return writer.guardToBigInt(valId);
   4625 
   4626    case Scalar::MaxTypedArrayViewType:
   4627    case Scalar::Int64:
   4628    case Scalar::Simd128:
   4629      break;
   4630  }
   4631  MOZ_CRASH("Unsupported TypedArray type");
   4632 }
   4633 
   4634 void SetPropIRGenerator::trackAttached(const char* name) {
   4635  stubName_ = name ? name : "NotAttached";
   4636 #ifdef JS_CACHEIR_SPEW
   4637  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   4638    sp.opcodeProperty("op", JSOp(*pc_));
   4639    sp.valueProperty("base", lhsVal_);
   4640    sp.valueProperty("property", idVal_);
   4641    sp.valueProperty("value", rhsVal_);
   4642  }
   4643 #endif
   4644 }
   4645 
   4646 static bool IsCacheableSetPropCallNative(NativeObject* obj,
   4647                                         NativeObject* holder,
   4648                                         PropertyInfo prop) {
   4649  MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
   4650 
   4651  if (!prop.isAccessorProperty()) {
   4652    return false;
   4653  }
   4654 
   4655  JSObject* setterObject = holder->getSetter(prop);
   4656  if (!setterObject || !setterObject->is<JSFunction>()) {
   4657    return false;
   4658  }
   4659 
   4660  JSFunction& setter = setterObject->as<JSFunction>();
   4661  if (!setter.isNativeWithoutJitEntry()) {
   4662    return false;
   4663  }
   4664 
   4665  if (setter.isClassConstructor()) {
   4666    return false;
   4667  }
   4668 
   4669  return true;
   4670 }
   4671 
   4672 static bool IsCacheableSetPropCallScripted(NativeObject* obj,
   4673                                           NativeObject* holder,
   4674                                           PropertyInfo prop) {
   4675  MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
   4676 
   4677  if (!prop.isAccessorProperty()) {
   4678    return false;
   4679  }
   4680 
   4681  JSObject* setterObject = holder->getSetter(prop);
   4682  if (!setterObject || !setterObject->is<JSFunction>()) {
   4683    return false;
   4684  }
   4685 
   4686  JSFunction& setter = setterObject->as<JSFunction>();
   4687  if (setter.isClassConstructor()) {
   4688    return false;
   4689  }
   4690 
   4691  // Scripted functions and natives with JIT entry can use the scripted path.
   4692  return setter.hasJitEntry();
   4693 }
   4694 
   4695 static bool CanAttachSetter(JSContext* cx, jsbytecode* pc, JSObject* obj,
   4696                            PropertyKey id, NativeObject** holder,
   4697                            Maybe<PropertyInfo>* propInfo) {
   4698  // Don't attach a setter stub for ops like JSOp::InitElem.
   4699  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
   4700 
   4701  PropertyResult prop;
   4702  if (!LookupPropertyPure(cx, obj, id, holder, &prop)) {
   4703    return false;
   4704  }
   4705  auto* nobj = &obj->as<NativeObject>();
   4706 
   4707  if (!prop.isNativeProperty()) {
   4708    return false;
   4709  }
   4710 
   4711  if (!IsCacheableSetPropCallScripted(nobj, *holder, prop.propertyInfo()) &&
   4712      !IsCacheableSetPropCallNative(nobj, *holder, prop.propertyInfo())) {
   4713    return false;
   4714  }
   4715 
   4716  *propInfo = mozilla::Some(prop.propertyInfo());
   4717  return true;
   4718 }
   4719 
   4720 void SetPropIRGenerator::emitCallSetterNoGuards(NativeObject* obj,
   4721                                                NativeObject* holder,
   4722                                                PropertyInfo prop,
   4723                                                ObjOperandId receiverId,
   4724                                                ValOperandId rhsId) {
   4725  JSFunction* target = &holder->getSetter(prop)->as<JSFunction>();
   4726  bool sameRealm = cx_->realm() == target->realm();
   4727 
   4728  if (target->isNativeWithoutJitEntry()) {
   4729    MOZ_ASSERT(IsCacheableSetPropCallNative(obj, holder, prop));
   4730    writer.callNativeSetter(receiverId, target, rhsId, sameRealm);
   4731    writer.returnFromIC();
   4732    return;
   4733  }
   4734 
   4735  MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, prop));
   4736  writer.callScriptedSetter(receiverId, target, rhsId, sameRealm);
   4737  writer.returnFromIC();
   4738 }
   4739 
   4740 void SetPropIRGenerator::emitCallDOMSetterNoGuards(NativeObject* holder,
   4741                                                   PropertyInfo prop,
   4742                                                   ObjOperandId objId,
   4743                                                   ValOperandId rhsId) {
   4744  JSFunction* setter = &holder->getSetter(prop)->as<JSFunction>();
   4745  MOZ_ASSERT(cx_->realm() == setter->realm());
   4746 
   4747  writer.callDOMSetter(objId, setter->jitInfo(), rhsId);
   4748  writer.returnFromIC();
   4749 }
   4750 
   4751 AttachDecision SetPropIRGenerator::tryAttachSetter(HandleObject obj,
   4752                                                   ObjOperandId objId,
   4753                                                   HandleId id,
   4754                                                   ValOperandId rhsId) {
   4755  // Don't attach a setter stub for ops like JSOp::InitElem.
   4756  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   4757 
   4758  NativeObject* holder = nullptr;
   4759  Maybe<PropertyInfo> prop;
   4760  if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &prop)) {
   4761    return AttachDecision::NoAction;
   4762  }
   4763  auto* nobj = &obj->as<NativeObject>();
   4764 
   4765  bool needsWindowProxy =
   4766      IsWindow(nobj) && SetterNeedsWindowProxyThis(holder, *prop);
   4767 
   4768  maybeEmitIdGuard(id);
   4769 
   4770  emitCallAccessorGuards(nobj, holder, id, *prop, objId, AccessorKind::Setter);
   4771 
   4772  if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Setter, nobj, holder, *prop,
   4773                               mode_)) {
   4774    MOZ_ASSERT(!needsWindowProxy);
   4775    emitCallDOMSetterNoGuards(holder, *prop, objId, rhsId);
   4776 
   4777    trackAttached("SetProp.DOMSetter");
   4778    return AttachDecision::Attach;
   4779  }
   4780 
   4781  ObjOperandId receiverId;
   4782  if (needsWindowProxy) {
   4783    MOZ_ASSERT(cx_->global()->maybeWindowProxy());
   4784    receiverId = writer.loadObject(cx_->global()->maybeWindowProxy());
   4785  } else {
   4786    receiverId = objId;
   4787  }
   4788  emitCallSetterNoGuards(nobj, holder, *prop, receiverId, rhsId);
   4789 
   4790  trackAttached("SetProp.Setter");
   4791  return AttachDecision::Attach;
   4792 }
   4793 
   4794 AttachDecision SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj,
   4795                                                           ObjOperandId objId,
   4796                                                           HandleId id,
   4797                                                           ValOperandId rhsId) {
   4798  // Don't attach an array length stub for ops like JSOp::InitElem.
   4799  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   4800 
   4801  if (!obj->is<ArrayObject>() || !id.isAtom(cx_->names().length) ||
   4802      !obj->as<ArrayObject>().lengthIsWritable()) {
   4803    return AttachDecision::NoAction;
   4804  }
   4805 
   4806  maybeEmitIdGuard(id);
   4807  emitOptimisticClassGuard(objId, obj, GuardClassKind::Array);
   4808  writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
   4809  writer.returnFromIC();
   4810 
   4811  trackAttached("SetProp.ArrayLength");
   4812  return AttachDecision::Attach;
   4813 }
   4814 
   4815 AttachDecision SetPropIRGenerator::tryAttachSetDenseElement(
   4816    HandleObject obj, ObjOperandId objId, uint32_t index,
   4817    Int32OperandId indexId, ValOperandId rhsId) {
   4818  if (!obj->is<NativeObject>()) {
   4819    return AttachDecision::NoAction;
   4820  }
   4821 
   4822  NativeObject* nobj = &obj->as<NativeObject>();
   4823  if (!nobj->containsDenseElement(index) || nobj->denseElementsAreFrozen()) {
   4824    return AttachDecision::NoAction;
   4825  }
   4826 
   4827  // Setting holes requires extra code for marking the elements non-packed.
   4828  MOZ_ASSERT(!rhsVal_.isMagic(JS_ELEMENTS_HOLE));
   4829 
   4830  JSOp op = JSOp(*pc_);
   4831 
   4832  // We don't currently emit locked init for any indexed properties.
   4833  MOZ_ASSERT(!IsLockedInitOp(op));
   4834 
   4835  // We don't currently emit hidden init for any existing indexed properties.
   4836  MOZ_ASSERT(!IsHiddenInitOp(op));
   4837 
   4838  // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
   4839  // the elements are sealed, we have to throw an exception. Note that we have
   4840  // to check !isExtensible instead of denseElementsAreSealed because sealing
   4841  // a (non-extensible) object does not necessarily trigger a Shape change.
   4842  if (IsPropertyInitOp(op) && !nobj->isExtensible()) {
   4843    return AttachDecision::NoAction;
   4844  }
   4845 
   4846  TestMatchingNativeReceiver(writer, nobj, objId);
   4847 
   4848  bool expectPackedElements = nobj->denseElementsArePacked();
   4849  writer.storeDenseElement(objId, indexId, rhsId, expectPackedElements);
   4850  writer.returnFromIC();
   4851 
   4852  trackAttached("SetProp.DenseElement");
   4853  return AttachDecision::Attach;
   4854 }
   4855 
   4856 static bool CanAttachAddElement(NativeObject* obj, bool isInit,
   4857                                AllowIndexedReceiver allowIndexedReceiver) {
   4858  MOZ_ASSERT(!obj->is<TypedArrayObject>());
   4859 
   4860  // Make sure the receiver doesn't have any indexed properties and that such
   4861  // properties can't appear without a shape change.
   4862  if (allowIndexedReceiver == AllowIndexedReceiver::No && obj->isIndexed()) {
   4863    return false;
   4864  }
   4865 
   4866  do {
   4867    // This check is also relevant for the receiver object.
   4868    const JSClass* clasp = obj->getClass();
   4869    if (clasp != &ArrayObject::class_ &&
   4870        (clasp->getAddProperty() || clasp->getResolve() ||
   4871         clasp->getOpsLookupProperty() || clasp->getOpsSetProperty() ||
   4872         obj->hasUnpreservedWrapper())) {
   4873      return false;
   4874    }
   4875 
   4876    // If we're initializing a property instead of setting one, the objects
   4877    // on the prototype are not relevant.
   4878    if (isInit) {
   4879      break;
   4880    }
   4881 
   4882    JSObject* proto = obj->staticPrototype();
   4883    if (!proto) {
   4884      break;
   4885    }
   4886 
   4887    if (!proto->is<NativeObject>()) {
   4888      return false;
   4889    }
   4890 
   4891    // We shouldn't add an element if the index is OOB for a typed array on the
   4892    // prototype chain.
   4893    if (proto->is<TypedArrayObject>()) {
   4894      return false;
   4895    }
   4896 
   4897    NativeObject* nproto = &proto->as<NativeObject>();
   4898    if (nproto->isIndexed()) {
   4899      return false;
   4900    }
   4901 
   4902    // We have to make sure the proto has no non-writable (frozen) elements
   4903    // because we're not allowed to shadow them.
   4904    if (nproto->denseElementsAreFrozen() &&
   4905        nproto->getDenseInitializedLength() > 0) {
   4906      return false;
   4907    }
   4908 
   4909    obj = nproto;
   4910  } while (true);
   4911 
   4912  return true;
   4913 }
   4914 
   4915 AttachDecision SetPropIRGenerator::tryAttachSetDenseElementHole(
   4916    HandleObject obj, ObjOperandId objId, uint32_t index,
   4917    Int32OperandId indexId, ValOperandId rhsId) {
   4918  if (!obj->is<NativeObject>()) {
   4919    return AttachDecision::NoAction;
   4920  }
   4921 
   4922  // Setting holes requires extra code for marking the elements non-packed.
   4923  if (rhsVal_.isMagic(JS_ELEMENTS_HOLE)) {
   4924    return AttachDecision::NoAction;
   4925  }
   4926 
   4927  JSOp op = JSOp(*pc_);
   4928  MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
   4929 
   4930  // We don't currently emit locked init for any indexed properties.
   4931  MOZ_ASSERT(!IsLockedInitOp(op));
   4932 
   4933  // Hidden init can be emitted for absent indexed properties.
   4934  if (IsHiddenInitOp(op)) {
   4935    MOZ_ASSERT(op == JSOp::InitHiddenElem);
   4936    return AttachDecision::NoAction;
   4937  }
   4938 
   4939  NativeObject* nobj = &obj->as<NativeObject>();
   4940  if (!nobj->isExtensible()) {
   4941    return AttachDecision::NoAction;
   4942  }
   4943 
   4944  MOZ_ASSERT(!nobj->denseElementsAreFrozen(),
   4945             "Extensible objects should not have frozen elements");
   4946 
   4947  uint32_t initLength = nobj->getDenseInitializedLength();
   4948  uint32_t capacity = nobj->getDenseCapacity();
   4949 
   4950  // Optimize if:
   4951  // a) we're adding an element inside capacity, or one element past.
   4952  // b) we're writing to a hole inside initLength.
   4953  bool isAdd = index >= initLength && index <= capacity;
   4954  bool isHoleInBounds =
   4955      index < initLength && !nobj->containsDenseElement(index);
   4956  if (!isAdd && !isHoleInBounds) {
   4957    return AttachDecision::NoAction;
   4958  }
   4959 
   4960  // Can't add new elements to arrays with non-writable length.
   4961  if (isAdd && nobj->is<ArrayObject>() &&
   4962      !nobj->as<ArrayObject>().lengthIsWritable()) {
   4963    return AttachDecision::NoAction;
   4964  }
   4965 
   4966  // Typed arrays don't have dense elements.
   4967  if (nobj->is<TypedArrayObject>()) {
   4968    return AttachDecision::NoAction;
   4969  }
   4970 
   4971  // Check for other indexed properties or class hooks.
   4972  if (!CanAttachAddElement(nobj, IsPropertyInitOp(op),
   4973                           AllowIndexedReceiver::No)) {
   4974    return AttachDecision::NoAction;
   4975  }
   4976 
   4977  TestMatchingNativeReceiver(writer, nobj, objId);
   4978 
   4979  // Also shape guard the proto chain, unless this is an InitElem.
   4980  if (IsPropertySetOp(op)) {
   4981    ShapeGuardProtoChain(writer, nobj, objId);
   4982  }
   4983 
   4984  writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
   4985  writer.returnFromIC();
   4986 
   4987  trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
   4988  return AttachDecision::Attach;
   4989 }
   4990 
   4991 // Add an IC for adding or updating a sparse element.
   4992 AttachDecision SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
   4993    HandleObject obj, ObjOperandId objId, uint32_t index,
   4994    Int32OperandId indexId, ValOperandId rhsId) {
   4995  JSOp op = JSOp(*pc_);
   4996  MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
   4997 
   4998  if (op != JSOp::SetElem && op != JSOp::StrictSetElem) {
   4999    return AttachDecision::NoAction;
   5000  }
   5001 
   5002  if (!obj->is<NativeObject>()) {
   5003    return AttachDecision::NoAction;
   5004  }
   5005  NativeObject* nobj = &obj->as<NativeObject>();
   5006 
   5007  // We cannot attach a stub to a non-extensible object
   5008  if (!nobj->isExtensible()) {
   5009    return AttachDecision::NoAction;
   5010  }
   5011 
   5012  // Stub doesn't handle negative indices.
   5013  if (index > INT32_MAX) {
   5014    return AttachDecision::NoAction;
   5015  }
   5016 
   5017  // The index must not be for a dense element.
   5018  if (nobj->containsDenseElement(index)) {
   5019    return AttachDecision::NoAction;
   5020  }
   5021 
   5022  // Only handle ArrayObject and PlainObject in this stub.
   5023  if (!nobj->is<ArrayObject>() && !nobj->is<PlainObject>()) {
   5024    return AttachDecision::NoAction;
   5025  }
   5026 
   5027  // Don't attach if we're adding to an array with non-writable length.
   5028  if (nobj->is<ArrayObject>()) {
   5029    ArrayObject* aobj = &nobj->as<ArrayObject>();
   5030    bool isAdd = (index >= aobj->length());
   5031    if (isAdd && !aobj->lengthIsWritable()) {
   5032      return AttachDecision::NoAction;
   5033    }
   5034  }
   5035 
   5036  // Check for class hooks or indexed properties on the prototype chain that
   5037  // we're not allowed to shadow.
   5038  if (!CanAttachAddElement(nobj, /* isInit = */ false,
   5039                           AllowIndexedReceiver::Yes)) {
   5040    return AttachDecision::NoAction;
   5041  }
   5042 
   5043  // Ensure that obj is an ArrayObject or PlainObject.
   5044  if (nobj->is<ArrayObject>()) {
   5045    writer.guardClass(objId, GuardClassKind::Array);
   5046  } else {
   5047    MOZ_ASSERT(nobj->is<PlainObject>());
   5048    writer.guardClass(objId, GuardClassKind::PlainObject);
   5049  }
   5050 
   5051  // The helper we are going to call only applies to non-dense elements.
   5052  writer.guardIndexIsNotDenseElement(objId, indexId);
   5053 
   5054  // Guard extensible: We may be trying to add a new element, and so we'd best
   5055  // be able to do so safely.
   5056  writer.guardIsExtensible(objId);
   5057 
   5058  // Ensures we are able to efficiently able to map to an integral jsid.
   5059  writer.guardInt32IsNonNegative(indexId);
   5060 
   5061  // Shape guard the prototype chain to avoid shadowing indexes from appearing.
   5062  // Guard the prototype of the receiver explicitly, because the receiver's
   5063  // shape is not being guarded as a proxy for that.
   5064  GuardReceiverProto(writer, nobj, objId);
   5065 
   5066  // Dense elements may appear on the prototype chain (and prototypes may
   5067  // have a different notion of which elements are dense), but they can
   5068  // only be data properties, so our specialized Set handler is ok to bind
   5069  // to them.
   5070  if (IsPropertySetOp(op)) {
   5071    ShapeGuardProtoChain(writer, nobj, objId);
   5072  }
   5073 
   5074  // Ensure that if we're adding an element to the object, the object's
   5075  // length is writable.
   5076  if (nobj->is<ArrayObject>()) {
   5077    writer.guardIndexIsValidUpdateOrAdd(objId, indexId);
   5078  }
   5079 
   5080  writer.callAddOrUpdateSparseElementHelper(
   5081      objId, indexId, rhsId,
   5082      /* strict = */ op == JSOp::StrictSetElem);
   5083  writer.returnFromIC();
   5084 
   5085  trackAttached("SetProp.AddOrUpdateSparseElement");
   5086  return AttachDecision::Attach;
   5087 }
   5088 
   5089 AttachDecision SetPropIRGenerator::tryAttachSetTypedArrayElement(
   5090    HandleObject obj, ObjOperandId objId, ValOperandId rhsId) {
   5091  if (!obj->is<TypedArrayObject>()) {
   5092    return AttachDecision::NoAction;
   5093  }
   5094  if (!idVal_.isNumber()) {
   5095    return AttachDecision::NoAction;
   5096  }
   5097 
   5098  auto* tarr = &obj->as<TypedArrayObject>();
   5099  Scalar::Type elementType = tarr->type();
   5100 
   5101  // Immutable TypedArrays can't be modified.
   5102  if (tarr->is<ImmutableTypedArrayObject>()) {
   5103    return AttachDecision::NoAction;
   5104  }
   5105 
   5106  // Don't attach if the input type doesn't match the guard added below.
   5107  if (!ValueCanConvertToNumeric(elementType, rhsVal_)) {
   5108    return AttachDecision::NoAction;
   5109  }
   5110 
   5111  bool handleOOB = false;
   5112  int64_t indexInt64;
   5113  if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 ||
   5114      uint64_t(indexInt64) >= tarr->length().valueOr(0)) {
   5115    handleOOB = true;
   5116  }
   5117 
   5118  JSOp op = JSOp(*pc_);
   5119 
   5120  // The only expected property init operation is InitElem.
   5121  MOZ_ASSERT_IF(IsPropertyInitOp(op), op == JSOp::InitElem);
   5122 
   5123  // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
   5124  if (handleOOB && IsPropertyInitOp(op)) {
   5125    return AttachDecision::NoAction;
   5126  }
   5127 
   5128  writer.guardShapeForClass(objId, tarr->shape());
   5129 
   5130  OperandId rhsValId = emitNumericGuard(rhsId, rhsVal_, elementType);
   5131 
   5132  ValOperandId keyId = setElemKeyValueId();
   5133  IntPtrOperandId indexId = guardToIntPtrIndex(idVal_, keyId, handleOOB);
   5134 
   5135  auto viewKind = ToArrayBufferViewKind(tarr);
   5136  writer.storeTypedArrayElement(objId, elementType, indexId, rhsValId,
   5137                                handleOOB, viewKind);
   5138  writer.returnFromIC();
   5139 
   5140  trackAttached(handleOOB ? "SetTypedElementOOB" : "SetTypedElement");
   5141  return AttachDecision::Attach;
   5142 }
   5143 
   5144 AttachDecision SetPropIRGenerator::tryAttachGenericProxy(
   5145    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   5146    ValOperandId rhsId, bool handleDOMProxies) {
   5147  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5148  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5149 
   5150  writer.guardIsProxy(objId);
   5151 
   5152  if (!handleDOMProxies) {
   5153    // Ensure that the incoming object is not a DOM proxy, so that we can
   5154    // get to the specialized stubs. If handleDOMProxies is true, we were
   5155    // unable to attach a specialized DOM stub, so we just handle all
   5156    // proxies here.
   5157    writer.guardIsNotDOMProxy(objId);
   5158  }
   5159 
   5160  if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
   5161    maybeEmitIdGuard(id);
   5162    writer.proxySet(objId, id, rhsId, IsStrictSetPC(pc_));
   5163  } else {
   5164    // Attach a stub that handles every id.
   5165    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   5166    MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
   5167    writer.proxySetByValue(objId, setElemKeyValueId(), rhsId,
   5168                           IsStrictSetPC(pc_));
   5169  }
   5170 
   5171  writer.returnFromIC();
   5172 
   5173  trackAttached("SetProp.GenericProxy");
   5174  return AttachDecision::Attach;
   5175 }
   5176 
   5177 AttachDecision SetPropIRGenerator::tryAttachDOMProxyShadowed(
   5178    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   5179    ValOperandId rhsId) {
   5180  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5181  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5182 
   5183  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   5184 
   5185  maybeEmitIdGuard(id);
   5186  TestMatchingProxyReceiver(writer, obj, objId);
   5187  writer.proxySet(objId, id, rhsId, IsStrictSetPC(pc_));
   5188  writer.returnFromIC();
   5189 
   5190  trackAttached("SetProp.DOMProxyShadowed");
   5191  return AttachDecision::Attach;
   5192 }
   5193 
   5194 AttachDecision SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
   5195    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   5196    ValOperandId rhsId) {
   5197  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5198  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5199 
   5200  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   5201 
   5202  JSObject* proto = obj->staticPrototype();
   5203  if (!proto) {
   5204    return AttachDecision::NoAction;
   5205  }
   5206 
   5207  NativeObject* holder = nullptr;
   5208  Maybe<PropertyInfo> prop;
   5209  if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &prop)) {
   5210    return AttachDecision::NoAction;
   5211  }
   5212  auto* nproto = &proto->as<NativeObject>();
   5213 
   5214  maybeEmitIdGuard(id);
   5215 
   5216  // Guard that our proxy (expando) object hasn't started shadowing this
   5217  // property.
   5218  TestMatchingProxyReceiver(writer, obj, objId);
   5219  bool canOptimizeMissing = false;
   5220  CheckDOMProxyDoesNotShadow(writer, obj, id, objId, &canOptimizeMissing);
   5221 
   5222  GeneratePrototypeGuards(writer, obj, holder, objId);
   5223 
   5224  // Guard on the holder of the property.
   5225  ObjOperandId holderId = writer.loadObject(holder);
   5226  TestMatchingHolder(writer, holder, holderId);
   5227 
   5228  emitGuardGetterSetterSlot(holder, *prop, holderId, AccessorKind::Setter,
   5229                            /* holderIsConstant = */ true);
   5230 
   5231  // EmitCallSetterNoGuards expects |obj| to be the object the property is
   5232  // on to do some checks. Since we actually looked at proto, and no extra
   5233  // guards will be generated, we can just pass that instead.
   5234  emitCallSetterNoGuards(nproto, holder, *prop, objId, rhsId);
   5235 
   5236  trackAttached("SetProp.DOMProxyUnshadowed");
   5237  return AttachDecision::Attach;
   5238 }
   5239 
   5240 AttachDecision SetPropIRGenerator::tryAttachDOMProxyExpando(
   5241    Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
   5242    ValOperandId rhsId) {
   5243  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5244  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5245 
   5246  MOZ_ASSERT(IsCacheableDOMProxy(obj));
   5247 
   5248  Value expandoVal = GetProxyPrivate(obj);
   5249  JSObject* expandoObj;
   5250  if (expandoVal.isObject()) {
   5251    expandoObj = &expandoVal.toObject();
   5252  } else {
   5253    MOZ_ASSERT(!expandoVal.isUndefined(),
   5254               "How did a missing expando manage to shadow things?");
   5255    auto expandoAndGeneration =
   5256        static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
   5257    MOZ_ASSERT(expandoAndGeneration);
   5258    expandoObj = &expandoAndGeneration->expando.toObject();
   5259  }
   5260 
   5261  Maybe<PropertyInfo> prop;
   5262  SetSlotOptimizable optimizable =
   5263      canAttachNativeSetSlot(expandoObj, id, &prop);
   5264  if (optimizable == SetSlotOptimizable::Yes) {
   5265    auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
   5266    MOZ_ASSERT(!nativeExpandoObj->hasObjectFuse());
   5267 
   5268    maybeEmitIdGuard(id);
   5269    ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
   5270        obj, objId, expandoVal, nativeExpandoObj);
   5271 
   5272    EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, *prop,
   5273                           rhsId);
   5274    trackAttached("SetProp.DOMProxyExpandoSlot");
   5275    return AttachDecision::Attach;
   5276  }
   5277  MOZ_ASSERT(optimizable == SetSlotOptimizable::No);
   5278 
   5279  NativeObject* holder = nullptr;
   5280  if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &prop)) {
   5281    auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
   5282 
   5283    // Call the setter. Note that we pass objId, the DOM proxy, as |this|
   5284    // and not the expando object.
   5285    maybeEmitIdGuard(id);
   5286    ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
   5287        obj, objId, expandoVal, nativeExpandoObj);
   5288 
   5289    MOZ_ASSERT(holder == nativeExpandoObj);
   5290    emitGuardGetterSetterSlot(nativeExpandoObj, *prop, expandoObjId,
   5291                              AccessorKind::Setter);
   5292    emitCallSetterNoGuards(nativeExpandoObj, nativeExpandoObj, *prop, objId,
   5293                           rhsId);
   5294    trackAttached("SetProp.DOMProxyExpandoSetter");
   5295    return AttachDecision::Attach;
   5296  }
   5297 
   5298  return AttachDecision::NoAction;
   5299 }
   5300 
   5301 AttachDecision SetPropIRGenerator::tryAttachProxy(HandleObject obj,
   5302                                                  ObjOperandId objId,
   5303                                                  HandleId id,
   5304                                                  ValOperandId rhsId) {
   5305  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5306  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5307 
   5308  ProxyStubType type = GetProxyStubType(cx_, obj, id);
   5309  if (type == ProxyStubType::None) {
   5310    return AttachDecision::NoAction;
   5311  }
   5312  auto proxy = obj.as<ProxyObject>();
   5313 
   5314  if (mode_ == ICState::Mode::Megamorphic) {
   5315    return tryAttachGenericProxy(proxy, objId, id, rhsId,
   5316                                 /* handleDOMProxies = */ true);
   5317  }
   5318 
   5319  switch (type) {
   5320    case ProxyStubType::None:
   5321      break;
   5322    case ProxyStubType::DOMExpando:
   5323      TRY_ATTACH(tryAttachDOMProxyExpando(proxy, objId, id, rhsId));
   5324      [[fallthrough]];  // Fall through to the generic shadowed case.
   5325    case ProxyStubType::DOMShadowed:
   5326      return tryAttachDOMProxyShadowed(proxy, objId, id, rhsId);
   5327    case ProxyStubType::DOMUnshadowed:
   5328      TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy, objId, id, rhsId));
   5329      return tryAttachGenericProxy(proxy, objId, id, rhsId,
   5330                                   /* handleDOMProxies = */ true);
   5331    case ProxyStubType::Generic:
   5332      return tryAttachGenericProxy(proxy, objId, id, rhsId,
   5333                                   /* handleDOMProxies = */ false);
   5334  }
   5335 
   5336  MOZ_CRASH("Unexpected ProxyStubType");
   5337 }
   5338 
   5339 AttachDecision SetPropIRGenerator::tryAttachProxyElement(HandleObject obj,
   5340                                                         ObjOperandId objId,
   5341                                                         ValOperandId rhsId) {
   5342  // Don't attach a proxy stub for ops like JSOp::InitElem.
   5343  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5344 
   5345  if (!obj->is<ProxyObject>()) {
   5346    return AttachDecision::NoAction;
   5347  }
   5348 
   5349  writer.guardIsProxy(objId);
   5350 
   5351  // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
   5352  // proxies here as we don't have specialized DOM stubs for this.
   5353  MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   5354  writer.proxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
   5355  writer.returnFromIC();
   5356 
   5357  trackAttached("SetProp.ProxyElement");
   5358  return AttachDecision::Attach;
   5359 }
   5360 
   5361 AttachDecision SetPropIRGenerator::tryAttachMegamorphicSetElement(
   5362    HandleObject obj, ObjOperandId objId, ValOperandId rhsId) {
   5363  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5364 
   5365  if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetElem) {
   5366    return AttachDecision::NoAction;
   5367  }
   5368 
   5369  // The generic proxy stubs are faster.
   5370  if (obj->is<ProxyObject>()) {
   5371    return AttachDecision::NoAction;
   5372  }
   5373 
   5374  writer.megamorphicSetElement(objId, setElemKeyValueId(), rhsId,
   5375                               IsStrictSetPC(pc_));
   5376  writer.returnFromIC();
   5377 
   5378  trackAttached("SetProp.MegamorphicSetElement");
   5379  return AttachDecision::Attach;
   5380 }
   5381 
   5382 AttachDecision SetPropIRGenerator::tryAttachMegamorphicSetSlot(
   5383    HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId) {
   5384  if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetProp) {
   5385    return AttachDecision::NoAction;
   5386  }
   5387 
   5388  writer.megamorphicStoreSlot(objId, id, rhsId, IsStrictSetPC(pc_));
   5389  writer.returnFromIC();
   5390  trackAttached("SetProp.MegamorphicNativeSlot");
   5391  return AttachDecision::Attach;
   5392 }
   5393 
   5394 AttachDecision SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
   5395                                                        ObjOperandId objId,
   5396                                                        HandleId id,
   5397                                                        ValOperandId rhsId) {
   5398  // Don't attach a window proxy stub for ops like JSOp::InitElem.
   5399  MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
   5400 
   5401  // Attach a stub when the receiver is a WindowProxy and we can do the set
   5402  // on the Window (the global object).
   5403 
   5404  if (!IsWindowProxyForScriptGlobal(script_, obj)) {
   5405    return AttachDecision::NoAction;
   5406  }
   5407 
   5408  // If we're megamorphic prefer a generic proxy stub that handles a lot more
   5409  // cases.
   5410  if (mode_ == ICState::Mode::Megamorphic) {
   5411    return AttachDecision::NoAction;
   5412  }
   5413 
   5414  // Now try to do the set on the Window (the current global).
   5415  GlobalObject* windowObj = cx_->global();
   5416 
   5417  Maybe<PropertyInfo> prop;
   5418  SetSlotOptimizable optimizable = canAttachNativeSetSlot(windowObj, id, &prop);
   5419  switch (optimizable) {
   5420    case SetSlotOptimizable::No:
   5421      return AttachDecision::NoAction;
   5422    case SetSlotOptimizable::NotYet:
   5423      return AttachDecision::TemporarilyUnoptimizable;
   5424    case SetSlotOptimizable::Yes:
   5425      break;
   5426  }
   5427 
   5428  maybeEmitIdGuard(id);
   5429 
   5430  // Note: we don't need to GuardSpecificObject here for the ObjectFuse,
   5431  // because this GlobalObject is the only object with this shape.
   5432  ObjOperandId windowObjId =
   5433      GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
   5434  writer.guardShape(windowObjId, windowObj->shape());
   5435 
   5436  EmitStoreSlotAndReturn(writer, windowObjId, windowObj, *prop, rhsId);
   5437 
   5438  trackAttached("SetProp.WindowProxySlot");
   5439  return AttachDecision::Attach;
   5440 }
   5441 
   5442 // Detect if |id| refers to the 'prototype' property of a function object. This
   5443 // property is special-cased in canAttachAddSlotStub().
   5444 static bool IsFunctionPrototype(const JSAtomState& names, JSObject* obj,
   5445                                PropertyKey id) {
   5446  return obj->is<JSFunction>() && id.isAtom(names.prototype);
   5447 }
   5448 
   5449 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj, HandleId id) {
   5450  if (!obj->is<NativeObject>()) {
   5451    return false;
   5452  }
   5453  auto* nobj = &obj->as<NativeObject>();
   5454 
   5455  // Special-case JSFunction resolve hook to allow redefining the 'prototype'
   5456  // property without triggering lazy expansion of property and object
   5457  // allocation.
   5458  if (IsFunctionPrototype(cx_->names(), nobj, id)) {
   5459    MOZ_ASSERT(ClassMayResolveId(cx_->names(), nobj->getClass(), id, nobj));
   5460 
   5461    // We're only interested in functions that have a builtin .prototype
   5462    // property (needsPrototypeProperty). The stub will guard on this because
   5463    // the builtin .prototype property is non-configurable/non-enumerable and it
   5464    // would be wrong to add a property with those attributes to a function that
   5465    // doesn't have a builtin .prototype.
   5466    //
   5467    // Inlining needsPrototypeProperty in JIT code is complicated so we use
   5468    // isNonBuiltinConstructor as a stronger condition that's easier to check
   5469    // from JIT code.
   5470    JSFunction* fun = &nobj->as<JSFunction>();
   5471    if (!fun->isNonBuiltinConstructor()) {
   5472      return false;
   5473    }
   5474    MOZ_ASSERT(fun->needsPrototypeProperty());
   5475 
   5476    // If property exists this isn't an "add".
   5477    if (fun->lookupPure(id)) {
   5478      return false;
   5479    }
   5480  } else {
   5481    // Normal Case: If property exists or is an OOB typed array index, this
   5482    // isn't an "add".
   5483    PropertyResult prop;
   5484    if (!LookupOwnPropertyPure(cx_, nobj, id, &prop)) {
   5485      return false;
   5486    }
   5487    if (prop.isFound() || prop.isTypedArrayOutOfRange()) {
   5488      return false;
   5489    }
   5490  }
   5491 
   5492  // For now we don't optimize Watchtower-monitored objects.
   5493  if (Watchtower::watchesPropertyAdd(nobj)) {
   5494    return false;
   5495  }
   5496 
   5497  // Object must be extensible, or we must be initializing a private
   5498  // elem.
   5499  bool canAddNewProperty = nobj->isExtensible() || id.isPrivateName();
   5500  if (!canAddNewProperty) {
   5501    return false;
   5502  }
   5503 
   5504  JSOp op = JSOp(*pc_);
   5505  if (IsPropertyInitOp(op)) {
   5506    return true;
   5507  }
   5508 
   5509  MOZ_ASSERT(IsPropertySetOp(op));
   5510 
   5511  // Walk up the object prototype chain and ensure that all prototypes are
   5512  // native, and that all prototypes have no setter defined on the property.
   5513  for (JSObject* proto = nobj->staticPrototype(); proto;
   5514       proto = proto->staticPrototype()) {
   5515    if (!proto->is<NativeObject>()) {
   5516      return false;
   5517    }
   5518 
   5519    // If prototype defines this property in a non-plain way, don't optimize.
   5520    Maybe<PropertyInfo> protoProp = proto->as<NativeObject>().lookup(cx_, id);
   5521    if (protoProp.isSome() && !protoProp->isDataProperty()) {
   5522      return false;
   5523    }
   5524 
   5525    // Otherwise, if there's no such property, watch out for a resolve hook
   5526    // that would need to be invoked and thus prevent inlining of property
   5527    // addition. Allow the JSFunction resolve hook as it only defines plain
   5528    // data properties and we don't need to invoke it for objects on the
   5529    // proto chain.
   5530    if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
   5531        !proto->is<JSFunction>()) {
   5532      return false;
   5533    }
   5534  }
   5535 
   5536  return true;
   5537 }
   5538 
   5539 static PropertyFlags SetPropertyFlags(JSOp op, bool isFunctionPrototype) {
   5540  // Locked properties are non-writable, non-enumerable, and non-configurable.
   5541  if (IsLockedInitOp(op)) {
   5542    return {};
   5543  }
   5544 
   5545  // Hidden properties are writable, non-enumerable, and configurable.
   5546  if (IsHiddenInitOp(op)) {
   5547    return {
   5548        PropertyFlag::Writable,
   5549        PropertyFlag::Configurable,
   5550    };
   5551  }
   5552 
   5553  // This is a special case to overwrite an unresolved function.prototype
   5554  // property. The initial property flags of this property are writable,
   5555  // non-enumerable, and non-configurable. See canAttachAddSlotStub.
   5556  if (isFunctionPrototype) {
   5557    return {
   5558        PropertyFlag::Writable,
   5559    };
   5560  }
   5561 
   5562  // Other properties are writable, enumerable, and configurable.
   5563  return PropertyFlags::defaultDataPropFlags;
   5564 }
   5565 
   5566 AttachDecision SetPropIRGenerator::tryAttachAddSlotStub(
   5567    Handle<Shape*> oldShape) {
   5568  ValOperandId objValId(writer.setInputOperandId(0));
   5569  ValOperandId rhsValId;
   5570  if (cacheKind_ == CacheKind::SetProp) {
   5571    rhsValId = ValOperandId(writer.setInputOperandId(1));
   5572  } else {
   5573    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
   5574    MOZ_ASSERT(setElemKeyValueId().id() == 1);
   5575    writer.setInputOperandId(1);
   5576    rhsValId = ValOperandId(writer.setInputOperandId(2));
   5577  }
   5578 
   5579  RootedId id(cx_);
   5580  bool nameOrSymbol;
   5581  if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
   5582    cx_->clearPendingException();
   5583    return AttachDecision::NoAction;
   5584  }
   5585 
   5586  if (!lhsVal_.isObject() || !nameOrSymbol) {
   5587    return AttachDecision::NoAction;
   5588  }
   5589 
   5590  JSObject* obj = &lhsVal_.toObject();
   5591  if (!obj->is<NativeObject>()) {
   5592    return AttachDecision::NoAction;
   5593  }
   5594  NativeObject* nobj = &obj->as<NativeObject>();
   5595 
   5596  PropertyResult prop;
   5597  if (!LookupOwnPropertyPure(cx_, obj, id, &prop)) {
   5598    return AttachDecision::NoAction;
   5599  }
   5600  if (prop.isNotFound()) {
   5601    return AttachDecision::NoAction;
   5602  }
   5603 
   5604  MOZ_RELEASE_ASSERT(prop.isNativeProperty());
   5605  PropertyInfo propInfo = prop.propertyInfo();
   5606  NativeObject* holder = nobj;
   5607 
   5608  if (holder->inDictionaryMode()) {
   5609    return AttachDecision::NoAction;
   5610  }
   5611 
   5612  SharedShape* oldSharedShape = &oldShape->asShared();
   5613 
   5614  // The property must be the last added property of the object.
   5615  SharedShape* newShape = holder->sharedShape();
   5616  MOZ_RELEASE_ASSERT(oldShape != newShape);
   5617  MOZ_RELEASE_ASSERT(newShape->lastProperty() == propInfo);
   5618 
   5619 #ifdef DEBUG
   5620  // Verify exactly one property was added by comparing the property map
   5621  // lengths.
   5622  if (oldSharedShape->propMapLength() == PropMap::Capacity) {
   5623    MOZ_ASSERT(newShape->propMapLength() == 1);
   5624  } else {
   5625    MOZ_ASSERT(newShape->propMapLength() ==
   5626               oldSharedShape->propMapLength() + 1);
   5627  }
   5628 #endif
   5629 
   5630  bool isFunctionPrototype = IsFunctionPrototype(cx_->names(), nobj, id);
   5631 
   5632  JSOp op = JSOp(*pc_);
   5633  PropertyFlags flags = SetPropertyFlags(op, isFunctionPrototype);
   5634 
   5635  // Basic property checks.
   5636  if (!propInfo.isDataProperty() || propInfo.flags() != flags) {
   5637    return AttachDecision::NoAction;
   5638  }
   5639 
   5640  ObjOperandId objId = writer.guardToObject(objValId);
   5641  maybeEmitIdGuard(id);
   5642 
   5643  // Shape guard the object.
   5644  writer.guardShape(objId, oldShape);
   5645 
   5646  // If this is the special function.prototype case, we need to guard the
   5647  // function is a non-builtin constructor. See canAttachAddSlotStub.
   5648  if (isFunctionPrototype) {
   5649    MOZ_ASSERT(nobj->as<JSFunction>().isNonBuiltinConstructor());
   5650    writer.guardFunctionIsNonBuiltinCtor(objId);
   5651  }
   5652 
   5653  // Also shape guard the proto chain, unless this is an InitElem.
   5654  if (IsPropertySetOp(op)) {
   5655    ShapeGuardProtoChain(writer, nobj, objId);
   5656  }
   5657 
   5658  // If the JSClass has an addProperty hook, we need to call a VM function to
   5659  // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
   5660  // anything for non-index properties.
   5661  DebugOnly<uint32_t> index;
   5662  MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
   5663  bool mustCallAddPropertyHook =
   5664      !obj->is<ArrayObject>() && obj->getClass()->getAddProperty();
   5665  bool preserveWrapper =
   5666      obj->getClass()->preservesWrapper() &&
   5667      !oldShape->hasObjectFlag(ObjectFlag::HasPreservedWrapper);
   5668 
   5669  if (mustCallAddPropertyHook) {
   5670    writer.addSlotAndCallAddPropHook(objId, rhsValId, newShape);
   5671    trackAttached("SetProp.AddSlotWithAddPropertyHook");
   5672  } else if (holder->isFixedSlot(propInfo.slot())) {
   5673    size_t offset = NativeObject::getFixedSlotOffset(propInfo.slot());
   5674    writer.addAndStoreFixedSlot(objId, offset, rhsValId, newShape,
   5675                                preserveWrapper);
   5676    trackAttached("SetProp.AddSlotFixed");
   5677  } else {
   5678    size_t offset = holder->dynamicSlotIndex(propInfo.slot()) * sizeof(Value);
   5679    uint32_t numOldSlots = NativeObject::calculateDynamicSlots(oldSharedShape);
   5680    uint32_t numNewSlots = holder->numDynamicSlots();
   5681    if (numOldSlots == numNewSlots) {
   5682      writer.addAndStoreDynamicSlot(objId, offset, rhsValId, newShape,
   5683                                    preserveWrapper);
   5684      trackAttached("SetProp.AddSlotDynamic");
   5685    } else {
   5686      MOZ_ASSERT(numNewSlots > numOldSlots);
   5687      writer.allocateAndStoreDynamicSlot(objId, offset, rhsValId, newShape,
   5688                                         numNewSlots, preserveWrapper);
   5689      trackAttached("SetProp.AllocateSlot");
   5690    }
   5691  }
   5692  writer.returnFromIC();
   5693 
   5694  return AttachDecision::Attach;
   5695 }
   5696 
   5697 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script,
   5698                                             jsbytecode* pc, ICState state,
   5699                                             HandleValue lhs, HandleObject rhs)
   5700    : IRGenerator(cx, script, pc, CacheKind::InstanceOf, state),
   5701      lhsVal_(lhs),
   5702      rhsObj_(rhs) {}
   5703 
   5704 AttachDecision InstanceOfIRGenerator::tryAttachStub() {
   5705  MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf);
   5706  AutoAssertNoPendingException aanpe(cx_);
   5707 
   5708  TRY_ATTACH(tryAttachFunction());
   5709 
   5710  trackAttached(IRGenerator::NotAttached);
   5711  return AttachDecision::NoAction;
   5712 }
   5713 
   5714 AttachDecision InstanceOfIRGenerator::tryAttachFunction() {
   5715  // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
   5716  // to handle.
   5717  if (!rhsObj_->is<JSFunction>()) {
   5718    return AttachDecision::NoAction;
   5719  }
   5720 
   5721  HandleFunction fun = rhsObj_.as<JSFunction>();
   5722 
   5723  // Look up the @@hasInstance property, and check that Function.__proto__ is
   5724  // the property holder, and that no object further down the prototype chain
   5725  // (including this function) has shadowed it; together with the fact that
   5726  // Function.__proto__[@@hasInstance] is immutable, this ensures that the
   5727  // hasInstance hook will not change without the need to guard on the actual
   5728  // property value.
   5729  PropertyResult hasInstanceProp;
   5730  Rooted<NativeObject*> hasInstanceHolder(cx_);
   5731  jsid hasInstanceID = PropertyKey::Symbol(cx_->wellKnownSymbols().hasInstance);
   5732  if (!LookupPropertyPure(cx_, fun, hasInstanceID, hasInstanceHolder.address(),
   5733                          &hasInstanceProp) ||
   5734      !hasInstanceProp.isNativeProperty()) {
   5735    return AttachDecision::NoAction;
   5736  }
   5737 
   5738  if (hasInstanceHolder != &cx_->global()->getPrototype(JSProto_Function)) {
   5739    return AttachDecision::NoAction;
   5740  }
   5741 
   5742  // If the above succeeded, then these should be true about @@hasInstance,
   5743  // because the property on Function.__proto__ is an immutable data property:
   5744  MOZ_ASSERT(hasInstanceProp.propertyInfo().isDataProperty());
   5745  MOZ_ASSERT(!hasInstanceProp.propertyInfo().configurable());
   5746  MOZ_ASSERT(!hasInstanceProp.propertyInfo().writable());
   5747 
   5748  MOZ_ASSERT(IsCacheableProtoChain(fun, hasInstanceHolder));
   5749 
   5750  // Look up the function's .prototype property.
   5751  Maybe<PropertyInfo> prototypeProp = fun->lookupPure(cx_->names().prototype);
   5752  if (prototypeProp.isNothing()) {
   5753    if (!fun->needsPrototypeProperty()) {
   5754      return AttachDecision::NoAction;
   5755    }
   5756    // The function does not have a (lazily resolved) .prototype property yet.
   5757    // If the LHS is a primitive, the fallback code in OrdinaryHasInstance will
   5758    // return before resolving this property. Our CacheIR implementation expects
   5759    // a .prototype property so we resolve it now.
   5760    bool hasProp;
   5761    if (!HasProperty(cx_, fun, cx_->names().prototype, &hasProp)) {
   5762      cx_->clearPendingException();
   5763      return AttachDecision::NoAction;
   5764    }
   5765    MOZ_ASSERT(hasProp);
   5766    prototypeProp = fun->lookupPure(cx_->names().prototype);
   5767    MOZ_ASSERT(prototypeProp);
   5768  }
   5769  if (!prototypeProp->isDataProperty()) {
   5770    return AttachDecision::NoAction;
   5771  }
   5772 
   5773  // Ensure the .prototype value is an object.
   5774  uint32_t prototypeSlot = prototypeProp->slot();
   5775  MOZ_ASSERT(prototypeSlot >= fun->numFixedSlots(),
   5776             "LoadDynamicSlot expects a dynamic slot");
   5777  if (!fun->getSlot(prototypeSlot).isObject()) {
   5778    return AttachDecision::NoAction;
   5779  }
   5780 
   5781  // Abstract Objects
   5782  ValOperandId lhs(writer.setInputOperandId(0));
   5783  ValOperandId rhs(writer.setInputOperandId(1));
   5784 
   5785  ObjOperandId rhsId = writer.guardToObject(rhs);
   5786  writer.guardShape(rhsId, fun->shape());
   5787 
   5788  // Ensure that the shapes up the prototype chain for the RHS remain the same
   5789  // so that @@hasInstance is not shadowed by some intermediate prototype
   5790  // object.
   5791  if (hasInstanceHolder != fun) {
   5792    GeneratePrototypeGuards(writer, fun, hasInstanceHolder, rhsId);
   5793    ObjOperandId holderId = writer.loadObject(hasInstanceHolder);
   5794    TestMatchingHolder(writer, hasInstanceHolder, holderId);
   5795  }
   5796 
   5797  // Load the .prototype value and ensure it's an object.
   5798  ValOperandId protoValId =
   5799      writer.loadDynamicSlot(rhsId, prototypeSlot - fun->numFixedSlots());
   5800  ObjOperandId protoId = writer.guardToObject(protoValId);
   5801 
   5802  // Needn't guard LHS is object, because the actual stub can handle that
   5803  // and correctly return false.
   5804  writer.loadInstanceOfObjectResult(lhs, protoId);
   5805  writer.returnFromIC();
   5806  trackAttached("InstanceOf");
   5807  return AttachDecision::Attach;
   5808 }
   5809 
   5810 void InstanceOfIRGenerator::trackAttached(const char* name) {
   5811  stubName_ = name ? name : "NotAttached";
   5812 #ifdef JS_CACHEIR_SPEW
   5813  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   5814    sp.valueProperty("lhs", lhsVal_);
   5815    sp.valueProperty("rhs", ObjectValue(*rhsObj_));
   5816  }
   5817 #else
   5818  // Silence Clang -Wunused-private-field warning.
   5819  (void)lhsVal_;
   5820 #endif
   5821 }
   5822 
   5823 TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script,
   5824                                     jsbytecode* pc, ICState state,
   5825                                     HandleValue value)
   5826    : IRGenerator(cx, script, pc, CacheKind::TypeOf, state), val_(value) {}
   5827 
   5828 void TypeOfIRGenerator::trackAttached(const char* name) {
   5829  stubName_ = name ? name : "NotAttached";
   5830 #ifdef JS_CACHEIR_SPEW
   5831  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   5832    sp.valueProperty("val", val_);
   5833  }
   5834 #endif
   5835 }
   5836 
   5837 AttachDecision TypeOfIRGenerator::tryAttachStub() {
   5838  MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
   5839 
   5840  AutoAssertNoPendingException aanpe(cx_);
   5841 
   5842  ValOperandId valId(writer.setInputOperandId(0));
   5843 
   5844  TRY_ATTACH(tryAttachPrimitive(valId));
   5845  TRY_ATTACH(tryAttachObject(valId));
   5846 
   5847  MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
   5848  return AttachDecision::NoAction;
   5849 }
   5850 
   5851 AttachDecision TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId) {
   5852  if (!val_.isPrimitive()) {
   5853    return AttachDecision::NoAction;
   5854  }
   5855 
   5856  // Note: we don't use GuardIsNumber for int32 values because it's less
   5857  // efficient in Warp (unboxing to double instead of int32).
   5858  if (val_.isDouble()) {
   5859    writer.guardIsNumber(valId);
   5860  } else {
   5861    writer.guardNonDoubleType(valId, val_.type());
   5862  }
   5863 
   5864  writer.loadConstantStringResult(
   5865      TypeName(js::TypeOfValue(val_), cx_->names()));
   5866  writer.returnFromIC();
   5867  writer.setTypeData(TypeData(JSValueType(val_.type())));
   5868  trackAttached("TypeOf.Primitive");
   5869  return AttachDecision::Attach;
   5870 }
   5871 
   5872 AttachDecision TypeOfIRGenerator::tryAttachObject(ValOperandId valId) {
   5873  if (!val_.isObject()) {
   5874    return AttachDecision::NoAction;
   5875  }
   5876 
   5877  ObjOperandId objId = writer.guardToObject(valId);
   5878  writer.loadTypeOfObjectResult(objId);
   5879  writer.returnFromIC();
   5880  writer.setTypeData(TypeData(JSValueType(val_.type())));
   5881  trackAttached("TypeOf.Object");
   5882  return AttachDecision::Attach;
   5883 }
   5884 
   5885 TypeOfEqIRGenerator::TypeOfEqIRGenerator(JSContext* cx, HandleScript script,
   5886                                         jsbytecode* pc, ICState state,
   5887                                         HandleValue value, JSType type,
   5888                                         JSOp compareOp)
   5889    : IRGenerator(cx, script, pc, CacheKind::TypeOfEq, state),
   5890      val_(value),
   5891      type_(type),
   5892      compareOp_(compareOp) {}
   5893 
   5894 void TypeOfEqIRGenerator::trackAttached(const char* name) {
   5895  stubName_ = name ? name : "NotAttached";
   5896 #ifdef JS_CACHEIR_SPEW
   5897  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   5898    sp.valueProperty("val", val_);
   5899    sp.jstypeProperty("type", type_);
   5900    sp.opcodeProperty("compareOp", compareOp_);
   5901  }
   5902 #endif
   5903 }
   5904 
   5905 AttachDecision TypeOfEqIRGenerator::tryAttachStub() {
   5906  MOZ_ASSERT(cacheKind_ == CacheKind::TypeOfEq);
   5907 
   5908  AutoAssertNoPendingException aanpe(cx_);
   5909 
   5910  ValOperandId valId(writer.setInputOperandId(0));
   5911 
   5912  TRY_ATTACH(tryAttachPrimitive(valId));
   5913  TRY_ATTACH(tryAttachObject(valId));
   5914 
   5915  MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOfEq");
   5916  return AttachDecision::NoAction;
   5917 }
   5918 
   5919 AttachDecision TypeOfEqIRGenerator::tryAttachPrimitive(ValOperandId valId) {
   5920  if (!val_.isPrimitive()) {
   5921    return AttachDecision::NoAction;
   5922  }
   5923 
   5924  // Note: we don't use GuardIsNumber for int32 values because it's less
   5925  // efficient in Warp (unboxing to double instead of int32).
   5926  if (val_.isDouble()) {
   5927    writer.guardIsNumber(valId);
   5928  } else {
   5929    writer.guardNonDoubleType(valId, val_.type());
   5930  }
   5931 
   5932  bool result = js::TypeOfValue(val_) == type_;
   5933  if (compareOp_ == JSOp::Ne) {
   5934    result = !result;
   5935  }
   5936  writer.loadBooleanResult(result);
   5937  writer.returnFromIC();
   5938  writer.setTypeData(TypeData(JSValueType(val_.type())));
   5939  trackAttached("TypeOfEq.Primitive");
   5940  return AttachDecision::Attach;
   5941 }
   5942 
   5943 AttachDecision TypeOfEqIRGenerator::tryAttachObject(ValOperandId valId) {
   5944  if (!val_.isObject()) {
   5945    return AttachDecision::NoAction;
   5946  }
   5947 
   5948  ObjOperandId objId = writer.guardToObject(valId);
   5949  writer.loadTypeOfEqObjectResult(objId, TypeofEqOperand(type_, compareOp_));
   5950  writer.returnFromIC();
   5951  writer.setTypeData(TypeData(JSValueType(val_.type())));
   5952  trackAttached("TypeOfEq.Object");
   5953  return AttachDecision::Attach;
   5954 }
   5955 
   5956 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx,
   5957                                               HandleScript script,
   5958                                               jsbytecode* pc, ICState state,
   5959                                               HandleValue value)
   5960    : IRGenerator(cx, script, pc, CacheKind::GetIterator, state), val_(value) {}
   5961 
   5962 AttachDecision GetIteratorIRGenerator::tryAttachStub() {
   5963  MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
   5964 
   5965  AutoAssertNoPendingException aanpe(cx_);
   5966 
   5967  ValOperandId valId(writer.setInputOperandId(0));
   5968 
   5969  TRY_ATTACH(tryAttachObject(valId));
   5970  TRY_ATTACH(tryAttachNullOrUndefined(valId));
   5971  TRY_ATTACH(tryAttachGeneric(valId));
   5972 
   5973  trackAttached(IRGenerator::NotAttached);
   5974  return AttachDecision::NoAction;
   5975 }
   5976 
   5977 AttachDecision GetIteratorIRGenerator::tryAttachObject(ValOperandId valId) {
   5978  if (!val_.isObject()) {
   5979    return AttachDecision::NoAction;
   5980  }
   5981 
   5982  MOZ_ASSERT(val_.toObject().compartment() == cx_->compartment());
   5983 
   5984  ObjOperandId objId = writer.guardToObject(valId);
   5985  writer.objectToIteratorResult(objId, cx_->compartment()->enumeratorsAddr());
   5986  writer.returnFromIC();
   5987 
   5988  trackAttached("GetIterator.Object");
   5989  return AttachDecision::Attach;
   5990 }
   5991 
   5992 AttachDecision GetIteratorIRGenerator::tryAttachNullOrUndefined(
   5993    ValOperandId valId) {
   5994  MOZ_ASSERT(JSOp(*pc_) == JSOp::Iter);
   5995 
   5996  // For null/undefined we can simply return the empty iterator singleton. This
   5997  // works because this iterator is unlinked and immutable.
   5998 
   5999  if (!val_.isNullOrUndefined()) {
   6000    return AttachDecision::NoAction;
   6001  }
   6002 
   6003  PropertyIteratorObject* emptyIter =
   6004      GlobalObject::getOrCreateEmptyIterator(cx_);
   6005  if (!emptyIter) {
   6006    cx_->recoverFromOutOfMemory();
   6007    return AttachDecision::NoAction;
   6008  }
   6009 
   6010  writer.guardIsNullOrUndefined(valId);
   6011 
   6012  ObjOperandId iterId = writer.loadObject(emptyIter);
   6013  writer.loadObjectResult(iterId);
   6014  writer.returnFromIC();
   6015 
   6016  trackAttached("GetIterator.NullOrUndefined");
   6017  return AttachDecision::Attach;
   6018 }
   6019 
   6020 AttachDecision GetIteratorIRGenerator::tryAttachGeneric(ValOperandId valId) {
   6021  writer.valueToIteratorResult(valId);
   6022  writer.returnFromIC();
   6023 
   6024  trackAttached("GetIterator.Generic");
   6025  return AttachDecision::Attach;
   6026 }
   6027 
   6028 void GetIteratorIRGenerator::trackAttached(const char* name) {
   6029  stubName_ = name ? name : "NotAttached";
   6030 #ifdef JS_CACHEIR_SPEW
   6031  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   6032    sp.valueProperty("val", val_);
   6033  }
   6034 #endif
   6035 }
   6036 
   6037 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
   6038    JSContext* cx, HandleScript script, jsbytecode* pc, ICState state,
   6039    HandleValue value)
   6040    : IRGenerator(cx, script, pc, CacheKind::OptimizeSpreadCall, state),
   6041      val_(value) {}
   6042 
   6043 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachStub() {
   6044  MOZ_ASSERT(cacheKind_ == CacheKind::OptimizeSpreadCall);
   6045 
   6046  AutoAssertNoPendingException aanpe(cx_);
   6047 
   6048  TRY_ATTACH(tryAttachArray());
   6049  TRY_ATTACH(tryAttachArguments());
   6050  TRY_ATTACH(tryAttachNotOptimizable());
   6051 
   6052  trackAttached(IRGenerator::NotAttached);
   6053  return AttachDecision::NoAction;
   6054 }
   6055 
   6056 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArray() {
   6057  if (!isFirstStub_) {
   6058    return AttachDecision::NoAction;
   6059  }
   6060 
   6061  // The value must be a packed array.
   6062  if (!val_.isObject()) {
   6063    return AttachDecision::NoAction;
   6064  }
   6065  Rooted<JSObject*> obj(cx_, &val_.toObject());
   6066  if (!IsArrayWithDefaultIterator<MustBePacked::Yes>(obj, cx_)) {
   6067    return AttachDecision::NoAction;
   6068  }
   6069 
   6070  ValOperandId valId(writer.setInputOperandId(0));
   6071  ObjOperandId objId = writer.guardToObject(valId);
   6072 
   6073  // Guard the object is a packed array with Array.prototype as proto.
   6074  MOZ_ASSERT(obj->is<ArrayObject>());
   6075  writer.guardShape(objId, obj->shape());
   6076  writer.guardArrayIsPacked(objId);
   6077 
   6078  // Ensure Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next
   6079  // haven't been mutated.
   6080  writer.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse);
   6081 
   6082  writer.loadObjectResult(objId);
   6083  writer.returnFromIC();
   6084 
   6085  trackAttached("OptimizeSpreadCall.Array");
   6086  return AttachDecision::Attach;
   6087 }
   6088 
   6089 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArguments() {
   6090  // The value must be an arguments object.
   6091  if (!val_.isObject()) {
   6092    return AttachDecision::NoAction;
   6093  }
   6094  RootedObject obj(cx_, &val_.toObject());
   6095  if (!obj->is<ArgumentsObject>()) {
   6096    return AttachDecision::NoAction;
   6097  }
   6098  auto args = obj.as<ArgumentsObject>();
   6099 
   6100  // Ensure neither elements, nor the length, nor the iterator has been
   6101  // overridden. Also ensure no args are forwarded to allow reading them
   6102  // directly from the frame.
   6103  if (args->hasOverriddenElement() || args->hasOverriddenLength() ||
   6104      args->hasOverriddenIterator() || args->anyArgIsForwarded()) {
   6105    return AttachDecision::NoAction;
   6106  }
   6107 
   6108  // Don't optimize arguments objects from a different realm because in this
   6109  // case we have to use the other realm's %ArrayIteratorPrototype% object.
   6110  if (cx_->realm() != args->realm()) {
   6111    return AttachDecision::NoAction;
   6112  }
   6113 
   6114  if (!HasOptimizableArrayIteratorPrototype(cx_)) {
   6115    return AttachDecision::NoAction;
   6116  }
   6117 
   6118  Rooted<Shape*> shape(cx_, GlobalObject::getArrayShapeWithDefaultProto(cx_));
   6119  if (!shape) {
   6120    cx_->clearPendingException();
   6121    return AttachDecision::NoAction;
   6122  }
   6123 
   6124  ValOperandId valId(writer.setInputOperandId(0));
   6125  ObjOperandId objId = writer.guardToObject(valId);
   6126 
   6127  if (args->is<MappedArgumentsObject>()) {
   6128    writer.guardClass(objId, GuardClassKind::MappedArguments);
   6129  } else {
   6130    MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   6131    writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   6132  }
   6133  uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
   6134                  ArgumentsObject::LENGTH_OVERRIDDEN_BIT |
   6135                  ArgumentsObject::ITERATOR_OVERRIDDEN_BIT |
   6136                  ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
   6137  writer.guardArgumentsObjectFlags(objId, flags);
   6138  writer.guardObjectHasSameRealm(objId);
   6139 
   6140  writer.guardFuse(RealmFuses::FuseIndex::OptimizeArrayIteratorPrototypeFuse);
   6141 
   6142  writer.arrayFromArgumentsObjectResult(objId, shape);
   6143  writer.returnFromIC();
   6144 
   6145  trackAttached("OptimizeSpreadCall.Arguments");
   6146  return AttachDecision::Attach;
   6147 }
   6148 
   6149 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
   6150  ValOperandId valId(writer.setInputOperandId(0));
   6151 
   6152  writer.loadUndefinedResult();
   6153  writer.returnFromIC();
   6154 
   6155  trackAttached("OptimizeSpreadCall.NotOptimizable");
   6156  return AttachDecision::Attach;
   6157 }
   6158 
   6159 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name) {
   6160  stubName_ = name ? name : "NotAttached";
   6161 #ifdef JS_CACHEIR_SPEW
   6162  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
   6163    sp.valueProperty("val", val_);
   6164  }
   6165 #endif
   6166 }
   6167 
   6168 CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script,
   6169                                 jsbytecode* pc, ICState state,
   6170                                 BaselineFrame* frame, uint32_t argc,
   6171                                 HandleValue callee, HandleValue thisval,
   6172                                 HandleValue newTarget, HandleValueArray args)
   6173    : IRGenerator(cx, script, pc, CacheKind::Call, state, frame),
   6174      argc_(argc),
   6175      callee_(callee),
   6176      thisval_(thisval),
   6177      newTarget_(newTarget),
   6178      args_(args) {}
   6179 
   6180 bool InlinableNativeIRGenerator::isCalleeBoundFunction() const {
   6181  return callee()->is<BoundFunctionObject>();
   6182 }
   6183 
   6184 BoundFunctionObject* InlinableNativeIRGenerator::boundCallee() const {
   6185  MOZ_ASSERT(isCalleeBoundFunction());
   6186  return &callee()->as<BoundFunctionObject>();
   6187 }
   6188 
   6189 ObjOperandId InlinableNativeIRGenerator::emitNativeCalleeGuard(
   6190    Int32OperandId argcId) {
   6191  // Note: we rely on GuardSpecificFunction to also guard against the same
   6192  // native from a different realm.
   6193  MOZ_ASSERT(target_->isNativeWithoutJitEntry());
   6194 
   6195  // Guards already emitted in GetPropIRGenerator::emitCallGetterResultGuards.
   6196  if (isAccessorOp()) {
   6197    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard);
   6198    MOZ_ASSERT(!flags_.isConstructing());
   6199    MOZ_ASSERT(!isCalleeBoundFunction());
   6200    MOZ_ASSERT(!isTargetBoundFunction());
   6201    return ObjOperandId();
   6202  }
   6203 
   6204  ValOperandId calleeValId;
   6205  switch (flags_.getArgFormat()) {
   6206    case CallFlags::Standard:
   6207    case CallFlags::Spread:
   6208      calleeValId = writer.loadArgumentFixedSlot(ArgumentKind::Callee,
   6209                                                 stackArgc(), flags_);
   6210      break;
   6211    case CallFlags::FunCall:
   6212    case CallFlags::FunApplyArray:
   6213    case CallFlags::FunApplyNullUndefined:
   6214      calleeValId = writer.loadArgumentFixedSlot(
   6215          ArgumentKind::Callee, stackArgc(), CallFlags(CallFlags::Standard));
   6216      break;
   6217    case CallFlags::Unknown:
   6218    case CallFlags::FunApplyArgsObj:
   6219      MOZ_CRASH("Unsupported arg format");
   6220  }
   6221 
   6222  // Guard that |callee| is an object.
   6223  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
   6224  ObjOperandId targetId = calleeObjId;
   6225 
   6226  // The callee is a bound function whose bound target is |target_|.
   6227  //
   6228  // Example:
   6229  // ```
   6230  // var boundPush = Array.prototype.push.bind(arr);
   6231  // boundPush(1);
   6232  // ```
   6233  //
   6234  // Relevant generator members:
   6235  // - |CallIRGenerator::callee_| is `boundPush`
   6236  // - |InlinableNativeIRGenerator::target_| is `Array.prototype.push`
   6237  //
   6238  // Also see tryAttachBound{Native,FunCall,FunApply}.
   6239  if (isCalleeBoundFunction()) {
   6240    // Ensure the callee is a bound function.
   6241    writer.guardClass(calleeObjId, GuardClassKind::BoundFunction);
   6242 
   6243    // Ensure numBoundArgs matches.
   6244    size_t numBoundArgs = boundCallee()->numBoundArgs();
   6245    Int32OperandId numBoundArgsId =
   6246        writer.loadBoundFunctionNumArgs(calleeObjId);
   6247    writer.guardSpecificInt32(numBoundArgsId, numBoundArgs);
   6248 
   6249    // Load the bound function target.
   6250    targetId = writer.loadBoundFunctionTarget(calleeObjId);
   6251  }
   6252 
   6253  if (flags_.getArgFormat() == CallFlags::FunCall ||
   6254      flags_.getArgFormat() == CallFlags::FunApplyArray ||
   6255      flags_.getArgFormat() == CallFlags::FunApplyNullUndefined) {
   6256    JSFunction* funCallOrApply;
   6257    ValOperandId thisValId;
   6258    if (isCalleeBoundFunction()) {
   6259      MOZ_ASSERT(flags_.getArgFormat() == CallFlags::FunCall ||
   6260                     flags_.getArgFormat() == CallFlags::FunApplyNullUndefined,
   6261                 "unexpected bound function");
   6262 
   6263      funCallOrApply = &boundCallee()->getTarget()->as<JSFunction>();
   6264      thisValId = writer.loadFixedSlot(
   6265          calleeObjId, BoundFunctionObject::offsetOfBoundThisSlot());
   6266    } else {
   6267      funCallOrApply = &callee()->as<JSFunction>();
   6268      thisValId = writer.loadArgumentFixedSlot(ArgumentKind::This, stackArgc(),
   6269                                               CallFlags(CallFlags::Standard));
   6270    }
   6271    MOZ_ASSERT(funCallOrApply->native() == fun_call ||
   6272               funCallOrApply->native() == fun_apply);
   6273 
   6274    // Guard that |target| is the |fun_call| or |fun_apply| native function.
   6275    writer.guardSpecificFunction(targetId, funCallOrApply);
   6276 
   6277    // Guard that |this| is an object.
   6278    targetId = writer.guardToObject(thisValId);
   6279  }
   6280 
   6281  // The callee calls a bound function whose bound target is |target_|.
   6282  //
   6283  // For example:
   6284  // ```
   6285  // var boundPush = Array.prototype.push.bind(arr);
   6286  // boundPush.call(null, 1);
   6287  // ```
   6288  //
   6289  // Relevant generator members:
   6290  // - |CallIRGenerator::callee_| is `Function.prototype.call`
   6291  // - |CallIRGenerator::thisval_| is `boundPush`
   6292  // - |InlinableNativeIRGenerator::target_| is `Array.prototype.push`
   6293  //
   6294  // Also see tryAttach{FunCall,FunApply}Bound.
   6295  if (isTargetBoundFunction()) {
   6296    MOZ_ASSERT(!isCalleeBoundFunction(), "unexpected nested bound functions");
   6297    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::FunCall ||
   6298                   flags_.getArgFormat() == CallFlags::FunApplyNullUndefined,
   6299               "unsupported arg-format for bound target");
   6300 
   6301    // Ensure that |target| is a bound function.
   6302    writer.guardClass(targetId, GuardClassKind::BoundFunction);
   6303 
   6304    // Ensure numBoundArgs matches.
   6305    size_t numBoundArgs = boundTarget()->numBoundArgs();
   6306    Int32OperandId numBoundArgsId = writer.loadBoundFunctionNumArgs(targetId);
   6307    writer.guardSpecificInt32(numBoundArgsId, numBoundArgs);
   6308 
   6309    // Return the bound function as callee to support loading bound arguments.
   6310    calleeObjId = targetId;
   6311 
   6312    // Load the bound function target.
   6313    targetId = writer.loadBoundFunctionTarget(targetId);
   6314  }
   6315 
   6316  writer.guardSpecificFunction(targetId, target_);
   6317 
   6318  // If we're constructing we also need to guard newTarget == callee.
   6319  if (flags_.isConstructing()) {
   6320    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard);
   6321    MOZ_ASSERT(&newTarget_.toObject() == callee());
   6322 
   6323    ValOperandId newTargetValId = writer.loadArgumentFixedSlot(
   6324        ArgumentKind::NewTarget, stackArgc(), flags_);
   6325    ObjOperandId newTargetObjId = writer.guardToObject(newTargetValId);
   6326 
   6327    if (isCalleeBoundFunction()) {
   6328      writer.guardObjectIdentity(newTargetObjId, calleeObjId);
   6329    } else {
   6330      writer.guardSpecificFunction(newTargetObjId, target_);
   6331    }
   6332  }
   6333 
   6334  // Guard the second argument is null or undefined.
   6335  if (flags_.getArgFormat() == CallFlags::FunApplyNullUndefined) {
   6336    constexpr size_t argIndex = 1;
   6337 
   6338    size_t numBoundArgs = 0;
   6339    if (isCalleeBoundFunction()) {
   6340      numBoundArgs = boundCallee()->numBoundArgs();
   6341    }
   6342    MOZ_ASSERT(numBoundArgs <= 2);
   6343 
   6344    ValOperandId argValId;
   6345    if (argIndex < numBoundArgs) {
   6346      argValId = loadBoundArgument(calleeObjId, argIndex);
   6347    } else {
   6348      auto argKind = ArgumentKindForArgIndex(argIndex - numBoundArgs);
   6349      argValId = writer.loadArgumentFixedSlot(argKind, stackArgc(),
   6350                                              CallFlags(CallFlags::Standard));
   6351    }
   6352 
   6353    writer.guardIsNullOrUndefined(argValId);
   6354  }
   6355 
   6356  return calleeObjId;
   6357 }
   6358 
   6359 ObjOperandId InlinableNativeIRGenerator::emitLoadArgsArray() {
   6360  MOZ_ASSERT(!hasBoundArguments());
   6361 
   6362  if (flags_.getArgFormat() == CallFlags::Spread) {
   6363    return writer.loadSpreadArgs();
   6364  }
   6365 
   6366  MOZ_ASSERT(flags_.getArgFormat() == CallFlags::FunApplyArray);
   6367  return gen_.as<CallIRGenerator*>()
   6368      ->emitFunApplyArgsGuard(flags_.getArgFormat())
   6369      .ref();
   6370 }
   6371 
   6372 ValOperandId InlinableNativeIRGenerator::loadBoundArgument(
   6373    ObjOperandId calleeId, size_t argIndex) {
   6374  MOZ_ASSERT(isCalleeBoundFunction() || isTargetBoundFunction());
   6375 
   6376  auto* bound = isCalleeBoundFunction() ? boundCallee() : boundTarget();
   6377  size_t numBoundArgs = bound->numBoundArgs();
   6378  MOZ_ASSERT(argIndex < numBoundArgs);
   6379 
   6380  if (numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs) {
   6381    constexpr size_t inlineArgsOffset =
   6382        BoundFunctionObject::offsetOfFirstInlineBoundArg();
   6383 
   6384    size_t argSlot = inlineArgsOffset + argIndex * sizeof(Value);
   6385    return writer.loadFixedSlot(calleeId, argSlot);
   6386  }
   6387  return writer.loadBoundFunctionArgument(calleeId, argIndex);
   6388 }
   6389 
   6390 ValOperandId InlinableNativeIRGenerator::loadThis(ObjOperandId calleeId) {
   6391  // Accessor operations use the receiver as their |this| value.
   6392  if (isAccessorOp()) {
   6393    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard);
   6394    MOZ_ASSERT(!isTargetBoundFunction());
   6395    MOZ_ASSERT(!isCalleeBoundFunction());
   6396    MOZ_ASSERT(receiverId_.valid());
   6397    return receiverId_;
   6398  }
   6399 
   6400  switch (flags_.getArgFormat()) {
   6401    case CallFlags::Standard:
   6402    case CallFlags::Spread:
   6403      MOZ_ASSERT(!isTargetBoundFunction());
   6404      if (isCalleeBoundFunction()) {
   6405        return writer.loadFixedSlot(
   6406            calleeId, BoundFunctionObject::offsetOfBoundThisSlot());
   6407      }
   6408      return writer.loadArgumentFixedSlot(ArgumentKind::This, stackArgc(),
   6409                                          flags_);
   6410    case CallFlags::FunCall:
   6411    case CallFlags::FunApplyNullUndefined:
   6412      // Load |this| from bound this.
   6413      if (isTargetBoundFunction()) {
   6414        return writer.loadFixedSlot(
   6415            calleeId, BoundFunctionObject::offsetOfBoundThisSlot());
   6416      }
   6417 
   6418      // Load |this| from bound arguments, if present.
   6419      if (hasBoundArguments()) {
   6420        MOZ_ASSERT(isCalleeBoundFunction());
   6421        return loadBoundArgument(calleeId, 0);
   6422      }
   6423 
   6424      // The stack layout is already in the correct form for calls with at least
   6425      // one argument.
   6426      //
   6427      // clang-format off
   6428      //
   6429      // *** STACK LAYOUT (bottom to top) ***   *** INDEX ***
   6430      //   Callee                               <-- argc+1
   6431      //   ThisValue                            <-- argc
   6432      //   Args: | Arg0 |                       <-- argc-1
   6433      //         | Arg1 |                       <-- argc-2
   6434      //         | ...  |                       <-- ...
   6435      //         | ArgN |                       <-- 0
   6436      //
   6437      // When passing |argc-1| as the number of arguments, we get:
   6438      //
   6439      // *** STACK LAYOUT (bottom to top) ***   *** INDEX ***
   6440      //   Callee                               <-- (argc-1)+1 = argc   = ThisValue
   6441      //   ThisValue                            <-- (argc-1)   = argc-1 = Arg0
   6442      //   Args: | Arg0   |                     <-- (argc-1)-1 = argc-2 = Arg1
   6443      //         | Arg1   |                     <-- (argc-1)-2 = argc-3 = Arg2
   6444      //         | ...    |                     <-- ...
   6445      //
   6446      // clang-format on
   6447      //
   6448      // This allows to call |loadArgumentFixedSlot(ArgumentKind::This)| and we
   6449      // still load the correct argument index from |ArgumentKind::Arg0|.
   6450      //
   6451      // When no arguments are passed, i.e. |argc==0|, we have to replace
   6452      // |ArgumentKind::Arg0| with the undefined value.
   6453      if (stackArgc() == 0) {
   6454        return writer.loadUndefined();
   6455      }
   6456      return writer.loadArgumentFixedSlot(ArgumentKind::This, stackArgc() - 1,
   6457                                          CallFlags(CallFlags::Standard));
   6458    case CallFlags::FunApplyArray:
   6459    case CallFlags::FunApplyArgsObj:
   6460      MOZ_ASSERT(stackArgc() > 0);
   6461      MOZ_ASSERT(!isCalleeBoundFunction());
   6462      MOZ_ASSERT(!isTargetBoundFunction());
   6463      return writer.loadArgumentFixedSlot(ArgumentKind::This, stackArgc() - 1,
   6464                                          CallFlags(CallFlags::Standard));
   6465    case CallFlags::Unknown:
   6466      break;
   6467  }
   6468  MOZ_CRASH("Unsupported arg format");
   6469 }
   6470 
   6471 ValOperandId InlinableNativeIRGenerator::loadArgument(ObjOperandId calleeId,
   6472                                                      ArgumentKind kind) {
   6473  MOZ_ASSERT(kind >= ArgumentKind::Arg0);
   6474  MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard ||
   6475             flags_.getArgFormat() == CallFlags::FunCall ||
   6476             flags_.getArgFormat() == CallFlags::FunApplyNullUndefined);
   6477  MOZ_ASSERT_IF(flags_.getArgFormat() == CallFlags::FunApplyNullUndefined,
   6478                isTargetBoundFunction() && hasBoundArguments());
   6479  MOZ_ASSERT(!isAccessorOp(), "get property operations don't have arguments");
   6480 
   6481  // Check if the |this| value is stored in the bound arguments.
   6482  bool thisFromBoundArgs = flags_.getArgFormat() == CallFlags::FunCall &&
   6483                           isCalleeBoundFunction() && hasBoundArguments();
   6484 
   6485  if (hasBoundArguments()) {
   6486    auto* bound = isCalleeBoundFunction() ? boundCallee() : boundTarget();
   6487    size_t numBoundArgs = bound->numBoundArgs();
   6488    size_t argIndex = uint8_t(kind) - uint8_t(ArgumentKind::Arg0);
   6489 
   6490    // Skip over the first bound argument, which stores the |this| value for
   6491    // bound FunCall.
   6492    if (thisFromBoundArgs) {
   6493      argIndex += 1;
   6494    }
   6495 
   6496    // Load from bound args.
   6497    if (argIndex < numBoundArgs) {
   6498      return loadBoundArgument(calleeId, argIndex);
   6499    }
   6500 
   6501    // Load from stack arguments.
   6502    kind = ArgumentKindForArgIndex(argIndex - numBoundArgs);
   6503  }
   6504 
   6505  switch (flags_.getArgFormat()) {
   6506    case CallFlags::Standard:
   6507      return writer.loadArgumentFixedSlot(kind, stackArgc(), flags_);
   6508    case CallFlags::FunCall:
   6509      if (thisFromBoundArgs) {
   6510        return writer.loadArgumentFixedSlot(kind, stackArgc(),
   6511                                            CallFlags(CallFlags::Standard));
   6512      }
   6513      MOZ_ASSERT(stackArgc() > 1);
   6514      // See |loadThis| for why we subtract |argc - 1| here.
   6515      return writer.loadArgumentFixedSlot(kind, stackArgc() - 1,
   6516                                          CallFlags(CallFlags::Standard));
   6517    case CallFlags::Spread:
   6518    case CallFlags::FunApplyArray:
   6519    case CallFlags::FunApplyArgsObj:
   6520    case CallFlags::FunApplyNullUndefined:
   6521    case CallFlags::Unknown:
   6522      break;
   6523  }
   6524  MOZ_CRASH("Unsupported arg format");
   6525 }
   6526 
   6527 bool InlinableNativeIRGenerator::hasBoundArguments() const {
   6528  if (isCalleeBoundFunction()) {
   6529    return boundCallee()->numBoundArgs() != 0;
   6530  }
   6531  if (isTargetBoundFunction()) {
   6532    return boundTarget()->numBoundArgs() != 0;
   6533  }
   6534  return false;
   6535 }
   6536 
   6537 void IRGenerator::emitCalleeGuard(ObjOperandId calleeId, JSFunction* callee) {
   6538  // Guarding on the callee JSFunction* is most efficient, but doesn't work well
   6539  // for lambda clones (multiple functions with the same BaseScript). We guard
   6540  // on the function's BaseScript if the callee is scripted and this isn't the
   6541  // first IC stub.
   6542  if (isFirstStub_ || !FunctionHasStableBaseScript(callee)) {
   6543    writer.guardSpecificFunction(calleeId, callee);
   6544  } else {
   6545    MOZ_ASSERT_IF(callee->isSelfHostedBuiltin(),
   6546                  !callee->baseScript()->allowRelazify());
   6547    writer.guardClass(calleeId, GuardClassKind::JSFunction);
   6548    writer.guardFunctionScript(calleeId, callee->baseScript());
   6549  }
   6550 }
   6551 
   6552 ObjOperandId CallIRGenerator::emitFunCallOrApplyGuard(Int32OperandId argcId) {
   6553  JSFunction* callee = &callee_.toObject().as<JSFunction>();
   6554  MOZ_ASSERT(callee->native() == fun_call || callee->native() == fun_apply);
   6555 
   6556  // |GetIndexOfArgument| doesn't yet support FunCall/FunApply.
   6557  CallFlags flags(CallFlags::Standard);
   6558 
   6559  // Guard that callee is the |fun_call| or |fun_apply| native function.
   6560  ValOperandId calleeValId =
   6561      writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
   6562  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
   6563  writer.guardSpecificFunction(calleeObjId, callee);
   6564 
   6565  // Guard that |this| is an object.
   6566  ValOperandId thisValId =
   6567      writer.loadArgumentDynamicSlot(ArgumentKind::This, argcId, flags);
   6568  return writer.guardToObject(thisValId);
   6569 }
   6570 
   6571 ObjOperandId CallIRGenerator::emitFunCallGuard(Int32OperandId argcId) {
   6572  MOZ_ASSERT(callee_.toObject().as<JSFunction>().native() == fun_call);
   6573 
   6574  return emitFunCallOrApplyGuard(argcId);
   6575 }
   6576 
   6577 ObjOperandId CallIRGenerator::emitFunApplyGuard(Int32OperandId argcId) {
   6578  MOZ_ASSERT(callee_.toObject().as<JSFunction>().native() == fun_apply);
   6579 
   6580  return emitFunCallOrApplyGuard(argcId);
   6581 }
   6582 
   6583 Maybe<ObjOperandId> CallIRGenerator::emitFunApplyArgsGuard(
   6584    CallFlags::ArgFormat format) {
   6585  MOZ_ASSERT(argc_ == 2);
   6586 
   6587  // |GetIndexOfArgument| doesn't yet support FunCall/FunApply.
   6588  CallFlags flags(CallFlags::Standard);
   6589 
   6590  ValOperandId argValId =
   6591      writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_, flags);
   6592 
   6593  if (format == CallFlags::FunApplyArgsObj) {
   6594    ObjOperandId argObjId = writer.guardToObject(argValId);
   6595    if (args_[1].toObject().is<MappedArgumentsObject>()) {
   6596      writer.guardClass(argObjId, GuardClassKind::MappedArguments);
   6597    } else {
   6598      MOZ_ASSERT(args_[1].toObject().is<UnmappedArgumentsObject>());
   6599      writer.guardClass(argObjId, GuardClassKind::UnmappedArguments);
   6600    }
   6601    uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
   6602                    ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
   6603    writer.guardArgumentsObjectFlags(argObjId, flags);
   6604    return mozilla::Some(argObjId);
   6605  }
   6606 
   6607  if (format == CallFlags::FunApplyArray) {
   6608    ObjOperandId argObjId = writer.guardToObject(argValId);
   6609    emitOptimisticClassGuard(argObjId, &args_[1].toObject(),
   6610                             GuardClassKind::Array);
   6611    writer.guardArrayIsPacked(argObjId);
   6612    return mozilla::Some(argObjId);
   6613  }
   6614 
   6615  MOZ_ASSERT(format == CallFlags::FunApplyNullUndefined);
   6616  writer.guardIsNullOrUndefined(argValId);
   6617  return mozilla::Nothing();
   6618 }
   6619 
   6620 AttachDecision InlinableNativeIRGenerator::tryAttachArrayPush() {
   6621  // Only optimize on obj.push(val);
   6622  if (args_.length() != 1 || !thisval_.isObject()) {
   6623    return AttachDecision::NoAction;
   6624  }
   6625 
   6626  // Where |obj| is a native array.
   6627  JSObject* thisobj = &thisval_.toObject();
   6628  if (!thisobj->is<ArrayObject>()) {
   6629    return AttachDecision::NoAction;
   6630  }
   6631 
   6632  auto* thisarray = &thisobj->as<ArrayObject>();
   6633 
   6634  // Check for other indexed properties or class hooks.
   6635  if (!CanAttachAddElement(thisarray, /* isInit = */ false,
   6636                           AllowIndexedReceiver::No)) {
   6637    return AttachDecision::NoAction;
   6638  }
   6639 
   6640  // Can't add new elements to arrays with non-writable length.
   6641  if (!thisarray->lengthIsWritable()) {
   6642    return AttachDecision::NoAction;
   6643  }
   6644 
   6645  // Check that array is extensible.
   6646  if (!thisarray->isExtensible()) {
   6647    return AttachDecision::NoAction;
   6648  }
   6649 
   6650  // Check that the array is completely initialized (no holes).
   6651  if (thisarray->getDenseInitializedLength() != thisarray->length()) {
   6652    return AttachDecision::NoAction;
   6653  }
   6654 
   6655  MOZ_ASSERT(!thisarray->denseElementsAreFrozen(),
   6656             "Extensible arrays should not have frozen elements");
   6657 
   6658  // After this point, we can generate code fine.
   6659 
   6660  // Initialize the input operand.
   6661  Int32OperandId argcId = initializeInputOperand();
   6662 
   6663  // Guard callee is the 'push' native function.
   6664  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6665 
   6666  // Guard this is an array object.
   6667  ValOperandId thisValId = loadThis(calleeId);
   6668  ObjOperandId thisObjId = writer.guardToObject(thisValId);
   6669 
   6670  // Guard that the shape matches.
   6671  TestMatchingNativeReceiver(writer, thisarray, thisObjId);
   6672 
   6673  // Guard proto chain shapes.
   6674  ShapeGuardProtoChain(writer, thisarray, thisObjId);
   6675 
   6676  // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
   6677  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   6678  writer.arrayPush(thisObjId, argId);
   6679 
   6680  writer.returnFromIC();
   6681 
   6682  trackAttached("ArrayPush");
   6683  return AttachDecision::Attach;
   6684 }
   6685 
   6686 AttachDecision InlinableNativeIRGenerator::tryAttachArrayPopShift(
   6687    InlinableNative native) {
   6688  // Expecting no arguments.
   6689  if (args_.length() != 0) {
   6690    return AttachDecision::NoAction;
   6691  }
   6692 
   6693  // Only optimize if |this| is a packed array.
   6694  if (!thisval_.isObject() || !IsPackedArray(&thisval_.toObject())) {
   6695    return AttachDecision::NoAction;
   6696  }
   6697 
   6698  // Other conditions:
   6699  //
   6700  // * The array length needs to be writable because we're changing it.
   6701  // * The array must be extensible. Non-extensible arrays require preserving
   6702  //   the |initializedLength == capacity| invariant on ObjectElements.
   6703  //   See NativeObject::shrinkCapacityToInitializedLength.
   6704  //   This also ensures the elements aren't sealed/frozen.
   6705  // * There must not be a for-in iterator for the elements because the IC stub
   6706  //   does not suppress deleted properties.
   6707  ArrayObject* arr = &thisval_.toObject().as<ArrayObject>();
   6708  if (!arr->lengthIsWritable() || !arr->isExtensible() ||
   6709      arr->denseElementsHaveMaybeInIterationFlag()) {
   6710    return AttachDecision::NoAction;
   6711  }
   6712 
   6713  // Initialize the input operand.
   6714  Int32OperandId argcId = initializeInputOperand();
   6715 
   6716  // Guard callee is the 'pop' or 'shift' native function.
   6717  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6718 
   6719  ValOperandId thisValId = loadThis(calleeId);
   6720  ObjOperandId objId = writer.guardToObject(thisValId);
   6721  emitOptimisticClassGuard(objId, arr, GuardClassKind::Array);
   6722 
   6723  if (native == InlinableNative::ArrayPop) {
   6724    writer.packedArrayPopResult(objId);
   6725  } else {
   6726    MOZ_ASSERT(native == InlinableNative::ArrayShift);
   6727    writer.packedArrayShiftResult(objId);
   6728  }
   6729 
   6730  writer.returnFromIC();
   6731 
   6732  trackAttached("ArrayPopShift");
   6733  return AttachDecision::Attach;
   6734 }
   6735 
   6736 AttachDecision InlinableNativeIRGenerator::tryAttachArrayJoin() {
   6737  // Only handle argc <= 1.
   6738  if (args_.length() > 1) {
   6739    return AttachDecision::NoAction;
   6740  }
   6741 
   6742  // Only optimize if |this| is an array.
   6743  if (!thisval_.isObject() || !thisval_.toObject().is<ArrayObject>()) {
   6744    return AttachDecision::NoAction;
   6745  }
   6746 
   6747  // The separator argument must be a string, if present.
   6748  if (args_.length() > 0 && !args_[0].isString()) {
   6749    return AttachDecision::NoAction;
   6750  }
   6751 
   6752  // IC stub code can handle non-packed array.
   6753 
   6754  // Initialize the input operand.
   6755  Int32OperandId argcId = initializeInputOperand();
   6756 
   6757  // Guard callee is the 'join' native function.
   6758  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6759 
   6760  // Guard this is an array object.
   6761  ValOperandId thisValId = loadThis(calleeId);
   6762  ObjOperandId thisObjId = writer.guardToObject(thisValId);
   6763  emitOptimisticClassGuard(thisObjId, &thisval_.toObject(),
   6764                           GuardClassKind::Array);
   6765 
   6766  StringOperandId sepId;
   6767  if (args_.length() == 1) {
   6768    // If argcount is 1, guard that the argument is a string.
   6769    ValOperandId argValId = loadArgument(calleeId, ArgumentKind::Arg0);
   6770    sepId = writer.guardToString(argValId);
   6771  } else {
   6772    sepId = writer.loadConstantString(cx_->names().comma_);
   6773  }
   6774 
   6775  // Do the join.
   6776  writer.arrayJoinResult(thisObjId, sepId);
   6777 
   6778  writer.returnFromIC();
   6779 
   6780  trackAttached("ArrayJoin");
   6781  return AttachDecision::Attach;
   6782 }
   6783 
   6784 AttachDecision InlinableNativeIRGenerator::tryAttachArraySlice() {
   6785  // Only handle argc <= 2.
   6786  if (args_.length() > 2) {
   6787    return AttachDecision::NoAction;
   6788  }
   6789 
   6790  // Only optimize if |this| is a packed array or an arguments object.
   6791  if (!thisval_.isObject()) {
   6792    return AttachDecision::NoAction;
   6793  }
   6794 
   6795  bool isPackedArray = IsPackedArray(&thisval_.toObject());
   6796  if (!isPackedArray) {
   6797    if (!thisval_.toObject().is<ArgumentsObject>()) {
   6798      return AttachDecision::NoAction;
   6799    }
   6800    auto* args = &thisval_.toObject().as<ArgumentsObject>();
   6801 
   6802    // No elements must have been overridden or deleted.
   6803    if (args->hasOverriddenElement()) {
   6804      return AttachDecision::NoAction;
   6805    }
   6806 
   6807    // The length property mustn't be overridden.
   6808    if (args->hasOverriddenLength()) {
   6809      return AttachDecision::NoAction;
   6810    }
   6811 
   6812    // And finally also check that no argument is forwarded.
   6813    if (args->anyArgIsForwarded()) {
   6814      return AttachDecision::NoAction;
   6815    }
   6816  }
   6817 
   6818  // Arguments for the sliced region must be integers.
   6819  if (args_.length() > 0 && !args_[0].isInt32()) {
   6820    return AttachDecision::NoAction;
   6821  }
   6822  if (args_.length() > 1 && !args_[1].isInt32()) {
   6823    return AttachDecision::NoAction;
   6824  }
   6825 
   6826  JSObject* templateObj = NewDenseFullyAllocatedArray(cx_, 0, TenuredObject);
   6827  if (!templateObj) {
   6828    cx_->recoverFromOutOfMemory();
   6829    return AttachDecision::NoAction;
   6830  }
   6831 
   6832  // Initialize the input operand.
   6833  Int32OperandId argcId = initializeInputOperand();
   6834 
   6835  // Guard callee is the 'slice' native function.
   6836  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6837 
   6838  ValOperandId thisValId = loadThis(calleeId);
   6839  ObjOperandId objId = writer.guardToObject(thisValId);
   6840 
   6841  if (isPackedArray) {
   6842    emitOptimisticClassGuard(objId, &thisval_.toObject(),
   6843                             GuardClassKind::Array);
   6844    writer.guardArrayIsPacked(objId);
   6845  } else {
   6846    auto* args = &thisval_.toObject().as<ArgumentsObject>();
   6847 
   6848    if (args->is<MappedArgumentsObject>()) {
   6849      writer.guardClass(objId, GuardClassKind::MappedArguments);
   6850    } else {
   6851      MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
   6852      writer.guardClass(objId, GuardClassKind::UnmappedArguments);
   6853    }
   6854 
   6855    uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
   6856                    ArgumentsObject::LENGTH_OVERRIDDEN_BIT |
   6857                    ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
   6858    writer.guardArgumentsObjectFlags(objId, flags);
   6859  }
   6860 
   6861  Int32OperandId int32BeginId;
   6862  if (args_.length() > 0) {
   6863    ValOperandId beginId = loadArgument(calleeId, ArgumentKind::Arg0);
   6864    int32BeginId = writer.guardToInt32(beginId);
   6865  } else {
   6866    int32BeginId = writer.loadInt32Constant(0);
   6867  }
   6868 
   6869  Int32OperandId int32EndId;
   6870  if (args_.length() > 1) {
   6871    ValOperandId endId = loadArgument(calleeId, ArgumentKind::Arg1);
   6872    int32EndId = writer.guardToInt32(endId);
   6873  } else if (isPackedArray) {
   6874    int32EndId = writer.loadInt32ArrayLength(objId);
   6875  } else {
   6876    int32EndId = writer.loadArgumentsObjectLength(objId);
   6877  }
   6878 
   6879  if (isPackedArray) {
   6880    writer.packedArraySliceResult(templateObj, objId, int32BeginId, int32EndId);
   6881  } else {
   6882    writer.argumentsSliceResult(templateObj, objId, int32BeginId, int32EndId);
   6883  }
   6884  writer.returnFromIC();
   6885 
   6886  trackAttached(isPackedArray ? "ArraySlice" : "ArgumentsSlice");
   6887  return AttachDecision::Attach;
   6888 }
   6889 
   6890 AttachDecision InlinableNativeIRGenerator::tryAttachArrayIsArray() {
   6891  // Need a single argument.
   6892  if (args_.length() != 1) {
   6893    return AttachDecision::NoAction;
   6894  }
   6895 
   6896  // Initialize the input operand.
   6897  Int32OperandId argcId = initializeInputOperand();
   6898 
   6899  // Guard callee is the 'isArray' native function.
   6900  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6901 
   6902  // Check if the argument is an Array and return result.
   6903  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   6904  writer.isArrayResult(argId);
   6905  writer.returnFromIC();
   6906 
   6907  trackAttached("ArrayIsArray");
   6908  return AttachDecision::Attach;
   6909 }
   6910 
   6911 AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet(
   6912    Scalar::Type type) {
   6913  // Ensure |this| is a DataViewObject.
   6914  if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
   6915    return AttachDecision::NoAction;
   6916  }
   6917 
   6918  // Expected arguments: offset (number), optional littleEndian (boolean).
   6919  if (args_.length() < 1 || args_.length() > 2) {
   6920    return AttachDecision::NoAction;
   6921  }
   6922  int64_t offsetInt64;
   6923  if (!ValueIsInt64Index(args_[0], &offsetInt64)) {
   6924    return AttachDecision::NoAction;
   6925  }
   6926  if (args_.length() > 1 && !args_[1].isBoolean()) {
   6927    return AttachDecision::NoAction;
   6928  }
   6929 
   6930  auto* dv = &thisval_.toObject().as<DataViewObject>();
   6931 
   6932  // Bounds check the offset.
   6933  size_t byteLength = dv->byteLength().valueOr(0);
   6934  if (offsetInt64 < 0 || !DataViewObject::offsetIsInBounds(
   6935                             Scalar::byteSize(type), offsetInt64, byteLength)) {
   6936    return AttachDecision::NoAction;
   6937  }
   6938 
   6939  // For getUint32 we let the stub return an Int32 if we have not seen a
   6940  // double, to allow better codegen in Warp while avoiding bailout loops.
   6941  bool forceDoubleForUint32 = false;
   6942  if (type == Scalar::Uint32) {
   6943    bool isLittleEndian = args_.length() > 1 && args_[1].toBoolean();
   6944    uint32_t res = dv->read<uint32_t>(offsetInt64, byteLength, isLittleEndian);
   6945    forceDoubleForUint32 = res >= INT32_MAX;
   6946  }
   6947 
   6948  // Initialize the input operand.
   6949  Int32OperandId argcId = initializeInputOperand();
   6950 
   6951  // Guard callee is this DataView native function.
   6952  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   6953 
   6954  // Guard |this| is a DataViewObject.
   6955  ValOperandId thisValId = loadThis(calleeId);
   6956  ObjOperandId objId = writer.guardToObject(thisValId);
   6957 
   6958  if (dv->is<FixedLengthDataViewObject>()) {
   6959    emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView);
   6960  } else if (dv->is<ImmutableDataViewObject>()) {
   6961    emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView);
   6962  } else {
   6963    emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView);
   6964  }
   6965 
   6966  // Convert offset to intPtr.
   6967  ValOperandId offsetId = loadArgument(calleeId, ArgumentKind::Arg0);
   6968  IntPtrOperandId intPtrOffsetId =
   6969      guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false);
   6970 
   6971  BooleanOperandId boolLittleEndianId;
   6972  if (args_.length() > 1) {
   6973    ValOperandId littleEndianId = loadArgument(calleeId, ArgumentKind::Arg1);
   6974    boolLittleEndianId = writer.guardToBoolean(littleEndianId);
   6975  } else {
   6976    boolLittleEndianId = writer.loadBooleanConstant(false);
   6977  }
   6978 
   6979  auto viewKind = ToArrayBufferViewKind(dv);
   6980  writer.loadDataViewValueResult(objId, intPtrOffsetId, boolLittleEndianId,
   6981                                 type, forceDoubleForUint32, viewKind);
   6982 
   6983  writer.returnFromIC();
   6984 
   6985  trackAttached("DataViewGet");
   6986  return AttachDecision::Attach;
   6987 }
   6988 
   6989 AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet(
   6990    Scalar::Type type) {
   6991  // Ensure |this| is a DataViewObject.
   6992  if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
   6993    return AttachDecision::NoAction;
   6994  }
   6995 
   6996  // Expected arguments: offset (number), value, optional littleEndian (boolean)
   6997  if (args_.length() < 2 || args_.length() > 3) {
   6998    return AttachDecision::NoAction;
   6999  }
   7000  int64_t offsetInt64;
   7001  if (!ValueIsInt64Index(args_[0], &offsetInt64)) {
   7002    return AttachDecision::NoAction;
   7003  }
   7004  if (!ValueCanConvertToNumeric(type, args_[1])) {
   7005    return AttachDecision::NoAction;
   7006  }
   7007  if (args_.length() > 2 && !args_[2].isBoolean()) {
   7008    return AttachDecision::NoAction;
   7009  }
   7010 
   7011  auto* dv = &thisval_.toObject().as<DataViewObject>();
   7012 
   7013  // Immutable DataViews can't be modified.
   7014  if (dv->is<ImmutableDataViewObject>()) {
   7015    return AttachDecision::NoAction;
   7016  }
   7017 
   7018  // Bounds check the offset.
   7019  size_t byteLength = dv->byteLength().valueOr(0);
   7020  if (offsetInt64 < 0 || !DataViewObject::offsetIsInBounds(
   7021                             Scalar::byteSize(type), offsetInt64, byteLength)) {
   7022    return AttachDecision::NoAction;
   7023  }
   7024 
   7025  // Initialize the input operand.
   7026  Int32OperandId argcId = initializeInputOperand();
   7027 
   7028  // Guard callee is this DataView native function.
   7029  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   7030 
   7031  // Guard |this| is a DataViewObject.
   7032  ValOperandId thisValId = loadThis(calleeId);
   7033  ObjOperandId objId = writer.guardToObject(thisValId);
   7034 
   7035  if (dv->is<FixedLengthDataViewObject>()) {
   7036    emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView);
   7037  } else {
   7038    emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView);
   7039  }
   7040 
   7041  // Convert offset to intPtr.
   7042  ValOperandId offsetId = loadArgument(calleeId, ArgumentKind::Arg0);
   7043  IntPtrOperandId intPtrOffsetId =
   7044      guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false);
   7045 
   7046  // Convert value to number or BigInt.
   7047  ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg1);
   7048  OperandId numericValueId = emitNumericGuard(valueId, args_[1], type);
   7049 
   7050  BooleanOperandId boolLittleEndianId;
   7051  if (args_.length() > 2) {
   7052    ValOperandId littleEndianId = loadArgument(calleeId, ArgumentKind::Arg2);
   7053    boolLittleEndianId = writer.guardToBoolean(littleEndianId);
   7054  } else {
   7055    boolLittleEndianId = writer.loadBooleanConstant(false);
   7056  }
   7057 
   7058  auto viewKind = ToArrayBufferViewKind(dv);
   7059  writer.storeDataViewValueResult(objId, intPtrOffsetId, numericValueId,
   7060                                  boolLittleEndianId, type, viewKind);
   7061 
   7062  writer.returnFromIC();
   7063 
   7064  trackAttached("DataViewSet");
   7065  return AttachDecision::Attach;
   7066 }
   7067 
   7068 AttachDecision InlinableNativeIRGenerator::tryAttachDataViewByteLength() {
   7069  // Expecting no arguments.
   7070  if (args_.length() != 0) {
   7071    return AttachDecision::NoAction;
   7072  }
   7073 
   7074  // Ensure |this| is a DataViewObject.
   7075  if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
   7076    return AttachDecision::NoAction;
   7077  }
   7078 
   7079  auto* dv = &thisval_.toObject().as<DataViewObject>();
   7080 
   7081  // byteLength throws when the ArrayBuffer is detached.
   7082  if (dv->hasDetachedBuffer()) {
   7083    // The has-attached-arraybuffer guard is elided for immutable views. Assert
   7084    // we never see an immutable view with a detached buffer.
   7085    MOZ_ASSERT(!dv->is<ImmutableDataViewObject>(),
   7086               "immutable data views can't have their buffer detached");
   7087    return AttachDecision::NoAction;
   7088  }
   7089 
   7090  // byteLength throws when the ArrayBuffer is out-of-bounds.
   7091  if (dv->is<ResizableDataViewObject>() &&
   7092      dv->as<ResizableDataViewObject>().isOutOfBounds()) {
   7093    return AttachDecision::NoAction;
   7094  }
   7095 
   7096  // Initialize the input operand.
   7097  Int32OperandId argcId = initializeInputOperand();
   7098 
   7099  // Guard callee is the 'byteLength' native function.
   7100  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   7101 
   7102  // Guard |this| is a DataViewObject.
   7103  ValOperandId thisValId = loadThis(calleeId);
   7104  ObjOperandId objId = writer.guardToObject(thisValId);
   7105 
   7106  if (dv->is<FixedLengthDataViewObject>()) {
   7107    emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView);
   7108  } else if (dv->is<ImmutableDataViewObject>()) {
   7109    emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView);
   7110  } else {
   7111    emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView);
   7112  }
   7113 
   7114  // Immutable array buffers can never get detached.
   7115  if (!dv->is<ImmutableDataViewObject>()) {
   7116    writer.guardHasAttachedArrayBuffer(objId);
   7117  } else {
   7118 #ifdef DEBUG
   7119    // Add a guard in debug-mode, so if the buffer unexpectedly got detached,
   7120    // we bail out and rely on the above assertion to fire.
   7121    writer.guardHasAttachedArrayBuffer(objId);
   7122 #endif
   7123  }
   7124 
   7125  // Resizable array buffers can get out-of-bounds when shrunk.
   7126  if (dv->is<ResizableDataViewObject>()) {
   7127    writer.guardResizableArrayBufferViewInBounds(objId);
   7128  }
   7129 
   7130  size_t byteLength = dv->byteLength().valueOr(0);
   7131  if (!dv->is<ResizableDataViewObject>()) {
   7132    if (byteLength <= INT32_MAX) {
   7133      writer.loadArrayBufferViewLengthInt32Result(objId);
   7134    } else {
   7135      writer.loadArrayBufferViewLengthDoubleResult(objId);
   7136    }
   7137  } else {
   7138    if (byteLength <= INT32_MAX) {
   7139      writer.resizableDataViewByteLengthInt32Result(objId);
   7140    } else {
   7141      writer.resizableDataViewByteLengthDoubleResult(objId);
   7142    }
   7143  }
   7144 
   7145  writer.returnFromIC();
   7146 
   7147  trackAttached("DataViewByteLength");
   7148  return AttachDecision::Attach;
   7149 }
   7150 
   7151 AttachDecision InlinableNativeIRGenerator::tryAttachDataViewByteOffset() {
   7152  // Expecting no arguments.
   7153  if (args_.length() != 0) {
   7154    return AttachDecision::NoAction;
   7155  }
   7156 
   7157  // Ensure |this| is a DataViewObject.
   7158  if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
   7159    return AttachDecision::NoAction;
   7160  }
   7161 
   7162  auto* dv = &thisval_.toObject().as<DataViewObject>();
   7163 
   7164  // byteOffset throws when the ArrayBuffer is detached.
   7165  if (dv->hasDetachedBuffer()) {
   7166    // The has-attached-arraybuffer guard is elided for immutable views. Assert
   7167    // we never see an immutable view with a detached buffer.
   7168    MOZ_ASSERT(!dv->is<ImmutableDataViewObject>(),
   7169               "immutable data views can't have their buffer detached");
   7170    return AttachDecision::NoAction;
   7171  }
   7172 
   7173  // byteOffset throws when the ArrayBuffer is out-of-bounds.
   7174  if (dv->is<ResizableDataViewObject>() &&
   7175      dv->as<ResizableDataViewObject>().isOutOfBounds()) {
   7176    return AttachDecision::NoAction;
   7177  }
   7178 
   7179  // Initialize the input operand.
   7180  Int32OperandId argcId = initializeInputOperand();
   7181 
   7182  // Guard callee is the 'byteLength' native function.
   7183  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   7184 
   7185  // Guard |this| is a DataViewObject.
   7186  ValOperandId thisValId = loadThis(calleeId);
   7187  ObjOperandId objId = writer.guardToObject(thisValId);
   7188 
   7189  if (dv->is<FixedLengthDataViewObject>()) {
   7190    emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView);
   7191  } else if (dv->is<ImmutableDataViewObject>()) {
   7192    emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView);
   7193  } else {
   7194    emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView);
   7195  }
   7196 
   7197  // Immutable array buffers can never get detached.
   7198  if (!dv->is<ImmutableDataViewObject>()) {
   7199    writer.guardHasAttachedArrayBuffer(objId);
   7200  } else {
   7201 #ifdef DEBUG
   7202    // Add a guard in debug-mode, so if the buffer unexpectedly got detached,
   7203    // we bail out and rely on the above assertion to fire.
   7204    writer.guardHasAttachedArrayBuffer(objId);
   7205 #endif
   7206  }
   7207 
   7208  // Resizable array buffers can get out-of-bounds when shrunk.
   7209  if (dv->is<ResizableDataViewObject>()) {
   7210    writer.guardResizableArrayBufferViewInBounds(objId);
   7211  }
   7212 
   7213  // byteOffset doesn't need to use different code paths for fixed-length,
   7214  // resizable, or immutable DataViews.
   7215  size_t byteOffset = dv->byteOffset().valueOr(0);
   7216  if (byteOffset <= INT32_MAX) {
   7217    writer.arrayBufferViewByteOffsetInt32Result(objId);
   7218  } else {
   7219    writer.arrayBufferViewByteOffsetDoubleResult(objId);
   7220  }
   7221 
   7222  writer.returnFromIC();
   7223 
   7224  trackAttached("DataViewByteOffset");
   7225  return AttachDecision::Attach;
   7226 }
   7227 
   7228 AttachDecision InlinableNativeIRGenerator::tryAttachUnsafeGetReservedSlot(
   7229    InlinableNative native) {
   7230  // Self-hosted code calls this with (object, int32) arguments.
   7231  MOZ_ASSERT(args_.length() == 2);
   7232  MOZ_ASSERT(args_[0].isObject());
   7233  MOZ_ASSERT(args_[1].isInt32());
   7234  MOZ_ASSERT(args_[1].toInt32() >= 0);
   7235 
   7236  uint32_t slot = uint32_t(args_[1].toInt32());
   7237  if (slot >= NativeObject::MAX_FIXED_SLOTS) {
   7238    return AttachDecision::NoAction;
   7239  }
   7240  size_t offset = NativeObject::getFixedSlotOffset(slot);
   7241 
   7242  // Initialize the input operand.
   7243  initializeInputOperand();
   7244 
   7245  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7246 
   7247  // Guard that the first argument is an object.
   7248  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7249  ObjOperandId objId = writer.guardToObject(arg0Id);
   7250 
   7251  // BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot ensures that the
   7252  // slot argument is constant. (At least for direct calls)
   7253 
   7254  switch (native) {
   7255    case InlinableNative::IntrinsicUnsafeGetReservedSlot:
   7256      writer.loadFixedSlotResult(objId, offset);
   7257      break;
   7258    case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
   7259      writer.loadFixedSlotTypedResult(objId, offset, ValueType::Object);
   7260      break;
   7261    case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
   7262      writer.loadFixedSlotTypedResult(objId, offset, ValueType::Int32);
   7263      break;
   7264    case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
   7265      writer.loadFixedSlotTypedResult(objId, offset, ValueType::String);
   7266      break;
   7267    default:
   7268      MOZ_CRASH("unexpected native");
   7269  }
   7270 
   7271  writer.returnFromIC();
   7272 
   7273  trackAttached("UnsafeGetReservedSlot");
   7274  return AttachDecision::Attach;
   7275 }
   7276 
   7277 AttachDecision InlinableNativeIRGenerator::tryAttachUnsafeSetReservedSlot() {
   7278  // Self-hosted code calls this with (object, int32, value) arguments.
   7279  MOZ_ASSERT(args_.length() == 3);
   7280  MOZ_ASSERT(args_[0].isObject());
   7281  MOZ_ASSERT(args_[1].isInt32());
   7282  MOZ_ASSERT(args_[1].toInt32() >= 0);
   7283 
   7284  uint32_t slot = uint32_t(args_[1].toInt32());
   7285  if (slot >= NativeObject::MAX_FIXED_SLOTS) {
   7286    return AttachDecision::NoAction;
   7287  }
   7288  size_t offset = NativeObject::getFixedSlotOffset(slot);
   7289 
   7290  // Initialize the input operand.
   7291  initializeInputOperand();
   7292 
   7293  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7294 
   7295  // Guard that the first argument is an object.
   7296  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7297  ObjOperandId objId = writer.guardToObject(arg0Id);
   7298 
   7299  // BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot ensures that the
   7300  // slot argument is constant. (At least for direct calls)
   7301 
   7302  // Get the value to set.
   7303  ValOperandId valId = loadArgumentIntrinsic(ArgumentKind::Arg2);
   7304 
   7305  // Set the fixed slot and return undefined.
   7306  writer.storeFixedSlotUndefinedResult(objId, offset, valId);
   7307 
   7308  // This stub always returns undefined.
   7309  writer.returnFromIC();
   7310 
   7311  trackAttached("UnsafeSetReservedSlot");
   7312  return AttachDecision::Attach;
   7313 }
   7314 
   7315 AttachDecision InlinableNativeIRGenerator::tryAttachIsSuspendedGenerator() {
   7316  // The IsSuspendedGenerator intrinsic is only called in
   7317  // self-hosted code, so it's safe to assume we have a single
   7318  // argument and the callee is our intrinsic.
   7319 
   7320  MOZ_ASSERT(args_.length() == 1);
   7321 
   7322  initializeInputOperand();
   7323 
   7324  // Stack layout here is (bottom to top):
   7325  //  2: Callee
   7326  //  1: ThisValue
   7327  //  0: Arg <-- Top of stack.
   7328  // We only care about the argument.
   7329  ValOperandId valId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7330 
   7331  // Check whether the argument is a suspended generator.
   7332  // We don't need guards, because IsSuspendedGenerator returns
   7333  // false for values that are not generator objects.
   7334  writer.callIsSuspendedGeneratorResult(valId);
   7335  writer.returnFromIC();
   7336 
   7337  trackAttached("IsSuspendedGenerator");
   7338  return AttachDecision::Attach;
   7339 }
   7340 
   7341 AttachDecision InlinableNativeIRGenerator::tryAttachToObject() {
   7342  // Self-hosted code calls this with a single argument.
   7343  MOZ_ASSERT(args_.length() == 1);
   7344 
   7345  // Need a single object argument.
   7346  // TODO(Warp): Support all or more conversions to object.
   7347  if (!args_[0].isObject()) {
   7348    return AttachDecision::NoAction;
   7349  }
   7350 
   7351  // Initialize the input operand.
   7352  initializeInputOperand();
   7353 
   7354  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7355 
   7356  // Guard that the argument is an object.
   7357  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7358  ObjOperandId objId = writer.guardToObject(argId);
   7359 
   7360  // Return the object.
   7361  writer.loadObjectResult(objId);
   7362  writer.returnFromIC();
   7363 
   7364  trackAttached("ToObject");
   7365  return AttachDecision::Attach;
   7366 }
   7367 
   7368 AttachDecision InlinableNativeIRGenerator::tryAttachToInteger() {
   7369  // Self-hosted code calls this with a single argument.
   7370  MOZ_ASSERT(args_.length() == 1);
   7371 
   7372  // Need a single int32 argument.
   7373  // TODO(Warp): Support all or more conversions to integer.
   7374  // Make sure to update this code correctly if we ever start
   7375  // returning non-int32 integers.
   7376  if (!args_[0].isInt32()) {
   7377    return AttachDecision::NoAction;
   7378  }
   7379 
   7380  // Initialize the input operand.
   7381  initializeInputOperand();
   7382 
   7383  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7384 
   7385  // Guard that the argument is an int32.
   7386  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7387  Int32OperandId int32Id = writer.guardToInt32(argId);
   7388 
   7389  // Return the int32.
   7390  writer.loadInt32Result(int32Id);
   7391  writer.returnFromIC();
   7392 
   7393  trackAttached("ToInteger");
   7394  return AttachDecision::Attach;
   7395 }
   7396 
   7397 AttachDecision InlinableNativeIRGenerator::tryAttachToLength() {
   7398  // Self-hosted code calls this with a single argument.
   7399  MOZ_ASSERT(args_.length() == 1);
   7400 
   7401  // Need a single int32 argument.
   7402  if (!args_[0].isInt32()) {
   7403    return AttachDecision::NoAction;
   7404  }
   7405 
   7406  // Initialize the input operand.
   7407  initializeInputOperand();
   7408 
   7409  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7410 
   7411  // ToLength(int32) is equivalent to max(int32, 0).
   7412  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7413  Int32OperandId int32ArgId = writer.guardToInt32(argId);
   7414  Int32OperandId zeroId = writer.loadInt32Constant(0);
   7415  bool isMax = true;
   7416  Int32OperandId maxId = writer.int32MinMax(isMax, int32ArgId, zeroId);
   7417  writer.loadInt32Result(maxId);
   7418  writer.returnFromIC();
   7419 
   7420  trackAttached("ToLength");
   7421  return AttachDecision::Attach;
   7422 }
   7423 
   7424 AttachDecision InlinableNativeIRGenerator::tryAttachIsObject() {
   7425  // Self-hosted code calls this with a single argument.
   7426  MOZ_ASSERT(args_.length() == 1);
   7427 
   7428  // Initialize the input operand.
   7429  initializeInputOperand();
   7430 
   7431  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7432 
   7433  // Type check the argument and return result.
   7434  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7435  writer.isObjectResult(argId);
   7436  writer.returnFromIC();
   7437 
   7438  trackAttached("IsObject");
   7439  return AttachDecision::Attach;
   7440 }
   7441 
   7442 AttachDecision InlinableNativeIRGenerator::tryAttachIsPackedArray() {
   7443  // Self-hosted code calls this with a single object argument.
   7444  MOZ_ASSERT(args_.length() == 1);
   7445  MOZ_ASSERT(args_[0].isObject());
   7446 
   7447  // Initialize the input operand.
   7448  initializeInputOperand();
   7449 
   7450  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7451 
   7452  // Check if the argument is packed and return result.
   7453  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7454  ObjOperandId objArgId = writer.guardToObject(argId);
   7455  writer.isPackedArrayResult(objArgId);
   7456  writer.returnFromIC();
   7457 
   7458  trackAttached("IsPackedArray");
   7459  return AttachDecision::Attach;
   7460 }
   7461 
   7462 AttachDecision InlinableNativeIRGenerator::tryAttachIsCallable() {
   7463  // Self-hosted code calls this with a single argument.
   7464  MOZ_ASSERT(args_.length() == 1);
   7465 
   7466  // Initialize the input operand.
   7467  initializeInputOperand();
   7468 
   7469  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7470 
   7471  // Check if the argument is callable and return result.
   7472  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7473  writer.isCallableResult(argId);
   7474  writer.returnFromIC();
   7475 
   7476  trackAttached("IsCallable");
   7477  return AttachDecision::Attach;
   7478 }
   7479 
   7480 AttachDecision InlinableNativeIRGenerator::tryAttachIsConstructor() {
   7481  // Self-hosted code calls this with a single argument.
   7482  MOZ_ASSERT(args_.length() == 1);
   7483 
   7484  // Need a single object argument.
   7485  if (!args_[0].isObject()) {
   7486    return AttachDecision::NoAction;
   7487  }
   7488 
   7489  // Initialize the input operand.
   7490  initializeInputOperand();
   7491 
   7492  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7493 
   7494  // Guard that the argument is an object.
   7495  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7496  ObjOperandId objId = writer.guardToObject(argId);
   7497 
   7498  // Check if the argument is a constructor and return result.
   7499  writer.isConstructorResult(objId);
   7500  writer.returnFromIC();
   7501 
   7502  trackAttached("IsConstructor");
   7503  return AttachDecision::Attach;
   7504 }
   7505 
   7506 AttachDecision
   7507 InlinableNativeIRGenerator::tryAttachIsCrossRealmArrayConstructor() {
   7508  // Self-hosted code calls this with an object argument.
   7509  MOZ_ASSERT(args_.length() == 1);
   7510  MOZ_ASSERT(args_[0].isObject());
   7511 
   7512  if (args_[0].toObject().is<ProxyObject>()) {
   7513    return AttachDecision::NoAction;
   7514  }
   7515 
   7516  // Initialize the input operand.
   7517  initializeInputOperand();
   7518 
   7519  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7520 
   7521  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7522  ObjOperandId objId = writer.guardToObject(argId);
   7523  writer.guardIsNotProxy(objId);
   7524  writer.isCrossRealmArrayConstructorResult(objId);
   7525  writer.returnFromIC();
   7526 
   7527  trackAttached("IsCrossRealmArrayConstructor");
   7528  return AttachDecision::Attach;
   7529 }
   7530 
   7531 AttachDecision InlinableNativeIRGenerator::tryAttachCanOptimizeArraySpecies() {
   7532  // Self-hosted code calls this with an object argument.
   7533  MOZ_ASSERT(args_.length() == 1);
   7534  MOZ_ASSERT(args_[0].isObject());
   7535 
   7536  SharedShape* shape = GlobalObject::getArrayShapeWithDefaultProto(cx_);
   7537  if (!shape) {
   7538    cx_->recoverFromOutOfMemory();
   7539    return AttachDecision::NoAction;
   7540  }
   7541 
   7542  // Initialize the input operand.
   7543  initializeInputOperand();
   7544 
   7545  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7546 
   7547  if (cx_->realm()->realmFuses.optimizeArraySpeciesFuse.intact()) {
   7548    ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7549    ObjOperandId objId = writer.guardToObject(argId);
   7550    writer.guardFuse(RealmFuses::FuseIndex::OptimizeArraySpeciesFuse);
   7551    writer.hasShapeResult(objId, shape);
   7552    writer.returnFromIC();
   7553    trackAttached("CanOptimizeArraySpecies.Optimized");
   7554  } else {
   7555    writer.loadBooleanResult(false);
   7556    writer.returnFromIC();
   7557    trackAttached("CanOptimizeArraySpecies.Deoptimized");
   7558  }
   7559 
   7560  return AttachDecision::Attach;
   7561 }
   7562 
   7563 AttachDecision InlinableNativeIRGenerator::tryAttachGuardToClass(
   7564    InlinableNative native) {
   7565  // Self-hosted code calls this with an object argument.
   7566  MOZ_ASSERT(args_.length() == 1);
   7567  MOZ_ASSERT(args_[0].isObject());
   7568 
   7569  // Class must match.
   7570  const JSClass* clasp = InlinableNativeGuardToClass(native);
   7571  if (args_[0].toObject().getClass() != clasp) {
   7572    return AttachDecision::NoAction;
   7573  }
   7574 
   7575  // Initialize the input operand.
   7576  initializeInputOperand();
   7577 
   7578  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7579 
   7580  // Guard that the argument is an object.
   7581  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7582  ObjOperandId objId = writer.guardToObject(argId);
   7583 
   7584  // Guard that the object has the correct class.
   7585  writer.guardAnyClass(objId, clasp);
   7586 
   7587  // Return the object.
   7588  writer.loadObjectResult(objId);
   7589  writer.returnFromIC();
   7590 
   7591  trackAttached("GuardToClass");
   7592  return AttachDecision::Attach;
   7593 }
   7594 
   7595 AttachDecision InlinableNativeIRGenerator::tryAttachGuardToClass(
   7596    GuardClassKind kind) {
   7597  // Self-hosted code calls this with an object argument.
   7598  MOZ_ASSERT(args_.length() == 1);
   7599  MOZ_ASSERT(args_[0].isObject());
   7600 
   7601  // Class must match.
   7602  const JSClass* clasp = ClassFor(kind);
   7603  if (args_[0].toObject().getClass() != clasp) {
   7604    return AttachDecision::NoAction;
   7605  }
   7606 
   7607  // Initialize the input operand.
   7608  initializeInputOperand();
   7609 
   7610  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7611 
   7612  // Guard that the argument is an object.
   7613  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7614  ObjOperandId objId = writer.guardToObject(argId);
   7615 
   7616  // Guard that the object has the correct class.
   7617  writer.guardClass(objId, kind);
   7618 
   7619  // Return the object.
   7620  writer.loadObjectResult(objId);
   7621  writer.returnFromIC();
   7622 
   7623  trackAttached("GuardToClass");
   7624  return AttachDecision::Attach;
   7625 }
   7626 
   7627 AttachDecision InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() {
   7628  // Self-hosted code calls this with an object argument.
   7629  MOZ_ASSERT(args_.length() == 1);
   7630  MOZ_ASSERT(args_[0].isObject());
   7631 
   7632  // Class must match.
   7633  if (!args_[0].toObject().is<ArrayBufferObject>()) {
   7634    return AttachDecision::NoAction;
   7635  }
   7636 
   7637  // Initialize the input operand.
   7638  initializeInputOperand();
   7639 
   7640  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7641 
   7642  // Guard that the argument is an object.
   7643  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7644  ObjOperandId objId = writer.guardToObject(argId);
   7645 
   7646  // Guard that the object has the correct class.
   7647  writer.guardToArrayBuffer(objId);
   7648 
   7649  // Return the object.
   7650  writer.loadObjectResult(objId);
   7651  writer.returnFromIC();
   7652 
   7653  trackAttached("GuardToArrayBuffer");
   7654  return AttachDecision::Attach;
   7655 }
   7656 
   7657 AttachDecision InlinableNativeIRGenerator::tryAttachGuardToSharedArrayBuffer() {
   7658  // Self-hosted code calls this with an object argument.
   7659  MOZ_ASSERT(args_.length() == 1);
   7660  MOZ_ASSERT(args_[0].isObject());
   7661 
   7662  // Class must match.
   7663  if (!args_[0].toObject().is<SharedArrayBufferObject>()) {
   7664    return AttachDecision::NoAction;
   7665  }
   7666 
   7667  // Initialize the input operand.
   7668  initializeInputOperand();
   7669 
   7670  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7671 
   7672  // Guard that the argument is an object.
   7673  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7674  ObjOperandId objId = writer.guardToObject(argId);
   7675 
   7676  // Guard that the object has the correct class.
   7677  writer.guardToSharedArrayBuffer(objId);
   7678 
   7679  // Return the object.
   7680  writer.loadObjectResult(objId);
   7681  writer.returnFromIC();
   7682 
   7683  trackAttached("GuardToSharedArrayBuffer");
   7684  return AttachDecision::Attach;
   7685 }
   7686 
   7687 AttachDecision InlinableNativeIRGenerator::tryAttachHasClass(
   7688    const JSClass* clasp, bool isPossiblyWrapped) {
   7689  // Self-hosted code calls this with an object argument.
   7690  MOZ_ASSERT(args_.length() == 1);
   7691  MOZ_ASSERT(args_[0].isObject());
   7692 
   7693  // Only optimize when the object isn't a proxy.
   7694  if (isPossiblyWrapped && args_[0].toObject().is<ProxyObject>()) {
   7695    return AttachDecision::NoAction;
   7696  }
   7697 
   7698  // Initialize the input operand.
   7699  initializeInputOperand();
   7700 
   7701  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7702 
   7703  // Perform the Class check.
   7704  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7705  ObjOperandId objId = writer.guardToObject(argId);
   7706 
   7707  if (isPossiblyWrapped) {
   7708    writer.guardIsNotProxy(objId);
   7709  }
   7710 
   7711  writer.hasClassResult(objId, clasp);
   7712  writer.returnFromIC();
   7713 
   7714  trackAttached("HasClass");
   7715  return AttachDecision::Attach;
   7716 }
   7717 
   7718 AttachDecision InlinableNativeIRGenerator::tryAttachRegExpFlag(
   7719    JS::RegExpFlags flags) {
   7720  // Expecting no arguments.
   7721  if (args_.length() != 0) {
   7722    return AttachDecision::NoAction;
   7723  }
   7724 
   7725  // Ensure |this| is a RegExpObject.
   7726  if (!thisval_.isObject() || !thisval_.toObject().is<RegExpObject>()) {
   7727    return AttachDecision::NoAction;
   7728  }
   7729 
   7730  auto* regExp = &thisval_.toObject().as<RegExpObject>();
   7731 
   7732  // Initialize the input operand.
   7733  Int32OperandId argcId = initializeInputOperand();
   7734 
   7735  // Guard callee is the native RegExp getter function.
   7736  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   7737 
   7738  // Guard |this| is a RegExpObject.
   7739  ValOperandId thisValId = loadThis(calleeId);
   7740  ObjOperandId objId = writer.guardToObject(thisValId);
   7741  writer.guardShapeForClass(objId, regExp->shape());
   7742 
   7743  writer.regExpFlagResult(objId, flags.value());
   7744  writer.returnFromIC();
   7745 
   7746  trackAttached("RegExpFlag");
   7747  return AttachDecision::Attach;
   7748 }
   7749 
   7750 // Returns whether the .lastIndex property is a non-negative int32 value and is
   7751 // still writable.
   7752 static bool HasOptimizableLastIndexSlot(RegExpObject* regexp, JSContext* cx) {
   7753  auto lastIndexProp = regexp->lookupPure(cx->names().lastIndex);
   7754  MOZ_ASSERT(lastIndexProp->isDataProperty());
   7755  if (!lastIndexProp->writable()) {
   7756    return false;
   7757  }
   7758  Value lastIndex = regexp->getLastIndex();
   7759  if (!lastIndex.isInt32() || lastIndex.toInt32() < 0) {
   7760    return false;
   7761  }
   7762  return true;
   7763 }
   7764 
   7765 // Returns the RegExp stub used by the optimized code path for this intrinsic.
   7766 // We store a pointer to this in the IC stub to ensure GC doesn't discard it.
   7767 static JitCode* GetOrCreateRegExpStub(JSContext* cx, InlinableNative native) {
   7768 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
   7769  return nullptr;
   7770 #else
   7771  // The stubs assume the global has non-null RegExpStatics and match result
   7772  // shape.
   7773  if (!GlobalObject::getRegExpStatics(cx, cx->global()) ||
   7774      !cx->global()->regExpRealm().getOrCreateMatchResultShape(cx)) {
   7775    MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
   7776    cx->clearPendingException();
   7777    return nullptr;
   7778  }
   7779  JitZone::StubKind kind;
   7780  switch (native) {
   7781    case InlinableNative::IntrinsicRegExpBuiltinExecForTest:
   7782    case InlinableNative::IntrinsicRegExpExecForTest:
   7783      kind = JitZone::StubKind::RegExpExecTest;
   7784      break;
   7785    case InlinableNative::IntrinsicRegExpBuiltinExec:
   7786    case InlinableNative::IntrinsicRegExpExec:
   7787      kind = JitZone::StubKind::RegExpExecMatch;
   7788      break;
   7789    case InlinableNative::RegExpMatcher:
   7790      kind = JitZone::StubKind::RegExpMatcher;
   7791      break;
   7792    case InlinableNative::RegExpSearcher:
   7793      kind = JitZone::StubKind::RegExpSearcher;
   7794      break;
   7795    default:
   7796      MOZ_CRASH("Unexpected native");
   7797  }
   7798  JitCode* code = cx->zone()->jitZone()->ensureStubExists(cx, kind);
   7799  if (!code) {
   7800    MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
   7801    cx->clearPendingException();
   7802    return nullptr;
   7803  }
   7804  return code;
   7805 #endif
   7806 }
   7807 
   7808 static void EmitGuardLastIndexIsNonNegativeInt32(CacheIRWriter& writer,
   7809                                                 ObjOperandId regExpId) {
   7810  size_t offset =
   7811      NativeObject::getFixedSlotOffset(RegExpObject::lastIndexSlot());
   7812  ValOperandId lastIndexValId = writer.loadFixedSlot(regExpId, offset);
   7813  Int32OperandId lastIndexId = writer.guardToInt32(lastIndexValId);
   7814  writer.guardInt32IsNonNegative(lastIndexId);
   7815 }
   7816 
   7817 AttachDecision InlinableNativeIRGenerator::tryAttachIntrinsicRegExpBuiltinExec(
   7818    InlinableNative native) {
   7819  // Self-hosted code calls this with (regexp, string) arguments.
   7820  MOZ_ASSERT(args_.length() == 2);
   7821  MOZ_ASSERT(args_[0].isObject());
   7822  MOZ_ASSERT(args_[1].isString());
   7823 
   7824  JitCode* stub = GetOrCreateRegExpStub(cx_, native);
   7825  if (!stub) {
   7826    return AttachDecision::NoAction;
   7827  }
   7828 
   7829  RegExpObject* re = &args_[0].toObject().as<RegExpObject>();
   7830  if (!HasOptimizableLastIndexSlot(re, cx_)) {
   7831    return AttachDecision::NoAction;
   7832  }
   7833 
   7834  // Initialize the input operand.
   7835  initializeInputOperand();
   7836 
   7837  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7838 
   7839  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7840  ObjOperandId regExpId = writer.guardToObject(arg0Id);
   7841  writer.guardShape(regExpId, re->shape());
   7842  EmitGuardLastIndexIsNonNegativeInt32(writer, regExpId);
   7843 
   7844  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   7845  StringOperandId inputId = writer.guardToString(arg1Id);
   7846 
   7847  if (native == InlinableNative::IntrinsicRegExpBuiltinExecForTest) {
   7848    writer.regExpBuiltinExecTestResult(regExpId, inputId, stub);
   7849  } else {
   7850    writer.regExpBuiltinExecMatchResult(regExpId, inputId, stub);
   7851  }
   7852  writer.returnFromIC();
   7853 
   7854  trackAttached("IntrinsicRegExpBuiltinExec");
   7855  return AttachDecision::Attach;
   7856 }
   7857 
   7858 AttachDecision InlinableNativeIRGenerator::tryAttachIntrinsicRegExpExec(
   7859    InlinableNative native) {
   7860  // Self-hosted code calls this with (object, string) arguments.
   7861  MOZ_ASSERT(args_.length() == 2);
   7862  MOZ_ASSERT(args_[0].isObject());
   7863  MOZ_ASSERT(args_[1].isString());
   7864 
   7865  // Ensure the object is a RegExpObject with the builtin RegExp.prototype.exec
   7866  // function.
   7867  if (!IsOptimizableRegExpObject(&args_[0].toObject(), cx_)) {
   7868    return AttachDecision::NoAction;
   7869  }
   7870 
   7871  JitCode* stub = GetOrCreateRegExpStub(cx_, native);
   7872  if (!stub) {
   7873    return AttachDecision::NoAction;
   7874  }
   7875 
   7876  RegExpObject* re = &args_[0].toObject().as<RegExpObject>();
   7877  if (!HasOptimizableLastIndexSlot(re, cx_)) {
   7878    return AttachDecision::NoAction;
   7879  }
   7880 
   7881  // Initialize the input operand.
   7882  initializeInputOperand();
   7883 
   7884  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7885 
   7886  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7887  ObjOperandId regExpId = writer.guardToObject(arg0Id);
   7888  writer.guardShape(regExpId, re->shape());
   7889  writer.guardFuse(RealmFuses::FuseIndex::OptimizeRegExpPrototypeFuse);
   7890  EmitGuardLastIndexIsNonNegativeInt32(writer, regExpId);
   7891 
   7892  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   7893  StringOperandId inputId = writer.guardToString(arg1Id);
   7894 
   7895  if (native == InlinableNative::IntrinsicRegExpExecForTest) {
   7896    writer.regExpBuiltinExecTestResult(regExpId, inputId, stub);
   7897  } else {
   7898    writer.regExpBuiltinExecMatchResult(regExpId, inputId, stub);
   7899  }
   7900  writer.returnFromIC();
   7901 
   7902  trackAttached("IntrinsicRegExpExec");
   7903  return AttachDecision::Attach;
   7904 }
   7905 
   7906 AttachDecision InlinableNativeIRGenerator::tryAttachRegExpMatcherSearcher(
   7907    InlinableNative native) {
   7908  // Self-hosted code calls this with (object, string, number) arguments.
   7909  MOZ_ASSERT(args_.length() == 3);
   7910  MOZ_ASSERT(args_[0].isObject());
   7911  MOZ_ASSERT(args_[1].isString());
   7912  MOZ_ASSERT(args_[2].isNumber());
   7913 
   7914  // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
   7915  if (!args_[2].isInt32()) {
   7916    return AttachDecision::NoAction;
   7917  }
   7918 
   7919  JitCode* stub = GetOrCreateRegExpStub(cx_, native);
   7920  if (!stub) {
   7921    return AttachDecision::NoAction;
   7922  }
   7923 
   7924  // Initialize the input operand.
   7925  initializeInputOperand();
   7926 
   7927  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7928 
   7929  // Guard argument types.
   7930  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7931  ObjOperandId reId = writer.guardToObject(arg0Id);
   7932 
   7933  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   7934  StringOperandId inputId = writer.guardToString(arg1Id);
   7935 
   7936  ValOperandId arg2Id = loadArgumentIntrinsic(ArgumentKind::Arg2);
   7937  Int32OperandId lastIndexId = writer.guardToInt32(arg2Id);
   7938 
   7939  switch (native) {
   7940    case InlinableNative::RegExpMatcher:
   7941      writer.callRegExpMatcherResult(reId, inputId, lastIndexId, stub);
   7942      writer.returnFromIC();
   7943      trackAttached("RegExpMatcher");
   7944      break;
   7945 
   7946    case InlinableNative::RegExpSearcher:
   7947      writer.callRegExpSearcherResult(reId, inputId, lastIndexId, stub);
   7948      writer.returnFromIC();
   7949      trackAttached("RegExpSearcher");
   7950      break;
   7951 
   7952    default:
   7953      MOZ_CRASH("Unexpected native");
   7954  }
   7955 
   7956  return AttachDecision::Attach;
   7957 }
   7958 
   7959 AttachDecision InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
   7960  // Self-hosted code calls this with a string argument that's only used for an
   7961  // assertion.
   7962  MOZ_ASSERT(args_.length() == 1);
   7963  MOZ_ASSERT(args_[0].isString());
   7964 
   7965  // Initialize the input operand.
   7966  initializeInputOperand();
   7967 
   7968  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7969 
   7970  writer.regExpSearcherLastLimitResult();
   7971  writer.returnFromIC();
   7972 
   7973  trackAttached("RegExpSearcherLastLimit");
   7974  return AttachDecision::Attach;
   7975 }
   7976 
   7977 AttachDecision InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
   7978  // Self-hosted code calls this with object and string arguments.
   7979  MOZ_ASSERT(args_.length() == 2);
   7980  MOZ_ASSERT(args_[0].toObject().is<RegExpObject>());
   7981  MOZ_ASSERT(args_[1].isString());
   7982 
   7983  // Initialize the input operand.
   7984  initializeInputOperand();
   7985 
   7986  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   7987 
   7988  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   7989  ObjOperandId objId = writer.guardToObject(arg0Id);
   7990 
   7991  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   7992  StringOperandId inputId = writer.guardToString(arg1Id);
   7993 
   7994  writer.regExpHasCaptureGroupsResult(objId, inputId);
   7995  writer.returnFromIC();
   7996 
   7997  trackAttached("RegExpHasCaptureGroups");
   7998  return AttachDecision::Attach;
   7999 }
   8000 
   8001 AttachDecision
   8002 InlinableNativeIRGenerator::tryAttachIsRegExpPrototypeOptimizable() {
   8003  // Self-hosted code calls this with no arguments.
   8004  MOZ_ASSERT(args_.length() == 0);
   8005 
   8006  // Initialize the input operand.
   8007  initializeInputOperand();
   8008 
   8009  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8010 
   8011  if (cx_->realm()->realmFuses.optimizeRegExpPrototypeFuse.intact()) {
   8012    writer.guardFuse(RealmFuses::FuseIndex::OptimizeRegExpPrototypeFuse);
   8013    writer.loadBooleanResult(true);
   8014    writer.returnFromIC();
   8015    trackAttached("IsRegExpPrototypeOptimizable.Optimized");
   8016  } else {
   8017    writer.loadBooleanResult(false);
   8018    writer.returnFromIC();
   8019    trackAttached("IsRegExpPrototypeOptimizable.Deoptimized");
   8020  }
   8021 
   8022  return AttachDecision::Attach;
   8023 }
   8024 
   8025 AttachDecision
   8026 InlinableNativeIRGenerator::tryAttachIsOptimizableRegExpObject() {
   8027  // Self-hosted code calls this with a single object argument.
   8028  MOZ_ASSERT(args_.length() == 1);
   8029  MOZ_ASSERT(args_[0].isObject());
   8030 
   8031  Shape* optimizableShape = cx_->global()->maybeRegExpShapeWithDefaultProto();
   8032  if (!optimizableShape) {
   8033    return AttachDecision::NoAction;
   8034  }
   8035 
   8036  // Initialize the input operand.
   8037  initializeInputOperand();
   8038 
   8039  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8040 
   8041  if (cx_->realm()->realmFuses.optimizeRegExpPrototypeFuse.intact()) {
   8042    ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
   8043    ObjOperandId objId = writer.guardToObject(argId);
   8044    writer.guardFuse(RealmFuses::FuseIndex::OptimizeRegExpPrototypeFuse);
   8045    writer.hasShapeResult(objId, optimizableShape);
   8046    writer.returnFromIC();
   8047    trackAttached("IsOptimizableRegExpObject.Optimized");
   8048  } else {
   8049    writer.loadBooleanResult(false);
   8050    writer.returnFromIC();
   8051    trackAttached("IsOptimizableRegExpObject.Deoptimized");
   8052  }
   8053 
   8054  return AttachDecision::Attach;
   8055 }
   8056 
   8057 AttachDecision InlinableNativeIRGenerator::tryAttachGetFirstDollarIndex() {
   8058  // Self-hosted code calls this with a single string argument.
   8059  MOZ_ASSERT(args_.length() == 1);
   8060  MOZ_ASSERT(args_[0].isString());
   8061 
   8062  // Initialize the input operand.
   8063  initializeInputOperand();
   8064 
   8065  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8066 
   8067  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   8068  StringOperandId strId = writer.guardToString(arg0Id);
   8069 
   8070  writer.getFirstDollarIndexResult(strId);
   8071  writer.returnFromIC();
   8072 
   8073  trackAttached("GetFirstDollarIndex");
   8074  return AttachDecision::Attach;
   8075 }
   8076 
   8077 AttachDecision InlinableNativeIRGenerator::tryAttachSubstringKernel() {
   8078  // Self-hosted code calls this with (string, int32, int32) arguments.
   8079  MOZ_ASSERT(args_.length() == 3);
   8080  MOZ_ASSERT(args_[0].isString());
   8081  MOZ_ASSERT(args_[1].isInt32());
   8082  MOZ_ASSERT(args_[2].isInt32());
   8083 
   8084  // Initialize the input operand.
   8085  initializeInputOperand();
   8086 
   8087  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8088 
   8089  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   8090  StringOperandId strId = writer.guardToString(arg0Id);
   8091 
   8092  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   8093  Int32OperandId beginId = writer.guardToInt32(arg1Id);
   8094 
   8095  ValOperandId arg2Id = loadArgumentIntrinsic(ArgumentKind::Arg2);
   8096  Int32OperandId lengthId = writer.guardToInt32(arg2Id);
   8097 
   8098  writer.callSubstringKernelResult(strId, beginId, lengthId);
   8099  writer.returnFromIC();
   8100 
   8101  trackAttached("SubstringKernel");
   8102  return AttachDecision::Attach;
   8103 }
   8104 
   8105 static bool CanConvertToString(const Value& v) {
   8106  return v.isString() || v.isNumber() || v.isBoolean() || v.isNullOrUndefined();
   8107 }
   8108 
   8109 AttachDecision InlinableNativeIRGenerator::tryAttachString() {
   8110  // Need a single argument that is or can be converted to a string.
   8111  if (args_.length() != 1 || !CanConvertToString(args_[0])) {
   8112    return AttachDecision::NoAction;
   8113  }
   8114 
   8115  // Initialize the input operand.
   8116  Int32OperandId argcId = initializeInputOperand();
   8117 
   8118  // Guard callee is the 'String' function.
   8119  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8120 
   8121  // Guard that the argument is a string or can be converted to one.
   8122  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8123  StringOperandId strId = emitToStringGuard(argId, args_[0]);
   8124 
   8125  // Return the string.
   8126  writer.loadStringResult(strId);
   8127  writer.returnFromIC();
   8128 
   8129  trackAttached("String");
   8130  return AttachDecision::Attach;
   8131 }
   8132 
   8133 AttachDecision InlinableNativeIRGenerator::tryAttachStringConstructor() {
   8134  // Need a single argument that is or can be converted to a string.
   8135  if (args_.length() != 1 || !CanConvertToString(args_[0])) {
   8136    return AttachDecision::NoAction;
   8137  }
   8138 
   8139  RootedString emptyString(cx_, cx_->runtime()->emptyString);
   8140  JSObject* templateObj = StringObject::create(
   8141      cx_, emptyString, /* proto = */ nullptr, TenuredObject);
   8142  if (!templateObj) {
   8143    cx_->recoverFromOutOfMemory();
   8144    return AttachDecision::NoAction;
   8145  }
   8146 
   8147  // Initialize the input operand.
   8148  Int32OperandId argcId = initializeInputOperand();
   8149 
   8150  // Guard callee is the 'String' function.
   8151  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8152 
   8153  // Guard on number and convert to string.
   8154  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8155  StringOperandId strId = emitToStringGuard(argId, args_[0]);
   8156 
   8157  writer.newStringObjectResult(templateObj, strId);
   8158  writer.returnFromIC();
   8159 
   8160  trackAttached("StringConstructor");
   8161  return AttachDecision::Attach;
   8162 }
   8163 
   8164 AttachDecision InlinableNativeIRGenerator::tryAttachStringToStringValueOf() {
   8165  // Expecting no arguments.
   8166  if (args_.length() != 0) {
   8167    return AttachDecision::NoAction;
   8168  }
   8169 
   8170  // Ensure |this| is a primitive string value.
   8171  if (!thisval_.isString()) {
   8172    return AttachDecision::NoAction;
   8173  }
   8174 
   8175  // Initialize the input operand.
   8176  Int32OperandId argcId = initializeInputOperand();
   8177 
   8178  // Guard callee is the 'toString' OR 'valueOf' native function.
   8179  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8180 
   8181  // Guard |this| is a string.
   8182  ValOperandId thisValId = loadThis(calleeId);
   8183  StringOperandId strId = writer.guardToString(thisValId);
   8184 
   8185  // Return the string
   8186  writer.loadStringResult(strId);
   8187  writer.returnFromIC();
   8188 
   8189  trackAttached("StringToStringValueOf");
   8190  return AttachDecision::Attach;
   8191 }
   8192 
   8193 AttachDecision InlinableNativeIRGenerator::tryAttachStringReplaceString() {
   8194  // Self-hosted code calls this with (string, string, string) arguments.
   8195  MOZ_ASSERT(args_.length() == 3);
   8196  MOZ_ASSERT(args_[0].isString());
   8197  MOZ_ASSERT(args_[1].isString());
   8198  MOZ_ASSERT(args_[2].isString());
   8199 
   8200  // Initialize the input operand.
   8201  initializeInputOperand();
   8202 
   8203  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8204 
   8205  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   8206  StringOperandId strId = writer.guardToString(arg0Id);
   8207 
   8208  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   8209  StringOperandId patternId = writer.guardToString(arg1Id);
   8210 
   8211  ValOperandId arg2Id = loadArgumentIntrinsic(ArgumentKind::Arg2);
   8212  StringOperandId replacementId = writer.guardToString(arg2Id);
   8213 
   8214  writer.stringReplaceStringResult(strId, patternId, replacementId);
   8215  writer.returnFromIC();
   8216 
   8217  trackAttached("StringReplaceString");
   8218  return AttachDecision::Attach;
   8219 }
   8220 
   8221 AttachDecision InlinableNativeIRGenerator::tryAttachStringSplitString() {
   8222  // Self-hosted code calls this with (string, string) arguments.
   8223  MOZ_ASSERT(args_.length() == 2);
   8224  MOZ_ASSERT(args_[0].isString());
   8225  MOZ_ASSERT(args_[1].isString());
   8226 
   8227  // Initialize the input operand.
   8228  initializeInputOperand();
   8229 
   8230  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
   8231 
   8232  ValOperandId arg0Id = loadArgumentIntrinsic(ArgumentKind::Arg0);
   8233  StringOperandId strId = writer.guardToString(arg0Id);
   8234 
   8235  ValOperandId arg1Id = loadArgumentIntrinsic(ArgumentKind::Arg1);
   8236  StringOperandId separatorId = writer.guardToString(arg1Id);
   8237 
   8238  writer.stringSplitStringResult(strId, separatorId);
   8239  writer.returnFromIC();
   8240 
   8241  trackAttached("StringSplitString");
   8242  return AttachDecision::Attach;
   8243 }
   8244 
   8245 AttachDecision InlinableNativeIRGenerator::tryAttachStringChar(
   8246    StringChar kind) {
   8247  // Need zero or one argument.
   8248  if (args_.length() > 1) {
   8249    return AttachDecision::NoAction;
   8250  }
   8251 
   8252  // Absent index argument defaults to zero:
   8253  // ToInteger(ToNumber(undefined)) = ToInteger(NaN) = 0.
   8254  auto indexArg = args_.length() > 0 ? args_[0] : Int32Value(0);
   8255 
   8256  auto attach = CanAttachStringChar(thisval_, indexArg, kind);
   8257  if (attach == AttachStringChar::No) {
   8258    return AttachDecision::NoAction;
   8259  }
   8260 
   8261  bool handleOOB = attach == AttachStringChar::OutOfBounds;
   8262 
   8263  // Initialize the input operand.
   8264  Int32OperandId argcId = initializeInputOperand();
   8265 
   8266  // Guard callee is the 'charCodeAt', 'codePointAt', 'charAt', or 'at' native
   8267  // function.
   8268  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8269 
   8270  // Guard this is a string.
   8271  ValOperandId thisValId = loadThis(calleeId);
   8272  StringOperandId strId = writer.guardToString(thisValId);
   8273 
   8274  // Guard int32 index.
   8275  Int32OperandId int32IndexId;
   8276  if (args_.length() > 0) {
   8277    ValOperandId indexId = loadArgument(calleeId, ArgumentKind::Arg0);
   8278    int32IndexId = EmitGuardToInt32Index(writer, args_[0], indexId);
   8279  } else {
   8280    int32IndexId = writer.loadInt32Constant(0);
   8281  }
   8282 
   8283  // Handle relative string indices, if necessary.
   8284  if (kind == StringChar::At) {
   8285    int32IndexId = writer.toRelativeStringIndex(int32IndexId, strId);
   8286  }
   8287 
   8288  // Linearize the string.
   8289  //
   8290  // AttachStringChar doesn't have a separate state when OOB access happens on
   8291  // a string which needs to be linearized, so just linearize unconditionally
   8292  // for out-of-bounds accesses.
   8293  if (attach == AttachStringChar::Linearize ||
   8294      attach == AttachStringChar::OutOfBounds) {
   8295    switch (kind) {
   8296      case StringChar::CharCodeAt:
   8297      case StringChar::CharAt:
   8298      case StringChar::At:
   8299        strId = writer.linearizeForCharAccess(strId, int32IndexId);
   8300        break;
   8301      case StringChar::CodePointAt:
   8302        strId = writer.linearizeForCodePointAccess(strId, int32IndexId);
   8303        break;
   8304    }
   8305  }
   8306 
   8307  // Load string char or code.
   8308  switch (kind) {
   8309    case StringChar::CharCodeAt:
   8310      writer.loadStringCharCodeResult(strId, int32IndexId, handleOOB);
   8311      break;
   8312    case StringChar::CodePointAt:
   8313      writer.loadStringCodePointResult(strId, int32IndexId, handleOOB);
   8314      break;
   8315    case StringChar::CharAt:
   8316      writer.loadStringCharResult(strId, int32IndexId, handleOOB);
   8317      break;
   8318    case StringChar::At:
   8319      writer.loadStringAtResult(strId, int32IndexId, handleOOB);
   8320      break;
   8321  }
   8322 
   8323  writer.returnFromIC();
   8324 
   8325  switch (kind) {
   8326    case StringChar::CharCodeAt:
   8327      trackAttached("StringCharCodeAt");
   8328      break;
   8329    case StringChar::CodePointAt:
   8330      trackAttached("StringCodePointAt");
   8331      break;
   8332    case StringChar::CharAt:
   8333      trackAttached("StringCharAt");
   8334      break;
   8335    case StringChar::At:
   8336      trackAttached("StringAt");
   8337      break;
   8338  }
   8339 
   8340  return AttachDecision::Attach;
   8341 }
   8342 
   8343 AttachDecision InlinableNativeIRGenerator::tryAttachStringCharCodeAt() {
   8344  return tryAttachStringChar(StringChar::CharCodeAt);
   8345 }
   8346 
   8347 AttachDecision InlinableNativeIRGenerator::tryAttachStringCodePointAt() {
   8348  return tryAttachStringChar(StringChar::CodePointAt);
   8349 }
   8350 
   8351 AttachDecision InlinableNativeIRGenerator::tryAttachStringCharAt() {
   8352  return tryAttachStringChar(StringChar::CharAt);
   8353 }
   8354 
   8355 AttachDecision InlinableNativeIRGenerator::tryAttachStringAt() {
   8356  return tryAttachStringChar(StringChar::At);
   8357 }
   8358 
   8359 AttachDecision InlinableNativeIRGenerator::tryAttachStringFromCharCode() {
   8360  // Need one number argument.
   8361  if (args_.length() != 1 || !args_[0].isNumber()) {
   8362    return AttachDecision::NoAction;
   8363  }
   8364 
   8365  // Initialize the input operand.
   8366  Int32OperandId argcId = initializeInputOperand();
   8367 
   8368  // Guard callee is the 'fromCharCode' native function.
   8369  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8370 
   8371  // Guard int32 argument.
   8372  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8373  Int32OperandId codeId;
   8374  if (args_[0].isInt32()) {
   8375    codeId = writer.guardToInt32(argId);
   8376  } else {
   8377    // 'fromCharCode' performs ToUint16 on its input. We can use Uint32
   8378    // semantics, because ToUint16(ToUint32(v)) == ToUint16(v).
   8379    codeId = writer.guardToInt32ModUint32(argId);
   8380  }
   8381 
   8382  // Return string created from code.
   8383  writer.stringFromCharCodeResult(codeId);
   8384  writer.returnFromIC();
   8385 
   8386  trackAttached("StringFromCharCode");
   8387  return AttachDecision::Attach;
   8388 }
   8389 
   8390 AttachDecision InlinableNativeIRGenerator::tryAttachStringFromCodePoint() {
   8391  // Need one int32 argument.
   8392  if (args_.length() != 1 || !args_[0].isInt32()) {
   8393    return AttachDecision::NoAction;
   8394  }
   8395 
   8396  // String.fromCodePoint throws for invalid code points.
   8397  int32_t codePoint = args_[0].toInt32();
   8398  if (codePoint < 0 || codePoint > int32_t(unicode::NonBMPMax)) {
   8399    return AttachDecision::NoAction;
   8400  }
   8401 
   8402  // Initialize the input operand.
   8403  Int32OperandId argcId = initializeInputOperand();
   8404 
   8405  // Guard callee is the 'fromCodePoint' native function.
   8406  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8407 
   8408  // Guard int32 argument.
   8409  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8410  Int32OperandId codeId = writer.guardToInt32(argId);
   8411 
   8412  // Return string created from code point.
   8413  writer.stringFromCodePointResult(codeId);
   8414  writer.returnFromIC();
   8415 
   8416  trackAttached("StringFromCodePoint");
   8417  return AttachDecision::Attach;
   8418 }
   8419 
   8420 AttachDecision InlinableNativeIRGenerator::tryAttachStringIncludes() {
   8421  // Need one string argument.
   8422  if (args_.length() != 1 || !args_[0].isString()) {
   8423    return AttachDecision::NoAction;
   8424  }
   8425 
   8426  // Ensure |this| is a primitive string value.
   8427  if (!thisval_.isString()) {
   8428    return AttachDecision::NoAction;
   8429  }
   8430 
   8431  // Initialize the input operand.
   8432  Int32OperandId argcId = initializeInputOperand();
   8433 
   8434  // Guard callee is the 'includes' native function.
   8435  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8436 
   8437  // Guard this is a string.
   8438  ValOperandId thisValId = loadThis(calleeId);
   8439  StringOperandId strId = writer.guardToString(thisValId);
   8440 
   8441  // Guard string argument.
   8442  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8443  StringOperandId searchStrId = writer.guardToString(argId);
   8444 
   8445  writer.stringIncludesResult(strId, searchStrId);
   8446  writer.returnFromIC();
   8447 
   8448  trackAttached("StringIncludes");
   8449  return AttachDecision::Attach;
   8450 }
   8451 
   8452 AttachDecision InlinableNativeIRGenerator::tryAttachStringIndexOf() {
   8453  // Need one string argument.
   8454  if (args_.length() != 1 || !args_[0].isString()) {
   8455    return AttachDecision::NoAction;
   8456  }
   8457 
   8458  // Ensure |this| is a primitive string value.
   8459  if (!thisval_.isString()) {
   8460    return AttachDecision::NoAction;
   8461  }
   8462 
   8463  // Initialize the input operand.
   8464  Int32OperandId argcId = initializeInputOperand();
   8465 
   8466  // Guard callee is the 'indexOf' native function.
   8467  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8468 
   8469  // Guard this is a string.
   8470  ValOperandId thisValId = loadThis(calleeId);
   8471  StringOperandId strId = writer.guardToString(thisValId);
   8472 
   8473  // Guard string argument.
   8474  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8475  StringOperandId searchStrId = writer.guardToString(argId);
   8476 
   8477  writer.stringIndexOfResult(strId, searchStrId);
   8478  writer.returnFromIC();
   8479 
   8480  trackAttached("StringIndexOf");
   8481  return AttachDecision::Attach;
   8482 }
   8483 
   8484 AttachDecision InlinableNativeIRGenerator::tryAttachStringLastIndexOf() {
   8485  // Need one string argument.
   8486  if (args_.length() != 1 || !args_[0].isString()) {
   8487    return AttachDecision::NoAction;
   8488  }
   8489 
   8490  // Ensure |this| is a primitive string value.
   8491  if (!thisval_.isString()) {
   8492    return AttachDecision::NoAction;
   8493  }
   8494 
   8495  // Initialize the input operand.
   8496  Int32OperandId argcId = initializeInputOperand();
   8497 
   8498  // Guard callee is the 'lastIndexOf' native function.
   8499  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8500 
   8501  // Guard this is a string.
   8502  ValOperandId thisValId = loadThis(calleeId);
   8503  StringOperandId strId = writer.guardToString(thisValId);
   8504 
   8505  // Guard string argument.
   8506  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8507  StringOperandId searchStrId = writer.guardToString(argId);
   8508 
   8509  writer.stringLastIndexOfResult(strId, searchStrId);
   8510  writer.returnFromIC();
   8511 
   8512  trackAttached("StringLastIndexOf");
   8513  return AttachDecision::Attach;
   8514 }
   8515 
   8516 AttachDecision InlinableNativeIRGenerator::tryAttachStringStartsWith() {
   8517  // Need one string argument.
   8518  if (args_.length() != 1 || !args_[0].isString()) {
   8519    return AttachDecision::NoAction;
   8520  }
   8521 
   8522  // Ensure |this| is a primitive string value.
   8523  if (!thisval_.isString()) {
   8524    return AttachDecision::NoAction;
   8525  }
   8526 
   8527  // Initialize the input operand.
   8528  Int32OperandId argcId = initializeInputOperand();
   8529 
   8530  // Guard callee is the 'startsWith' native function.
   8531  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8532 
   8533  // Guard this is a string.
   8534  ValOperandId thisValId = loadThis(calleeId);
   8535  StringOperandId strId = writer.guardToString(thisValId);
   8536 
   8537  // Guard string argument.
   8538  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8539  StringOperandId searchStrId = writer.guardToString(argId);
   8540 
   8541  writer.stringStartsWithResult(strId, searchStrId);
   8542  writer.returnFromIC();
   8543 
   8544  trackAttached("StringStartsWith");
   8545  return AttachDecision::Attach;
   8546 }
   8547 
   8548 AttachDecision InlinableNativeIRGenerator::tryAttachStringEndsWith() {
   8549  // Need one string argument.
   8550  if (args_.length() != 1 || !args_[0].isString()) {
   8551    return AttachDecision::NoAction;
   8552  }
   8553 
   8554  // Ensure |this| is a primitive string value.
   8555  if (!thisval_.isString()) {
   8556    return AttachDecision::NoAction;
   8557  }
   8558 
   8559  // Initialize the input operand.
   8560  Int32OperandId argcId = initializeInputOperand();
   8561 
   8562  // Guard callee is the 'endsWith' native function.
   8563  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8564 
   8565  // Guard this is a string.
   8566  ValOperandId thisValId = loadThis(calleeId);
   8567  StringOperandId strId = writer.guardToString(thisValId);
   8568 
   8569  // Guard string argument.
   8570  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8571  StringOperandId searchStrId = writer.guardToString(argId);
   8572 
   8573  writer.stringEndsWithResult(strId, searchStrId);
   8574  writer.returnFromIC();
   8575 
   8576  trackAttached("StringEndsWith");
   8577  return AttachDecision::Attach;
   8578 }
   8579 
   8580 AttachDecision InlinableNativeIRGenerator::tryAttachStringToLowerCase() {
   8581  // Expecting no arguments.
   8582  if (args_.length() != 0) {
   8583    return AttachDecision::NoAction;
   8584  }
   8585 
   8586  // Ensure |this| is a primitive string value.
   8587  if (!thisval_.isString()) {
   8588    return AttachDecision::NoAction;
   8589  }
   8590 
   8591  // Initialize the input operand.
   8592  Int32OperandId argcId = initializeInputOperand();
   8593 
   8594  // Guard callee is the 'toLowerCase' native function.
   8595  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8596 
   8597  // Guard this is a string.
   8598  ValOperandId thisValId = loadThis(calleeId);
   8599  StringOperandId strId = writer.guardToString(thisValId);
   8600 
   8601  // Return string converted to lower-case.
   8602  writer.stringToLowerCaseResult(strId);
   8603  writer.returnFromIC();
   8604 
   8605  trackAttached("StringToLowerCase");
   8606  return AttachDecision::Attach;
   8607 }
   8608 
   8609 AttachDecision InlinableNativeIRGenerator::tryAttachStringToUpperCase() {
   8610  // Expecting no arguments.
   8611  if (args_.length() != 0) {
   8612    return AttachDecision::NoAction;
   8613  }
   8614 
   8615  // Ensure |this| is a primitive string value.
   8616  if (!thisval_.isString()) {
   8617    return AttachDecision::NoAction;
   8618  }
   8619 
   8620  // Initialize the input operand.
   8621  Int32OperandId argcId = initializeInputOperand();
   8622 
   8623  // Guard callee is the 'toUpperCase' native function.
   8624  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8625 
   8626  // Guard this is a string.
   8627  ValOperandId thisValId = loadThis(calleeId);
   8628  StringOperandId strId = writer.guardToString(thisValId);
   8629 
   8630  // Return string converted to upper-case.
   8631  writer.stringToUpperCaseResult(strId);
   8632  writer.returnFromIC();
   8633 
   8634  trackAttached("StringToUpperCase");
   8635  return AttachDecision::Attach;
   8636 }
   8637 
   8638 AttachDecision InlinableNativeIRGenerator::tryAttachStringToLocaleLowerCase() {
   8639 #if JS_HAS_INTL_API
   8640  // Expecting no arguments.
   8641  if (args_.length() != 0) {
   8642    return AttachDecision::NoAction;
   8643  }
   8644 
   8645  // Ensure |this| is a primitive string value.
   8646  if (!thisval_.isString()) {
   8647    return AttachDecision::NoAction;
   8648  }
   8649 
   8650  // Don't inline when not using the default locale.
   8651  if (cx_->realm()->behaviors().localeOverride()) {
   8652    return AttachDecision::NoAction;
   8653  }
   8654 
   8655  // Default case mapping fuse must still be intact.
   8656  if (!cx_->runtime()
   8657           ->runtimeFuses.ref()
   8658           .defaultLocaleHasDefaultCaseMappingFuse.intact()) {
   8659    return AttachDecision::NoAction;
   8660  }
   8661 
   8662  // Initialize the input operand.
   8663  Int32OperandId argcId = initializeInputOperand();
   8664 
   8665  // Guard callee is the 'toLocaleLowerCase' native function.
   8666  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8667 
   8668  // Guard this is a string.
   8669  ValOperandId thisValId = loadThis(calleeId);
   8670  StringOperandId strId = writer.guardToString(thisValId);
   8671 
   8672  // Guard runtime default locale uses default case mapping.
   8673  writer.guardRuntimeFuse(
   8674      RuntimeFuses::FuseIndex::DefaultLocaleHasDefaultCaseMappingFuse);
   8675 
   8676  // Return string converted to lower-case.
   8677  writer.stringToLowerCaseResult(strId);
   8678  writer.returnFromIC();
   8679 
   8680  trackAttached("StringToLocaleLowerCase");
   8681  return AttachDecision::Attach;
   8682 #else
   8683  // No inlining when Intl support is disabled.
   8684  return AttachDecision::Attach;
   8685 #endif
   8686 }
   8687 
   8688 AttachDecision InlinableNativeIRGenerator::tryAttachStringToLocaleUpperCase() {
   8689 #if JS_HAS_INTL_API
   8690  // Expecting no arguments.
   8691  if (args_.length() != 0) {
   8692    return AttachDecision::NoAction;
   8693  }
   8694 
   8695  // Ensure |this| is a primitive string value.
   8696  if (!thisval_.isString()) {
   8697    return AttachDecision::NoAction;
   8698  }
   8699 
   8700  // Don't inline when not using the default locale.
   8701  if (cx_->realm()->behaviors().localeOverride()) {
   8702    return AttachDecision::NoAction;
   8703  }
   8704 
   8705  // Default case mapping fuse must still be intact.
   8706  if (!cx_->runtime()
   8707           ->runtimeFuses.ref()
   8708           .defaultLocaleHasDefaultCaseMappingFuse.intact()) {
   8709    return AttachDecision::NoAction;
   8710  }
   8711 
   8712  // Initialize the input operand.
   8713  Int32OperandId argcId = initializeInputOperand();
   8714 
   8715  // Guard callee is the 'toLocaleUpperCase' native function.
   8716  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8717 
   8718  // Guard this is a string.
   8719  ValOperandId thisValId = loadThis(calleeId);
   8720  StringOperandId strId = writer.guardToString(thisValId);
   8721 
   8722  // Guard runtime default locale uses default case mapping.
   8723  writer.guardRuntimeFuse(
   8724      RuntimeFuses::FuseIndex::DefaultLocaleHasDefaultCaseMappingFuse);
   8725 
   8726  // Return string converted to upper-case.
   8727  writer.stringToUpperCaseResult(strId);
   8728  writer.returnFromIC();
   8729 
   8730  trackAttached("StringToLocaleUpperCase");
   8731  return AttachDecision::Attach;
   8732 #else
   8733  // No inlining when Intl support is disabled.
   8734  return AttachDecision::Attach;
   8735 #endif
   8736 }
   8737 
   8738 AttachDecision InlinableNativeIRGenerator::tryAttachStringTrim() {
   8739  // Expecting no arguments.
   8740  if (args_.length() != 0) {
   8741    return AttachDecision::NoAction;
   8742  }
   8743 
   8744  // Ensure |this| is a primitive string value.
   8745  if (!thisval_.isString()) {
   8746    return AttachDecision::NoAction;
   8747  }
   8748 
   8749  // Initialize the input operand.
   8750  Int32OperandId argcId = initializeInputOperand();
   8751 
   8752  // Guard callee is the 'trim' native function.
   8753  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8754 
   8755  // Guard this is a string.
   8756  ValOperandId thisValId = loadThis(calleeId);
   8757  StringOperandId strId = writer.guardToString(thisValId);
   8758 
   8759  writer.stringTrimResult(strId);
   8760  writer.returnFromIC();
   8761 
   8762  trackAttached("StringTrim");
   8763  return AttachDecision::Attach;
   8764 }
   8765 
   8766 AttachDecision InlinableNativeIRGenerator::tryAttachStringTrimStart() {
   8767  // Expecting no arguments.
   8768  if (args_.length() != 0) {
   8769    return AttachDecision::NoAction;
   8770  }
   8771 
   8772  // Ensure |this| is a primitive string value.
   8773  if (!thisval_.isString()) {
   8774    return AttachDecision::NoAction;
   8775  }
   8776 
   8777  // Initialize the input operand.
   8778  Int32OperandId argcId = initializeInputOperand();
   8779 
   8780  // Guard callee is the 'trimStart' native function.
   8781  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8782 
   8783  // Guard this is a string.
   8784  ValOperandId thisValId = loadThis(calleeId);
   8785  StringOperandId strId = writer.guardToString(thisValId);
   8786 
   8787  writer.stringTrimStartResult(strId);
   8788  writer.returnFromIC();
   8789 
   8790  trackAttached("StringTrimStart");
   8791  return AttachDecision::Attach;
   8792 }
   8793 
   8794 AttachDecision InlinableNativeIRGenerator::tryAttachStringTrimEnd() {
   8795  // Expecting no arguments.
   8796  if (args_.length() != 0) {
   8797    return AttachDecision::NoAction;
   8798  }
   8799 
   8800  // Ensure |this| is a primitive string value.
   8801  if (!thisval_.isString()) {
   8802    return AttachDecision::NoAction;
   8803  }
   8804 
   8805  // Initialize the input operand.
   8806  Int32OperandId argcId = initializeInputOperand();
   8807 
   8808  // Guard callee is the 'trimEnd' native function.
   8809  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8810 
   8811  // Guard this is a string.
   8812  ValOperandId thisValId = loadThis(calleeId);
   8813  StringOperandId strId = writer.guardToString(thisValId);
   8814 
   8815  writer.stringTrimEndResult(strId);
   8816  writer.returnFromIC();
   8817 
   8818  trackAttached("StringTrimEnd");
   8819  return AttachDecision::Attach;
   8820 }
   8821 
   8822 AttachDecision InlinableNativeIRGenerator::tryAttachMathRandom() {
   8823  // Expecting no arguments.
   8824  if (args_.length() != 0) {
   8825    return AttachDecision::NoAction;
   8826  }
   8827 
   8828  MOZ_ASSERT(cx_->realm() == target_->realm(),
   8829             "Shouldn't inline cross-realm Math.random because per-realm RNG");
   8830 
   8831  // Initialize the input operand.
   8832  Int32OperandId argcId = initializeInputOperand();
   8833 
   8834  // Guard callee is the 'random' native function.
   8835  emitNativeCalleeGuard(argcId);
   8836 
   8837  mozilla::non_crypto::XorShift128PlusRNG* rng =
   8838      &cx_->realm()->getOrCreateRandomNumberGenerator();
   8839  writer.mathRandomResult(rng);
   8840 
   8841  writer.returnFromIC();
   8842 
   8843  trackAttached("MathRandom");
   8844  return AttachDecision::Attach;
   8845 }
   8846 
   8847 AttachDecision InlinableNativeIRGenerator::tryAttachMathAbs() {
   8848  // Need one argument.
   8849  if (args_.length() != 1) {
   8850    return AttachDecision::NoAction;
   8851  }
   8852 
   8853  if (!args_[0].isNumber()) {
   8854    return AttachDecision::NoAction;
   8855  }
   8856 
   8857  // Initialize the input operand.
   8858  Int32OperandId argcId = initializeInputOperand();
   8859 
   8860  // Guard callee is the 'abs' native function.
   8861  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8862 
   8863  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   8864 
   8865  // abs(INT_MIN) is a double.
   8866  if (args_[0].isInt32() && args_[0].toInt32() != INT_MIN) {
   8867    Int32OperandId int32Id = writer.guardToInt32(argumentId);
   8868    writer.mathAbsInt32Result(int32Id);
   8869  } else {
   8870    NumberOperandId numberId = writer.guardIsNumber(argumentId);
   8871    writer.mathAbsNumberResult(numberId);
   8872  }
   8873 
   8874  writer.returnFromIC();
   8875 
   8876  trackAttached("MathAbs");
   8877  return AttachDecision::Attach;
   8878 }
   8879 
   8880 AttachDecision InlinableNativeIRGenerator::tryAttachMathClz32() {
   8881  // Need one (number) argument.
   8882  if (args_.length() != 1 || !args_[0].isNumber()) {
   8883    return AttachDecision::NoAction;
   8884  }
   8885 
   8886  // Initialize the input operand.
   8887  Int32OperandId argcId = initializeInputOperand();
   8888 
   8889  // Guard callee is the 'clz32' native function.
   8890  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8891 
   8892  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8893 
   8894  Int32OperandId int32Id;
   8895  if (args_[0].isInt32()) {
   8896    int32Id = writer.guardToInt32(argId);
   8897  } else {
   8898    MOZ_ASSERT(args_[0].isDouble());
   8899    NumberOperandId numId = writer.guardIsNumber(argId);
   8900    int32Id = writer.truncateDoubleToUInt32(numId);
   8901  }
   8902  writer.mathClz32Result(int32Id);
   8903  writer.returnFromIC();
   8904 
   8905  trackAttached("MathClz32");
   8906  return AttachDecision::Attach;
   8907 }
   8908 
   8909 AttachDecision InlinableNativeIRGenerator::tryAttachMathSign() {
   8910  // Need one (number) argument.
   8911  if (args_.length() != 1 || !args_[0].isNumber()) {
   8912    return AttachDecision::NoAction;
   8913  }
   8914 
   8915  // Initialize the input operand.
   8916  Int32OperandId argcId = initializeInputOperand();
   8917 
   8918  // Guard callee is the 'sign' native function.
   8919  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8920 
   8921  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   8922 
   8923  if (args_[0].isInt32()) {
   8924    Int32OperandId int32Id = writer.guardToInt32(argId);
   8925    writer.mathSignInt32Result(int32Id);
   8926  } else {
   8927    // Math.sign returns a double only if the input is -0 or NaN so try to
   8928    // optimize the common Number => Int32 case.
   8929    double d = math_sign_impl(args_[0].toDouble());
   8930    int32_t unused;
   8931    bool resultIsInt32 = mozilla::NumberIsInt32(d, &unused);
   8932 
   8933    NumberOperandId numId = writer.guardIsNumber(argId);
   8934    if (resultIsInt32) {
   8935      writer.mathSignNumberToInt32Result(numId);
   8936    } else {
   8937      writer.mathSignNumberResult(numId);
   8938    }
   8939  }
   8940 
   8941  writer.returnFromIC();
   8942 
   8943  trackAttached("MathSign");
   8944  return AttachDecision::Attach;
   8945 }
   8946 
   8947 AttachDecision InlinableNativeIRGenerator::tryAttachMathImul() {
   8948  // Need two (number) arguments.
   8949  if (args_.length() != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
   8950    return AttachDecision::NoAction;
   8951  }
   8952 
   8953  // Initialize the input operand.
   8954  Int32OperandId argcId = initializeInputOperand();
   8955 
   8956  // Guard callee is the 'imul' native function.
   8957  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8958 
   8959  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
   8960  ValOperandId arg1Id = loadArgument(calleeId, ArgumentKind::Arg1);
   8961 
   8962  Int32OperandId int32Arg0Id, int32Arg1Id;
   8963  if (args_[0].isInt32() && args_[1].isInt32()) {
   8964    int32Arg0Id = writer.guardToInt32(arg0Id);
   8965    int32Arg1Id = writer.guardToInt32(arg1Id);
   8966  } else {
   8967    // Treat both arguments as numbers if at least one of them is non-int32.
   8968    NumberOperandId numArg0Id = writer.guardIsNumber(arg0Id);
   8969    NumberOperandId numArg1Id = writer.guardIsNumber(arg1Id);
   8970    int32Arg0Id = writer.truncateDoubleToUInt32(numArg0Id);
   8971    int32Arg1Id = writer.truncateDoubleToUInt32(numArg1Id);
   8972  }
   8973  writer.mathImulResult(int32Arg0Id, int32Arg1Id);
   8974  writer.returnFromIC();
   8975 
   8976  trackAttached("MathImul");
   8977  return AttachDecision::Attach;
   8978 }
   8979 
   8980 AttachDecision InlinableNativeIRGenerator::tryAttachMathFloor() {
   8981  // Need one (number) argument.
   8982  if (args_.length() != 1 || !args_[0].isNumber()) {
   8983    return AttachDecision::NoAction;
   8984  }
   8985 
   8986  // Check if the result fits in int32.
   8987  double res = math_floor_impl(args_[0].toNumber());
   8988  int32_t unused;
   8989  bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
   8990 
   8991  // Initialize the input operand.
   8992  Int32OperandId argcId = initializeInputOperand();
   8993 
   8994  // Guard callee is the 'floor' native function.
   8995  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   8996 
   8997  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   8998 
   8999  if (args_[0].isInt32()) {
   9000    MOZ_ASSERT(resultIsInt32);
   9001 
   9002    // Use an indirect truncation to inform the optimizer it needs to preserve
   9003    // a bailout when the input can't be represented as an int32, even if the
   9004    // final result is fully truncated.
   9005    Int32OperandId intId = writer.guardToInt32(argumentId);
   9006    writer.indirectTruncateInt32Result(intId);
   9007  } else {
   9008    NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9009 
   9010    if (resultIsInt32) {
   9011      writer.mathFloorToInt32Result(numberId);
   9012    } else {
   9013      writer.mathFloorNumberResult(numberId);
   9014    }
   9015  }
   9016 
   9017  writer.returnFromIC();
   9018 
   9019  trackAttached("MathFloor");
   9020  return AttachDecision::Attach;
   9021 }
   9022 
   9023 AttachDecision InlinableNativeIRGenerator::tryAttachMathCeil() {
   9024  // Need one (number) argument.
   9025  if (args_.length() != 1 || !args_[0].isNumber()) {
   9026    return AttachDecision::NoAction;
   9027  }
   9028 
   9029  // Check if the result fits in int32.
   9030  double res = math_ceil_impl(args_[0].toNumber());
   9031  int32_t unused;
   9032  bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
   9033 
   9034  // Initialize the input operand.
   9035  Int32OperandId argcId = initializeInputOperand();
   9036 
   9037  // Guard callee is the 'ceil' native function.
   9038  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9039 
   9040  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9041 
   9042  if (args_[0].isInt32()) {
   9043    MOZ_ASSERT(resultIsInt32);
   9044 
   9045    // Use an indirect truncation to inform the optimizer it needs to preserve
   9046    // a bailout when the input can't be represented as an int32, even if the
   9047    // final result is fully truncated.
   9048    Int32OperandId intId = writer.guardToInt32(argumentId);
   9049    writer.indirectTruncateInt32Result(intId);
   9050  } else {
   9051    NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9052 
   9053    if (resultIsInt32) {
   9054      writer.mathCeilToInt32Result(numberId);
   9055    } else {
   9056      writer.mathCeilNumberResult(numberId);
   9057    }
   9058  }
   9059 
   9060  writer.returnFromIC();
   9061 
   9062  trackAttached("MathCeil");
   9063  return AttachDecision::Attach;
   9064 }
   9065 
   9066 AttachDecision InlinableNativeIRGenerator::tryAttachMathTrunc() {
   9067  // Need one (number) argument.
   9068  if (args_.length() != 1 || !args_[0].isNumber()) {
   9069    return AttachDecision::NoAction;
   9070  }
   9071 
   9072  // Check if the result fits in int32.
   9073  double res = math_trunc_impl(args_[0].toNumber());
   9074  int32_t unused;
   9075  bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
   9076 
   9077  // Initialize the input operand.
   9078  Int32OperandId argcId = initializeInputOperand();
   9079 
   9080  // Guard callee is the 'trunc' native function.
   9081  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9082 
   9083  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9084 
   9085  if (args_[0].isInt32()) {
   9086    MOZ_ASSERT(resultIsInt32);
   9087 
   9088    // We don't need an indirect truncation barrier here, because Math.trunc
   9089    // always truncates, but never rounds its input away from zero.
   9090    Int32OperandId intId = writer.guardToInt32(argumentId);
   9091    writer.loadInt32Result(intId);
   9092  } else {
   9093    NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9094 
   9095    if (resultIsInt32) {
   9096      writer.mathTruncToInt32Result(numberId);
   9097    } else {
   9098      writer.mathTruncNumberResult(numberId);
   9099    }
   9100  }
   9101 
   9102  writer.returnFromIC();
   9103 
   9104  trackAttached("MathTrunc");
   9105  return AttachDecision::Attach;
   9106 }
   9107 
   9108 AttachDecision InlinableNativeIRGenerator::tryAttachMathRound() {
   9109  // Need one (number) argument.
   9110  if (args_.length() != 1 || !args_[0].isNumber()) {
   9111    return AttachDecision::NoAction;
   9112  }
   9113 
   9114  // Check if the result fits in int32.
   9115  double res = math_round_impl(args_[0].toNumber());
   9116  int32_t unused;
   9117  bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
   9118 
   9119  // Initialize the input operand.
   9120  Int32OperandId argcId = initializeInputOperand();
   9121 
   9122  // Guard callee is the 'round' native function.
   9123  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9124 
   9125  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9126 
   9127  if (args_[0].isInt32()) {
   9128    MOZ_ASSERT(resultIsInt32);
   9129 
   9130    // Use an indirect truncation to inform the optimizer it needs to preserve
   9131    // a bailout when the input can't be represented as an int32, even if the
   9132    // final result is fully truncated.
   9133    Int32OperandId intId = writer.guardToInt32(argumentId);
   9134    writer.indirectTruncateInt32Result(intId);
   9135  } else {
   9136    NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9137 
   9138    if (resultIsInt32) {
   9139      writer.mathRoundToInt32Result(numberId);
   9140    } else {
   9141      writer.mathRoundNumberResult(numberId);
   9142    }
   9143  }
   9144 
   9145  writer.returnFromIC();
   9146 
   9147  trackAttached("MathRound");
   9148  return AttachDecision::Attach;
   9149 }
   9150 
   9151 AttachDecision InlinableNativeIRGenerator::tryAttachMathSqrt() {
   9152  // Need one (number) argument.
   9153  if (args_.length() != 1 || !args_[0].isNumber()) {
   9154    return AttachDecision::NoAction;
   9155  }
   9156 
   9157  // Initialize the input operand.
   9158  Int32OperandId argcId = initializeInputOperand();
   9159 
   9160  // Guard callee is the 'sqrt' native function.
   9161  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9162 
   9163  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9164  NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9165  writer.mathSqrtNumberResult(numberId);
   9166  writer.returnFromIC();
   9167 
   9168  trackAttached("MathSqrt");
   9169  return AttachDecision::Attach;
   9170 }
   9171 
   9172 AttachDecision InlinableNativeIRGenerator::tryAttachMathFRound() {
   9173  // Need one (number) argument.
   9174  if (args_.length() != 1 || !args_[0].isNumber()) {
   9175    return AttachDecision::NoAction;
   9176  }
   9177 
   9178  // Initialize the input operand.
   9179  Int32OperandId argcId = initializeInputOperand();
   9180 
   9181  // Guard callee is the 'fround' native function.
   9182  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9183 
   9184  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9185  NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9186  writer.mathFRoundNumberResult(numberId);
   9187  writer.returnFromIC();
   9188 
   9189  trackAttached("MathFRound");
   9190  return AttachDecision::Attach;
   9191 }
   9192 
   9193 AttachDecision InlinableNativeIRGenerator::tryAttachMathF16Round() {
   9194  // Need one (number) argument.
   9195  if (args_.length() != 1 || !args_[0].isNumber()) {
   9196    return AttachDecision::NoAction;
   9197  }
   9198 
   9199  // Initialize the input operand.
   9200  Int32OperandId argcId = initializeInputOperand();
   9201 
   9202  // Guard callee is the 'f16round' native function.
   9203  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9204 
   9205  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9206  NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9207  writer.mathF16RoundNumberResult(numberId);
   9208  writer.returnFromIC();
   9209 
   9210  trackAttached("MathF16Round");
   9211  return AttachDecision::Attach;
   9212 }
   9213 
   9214 static bool CanAttachInt32Pow(const Value& baseVal, const Value& powerVal) {
   9215  auto valToInt32 = [](const Value& v) {
   9216    if (v.isInt32()) {
   9217      return v.toInt32();
   9218    }
   9219    if (v.isBoolean()) {
   9220      return int32_t(v.toBoolean());
   9221    }
   9222    MOZ_ASSERT(v.isNull());
   9223    return 0;
   9224  };
   9225  int32_t base = valToInt32(baseVal);
   9226  int32_t power = valToInt32(powerVal);
   9227 
   9228  // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
   9229  // gets large enough. It's hard to determine when exactly y is "large enough",
   9230  // so we don't use Int32PowResult when x != 1 and y < 0.
   9231  // Note: it's important for this condition to match the code generated by
   9232  // MacroAssembler::pow32 to prevent failure loops.
   9233  if (power < 0) {
   9234    return base == 1;
   9235  }
   9236 
   9237  double res = powi(base, power);
   9238  int32_t unused;
   9239  return mozilla::NumberIsInt32(res, &unused);
   9240 }
   9241 
   9242 AttachDecision InlinableNativeIRGenerator::tryAttachMathPow() {
   9243  // Need two number arguments.
   9244  if (args_.length() != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
   9245    return AttachDecision::NoAction;
   9246  }
   9247 
   9248  // Initialize the input operand.
   9249  Int32OperandId argcId = initializeInputOperand();
   9250 
   9251  // Guard callee is the 'pow' function.
   9252  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9253 
   9254  ValOperandId baseId = loadArgument(calleeId, ArgumentKind::Arg0);
   9255  ValOperandId exponentId = loadArgument(calleeId, ArgumentKind::Arg1);
   9256 
   9257  if (args_[0].isInt32() && args_[1].isInt32() &&
   9258      CanAttachInt32Pow(args_[0], args_[1])) {
   9259    Int32OperandId baseInt32Id = writer.guardToInt32(baseId);
   9260    Int32OperandId exponentInt32Id = writer.guardToInt32(exponentId);
   9261    writer.int32PowResult(baseInt32Id, exponentInt32Id);
   9262  } else {
   9263    NumberOperandId baseNumberId = writer.guardIsNumber(baseId);
   9264    NumberOperandId exponentNumberId = writer.guardIsNumber(exponentId);
   9265    writer.doublePowResult(baseNumberId, exponentNumberId);
   9266  }
   9267 
   9268  writer.returnFromIC();
   9269 
   9270  trackAttached("MathPow");
   9271  return AttachDecision::Attach;
   9272 }
   9273 
   9274 AttachDecision InlinableNativeIRGenerator::tryAttachMathHypot() {
   9275  // Only optimize if there are 2-4 arguments.
   9276  if (args_.length() < 2 || args_.length() > 4) {
   9277    return AttachDecision::NoAction;
   9278  }
   9279 
   9280  for (size_t i = 0; i < args_.length(); i++) {
   9281    if (!args_[i].isNumber()) {
   9282      return AttachDecision::NoAction;
   9283    }
   9284  }
   9285 
   9286  // Initialize the input operand.
   9287  Int32OperandId argcId = initializeInputOperand();
   9288 
   9289  // Guard callee is the 'hypot' native function.
   9290  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9291 
   9292  ValOperandId firstId = loadArgument(calleeId, ArgumentKind::Arg0);
   9293  ValOperandId secondId = loadArgument(calleeId, ArgumentKind::Arg1);
   9294 
   9295  NumberOperandId firstNumId = writer.guardIsNumber(firstId);
   9296  NumberOperandId secondNumId = writer.guardIsNumber(secondId);
   9297 
   9298  ValOperandId thirdId;
   9299  ValOperandId fourthId;
   9300  NumberOperandId thirdNumId;
   9301  NumberOperandId fourthNumId;
   9302 
   9303  switch (args_.length()) {
   9304    case 2:
   9305      writer.mathHypot2NumberResult(firstNumId, secondNumId);
   9306      break;
   9307    case 3:
   9308      thirdId = loadArgument(calleeId, ArgumentKind::Arg2);
   9309      thirdNumId = writer.guardIsNumber(thirdId);
   9310      writer.mathHypot3NumberResult(firstNumId, secondNumId, thirdNumId);
   9311      break;
   9312    case 4:
   9313      thirdId = loadArgument(calleeId, ArgumentKind::Arg2);
   9314      fourthId = loadArgument(calleeId, ArgumentKind::Arg3);
   9315      thirdNumId = writer.guardIsNumber(thirdId);
   9316      fourthNumId = writer.guardIsNumber(fourthId);
   9317      writer.mathHypot4NumberResult(firstNumId, secondNumId, thirdNumId,
   9318                                    fourthNumId);
   9319      break;
   9320    default:
   9321      MOZ_CRASH("Unexpected number of arguments to hypot function.");
   9322  }
   9323 
   9324  writer.returnFromIC();
   9325 
   9326  trackAttached("MathHypot");
   9327  return AttachDecision::Attach;
   9328 }
   9329 
   9330 AttachDecision InlinableNativeIRGenerator::tryAttachMathATan2() {
   9331  // Requires two numbers as arguments.
   9332  if (args_.length() != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
   9333    return AttachDecision::NoAction;
   9334  }
   9335 
   9336  // Initialize the input operand.
   9337  Int32OperandId argcId = initializeInputOperand();
   9338 
   9339  // Guard callee is the 'atan2' native function.
   9340  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9341 
   9342  ValOperandId yId = loadArgument(calleeId, ArgumentKind::Arg0);
   9343  ValOperandId xId = loadArgument(calleeId, ArgumentKind::Arg1);
   9344 
   9345  NumberOperandId yNumberId = writer.guardIsNumber(yId);
   9346  NumberOperandId xNumberId = writer.guardIsNumber(xId);
   9347 
   9348  writer.mathAtan2NumberResult(yNumberId, xNumberId);
   9349  writer.returnFromIC();
   9350 
   9351  trackAttached("MathAtan2");
   9352  return AttachDecision::Attach;
   9353 }
   9354 
   9355 AttachDecision InlinableNativeIRGenerator::tryAttachMathMinMax(bool isMax) {
   9356  // For now only optimize if there are 1-4 arguments.
   9357  if (args_.length() < 1 || args_.length() > 4) {
   9358    return AttachDecision::NoAction;
   9359  }
   9360 
   9361  // Ensure all arguments are numbers.
   9362  bool allInt32 = true;
   9363  for (size_t i = 0; i < args_.length(); i++) {
   9364    if (!args_[i].isNumber()) {
   9365      return AttachDecision::NoAction;
   9366    }
   9367    if (!args_[i].isInt32()) {
   9368      allInt32 = false;
   9369    }
   9370  }
   9371 
   9372  // Initialize the input operand.
   9373  Int32OperandId argcId = initializeInputOperand();
   9374 
   9375  // Guard callee is this Math function.
   9376  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9377 
   9378  if (allInt32) {
   9379    ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg0);
   9380    Int32OperandId resId = writer.guardToInt32(valId);
   9381    for (size_t i = 1; i < args_.length(); i++) {
   9382      ValOperandId argId = loadArgument(calleeId, ArgumentKindForArgIndex(i));
   9383      Int32OperandId argInt32Id = writer.guardToInt32(argId);
   9384      resId = writer.int32MinMax(isMax, resId, argInt32Id);
   9385    }
   9386    writer.loadInt32Result(resId);
   9387  } else {
   9388    ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg0);
   9389    NumberOperandId resId = writer.guardIsNumber(valId);
   9390    for (size_t i = 1; i < args_.length(); i++) {
   9391      ValOperandId argId = loadArgument(calleeId, ArgumentKindForArgIndex(i));
   9392      NumberOperandId argNumId = writer.guardIsNumber(argId);
   9393      resId = writer.numberMinMax(isMax, resId, argNumId);
   9394    }
   9395    writer.loadDoubleResult(resId);
   9396  }
   9397 
   9398  writer.returnFromIC();
   9399 
   9400  trackAttached(isMax ? "MathMax" : "MathMin");
   9401  return AttachDecision::Attach;
   9402 }
   9403 
   9404 AttachDecision InlinableNativeIRGenerator::tryAttachSpreadMathMinMax(
   9405    bool isMax) {
   9406  MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Spread ||
   9407             flags_.getArgFormat() == CallFlags::FunApplyArray);
   9408 
   9409  // The result will be an int32 if there is at least one argument,
   9410  // and all the arguments are int32.
   9411  bool int32Result = args_.length() > 0;
   9412  for (size_t i = 0; i < args_.length(); i++) {
   9413    if (!args_[i].isNumber()) {
   9414      return AttachDecision::NoAction;
   9415    }
   9416    if (!args_[i].isInt32()) {
   9417      int32Result = false;
   9418    }
   9419  }
   9420 
   9421  // Initialize the input operand.
   9422  Int32OperandId argcId = initializeInputOperand();
   9423 
   9424  // Guard callee is this Math function.
   9425  emitNativeCalleeGuard(argcId);
   9426 
   9427  // Load the argument array.
   9428  ObjOperandId argsId = emitLoadArgsArray();
   9429 
   9430  if (int32Result) {
   9431    writer.int32MinMaxArrayResult(argsId, isMax);
   9432  } else {
   9433    writer.numberMinMaxArrayResult(argsId, isMax);
   9434  }
   9435 
   9436  writer.returnFromIC();
   9437 
   9438  trackAttached(isMax ? "MathMaxArray" : "MathMinArray");
   9439  return AttachDecision::Attach;
   9440 }
   9441 
   9442 AttachDecision InlinableNativeIRGenerator::tryAttachMathFunction(
   9443    UnaryMathFunction fun) {
   9444  // Need one argument.
   9445  if (args_.length() != 1) {
   9446    return AttachDecision::NoAction;
   9447  }
   9448 
   9449  if (!args_[0].isNumber()) {
   9450    return AttachDecision::NoAction;
   9451  }
   9452 
   9453  if (math_use_fdlibm_for_sin_cos_tan() ||
   9454      target_->realm()->creationOptions().alwaysUseFdlibm()) {
   9455    switch (fun) {
   9456      case UnaryMathFunction::SinNative:
   9457        fun = UnaryMathFunction::SinFdlibm;
   9458        break;
   9459      case UnaryMathFunction::CosNative:
   9460        fun = UnaryMathFunction::CosFdlibm;
   9461        break;
   9462      case UnaryMathFunction::TanNative:
   9463        fun = UnaryMathFunction::TanFdlibm;
   9464        break;
   9465      default:
   9466        break;
   9467    }
   9468  }
   9469 
   9470  // Initialize the input operand.
   9471  Int32OperandId argcId = initializeInputOperand();
   9472 
   9473  // Guard callee is this Math function.
   9474  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9475 
   9476  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9477  NumberOperandId numberId = writer.guardIsNumber(argumentId);
   9478  writer.mathFunctionNumberResult(numberId, fun);
   9479  writer.returnFromIC();
   9480 
   9481  trackAttached("MathFunction");
   9482  return AttachDecision::Attach;
   9483 }
   9484 
   9485 AttachDecision InlinableNativeIRGenerator::tryAttachNumber() {
   9486  // Expect a single string argument.
   9487  if (args_.length() != 1 || !args_[0].isString()) {
   9488    return AttachDecision::NoAction;
   9489  }
   9490 
   9491  double num;
   9492  if (!StringToNumber(cx_, args_[0].toString(), &num)) {
   9493    cx_->recoverFromOutOfMemory();
   9494    return AttachDecision::NoAction;
   9495  }
   9496 
   9497  // Initialize the input operand.
   9498  Int32OperandId argcId = initializeInputOperand();
   9499 
   9500  // Guard callee is the `Number` function.
   9501  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9502 
   9503  // Guard that the argument is a string.
   9504  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
   9505  StringOperandId strId = writer.guardToString(argId);
   9506 
   9507  // Return either an Int32 or Double result.
   9508  int32_t unused;
   9509  if (mozilla::NumberIsInt32(num, &unused)) {
   9510    Int32OperandId resultId = writer.guardStringToInt32(strId);
   9511    writer.loadInt32Result(resultId);
   9512  } else {
   9513    NumberOperandId resultId = writer.guardStringToNumber(strId);
   9514    writer.loadDoubleResult(resultId);
   9515  }
   9516  writer.returnFromIC();
   9517 
   9518  trackAttached("Number");
   9519  return AttachDecision::Attach;
   9520 }
   9521 
   9522 AttachDecision InlinableNativeIRGenerator::tryAttachNumberParseInt() {
   9523  // Expected arguments: input (string or number), optional radix (int32).
   9524  if (args_.length() < 1 || args_.length() > 2) {
   9525    return AttachDecision::NoAction;
   9526  }
   9527  if (!args_[0].isString() && !args_[0].isNumber()) {
   9528    return AttachDecision::NoAction;
   9529  }
   9530  if (args_[0].isDouble()) {
   9531    double d = args_[0].toDouble();
   9532 
   9533    // See num_parseInt for why we have to reject numbers smaller than 1.0e-6.
   9534    // Negative numbers in the exclusive range (-1, -0) return -0.
   9535    bool canTruncateToInt32 =
   9536        (DOUBLE_DECIMAL_IN_SHORTEST_LOW <= d && d <= double(INT32_MAX)) ||
   9537        (double(INT32_MIN) <= d && d <= -1.0) || (d == 0.0);
   9538    if (!canTruncateToInt32) {
   9539      return AttachDecision::NoAction;
   9540    }
   9541  }
   9542  if (args_.length() > 1 && !args_[1].isInt32(10)) {
   9543    return AttachDecision::NoAction;
   9544  }
   9545 
   9546  // Initialize the input operand.
   9547  Int32OperandId argcId = initializeInputOperand();
   9548 
   9549  // Guard callee is the 'parseInt' native function.
   9550  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9551 
   9552  auto guardRadix = [&]() {
   9553    ValOperandId radixId = loadArgument(calleeId, ArgumentKind::Arg1);
   9554    Int32OperandId intRadixId = writer.guardToInt32(radixId);
   9555    writer.guardSpecificInt32(intRadixId, 10);
   9556    return intRadixId;
   9557  };
   9558 
   9559  ValOperandId inputId = loadArgument(calleeId, ArgumentKind::Arg0);
   9560 
   9561  if (args_[0].isString()) {
   9562    StringOperandId strId = writer.guardToString(inputId);
   9563 
   9564    Int32OperandId intRadixId;
   9565    if (args_.length() > 1) {
   9566      intRadixId = guardRadix();
   9567    } else {
   9568      intRadixId = writer.loadInt32Constant(0);
   9569    }
   9570 
   9571    writer.numberParseIntResult(strId, intRadixId);
   9572  } else if (args_[0].isInt32()) {
   9573    Int32OperandId intId = writer.guardToInt32(inputId);
   9574    if (args_.length() > 1) {
   9575      guardRadix();
   9576    }
   9577    writer.loadInt32Result(intId);
   9578  } else {
   9579    MOZ_ASSERT(args_[0].isDouble());
   9580 
   9581    NumberOperandId numId = writer.guardIsNumber(inputId);
   9582    if (args_.length() > 1) {
   9583      guardRadix();
   9584    }
   9585    writer.doubleParseIntResult(numId);
   9586  }
   9587 
   9588  writer.returnFromIC();
   9589 
   9590  trackAttached("NumberParseInt");
   9591  return AttachDecision::Attach;
   9592 }
   9593 
   9594 StringOperandId IRGenerator::emitToStringGuard(ValOperandId id,
   9595                                               const Value& v) {
   9596  MOZ_ASSERT(CanConvertToString(v));
   9597  if (v.isString()) {
   9598    return writer.guardToString(id);
   9599  }
   9600  if (v.isBoolean()) {
   9601    BooleanOperandId boolId = writer.guardToBoolean(id);
   9602    return writer.booleanToString(boolId);
   9603  }
   9604  if (v.isNull()) {
   9605    writer.guardIsNull(id);
   9606    return writer.loadConstantString(cx_->names().null);
   9607  }
   9608  if (v.isUndefined()) {
   9609    writer.guardIsUndefined(id);
   9610    return writer.loadConstantString(cx_->names().undefined);
   9611  }
   9612  if (v.isInt32()) {
   9613    Int32OperandId intId = writer.guardToInt32(id);
   9614    return writer.callInt32ToString(intId);
   9615  }
   9616  // At this point we are creating an IC that will handle
   9617  // both Int32 and Double cases.
   9618  MOZ_ASSERT(v.isNumber());
   9619  NumberOperandId numId = writer.guardIsNumber(id);
   9620  return writer.callNumberToString(numId);
   9621 }
   9622 
   9623 AttachDecision InlinableNativeIRGenerator::tryAttachNumberToString() {
   9624  // Expecting no arguments or a single int32 argument.
   9625  if (args_.length() > 1) {
   9626    return AttachDecision::NoAction;
   9627  }
   9628  if (args_.length() == 1 && !args_[0].isInt32()) {
   9629    return AttachDecision::NoAction;
   9630  }
   9631 
   9632  // Ensure |this| is a primitive number value.
   9633  if (!thisval_.isNumber()) {
   9634    return AttachDecision::NoAction;
   9635  }
   9636 
   9637  // No arguments means base 10.
   9638  int32_t base = 10;
   9639  if (args_.length() > 0) {
   9640    base = args_[0].toInt32();
   9641    if (base < 2 || base > 36) {
   9642      return AttachDecision::NoAction;
   9643    }
   9644 
   9645    // Non-decimal bases currently only support int32 inputs.
   9646    if (base != 10 && !thisval_.isInt32()) {
   9647      return AttachDecision::NoAction;
   9648    }
   9649  }
   9650  MOZ_ASSERT(2 <= base && base <= 36);
   9651 
   9652  // Initialize the input operand.
   9653  Int32OperandId argcId = initializeInputOperand();
   9654 
   9655  // Guard callee is the 'toString' native function.
   9656  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9657 
   9658  // Initialize the |this| operand.
   9659  ValOperandId thisValId = loadThis(calleeId);
   9660 
   9661  // Guard on number and convert to string.
   9662  if (base == 10) {
   9663    // If an explicit base was passed, guard its value.
   9664    if (args_.length() > 0) {
   9665      // Guard the `base` argument is an int32.
   9666      ValOperandId baseId = loadArgument(calleeId, ArgumentKind::Arg0);
   9667      Int32OperandId intBaseId = writer.guardToInt32(baseId);
   9668 
   9669      // Guard `base` is 10 for decimal toString representation.
   9670      writer.guardSpecificInt32(intBaseId, 10);
   9671    }
   9672 
   9673    StringOperandId strId = emitToStringGuard(thisValId, thisval_);
   9674 
   9675    // Return the string.
   9676    writer.loadStringResult(strId);
   9677  } else {
   9678    MOZ_ASSERT(args_.length() > 0);
   9679 
   9680    // Guard the |this| value is an int32.
   9681    Int32OperandId thisIntId = writer.guardToInt32(thisValId);
   9682 
   9683    // Guard the `base` argument is an int32.
   9684    ValOperandId baseId = loadArgument(calleeId, ArgumentKind::Arg0);
   9685    Int32OperandId intBaseId = writer.guardToInt32(baseId);
   9686 
   9687    // Return the string.
   9688    writer.int32ToStringWithBaseResult(thisIntId, intBaseId);
   9689  }
   9690 
   9691  writer.returnFromIC();
   9692 
   9693  trackAttached("NumberToString");
   9694  return AttachDecision::Attach;
   9695 }
   9696 
   9697 AttachDecision InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
   9698  // Need one argument.
   9699  if (args_.length() != 1) {
   9700    return AttachDecision::NoAction;
   9701  }
   9702 
   9703  if (!args_[0].isObject()) {
   9704    return AttachDecision::NoAction;
   9705  }
   9706 
   9707  // Initialize the input operand.
   9708  Int32OperandId argcId = initializeInputOperand();
   9709 
   9710  // Guard callee is the 'getPrototypeOf' native function.
   9711  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9712 
   9713  ValOperandId argumentId = loadArgument(calleeId, ArgumentKind::Arg0);
   9714  ObjOperandId objId = writer.guardToObject(argumentId);
   9715 
   9716  writer.reflectGetPrototypeOfResult(objId);
   9717  writer.returnFromIC();
   9718 
   9719  trackAttached("ReflectGetPrototypeOf");
   9720  return AttachDecision::Attach;
   9721 }
   9722 
   9723 enum class AtomicAccess { Read, Write };
   9724 
   9725 static bool AtomicsMeetsPreconditions(TypedArrayObject* typedArray,
   9726                                      const Value& index, AtomicAccess access) {
   9727  // Can't write into immutable TypedArrays.
   9728  if (access == AtomicAccess::Write &&
   9729      typedArray->is<ImmutableTypedArrayObject>()) {
   9730    return false;
   9731  }
   9732 
   9733  switch (typedArray->type()) {
   9734    case Scalar::Int8:
   9735    case Scalar::Uint8:
   9736    case Scalar::Int16:
   9737    case Scalar::Uint16:
   9738    case Scalar::Int32:
   9739    case Scalar::Uint32:
   9740    case Scalar::BigInt64:
   9741    case Scalar::BigUint64:
   9742      break;
   9743 
   9744    case Scalar::Float16:
   9745    case Scalar::Float32:
   9746    case Scalar::Float64:
   9747    case Scalar::Uint8Clamped:
   9748      // Exclude floating types and Uint8Clamped.
   9749      return false;
   9750 
   9751    case Scalar::MaxTypedArrayViewType:
   9752    case Scalar::Int64:
   9753    case Scalar::Simd128:
   9754      MOZ_CRASH("Unsupported TypedArray type");
   9755  }
   9756 
   9757  // Bounds check the index argument.
   9758  int64_t indexInt64;
   9759  if (!ValueIsInt64Index(index, &indexInt64)) {
   9760    return false;
   9761  }
   9762  if (indexInt64 < 0 ||
   9763      uint64_t(indexInt64) >= typedArray->length().valueOr(0)) {
   9764    return false;
   9765  }
   9766 
   9767  return true;
   9768 }
   9769 
   9770 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
   9771  if (!JitSupportsAtomics()) {
   9772    return AttachDecision::NoAction;
   9773  }
   9774 
   9775  // Need four arguments.
   9776  if (args_.length() != 4) {
   9777    return AttachDecision::NoAction;
   9778  }
   9779 
   9780  // Arguments: typedArray, index (number), expected, replacement.
   9781  if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
   9782    return AttachDecision::NoAction;
   9783  }
   9784  if (!args_[1].isNumber()) {
   9785    return AttachDecision::NoAction;
   9786  }
   9787 
   9788  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9789  if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
   9790    return AttachDecision::NoAction;
   9791  }
   9792 
   9793  Scalar::Type elementType = typedArray->type();
   9794  if (!ValueCanConvertToNumeric(elementType, args_[2])) {
   9795    return AttachDecision::NoAction;
   9796  }
   9797  if (!ValueCanConvertToNumeric(elementType, args_[3])) {
   9798    return AttachDecision::NoAction;
   9799  }
   9800 
   9801  // Initialize the input operand.
   9802  Int32OperandId argcId = initializeInputOperand();
   9803 
   9804  // Guard callee is the `compareExchange` native function.
   9805  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9806 
   9807  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
   9808  ObjOperandId objId = writer.guardToObject(arg0Id);
   9809  writer.guardShapeForClass(objId, typedArray->shape());
   9810 
   9811  // Convert index to intPtr.
   9812  ValOperandId indexId = loadArgument(calleeId, ArgumentKind::Arg1);
   9813  IntPtrOperandId intPtrIndexId =
   9814      guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
   9815 
   9816  // Convert expected value to int32/BigInt.
   9817  ValOperandId expectedId = loadArgument(calleeId, ArgumentKind::Arg2);
   9818  OperandId numericExpectedId =
   9819      emitNumericGuard(expectedId, args_[2], elementType);
   9820 
   9821  // Convert replacement value to int32/BigInt.
   9822  ValOperandId replacementId = loadArgument(calleeId, ArgumentKind::Arg3);
   9823  OperandId numericReplacementId =
   9824      emitNumericGuard(replacementId, args_[3], elementType);
   9825 
   9826  auto viewKind = ToArrayBufferViewKind(typedArray);
   9827  writer.atomicsCompareExchangeResult(objId, intPtrIndexId, numericExpectedId,
   9828                                      numericReplacementId, typedArray->type(),
   9829                                      viewKind);
   9830  writer.returnFromIC();
   9831 
   9832  trackAttached("AtomicsCompareExchange");
   9833  return AttachDecision::Attach;
   9834 }
   9835 
   9836 bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
   9837  if (!JitSupportsAtomics()) {
   9838    return false;
   9839  }
   9840 
   9841  // Need three arguments.
   9842  if (args_.length() != 3) {
   9843    return false;
   9844  }
   9845 
   9846  // Arguments: typedArray, index (number), value.
   9847  if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
   9848    return false;
   9849  }
   9850  if (!args_[1].isNumber()) {
   9851    return false;
   9852  }
   9853 
   9854  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9855  if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
   9856    return false;
   9857  }
   9858  if (!ValueCanConvertToNumeric(typedArray->type(), args_[2])) {
   9859    return false;
   9860  }
   9861  return true;
   9862 }
   9863 
   9864 InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands
   9865 InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() {
   9866  MOZ_ASSERT(canAttachAtomicsReadWriteModify());
   9867 
   9868  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9869 
   9870  // Initialize the input operand.
   9871  Int32OperandId argcId = initializeInputOperand();
   9872 
   9873  // Guard callee is this Atomics function.
   9874  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
   9875 
   9876  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
   9877  ObjOperandId objId = writer.guardToObject(arg0Id);
   9878  writer.guardShapeForClass(objId, typedArray->shape());
   9879 
   9880  // Convert index to intPtr.
   9881  ValOperandId indexId = loadArgument(calleeId, ArgumentKind::Arg1);
   9882  IntPtrOperandId intPtrIndexId =
   9883      guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
   9884 
   9885  // Convert value to int32/BigInt.
   9886  ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg2);
   9887  OperandId numericValueId =
   9888      emitNumericGuard(valueId, args_[2], typedArray->type());
   9889 
   9890  return {objId, intPtrIndexId, numericValueId};
   9891 }
   9892 
   9893 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsExchange() {
   9894  if (!canAttachAtomicsReadWriteModify()) {
   9895    return AttachDecision::NoAction;
   9896  }
   9897 
   9898  auto [objId, intPtrIndexId, numericValueId] =
   9899      emitAtomicsReadWriteModifyOperands();
   9900 
   9901  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9902  auto viewKind = ToArrayBufferViewKind(typedArray);
   9903 
   9904  writer.atomicsExchangeResult(objId, intPtrIndexId, numericValueId,
   9905                               typedArray->type(), viewKind);
   9906  writer.returnFromIC();
   9907 
   9908  trackAttached("AtomicsExchange");
   9909  return AttachDecision::Attach;
   9910 }
   9911 
   9912 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsAdd() {
   9913  if (!canAttachAtomicsReadWriteModify()) {
   9914    return AttachDecision::NoAction;
   9915  }
   9916 
   9917  auto [objId, intPtrIndexId, numericValueId] =
   9918      emitAtomicsReadWriteModifyOperands();
   9919 
   9920  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9921  bool forEffect = ignoresResult();
   9922  auto viewKind = ToArrayBufferViewKind(typedArray);
   9923 
   9924  writer.atomicsAddResult(objId, intPtrIndexId, numericValueId,
   9925                          typedArray->type(), forEffect, viewKind);
   9926  writer.returnFromIC();
   9927 
   9928  trackAttached("AtomicsAdd");
   9929  return AttachDecision::Attach;
   9930 }
   9931 
   9932 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsSub() {
   9933  if (!canAttachAtomicsReadWriteModify()) {
   9934    return AttachDecision::NoAction;
   9935  }
   9936 
   9937  auto [objId, intPtrIndexId, numericValueId] =
   9938      emitAtomicsReadWriteModifyOperands();
   9939 
   9940  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9941  bool forEffect = ignoresResult();
   9942  auto viewKind = ToArrayBufferViewKind(typedArray);
   9943 
   9944  writer.atomicsSubResult(objId, intPtrIndexId, numericValueId,
   9945                          typedArray->type(), forEffect, viewKind);
   9946  writer.returnFromIC();
   9947 
   9948  trackAttached("AtomicsSub");
   9949  return AttachDecision::Attach;
   9950 }
   9951 
   9952 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsAnd() {
   9953  if (!canAttachAtomicsReadWriteModify()) {
   9954    return AttachDecision::NoAction;
   9955  }
   9956 
   9957  auto [objId, intPtrIndexId, numericValueId] =
   9958      emitAtomicsReadWriteModifyOperands();
   9959 
   9960  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9961  bool forEffect = ignoresResult();
   9962  auto viewKind = ToArrayBufferViewKind(typedArray);
   9963 
   9964  writer.atomicsAndResult(objId, intPtrIndexId, numericValueId,
   9965                          typedArray->type(), forEffect, viewKind);
   9966  writer.returnFromIC();
   9967 
   9968  trackAttached("AtomicsAnd");
   9969  return AttachDecision::Attach;
   9970 }
   9971 
   9972 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsOr() {
   9973  if (!canAttachAtomicsReadWriteModify()) {
   9974    return AttachDecision::NoAction;
   9975  }
   9976 
   9977  auto [objId, intPtrIndexId, numericValueId] =
   9978      emitAtomicsReadWriteModifyOperands();
   9979 
   9980  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
   9981  bool forEffect = ignoresResult();
   9982  auto viewKind = ToArrayBufferViewKind(typedArray);
   9983 
   9984  writer.atomicsOrResult(objId, intPtrIndexId, numericValueId,
   9985                         typedArray->type(), forEffect, viewKind);
   9986  writer.returnFromIC();
   9987 
   9988  trackAttached("AtomicsOr");
   9989  return AttachDecision::Attach;
   9990 }
   9991 
   9992 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsXor() {
   9993  if (!canAttachAtomicsReadWriteModify()) {
   9994    return AttachDecision::NoAction;
   9995  }
   9996 
   9997  auto [objId, intPtrIndexId, numericValueId] =
   9998      emitAtomicsReadWriteModifyOperands();
   9999 
  10000  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
  10001  bool forEffect = ignoresResult();
  10002  auto viewKind = ToArrayBufferViewKind(typedArray);
  10003 
  10004  writer.atomicsXorResult(objId, intPtrIndexId, numericValueId,
  10005                          typedArray->type(), forEffect, viewKind);
  10006  writer.returnFromIC();
  10007 
  10008  trackAttached("AtomicsXor");
  10009  return AttachDecision::Attach;
  10010 }
  10011 
  10012 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
  10013  if (!JitSupportsAtomics()) {
  10014    return AttachDecision::NoAction;
  10015  }
  10016 
  10017  // Need two arguments.
  10018  if (args_.length() != 2) {
  10019    return AttachDecision::NoAction;
  10020  }
  10021 
  10022  // Arguments: typedArray, index (number).
  10023  if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
  10024    return AttachDecision::NoAction;
  10025  }
  10026  if (!args_[1].isNumber()) {
  10027    return AttachDecision::NoAction;
  10028  }
  10029 
  10030  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
  10031  if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Read)) {
  10032    return AttachDecision::NoAction;
  10033  }
  10034 
  10035  // Initialize the input operand.
  10036  Int32OperandId argcId = initializeInputOperand();
  10037 
  10038  // Guard callee is the `load` native function.
  10039  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10040 
  10041  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  10042  ObjOperandId objId = writer.guardToObject(arg0Id);
  10043  writer.guardShapeForClass(objId, typedArray->shape());
  10044 
  10045  // Convert index to intPtr.
  10046  ValOperandId indexId = loadArgument(calleeId, ArgumentKind::Arg1);
  10047  IntPtrOperandId intPtrIndexId =
  10048      guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
  10049 
  10050  auto viewKind = ToArrayBufferViewKind(typedArray);
  10051  writer.atomicsLoadResult(objId, intPtrIndexId, typedArray->type(), viewKind);
  10052  writer.returnFromIC();
  10053 
  10054  trackAttached("AtomicsLoad");
  10055  return AttachDecision::Attach;
  10056 }
  10057 
  10058 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsStore() {
  10059  if (!JitSupportsAtomics()) {
  10060    return AttachDecision::NoAction;
  10061  }
  10062 
  10063  // Need three arguments.
  10064  if (args_.length() != 3) {
  10065    return AttachDecision::NoAction;
  10066  }
  10067 
  10068  // Atomics.store() is annoying because it returns the result of converting the
  10069  // value by ToInteger(), not the input value, nor the result of converting the
  10070  // value by ToInt32(). It is especially annoying because almost nobody uses
  10071  // the result value.
  10072  //
  10073  // As an expedient compromise, therefore, we inline only if the result is
  10074  // obviously unused or if the argument is already Int32 and thus requires no
  10075  // conversion.
  10076 
  10077  // Arguments: typedArray, index (number), value.
  10078  if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
  10079    return AttachDecision::NoAction;
  10080  }
  10081  if (!args_[1].isNumber()) {
  10082    return AttachDecision::NoAction;
  10083  }
  10084 
  10085  auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
  10086  if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
  10087    return AttachDecision::NoAction;
  10088  }
  10089 
  10090  Scalar::Type elementType = typedArray->type();
  10091  if (!ValueCanConvertToNumeric(elementType, args_[2])) {
  10092    return AttachDecision::NoAction;
  10093  }
  10094 
  10095  bool guardIsInt32 = !Scalar::isBigIntType(elementType) && !ignoresResult();
  10096 
  10097  if (guardIsInt32 && !args_[2].isInt32()) {
  10098    return AttachDecision::NoAction;
  10099  }
  10100 
  10101  // Initialize the input operand.
  10102  Int32OperandId argcId = initializeInputOperand();
  10103 
  10104  // Guard callee is the `store` native function.
  10105  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10106 
  10107  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  10108  ObjOperandId objId = writer.guardToObject(arg0Id);
  10109  writer.guardShapeForClass(objId, typedArray->shape());
  10110 
  10111  // Convert index to intPtr.
  10112  ValOperandId indexId = loadArgument(calleeId, ArgumentKind::Arg1);
  10113  IntPtrOperandId intPtrIndexId =
  10114      guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
  10115 
  10116  // Ensure value is int32 or BigInt.
  10117  ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg2);
  10118  OperandId numericValueId;
  10119  if (guardIsInt32) {
  10120    numericValueId = writer.guardToInt32(valueId);
  10121  } else {
  10122    numericValueId = emitNumericGuard(valueId, args_[2], elementType);
  10123  }
  10124 
  10125  auto viewKind = ToArrayBufferViewKind(typedArray);
  10126  writer.atomicsStoreResult(objId, intPtrIndexId, numericValueId,
  10127                            typedArray->type(), viewKind);
  10128  writer.returnFromIC();
  10129 
  10130  trackAttached("AtomicsStore");
  10131  return AttachDecision::Attach;
  10132 }
  10133 
  10134 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsIsLockFree() {
  10135  // Need one argument.
  10136  if (args_.length() != 1) {
  10137    return AttachDecision::NoAction;
  10138  }
  10139 
  10140  if (!args_[0].isInt32()) {
  10141    return AttachDecision::NoAction;
  10142  }
  10143 
  10144  // Initialize the input operand.
  10145  Int32OperandId argcId = initializeInputOperand();
  10146 
  10147  // Guard callee is the `isLockFree` native function.
  10148  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10149 
  10150  // Ensure value is int32.
  10151  ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg0);
  10152  Int32OperandId int32ValueId = writer.guardToInt32(valueId);
  10153 
  10154  writer.atomicsIsLockFreeResult(int32ValueId);
  10155  writer.returnFromIC();
  10156 
  10157  trackAttached("AtomicsIsLockFree");
  10158  return AttachDecision::Attach;
  10159 }
  10160 
  10161 AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsPause() {
  10162  // We don't yet support inlining when the iteration count argument is present.
  10163  if (args_.length() != 0) {
  10164    return AttachDecision::NoAction;
  10165  }
  10166 
  10167  // Initialize the input operand.
  10168  Int32OperandId argcId = initializeInputOperand();
  10169 
  10170  // Guard callee is the `pause` native function.
  10171  emitNativeCalleeGuard(argcId);
  10172 
  10173  writer.atomicsPauseResult();
  10174  writer.returnFromIC();
  10175 
  10176  trackAttached("AtomicsPause");
  10177  return AttachDecision::Attach;
  10178 }
  10179 
  10180 AttachDecision InlinableNativeIRGenerator::tryAttachBoolean() {
  10181  // Need zero or one argument.
  10182  if (args_.length() > 1) {
  10183    return AttachDecision::NoAction;
  10184  }
  10185 
  10186  // Initialize the input operand.
  10187  Int32OperandId argcId = initializeInputOperand();
  10188 
  10189  // Guard callee is the 'Boolean' native function.
  10190  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10191 
  10192  if (args_.length() == 0) {
  10193    writer.loadBooleanResult(false);
  10194  } else {
  10195    ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg0);
  10196 
  10197    writer.loadValueTruthyResult(valId);
  10198  }
  10199 
  10200  writer.returnFromIC();
  10201 
  10202  trackAttached("Boolean");
  10203  return AttachDecision::Attach;
  10204 }
  10205 
  10206 AttachDecision InlinableNativeIRGenerator::tryAttachBailout() {
  10207  // Expecting no arguments.
  10208  if (args_.length() != 0) {
  10209    return AttachDecision::NoAction;
  10210  }
  10211 
  10212  // Initialize the input operand.
  10213  Int32OperandId argcId = initializeInputOperand();
  10214 
  10215  // Guard callee is the 'bailout' native function.
  10216  emitNativeCalleeGuard(argcId);
  10217 
  10218  writer.bailout();
  10219  writer.loadUndefinedResult();
  10220  writer.returnFromIC();
  10221 
  10222  trackAttached("Bailout");
  10223  return AttachDecision::Attach;
  10224 }
  10225 
  10226 AttachDecision InlinableNativeIRGenerator::tryAttachAssertFloat32() {
  10227  // Expecting two arguments.
  10228  if (args_.length() != 2) {
  10229    return AttachDecision::NoAction;
  10230  }
  10231 
  10232  // (Fuzzing unsafe) testing function which must be called with a constant
  10233  // boolean as its second argument.
  10234  bool mustBeFloat32 = args_[1].toBoolean();
  10235 
  10236  // Initialize the input operand.
  10237  Int32OperandId argcId = initializeInputOperand();
  10238 
  10239  // Guard callee is the 'assertFloat32' native function.
  10240  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10241 
  10242  ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg0);
  10243 
  10244  writer.assertFloat32Result(valId, mustBeFloat32);
  10245  writer.returnFromIC();
  10246 
  10247  trackAttached("AssertFloat32");
  10248  return AttachDecision::Attach;
  10249 }
  10250 
  10251 AttachDecision InlinableNativeIRGenerator::tryAttachAssertRecoveredOnBailout() {
  10252  // Expecting two arguments.
  10253  if (args_.length() != 2) {
  10254    return AttachDecision::NoAction;
  10255  }
  10256 
  10257  // (Fuzzing unsafe) testing function which must be called with a constant
  10258  // boolean as its second argument.
  10259  bool mustBeRecovered = args_[1].toBoolean();
  10260 
  10261  // Initialize the input operand.
  10262  Int32OperandId argcId = initializeInputOperand();
  10263 
  10264  // Guard callee is the 'assertRecoveredOnBailout' native function.
  10265  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10266 
  10267  ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg0);
  10268 
  10269  writer.assertRecoveredOnBailoutResult(valId, mustBeRecovered);
  10270  writer.returnFromIC();
  10271 
  10272  trackAttached("AssertRecoveredOnBailout");
  10273  return AttachDecision::Attach;
  10274 }
  10275 
  10276 AttachDecision InlinableNativeIRGenerator::tryAttachObjectIs() {
  10277  // Need two arguments.
  10278  if (args_.length() != 2) {
  10279    return AttachDecision::NoAction;
  10280  }
  10281 
  10282  // Initialize the input operand.
  10283  Int32OperandId argcId = initializeInputOperand();
  10284 
  10285  // Guard callee is the `is` native function.
  10286  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10287 
  10288  ValOperandId lhsId = loadArgument(calleeId, ArgumentKind::Arg0);
  10289  ValOperandId rhsId = loadArgument(calleeId, ArgumentKind::Arg1);
  10290 
  10291  HandleValue lhs = args_[0];
  10292  HandleValue rhs = args_[1];
  10293 
  10294  if (!isFirstStub()) {
  10295    writer.sameValueResult(lhsId, rhsId);
  10296  } else if (lhs.isNumber() && rhs.isNumber() &&
  10297             !(lhs.isInt32() && rhs.isInt32())) {
  10298    NumberOperandId lhsNumId = writer.guardIsNumber(lhsId);
  10299    NumberOperandId rhsNumId = writer.guardIsNumber(rhsId);
  10300    writer.compareDoubleSameValueResult(lhsNumId, rhsNumId);
  10301  } else if (!SameType(lhs, rhs)) {
  10302    // Compare tags for strictly different types.
  10303    ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
  10304    ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
  10305    writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
  10306    writer.loadBooleanResult(false);
  10307  } else {
  10308    MOZ_ASSERT(lhs.type() == rhs.type());
  10309    MOZ_ASSERT(lhs.type() != JS::ValueType::Double);
  10310 
  10311    switch (lhs.type()) {
  10312      case JS::ValueType::Int32: {
  10313        Int32OperandId lhsIntId = writer.guardToInt32(lhsId);
  10314        Int32OperandId rhsIntId = writer.guardToInt32(rhsId);
  10315        writer.compareInt32Result(JSOp::StrictEq, lhsIntId, rhsIntId);
  10316        break;
  10317      }
  10318      case JS::ValueType::Boolean: {
  10319        Int32OperandId lhsIntId = writer.guardBooleanToInt32(lhsId);
  10320        Int32OperandId rhsIntId = writer.guardBooleanToInt32(rhsId);
  10321        writer.compareInt32Result(JSOp::StrictEq, lhsIntId, rhsIntId);
  10322        break;
  10323      }
  10324      case JS::ValueType::Undefined: {
  10325        writer.guardIsUndefined(lhsId);
  10326        writer.guardIsUndefined(rhsId);
  10327        writer.loadBooleanResult(true);
  10328        break;
  10329      }
  10330      case JS::ValueType::Null: {
  10331        writer.guardIsNull(lhsId);
  10332        writer.guardIsNull(rhsId);
  10333        writer.loadBooleanResult(true);
  10334        break;
  10335      }
  10336      case JS::ValueType::String: {
  10337        StringOperandId lhsStrId = writer.guardToString(lhsId);
  10338        StringOperandId rhsStrId = writer.guardToString(rhsId);
  10339        writer.compareStringResult(JSOp::StrictEq, lhsStrId, rhsStrId);
  10340        break;
  10341      }
  10342      case JS::ValueType::Symbol: {
  10343        SymbolOperandId lhsSymId = writer.guardToSymbol(lhsId);
  10344        SymbolOperandId rhsSymId = writer.guardToSymbol(rhsId);
  10345        writer.compareSymbolResult(JSOp::StrictEq, lhsSymId, rhsSymId);
  10346        break;
  10347      }
  10348      case JS::ValueType::BigInt: {
  10349        BigIntOperandId lhsBigIntId = writer.guardToBigInt(lhsId);
  10350        BigIntOperandId rhsBigIntId = writer.guardToBigInt(rhsId);
  10351        writer.compareBigIntResult(JSOp::StrictEq, lhsBigIntId, rhsBigIntId);
  10352        break;
  10353      }
  10354      case JS::ValueType::Object: {
  10355        ObjOperandId lhsObjId = writer.guardToObject(lhsId);
  10356        ObjOperandId rhsObjId = writer.guardToObject(rhsId);
  10357        writer.compareObjectResult(JSOp::StrictEq, lhsObjId, rhsObjId);
  10358        break;
  10359      }
  10360 
  10361      case JS::ValueType::Double:
  10362      case JS::ValueType::Magic:
  10363      case JS::ValueType::PrivateGCThing:
  10364        MOZ_CRASH("Unexpected type");
  10365    }
  10366  }
  10367 
  10368  writer.returnFromIC();
  10369 
  10370  trackAttached("ObjectIs");
  10371  return AttachDecision::Attach;
  10372 }
  10373 
  10374 AttachDecision InlinableNativeIRGenerator::tryAttachObjectIsPrototypeOf() {
  10375  // Ensure |this| is an object.
  10376  if (!thisval_.isObject()) {
  10377    return AttachDecision::NoAction;
  10378  }
  10379 
  10380  // Need a single argument.
  10381  if (args_.length() != 1) {
  10382    return AttachDecision::NoAction;
  10383  }
  10384 
  10385  // Initialize the input operand.
  10386  Int32OperandId argcId = initializeInputOperand();
  10387 
  10388  // Guard callee is the `isPrototypeOf` native function.
  10389  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10390 
  10391  // Guard that |this| is an object.
  10392  ValOperandId thisValId = loadThis(calleeId);
  10393  ObjOperandId thisObjId = writer.guardToObject(thisValId);
  10394 
  10395  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10396 
  10397  writer.loadInstanceOfObjectResult(argId, thisObjId);
  10398  writer.returnFromIC();
  10399 
  10400  trackAttached("ObjectIsPrototypeOf");
  10401  return AttachDecision::Attach;
  10402 }
  10403 
  10404 AttachDecision InlinableNativeIRGenerator::tryAttachObjectKeys() {
  10405  // Only handle argc <= 1.
  10406  if (args_.length() != 1) {
  10407    return AttachDecision::NoAction;
  10408  }
  10409 
  10410  // Do not attach any IC if the argument is not an object.
  10411  if (!args_[0].isObject()) {
  10412    return AttachDecision::NoAction;
  10413  }
  10414  // Do not attach any IC if the argument is a Proxy. While implementation could
  10415  // work with proxies the goal of this implementation is to provide an
  10416  // optimization for calls of `Object.keys(obj)` where there is no side-effect,
  10417  // and where the computation of the array of property name can be moved.
  10418  const JSClass* clasp = args_[0].toObject().getClass();
  10419  if (clasp->isProxyObject()) {
  10420    return AttachDecision::NoAction;
  10421  }
  10422 
  10423  Shape* expectedObjKeysShape =
  10424      GlobalObject::getArrayShapeWithDefaultProto(cx_);
  10425  if (!expectedObjKeysShape) {
  10426    cx_->recoverFromOutOfMemory();
  10427    return AttachDecision::NoAction;
  10428  }
  10429 
  10430  // Generate cache IR code to attach a new inline cache which will delegate the
  10431  // call to Object.keys to the native function.
  10432  Int32OperandId argcId = initializeInputOperand();
  10433 
  10434  // Guard callee is the 'keys' native function.
  10435  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10436 
  10437  // Implicit: Note `Object.keys` is a property of the `Object` global. The fact
  10438  // that we are in this function implies that we already identify the function
  10439  // as being the proper one. Thus there should not be any need to validate that
  10440  // this is the proper function. (test: ion/object-keys-05)
  10441 
  10442  // Guard `arg0` is an object.
  10443  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10444  ObjOperandId argObjId = writer.guardToObject(argId);
  10445 
  10446  // Guard against proxies.
  10447  writer.guardIsNotProxy(argObjId);
  10448 
  10449  // Compute the keys array.
  10450  writer.objectKeysResult(argObjId, expectedObjKeysShape);
  10451 
  10452  writer.returnFromIC();
  10453 
  10454  trackAttached("ObjectKeys");
  10455  return AttachDecision::Attach;
  10456 }
  10457 
  10458 AttachDecision InlinableNativeIRGenerator::tryAttachObjectToString() {
  10459  // Expecting no arguments.
  10460  if (args_.length() != 0) {
  10461    return AttachDecision::NoAction;
  10462  }
  10463 
  10464  // Ensure |this| is an object.
  10465  if (!thisval_.isObject()) {
  10466    return AttachDecision::NoAction;
  10467  }
  10468 
  10469  // Don't attach if the object has @@toStringTag or is a proxy.
  10470  if (!ObjectClassToString(cx_, &thisval_.toObject())) {
  10471    return AttachDecision::NoAction;
  10472  }
  10473 
  10474  // Initialize the input operand.
  10475  Int32OperandId argcId = initializeInputOperand();
  10476 
  10477  // Guard callee is the 'toString' native function.
  10478  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10479 
  10480  // Guard that |this| is an object.
  10481  ValOperandId thisValId = loadThis(calleeId);
  10482  ObjOperandId thisObjId = writer.guardToObject(thisValId);
  10483 
  10484  writer.objectToStringResult(thisObjId);
  10485  writer.returnFromIC();
  10486 
  10487  trackAttached("ObjectToString");
  10488  return AttachDecision::Attach;
  10489 }
  10490 
  10491 AttachDecision InlinableNativeIRGenerator::tryAttachBigInt() {
  10492  // Need a single argument (Int32).
  10493  if (args_.length() != 1 || !args_[0].isInt32()) {
  10494    return AttachDecision::NoAction;
  10495  }
  10496 
  10497  // Initialize the input operand.
  10498  Int32OperandId argcId = initializeInputOperand();
  10499 
  10500  // Guard callee is the 'BigInt' native function.
  10501  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10502 
  10503  // Guard that the argument is an Int32.
  10504  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10505  Int32OperandId int32Id = writer.guardToInt32(argId);
  10506 
  10507  // Convert Int32 to BigInt.
  10508  IntPtrOperandId intptrId = writer.int32ToIntPtr(int32Id);
  10509  writer.intPtrToBigIntResult(intptrId);
  10510  writer.returnFromIC();
  10511 
  10512  trackAttached("BigInt");
  10513  return AttachDecision::Attach;
  10514 }
  10515 
  10516 AttachDecision InlinableNativeIRGenerator::tryAttachBigIntAsIntN() {
  10517  // Need two arguments (Int32, BigInt).
  10518  if (args_.length() != 2 || !args_[0].isInt32() || !args_[1].isBigInt()) {
  10519    return AttachDecision::NoAction;
  10520  }
  10521 
  10522  // Negative bits throws an error.
  10523  if (args_[0].toInt32() < 0) {
  10524    return AttachDecision::NoAction;
  10525  }
  10526 
  10527  // Initialize the input operand.
  10528  Int32OperandId argcId = initializeInputOperand();
  10529 
  10530  // Guard callee is the 'BigInt.asIntN' native function.
  10531  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10532 
  10533  // Convert bits to int32.
  10534  ValOperandId bitsId = loadArgument(calleeId, ArgumentKind::Arg0);
  10535  Int32OperandId int32BitsId = EmitGuardToInt32Index(writer, args_[0], bitsId);
  10536 
  10537  // Number of bits mustn't be negative.
  10538  writer.guardInt32IsNonNegative(int32BitsId);
  10539 
  10540  ValOperandId arg1Id = loadArgument(calleeId, ArgumentKind::Arg1);
  10541  BigIntOperandId bigIntId = writer.guardToBigInt(arg1Id);
  10542 
  10543  writer.bigIntAsIntNResult(int32BitsId, bigIntId);
  10544  writer.returnFromIC();
  10545 
  10546  trackAttached("BigIntAsIntN");
  10547  return AttachDecision::Attach;
  10548 }
  10549 
  10550 AttachDecision InlinableNativeIRGenerator::tryAttachBigIntAsUintN() {
  10551  // Need two arguments (Int32, BigInt).
  10552  if (args_.length() != 2 || !args_[0].isInt32() || !args_[1].isBigInt()) {
  10553    return AttachDecision::NoAction;
  10554  }
  10555 
  10556  // Negative bits throws an error.
  10557  if (args_[0].toInt32() < 0) {
  10558    return AttachDecision::NoAction;
  10559  }
  10560 
  10561  // Initialize the input operand.
  10562  Int32OperandId argcId = initializeInputOperand();
  10563 
  10564  // Guard callee is the 'BigInt.asUintN' native function.
  10565  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10566 
  10567  // Convert bits to int32.
  10568  ValOperandId bitsId = loadArgument(calleeId, ArgumentKind::Arg0);
  10569  Int32OperandId int32BitsId = EmitGuardToInt32Index(writer, args_[0], bitsId);
  10570 
  10571  // Number of bits mustn't be negative.
  10572  writer.guardInt32IsNonNegative(int32BitsId);
  10573 
  10574  ValOperandId arg1Id = loadArgument(calleeId, ArgumentKind::Arg1);
  10575  BigIntOperandId bigIntId = writer.guardToBigInt(arg1Id);
  10576 
  10577  writer.bigIntAsUintNResult(int32BitsId, bigIntId);
  10578  writer.returnFromIC();
  10579 
  10580  trackAttached("BigIntAsUintN");
  10581  return AttachDecision::Attach;
  10582 }
  10583 
  10584 AttachDecision InlinableNativeIRGenerator::tryAttachSetHas() {
  10585  // Ensure |this| is a SetObject.
  10586  if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
  10587    return AttachDecision::NoAction;
  10588  }
  10589 
  10590  // Need a single argument.
  10591  if (args_.length() != 1) {
  10592    return AttachDecision::NoAction;
  10593  }
  10594 
  10595  // Initialize the input operand.
  10596  Int32OperandId argcId = initializeInputOperand();
  10597 
  10598  // Guard callee is the 'has' native function.
  10599  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10600 
  10601  // Guard |this| is a SetObject.
  10602  ValOperandId thisValId = loadThis(calleeId);
  10603  ObjOperandId objId = writer.guardToObject(thisValId);
  10604  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set);
  10605 
  10606  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10607 
  10608 #ifndef JS_CODEGEN_X86
  10609  // Assume the hash key will likely always have the same type when attaching
  10610  // the first stub. If the call is polymorphic on the hash key, attach a stub
  10611  // which handles any value.
  10612  if (isFirstStub()) {
  10613    switch (args_[0].type()) {
  10614      case ValueType::Double:
  10615      case ValueType::Int32:
  10616      case ValueType::Boolean:
  10617      case ValueType::Undefined:
  10618      case ValueType::Null: {
  10619        writer.guardToNonGCThing(argId);
  10620        writer.setHasNonGCThingResult(objId, argId);
  10621        break;
  10622      }
  10623      case ValueType::String: {
  10624        StringOperandId strId = writer.guardToString(argId);
  10625        writer.setHasStringResult(objId, strId);
  10626        break;
  10627      }
  10628      case ValueType::Symbol: {
  10629        SymbolOperandId symId = writer.guardToSymbol(argId);
  10630        writer.setHasSymbolResult(objId, symId);
  10631        break;
  10632      }
  10633      case ValueType::BigInt: {
  10634        BigIntOperandId bigIntId = writer.guardToBigInt(argId);
  10635        writer.setHasBigIntResult(objId, bigIntId);
  10636        break;
  10637      }
  10638      case ValueType::Object: {
  10639        // Currently only supported on 64-bit platforms.
  10640 #  ifdef JS_PUNBOX64
  10641        ObjOperandId valId = writer.guardToObject(argId);
  10642        writer.setHasObjectResult(objId, valId);
  10643 #  else
  10644        writer.setHasResult(objId, argId);
  10645 #  endif
  10646        break;
  10647      }
  10648 
  10649      case ValueType::Magic:
  10650      case ValueType::PrivateGCThing:
  10651        MOZ_CRASH("Unexpected type");
  10652    }
  10653  } else {
  10654    writer.setHasResult(objId, argId);
  10655  }
  10656 #else
  10657  // The optimized versions require too many registers on x86.
  10658  writer.setHasResult(objId, argId);
  10659 #endif
  10660 
  10661  writer.returnFromIC();
  10662 
  10663  trackAttached("SetHas");
  10664  return AttachDecision::Attach;
  10665 }
  10666 
  10667 AttachDecision InlinableNativeIRGenerator::tryAttachSetDelete() {
  10668  // Ensure |this| is a SetObject.
  10669  if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
  10670    return AttachDecision::NoAction;
  10671  }
  10672 
  10673  // Need a single argument.
  10674  if (args_.length() != 1) {
  10675    return AttachDecision::NoAction;
  10676  }
  10677 
  10678  // Initialize the input operand.
  10679  Int32OperandId argcId = initializeInputOperand();
  10680 
  10681  // Guard callee is the 'delete' native function.
  10682  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10683 
  10684  // Guard |this| is a SetObject.
  10685  ValOperandId thisValId = loadThis(calleeId);
  10686  ObjOperandId objId = writer.guardToObject(thisValId);
  10687  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set);
  10688 
  10689  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10690  writer.setDeleteResult(objId, argId);
  10691  writer.returnFromIC();
  10692 
  10693  trackAttached("SetDelete");
  10694  return AttachDecision::Attach;
  10695 }
  10696 
  10697 AttachDecision InlinableNativeIRGenerator::tryAttachSetAdd() {
  10698  // Ensure |this| is a SetObject.
  10699  if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
  10700    return AttachDecision::NoAction;
  10701  }
  10702 
  10703  // Need one argument.
  10704  if (args_.length() != 1) {
  10705    return AttachDecision::NoAction;
  10706  }
  10707 
  10708  // Initialize the input operand.
  10709  Int32OperandId argcId = initializeInputOperand();
  10710 
  10711  // Guard callee is the 'add' native function.
  10712  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10713 
  10714  // Guard |this| is a SetObject.
  10715  ValOperandId thisValId = loadThis(calleeId);
  10716  ObjOperandId objId = writer.guardToObject(thisValId);
  10717  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set);
  10718 
  10719  ValOperandId keyId = loadArgument(calleeId, ArgumentKind::Arg0);
  10720  writer.setAddResult(objId, keyId);
  10721  writer.returnFromIC();
  10722 
  10723  trackAttached("SetAdd");
  10724  return AttachDecision::Attach;
  10725 }
  10726 
  10727 AttachDecision InlinableNativeIRGenerator::tryAttachSetSize() {
  10728  // Ensure |this| is a SetObject.
  10729  if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
  10730    return AttachDecision::NoAction;
  10731  }
  10732 
  10733  // Expecting no arguments.
  10734  if (args_.length() != 0) {
  10735    return AttachDecision::NoAction;
  10736  }
  10737 
  10738  // Initialize the input operand.
  10739  Int32OperandId argcId = initializeInputOperand();
  10740 
  10741  // Guard callee is the 'size' native function.
  10742  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10743 
  10744  // Guard |this| is a SetObject.
  10745  ValOperandId thisValId = loadThis(calleeId);
  10746  ObjOperandId objId = writer.guardToObject(thisValId);
  10747  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set);
  10748 
  10749  writer.setSizeResult(objId);
  10750  writer.returnFromIC();
  10751 
  10752  trackAttached("SetSize");
  10753  return AttachDecision::Attach;
  10754 }
  10755 
  10756 AttachDecision InlinableNativeIRGenerator::tryAttachMapHas() {
  10757  // Ensure |this| is a MapObject.
  10758  if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
  10759    return AttachDecision::NoAction;
  10760  }
  10761 
  10762  // Need a single argument.
  10763  if (args_.length() != 1) {
  10764    return AttachDecision::NoAction;
  10765  }
  10766 
  10767  // Initialize the input operand.
  10768  Int32OperandId argcId = initializeInputOperand();
  10769 
  10770  // Guard callee is the 'has' native function.
  10771  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10772 
  10773  // Guard |this| is a MapObject.
  10774  ValOperandId thisValId = loadThis(calleeId);
  10775  ObjOperandId objId = writer.guardToObject(thisValId);
  10776  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
  10777 
  10778  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10779 
  10780 #ifndef JS_CODEGEN_X86
  10781  // Assume the hash key will likely always have the same type when attaching
  10782  // the first stub. If the call is polymorphic on the hash key, attach a stub
  10783  // which handles any value.
  10784  if (isFirstStub()) {
  10785    switch (args_[0].type()) {
  10786      case ValueType::Double:
  10787      case ValueType::Int32:
  10788      case ValueType::Boolean:
  10789      case ValueType::Undefined:
  10790      case ValueType::Null: {
  10791        writer.guardToNonGCThing(argId);
  10792        writer.mapHasNonGCThingResult(objId, argId);
  10793        break;
  10794      }
  10795      case ValueType::String: {
  10796        StringOperandId strId = writer.guardToString(argId);
  10797        writer.mapHasStringResult(objId, strId);
  10798        break;
  10799      }
  10800      case ValueType::Symbol: {
  10801        SymbolOperandId symId = writer.guardToSymbol(argId);
  10802        writer.mapHasSymbolResult(objId, symId);
  10803        break;
  10804      }
  10805      case ValueType::BigInt: {
  10806        BigIntOperandId bigIntId = writer.guardToBigInt(argId);
  10807        writer.mapHasBigIntResult(objId, bigIntId);
  10808        break;
  10809      }
  10810      case ValueType::Object: {
  10811        // Currently only supported on 64-bit platforms.
  10812 #  ifdef JS_PUNBOX64
  10813        ObjOperandId valId = writer.guardToObject(argId);
  10814        writer.mapHasObjectResult(objId, valId);
  10815 #  else
  10816        writer.mapHasResult(objId, argId);
  10817 #  endif
  10818        break;
  10819      }
  10820 
  10821      case ValueType::Magic:
  10822      case ValueType::PrivateGCThing:
  10823        MOZ_CRASH("Unexpected type");
  10824    }
  10825  } else {
  10826    writer.mapHasResult(objId, argId);
  10827  }
  10828 #else
  10829  // The optimized versions require too many registers on x86.
  10830  writer.mapHasResult(objId, argId);
  10831 #endif
  10832 
  10833  writer.returnFromIC();
  10834 
  10835  trackAttached("MapHas");
  10836  return AttachDecision::Attach;
  10837 }
  10838 
  10839 AttachDecision InlinableNativeIRGenerator::tryAttachMapGet() {
  10840  // Ensure |this| is a MapObject.
  10841  if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
  10842    return AttachDecision::NoAction;
  10843  }
  10844 
  10845  // Need a single argument.
  10846  if (args_.length() != 1) {
  10847    return AttachDecision::NoAction;
  10848  }
  10849 
  10850  // Initialize the input operand.
  10851  Int32OperandId argcId = initializeInputOperand();
  10852 
  10853  // Guard callee is the 'get' native function.
  10854  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10855 
  10856  // Guard |this| is a MapObject.
  10857  ValOperandId thisValId = loadThis(calleeId);
  10858  ObjOperandId objId = writer.guardToObject(thisValId);
  10859  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
  10860 
  10861  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10862 
  10863 #ifndef JS_CODEGEN_X86
  10864  // Assume the hash key will likely always have the same type when attaching
  10865  // the first stub. If the call is polymorphic on the hash key, attach a stub
  10866  // which handles any value.
  10867  if (isFirstStub()) {
  10868    switch (args_[0].type()) {
  10869      case ValueType::Double:
  10870      case ValueType::Int32:
  10871      case ValueType::Boolean:
  10872      case ValueType::Undefined:
  10873      case ValueType::Null: {
  10874        writer.guardToNonGCThing(argId);
  10875        writer.mapGetNonGCThingResult(objId, argId);
  10876        break;
  10877      }
  10878      case ValueType::String: {
  10879        StringOperandId strId = writer.guardToString(argId);
  10880        writer.mapGetStringResult(objId, strId);
  10881        break;
  10882      }
  10883      case ValueType::Symbol: {
  10884        SymbolOperandId symId = writer.guardToSymbol(argId);
  10885        writer.mapGetSymbolResult(objId, symId);
  10886        break;
  10887      }
  10888      case ValueType::BigInt: {
  10889        BigIntOperandId bigIntId = writer.guardToBigInt(argId);
  10890        writer.mapGetBigIntResult(objId, bigIntId);
  10891        break;
  10892      }
  10893      case ValueType::Object: {
  10894        // Currently only supported on 64-bit platforms.
  10895 #  ifdef JS_PUNBOX64
  10896        ObjOperandId valId = writer.guardToObject(argId);
  10897        writer.mapGetObjectResult(objId, valId);
  10898 #  else
  10899        writer.mapGetResult(objId, argId);
  10900 #  endif
  10901        break;
  10902      }
  10903 
  10904      case ValueType::Magic:
  10905      case ValueType::PrivateGCThing:
  10906        MOZ_CRASH("Unexpected type");
  10907    }
  10908  } else {
  10909    writer.mapGetResult(objId, argId);
  10910  }
  10911 #else
  10912  // The optimized versions require too many registers on x86.
  10913  writer.mapGetResult(objId, argId);
  10914 #endif
  10915 
  10916  writer.returnFromIC();
  10917 
  10918  trackAttached("MapGet");
  10919  return AttachDecision::Attach;
  10920 }
  10921 
  10922 AttachDecision InlinableNativeIRGenerator::tryAttachMapDelete() {
  10923  // Ensure |this| is a MapObject.
  10924  if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
  10925    return AttachDecision::NoAction;
  10926  }
  10927 
  10928  // Need a single argument.
  10929  if (args_.length() != 1) {
  10930    return AttachDecision::NoAction;
  10931  }
  10932 
  10933  // Initialize the input operand.
  10934  Int32OperandId argcId = initializeInputOperand();
  10935 
  10936  // Guard callee is the 'delete' native function.
  10937  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10938 
  10939  // Guard |this| is a MapObject.
  10940  ValOperandId thisValId = loadThis(calleeId);
  10941  ObjOperandId objId = writer.guardToObject(thisValId);
  10942  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
  10943 
  10944  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  10945  writer.mapDeleteResult(objId, argId);
  10946  writer.returnFromIC();
  10947 
  10948  trackAttached("MapDelete");
  10949  return AttachDecision::Attach;
  10950 }
  10951 
  10952 AttachDecision InlinableNativeIRGenerator::tryAttachMapSet() {
  10953 #ifdef JS_CODEGEN_X86
  10954  // 32-bit x86 does not have enough registers for the AutoCallVM output, the
  10955  // MapObject*, and two Values.
  10956  return AttachDecision::NoAction;
  10957 #endif
  10958 
  10959  // Ensure |this| is a MapObject.
  10960  if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
  10961    return AttachDecision::NoAction;
  10962  }
  10963 
  10964  // Need two arguments.
  10965  if (args_.length() != 2) {
  10966    return AttachDecision::NoAction;
  10967  }
  10968 
  10969  // Initialize the input operand.
  10970  Int32OperandId argcId = initializeInputOperand();
  10971 
  10972  // Guard callee is the 'set' native function.
  10973  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  10974 
  10975  // Guard |this| is a MapObject.
  10976  ValOperandId thisValId = loadThis(calleeId);
  10977  ObjOperandId objId = writer.guardToObject(thisValId);
  10978  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
  10979 
  10980  ValOperandId keyId = loadArgument(calleeId, ArgumentKind::Arg0);
  10981  ValOperandId valId = loadArgument(calleeId, ArgumentKind::Arg1);
  10982  writer.mapSetResult(objId, keyId, valId);
  10983  writer.returnFromIC();
  10984 
  10985  trackAttached("MapSet");
  10986  return AttachDecision::Attach;
  10987 }
  10988 
  10989 AttachDecision InlinableNativeIRGenerator::tryAttachMapSize() {
  10990  // Ensure |this| is a MapObject.
  10991  if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
  10992    return AttachDecision::NoAction;
  10993  }
  10994 
  10995  // Expecting no arguments.
  10996  if (args_.length() != 0) {
  10997    return AttachDecision::NoAction;
  10998  }
  10999 
  11000  // Initialize the input operand.
  11001  Int32OperandId argcId = initializeInputOperand();
  11002 
  11003  // Guard callee is the 'size' native function.
  11004  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11005 
  11006  // Guard |this| is a MapObject.
  11007  ValOperandId thisValId = loadThis(calleeId);
  11008  ObjOperandId objId = writer.guardToObject(thisValId);
  11009  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map);
  11010 
  11011  writer.mapSizeResult(objId);
  11012  writer.returnFromIC();
  11013 
  11014  trackAttached("MapSize");
  11015  return AttachDecision::Attach;
  11016 }
  11017 
  11018 AttachDecision InlinableNativeIRGenerator::tryAttachWeakMapGet() {
  11019  // Ensure |this| is a WeakMapObject.
  11020  if (!thisval_.isObject() || !thisval_.toObject().is<WeakMapObject>()) {
  11021    return AttachDecision::NoAction;
  11022  }
  11023 
  11024  // Need a single object argument.
  11025  if (args_.length() != 1 || !args_[0].isObject()) {
  11026    return AttachDecision::NoAction;
  11027  }
  11028 
  11029  // Initialize the input operand.
  11030  Int32OperandId argcId = initializeInputOperand();
  11031 
  11032  // Guard callee is the 'get' native function.
  11033  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11034 
  11035  // Guard |this| is a WeakMapObject.
  11036  ValOperandId thisValId = loadThis(calleeId);
  11037  ObjOperandId objId = writer.guardToObject(thisValId);
  11038  emitOptimisticClassGuard(objId, &thisval_.toObject(),
  11039                           GuardClassKind::WeakMap);
  11040 
  11041  // Guard the argument is an object.
  11042  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  11043  ObjOperandId objArgId = writer.guardToObject(argId);
  11044 
  11045  writer.weakMapGetObjectResult(objId, objArgId);
  11046  writer.returnFromIC();
  11047 
  11048  trackAttached("WeakMapGet");
  11049  return AttachDecision::Attach;
  11050 }
  11051 
  11052 AttachDecision InlinableNativeIRGenerator::tryAttachWeakMapHas() {
  11053  // Ensure |this| is a WeakMapObject.
  11054  if (!thisval_.isObject() || !thisval_.toObject().is<WeakMapObject>()) {
  11055    return AttachDecision::NoAction;
  11056  }
  11057 
  11058  // Need a single object argument.
  11059  if (args_.length() != 1 || !args_[0].isObject()) {
  11060    return AttachDecision::NoAction;
  11061  }
  11062 
  11063  // Initialize the input operand.
  11064  Int32OperandId argcId = initializeInputOperand();
  11065 
  11066  // Guard callee is the 'has' native function.
  11067  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11068 
  11069  // Guard |this| is a WeakMapObject.
  11070  ValOperandId thisValId = loadThis(calleeId);
  11071  ObjOperandId objId = writer.guardToObject(thisValId);
  11072  emitOptimisticClassGuard(objId, &thisval_.toObject(),
  11073                           GuardClassKind::WeakMap);
  11074 
  11075  // Guard the argument is an object.
  11076  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  11077  ObjOperandId objArgId = writer.guardToObject(argId);
  11078 
  11079  writer.weakMapHasObjectResult(objId, objArgId);
  11080  writer.returnFromIC();
  11081 
  11082  trackAttached("WeakMapHas");
  11083  return AttachDecision::Attach;
  11084 }
  11085 
  11086 AttachDecision InlinableNativeIRGenerator::tryAttachWeakSetHas() {
  11087  // Ensure |this| is a WeakSetObject.
  11088  if (!thisval_.isObject() || !thisval_.toObject().is<WeakSetObject>()) {
  11089    return AttachDecision::NoAction;
  11090  }
  11091 
  11092  // Need a single object argument.
  11093  if (args_.length() != 1 || !args_[0].isObject()) {
  11094    return AttachDecision::NoAction;
  11095  }
  11096 
  11097  // Initialize the input operand.
  11098  Int32OperandId argcId = initializeInputOperand();
  11099 
  11100  // Guard callee is the 'has' native function.
  11101  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11102 
  11103  // Guard |this| is a WeakSetObject.
  11104  ValOperandId thisValId = loadThis(calleeId);
  11105  ObjOperandId objId = writer.guardToObject(thisValId);
  11106  emitOptimisticClassGuard(objId, &thisval_.toObject(),
  11107                           GuardClassKind::WeakSet);
  11108 
  11109  // Guard the argument is an object.
  11110  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  11111  ObjOperandId objArgId = writer.guardToObject(argId);
  11112 
  11113  writer.weakSetHasObjectResult(objId, objArgId);
  11114  writer.returnFromIC();
  11115 
  11116  trackAttached("WeakSetHas");
  11117  return AttachDecision::Attach;
  11118 }
  11119 
  11120 AttachDecision InlinableNativeIRGenerator::tryAttachDateGetTime() {
  11121  // Ensure |this| is a DateObject.
  11122  if (!thisval_.isObject() || !thisval_.toObject().is<DateObject>()) {
  11123    return AttachDecision::NoAction;
  11124  }
  11125 
  11126  // Expecting no arguments.
  11127  if (args_.length() != 0) {
  11128    return AttachDecision::NoAction;
  11129  }
  11130 
  11131  // Initialize the input operand.
  11132  Int32OperandId argcId = initializeInputOperand();
  11133 
  11134  // Guard callee is the 'getTime' (or 'valueOf') native function.
  11135  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11136 
  11137  // Guard |this| is a DateObject.
  11138  ValOperandId thisValId = loadThis(calleeId);
  11139  ObjOperandId objId = writer.guardToObject(thisValId);
  11140  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Date);
  11141 
  11142  writer.loadFixedSlotTypedResult(objId, DateObject::offsetOfUTCTimeSlot(),
  11143                                  ValueType::Double);
  11144 
  11145  writer.returnFromIC();
  11146 
  11147  trackAttached("DateGetTime");
  11148  return AttachDecision::Attach;
  11149 }
  11150 
  11151 AttachDecision InlinableNativeIRGenerator::tryAttachDateGet(
  11152    DateComponent component) {
  11153  // Ensure |this| is a DateObject.
  11154  if (!thisval_.isObject() || !thisval_.toObject().is<DateObject>()) {
  11155    return AttachDecision::NoAction;
  11156  }
  11157 
  11158  // Expecting no arguments.
  11159  if (args_.length() != 0) {
  11160    return AttachDecision::NoAction;
  11161  }
  11162 
  11163  // Can't check DateTime cache when not using the default time zone.
  11164  if (cx_->realm()->behaviors().timeZoneOverride()) {
  11165    return AttachDecision::NoAction;
  11166  }
  11167 
  11168  // Initialize the input operand.
  11169  Int32OperandId argcId = initializeInputOperand();
  11170 
  11171  // Guard callee is the Date native function.
  11172  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11173 
  11174  // Guard |this| is a DateObject.
  11175  ValOperandId thisValId = loadThis(calleeId);
  11176  ObjOperandId objId = writer.guardToObject(thisValId);
  11177  emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Date);
  11178 
  11179  // Fill in the local time slots.
  11180  writer.dateFillLocalTimeSlots(objId);
  11181 
  11182  switch (component) {
  11183    case DateComponent::FullYear:
  11184      writer.loadFixedSlotResult(objId, DateObject::offsetOfLocalYearSlot());
  11185      break;
  11186    case DateComponent::Month:
  11187      writer.loadFixedSlotResult(objId, DateObject::offsetOfLocalMonthSlot());
  11188      break;
  11189    case DateComponent::Date:
  11190      writer.loadFixedSlotResult(objId, DateObject::offsetOfLocalDateSlot());
  11191      break;
  11192    case DateComponent::Day:
  11193      writer.loadFixedSlotResult(objId, DateObject::offsetOfLocalDaySlot());
  11194      break;
  11195    case DateComponent::Hours: {
  11196      ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
  11197          objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
  11198      writer.dateHoursFromSecondsIntoYearResult(secondsIntoYearValId);
  11199      break;
  11200    }
  11201    case DateComponent::Minutes: {
  11202      ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
  11203          objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
  11204      writer.dateMinutesFromSecondsIntoYearResult(secondsIntoYearValId);
  11205      break;
  11206    }
  11207    case DateComponent::Seconds: {
  11208      ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
  11209          objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
  11210      writer.dateSecondsFromSecondsIntoYearResult(secondsIntoYearValId);
  11211      break;
  11212    }
  11213  }
  11214 
  11215  writer.returnFromIC();
  11216 
  11217  switch (component) {
  11218    case DateComponent::FullYear:
  11219      trackAttached("DateGetFullYear");
  11220      break;
  11221    case DateComponent::Month:
  11222      trackAttached("DateGetMonth");
  11223      break;
  11224    case DateComponent::Date:
  11225      trackAttached("DateGetDate");
  11226      break;
  11227    case DateComponent::Day:
  11228      trackAttached("DateGetDay");
  11229      break;
  11230    case DateComponent::Hours:
  11231      trackAttached("DateGetHours");
  11232      break;
  11233    case DateComponent::Minutes:
  11234      trackAttached("DateGetMinutes");
  11235      break;
  11236    case DateComponent::Seconds:
  11237      trackAttached("DateGetSeconds");
  11238      break;
  11239  }
  11240  return AttachDecision::Attach;
  11241 }
  11242 
  11243 AttachDecision CallIRGenerator::tryAttachFunCall(HandleFunction callee) {
  11244  MOZ_ASSERT(callee->isNativeWithoutJitEntry());
  11245 
  11246  if (callee->native() != fun_call) {
  11247    return AttachDecision::NoAction;
  11248  }
  11249 
  11250  if (!thisval_.isObject() || !thisval_.toObject().is<JSFunction>()) {
  11251    return AttachDecision::NoAction;
  11252  }
  11253  RootedFunction target(cx_, &thisval_.toObject().as<JSFunction>());
  11254 
  11255  bool isScripted = target->hasJitEntry();
  11256  MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
  11257 
  11258  if (target->isClassConstructor()) {
  11259    return AttachDecision::NoAction;
  11260  }
  11261 
  11262  CallFlags targetFlags(CallFlags::FunCall);
  11263  if (mode_ == ICState::Mode::Specialized) {
  11264    if (cx_->realm() == target->realm()) {
  11265      targetFlags.setIsSameRealm();
  11266    }
  11267  }
  11268 
  11269  if (mode_ == ICState::Mode::Specialized && !isScripted) {
  11270    HandleValue newTarget = NullHandleValue;
  11271    HandleValue thisValue = argc_ > 0 ? args_[0] : UndefinedHandleValue;
  11272    HandleValueArray args =
  11273        argc_ > 0 ? HandleValueArray::subarray(args_, 1, args_.length() - 1)
  11274                  : HandleValueArray::empty();
  11275 
  11276    // Check for specific native-function optimizations.
  11277    InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget,
  11278                                         thisValue, args, targetFlags);
  11279    TRY_ATTACH(nativeGen.tryAttachStub());
  11280  }
  11281 
  11282  Int32OperandId argcId(writer.setInputOperandId(0));
  11283  ObjOperandId thisObjId = emitFunCallGuard(argcId);
  11284 
  11285  if (mode_ == ICState::Mode::Specialized) {
  11286    // Ensure that |this| is the expected target function.
  11287    emitCalleeGuard(thisObjId, target);
  11288 
  11289    if (isScripted) {
  11290      writer.callScriptedFunction(thisObjId, argcId, targetFlags,
  11291                                  ClampFixedArgc(argc_));
  11292    } else {
  11293      writer.callNativeFunction(thisObjId, argcId, jsop(), target, targetFlags,
  11294                                ClampFixedArgc(argc_));
  11295    }
  11296  } else {
  11297    // Guard that |this| is a function.
  11298    writer.guardClass(thisObjId, GuardClassKind::JSFunction);
  11299 
  11300    // Guard that function is not a class constructor.
  11301    writer.guardNotClassConstructor(thisObjId);
  11302 
  11303    if (isScripted) {
  11304      writer.guardFunctionHasJitEntry(thisObjId);
  11305      writer.callScriptedFunction(thisObjId, argcId, targetFlags,
  11306                                  ClampFixedArgc(argc_));
  11307    } else {
  11308      writer.guardFunctionHasNoJitEntry(thisObjId);
  11309      writer.callAnyNativeFunction(thisObjId, argcId, targetFlags,
  11310                                   ClampFixedArgc(argc_));
  11311    }
  11312  }
  11313 
  11314  writer.returnFromIC();
  11315 
  11316  if (isScripted) {
  11317    trackAttached("Scripted fun_call");
  11318  } else {
  11319    trackAttached("Native fun_call");
  11320  }
  11321 
  11322  return AttachDecision::Attach;
  11323 }
  11324 
  11325 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayFill() {
  11326  // Expected arguments: value, optional start, optional end.
  11327  if (args_.length() < 1 || args_.length() > 3) {
  11328    return AttachDecision::NoAction;
  11329  }
  11330 
  11331  if (!isFirstStub()) {
  11332    // Attach only once to prevent slowdowns for polymorphic calls.
  11333    return AttachDecision::NoAction;
  11334  }
  11335 
  11336  // Ensure |this| is a TypedArrayObject.
  11337  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11338    return AttachDecision::NoAction;
  11339  }
  11340 
  11341  // Both arguments must be valid indices.
  11342  int64_t unusedIndex;
  11343  if (args_.length() > 1 && !ValueIsInt64Index(args_[1], &unusedIndex)) {
  11344    return AttachDecision::NoAction;
  11345  }
  11346  if (args_.length() > 2 && !ValueIsInt64Index(args_[2], &unusedIndex)) {
  11347    return AttachDecision::NoAction;
  11348  }
  11349 
  11350  auto* tarr = &thisval_.toObject().as<TypedArrayObject>();
  11351  auto elementType = tarr->type();
  11352 
  11353  // Detached buffers throw.
  11354  if (tarr->hasDetachedBuffer()) {
  11355    return AttachDecision::NoAction;
  11356  }
  11357 
  11358  // Must not be an immutable typed array.
  11359  if (tarr->is<ImmutableTypedArrayObject>()) {
  11360    return AttachDecision::NoAction;
  11361  }
  11362 
  11363  // Resizable typed arrays not yet supported.
  11364  if (tarr->is<ResizableTypedArrayObject>()) {
  11365    return AttachDecision::NoAction;
  11366  }
  11367 
  11368  // Don't attach if the input type doesn't match the guard added below.
  11369  if (!ValueCanConvertToNumeric(elementType, args_[0])) {
  11370    return AttachDecision::NoAction;
  11371  }
  11372 
  11373  // Initialize the input operand.
  11374  Int32OperandId argcId = initializeInputOperand();
  11375 
  11376  // Guard callee is the `fill` native function.
  11377  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11378 
  11379  // Guard this is an object.
  11380  ValOperandId thisValId = loadThis(calleeId);
  11381  ObjOperandId objId = writer.guardToObject(thisValId);
  11382 
  11383  // Shape guard to check class.
  11384  writer.guardShapeForClass(objId, tarr->shape());
  11385 
  11386  // Guard the array buffer is not detached.
  11387  writer.guardHasAttachedArrayBuffer(objId);
  11388 
  11389  ValOperandId fillValId = loadArgument(calleeId, ArgumentKind::Arg0);
  11390  OperandId fillNumericId = emitNumericGuard(fillValId, args_[0], elementType);
  11391 
  11392  // Convert |start| to IntPtr.
  11393  IntPtrOperandId intPtrStartId;
  11394  if (args_.length() > 1) {
  11395    ValOperandId startId = loadArgument(calleeId, ArgumentKind::Arg1);
  11396    intPtrStartId =
  11397        guardToIntPtrIndex(args_[1], startId, /* supportOOB = */ false);
  11398  } else {
  11399    // Absent first argument defaults to zero.
  11400    intPtrStartId = writer.loadInt32AsIntPtrConstant(0);
  11401  }
  11402 
  11403  // Convert |end| to IntPtr.
  11404  IntPtrOperandId intPtrEndId;
  11405  if (args_.length() > 2) {
  11406    ValOperandId endId = loadArgument(calleeId, ArgumentKind::Arg2);
  11407    intPtrEndId = guardToIntPtrIndex(args_[2], endId, /* supportOOB = */ false);
  11408  } else {
  11409    // Absent second argument defaults to the typed array length.
  11410    intPtrEndId = writer.loadArrayBufferViewLength(objId);
  11411  }
  11412 
  11413  writer.typedArrayFillResult(objId, fillNumericId, intPtrStartId, intPtrEndId,
  11414                              elementType);
  11415  writer.returnFromIC();
  11416 
  11417  trackAttached("TypedArrayFill");
  11418  return AttachDecision::Attach;
  11419 }
  11420 
  11421 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArraySet() {
  11422  // Expected arguments: source (typed array), optional offset (int32).
  11423  if (args_.length() < 1 || args_.length() > 2) {
  11424    return AttachDecision::NoAction;
  11425  }
  11426 
  11427  if (!isFirstStub()) {
  11428    // Attach only once to prevent slowdowns for polymorphic calls.
  11429    return AttachDecision::NoAction;
  11430  }
  11431 
  11432  // Ensure |this| is a TypedArrayObject.
  11433  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11434    return AttachDecision::NoAction;
  11435  }
  11436 
  11437  // Ensure first argument is a TypedArrayObject.
  11438  if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
  11439    return AttachDecision::NoAction;
  11440  }
  11441 
  11442  // Ensure optional second argument is a non-negative index.
  11443  uint64_t targetOffset = 0;
  11444  if (args_.length() > 1) {
  11445    int64_t offsetIndex;
  11446    if (!ValueIsInt64Index(args_[1], &offsetIndex) || offsetIndex < 0) {
  11447      return AttachDecision::NoAction;
  11448    }
  11449    targetOffset = uint64_t(offsetIndex);
  11450  }
  11451 
  11452  auto* tarr = &thisval_.toObject().as<TypedArrayObject>();
  11453  auto* source = &args_[0].toObject().as<TypedArrayObject>();
  11454 
  11455  // Detached buffers throw.
  11456  if (tarr->hasDetachedBuffer() || source->hasDetachedBuffer()) {
  11457    return AttachDecision::NoAction;
  11458  }
  11459 
  11460  // Target must not be an immutable typed array.
  11461  if (tarr->is<ImmutableTypedArrayObject>()) {
  11462    return AttachDecision::NoAction;
  11463  }
  11464 
  11465  // Typed array contents must be compatible.
  11466  if (Scalar::isBigIntType(tarr->type()) !=
  11467      Scalar::isBigIntType(source->type())) {
  11468    return AttachDecision::NoAction;
  11469  }
  11470 
  11471  // `set()` throws if `sourceLength + targetOffset > targetLength`.
  11472  size_t targetLength = tarr->length().valueOr(0);
  11473  size_t sourceLength = source->length().valueOr(0);
  11474  if (targetOffset > targetLength ||
  11475      sourceLength > targetLength - targetOffset) {
  11476    return AttachDecision::NoAction;
  11477  }
  11478 
  11479  // Resizable typed arrays not yet supported.
  11480  if (tarr->is<ResizableTypedArrayObject>() ||
  11481      source->is<ResizableTypedArrayObject>()) {
  11482    return AttachDecision::NoAction;
  11483  }
  11484 
  11485  // Infallible operation if bit-wise copying is possible.
  11486  bool canUseBitwiseCopy = CanUseBitwiseCopy(tarr->type(), source->type());
  11487 
  11488  // Initialize the input operand.
  11489  Int32OperandId argcId = initializeInputOperand();
  11490 
  11491  // Guard callee is the `set` native function.
  11492  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11493 
  11494  // Guard this is an object.
  11495  ValOperandId thisValId = loadThis(calleeId);
  11496  ObjOperandId objId = writer.guardToObject(thisValId);
  11497 
  11498  // Shape guard to check class.
  11499  writer.guardShapeForClass(objId, tarr->shape());
  11500 
  11501  // Guard the array buffer is not detached.
  11502  writer.guardHasAttachedArrayBuffer(objId);
  11503 
  11504  // Guard first argument is an object.
  11505  ValOperandId sourceId = loadArgument(calleeId, ArgumentKind::Arg0);
  11506  ObjOperandId sourceObjId = writer.guardToObject(sourceId);
  11507 
  11508  // Shape guard to check class of first argument.
  11509  writer.guardShapeForClass(sourceObjId, source->shape());
  11510 
  11511  // Guard the source is not detached. (Immutable typed arrays can't get
  11512  // detached.)
  11513  if (!source->is<ImmutableTypedArrayObject>()) {
  11514    writer.guardHasAttachedArrayBuffer(sourceObjId);
  11515  }
  11516 
  11517  // Convert offset to IntPtr.
  11518  IntPtrOperandId intPtrOffsetId;
  11519  if (args_.length() > 1) {
  11520    ValOperandId offsetId = loadArgument(calleeId, ArgumentKind::Arg1);
  11521    intPtrOffsetId =
  11522        guardToIntPtrIndex(args_[1], offsetId, /* supportOOB = */ false);
  11523    writer.guardIntPtrIsNonNegative(intPtrOffsetId);
  11524  } else {
  11525    // Absent first argument defaults to zero.
  11526    intPtrOffsetId = writer.loadInt32AsIntPtrConstant(0);
  11527  }
  11528 
  11529  writer.typedArraySetResult(objId, sourceObjId, intPtrOffsetId,
  11530                             canUseBitwiseCopy);
  11531  writer.returnFromIC();
  11532 
  11533  trackAttached("TypedArraySet");
  11534  return AttachDecision::Attach;
  11535 }
  11536 
  11537 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArraySubarray() {
  11538  // Only handle argc <= 2.
  11539  if (args_.length() > 2) {
  11540    return AttachDecision::NoAction;
  11541  }
  11542 
  11543  if (!isFirstStub()) {
  11544    // Attach only once to prevent slowdowns for polymorphic calls.
  11545    return AttachDecision::NoAction;
  11546  }
  11547 
  11548  // Ensure |this| is a TypedArrayObject.
  11549  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11550    return AttachDecision::NoAction;
  11551  }
  11552 
  11553  // Both arguments must be valid indices.
  11554  int64_t unusedIndex;
  11555  if (args_.length() > 0 && !ValueIsInt64Index(args_[0], &unusedIndex)) {
  11556    return AttachDecision::NoAction;
  11557  }
  11558  if (args_.length() > 1 && !ValueIsInt64Index(args_[1], &unusedIndex)) {
  11559    return AttachDecision::NoAction;
  11560  }
  11561 
  11562  Rooted<TypedArrayObject*> tarr(cx_,
  11563                                 &thisval_.toObject().as<TypedArrayObject>());
  11564 
  11565  // Detached buffer throws.
  11566  if (tarr->hasDetachedBuffer()) {
  11567    return AttachDecision::NoAction;
  11568  }
  11569 
  11570  // Resizable typed arrays not yet supported.
  11571  if (tarr->is<ResizableTypedArrayObject>()) {
  11572    return AttachDecision::NoAction;
  11573  }
  11574 
  11575  // TypedArray species fuse must still be intact.
  11576  if (!cx_->realm()->realmFuses.optimizeTypedArraySpeciesFuse.intact()) {
  11577    return AttachDecision::NoAction;
  11578  }
  11579 
  11580  // Ensure |tarr|'s prototype is the actual concrete TypedArray.prototype.
  11581  auto protoKey = StandardProtoKeyOrNull(tarr);
  11582  auto* proto = cx_->global()->maybeGetPrototype(protoKey);
  11583  if (!proto || tarr->staticPrototype() != proto) {
  11584    return AttachDecision::NoAction;
  11585  }
  11586 
  11587  // Ensure no own "constructor" property.
  11588  if (tarr->containsPure(cx_->names().constructor)) {
  11589    return AttachDecision::NoAction;
  11590  }
  11591 
  11592  Rooted<TypedArrayObject*> templateObj(
  11593      cx_, TypedArrayObject::GetTemplateObjectForBufferView(cx_, tarr));
  11594  if (!templateObj) {
  11595    cx_->recoverFromOutOfMemory();
  11596    return AttachDecision::NoAction;
  11597  }
  11598 
  11599  // Initialize the input operand.
  11600  Int32OperandId argcId = initializeInputOperand();
  11601 
  11602  // Guard callee is the `subarray` native function.
  11603  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11604 
  11605  // Guard this is an object.
  11606  ValOperandId thisValId = loadThis(calleeId);
  11607  ObjOperandId objId = writer.guardToObject(thisValId);
  11608 
  11609  // Shape guard to check class and prototype, and to ensure no own
  11610  // "constructor" property is present.
  11611  writer.guardShape(objId, tarr->shape());
  11612 
  11613  // Guard the array buffer is not detached. (Immutable typed arrays can't get
  11614  // detached.)
  11615  if (!tarr->is<ImmutableTypedArrayObject>()) {
  11616    writer.guardHasAttachedArrayBuffer(objId);
  11617  }
  11618 
  11619  // Guard the fuse is intact.
  11620  writer.guardFuse(RealmFuses::FuseIndex::OptimizeTypedArraySpeciesFuse);
  11621 
  11622  // Convert |start| to IntPtr.
  11623  IntPtrOperandId intPtrStartId;
  11624  if (args_.length() > 0) {
  11625    ValOperandId startId = loadArgument(calleeId, ArgumentKind::Arg0);
  11626    intPtrStartId =
  11627        guardToIntPtrIndex(args_[0], startId, /* supportOOB = */ false);
  11628  } else {
  11629    // Absent first argument defaults to zero.
  11630    intPtrStartId = writer.loadInt32AsIntPtrConstant(0);
  11631  }
  11632 
  11633  // Convert |end| to IntPtr.
  11634  IntPtrOperandId intPtrEndId;
  11635  if (args_.length() > 1) {
  11636    ValOperandId endId = loadArgument(calleeId, ArgumentKind::Arg1);
  11637    intPtrEndId = guardToIntPtrIndex(args_[1], endId, /* supportOOB = */ false);
  11638  } else {
  11639    // Absent second argument defaults to the typed array length.
  11640    intPtrEndId = writer.loadArrayBufferViewLength(objId);
  11641  }
  11642 
  11643  writer.typedArraySubarrayResult(templateObj, objId, intPtrStartId,
  11644                                  intPtrEndId);
  11645  writer.returnFromIC();
  11646 
  11647  trackAttached("TypedArraySubarray");
  11648  return AttachDecision::Attach;
  11649 }
  11650 
  11651 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength() {
  11652  // Expecting no arguments.
  11653  if (args_.length() != 0) {
  11654    return AttachDecision::NoAction;
  11655  }
  11656 
  11657  // Ensure |this| is a TypedArrayObject.
  11658  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11659    return AttachDecision::NoAction;
  11660  }
  11661 
  11662  auto* tarr = &thisval_.toObject().as<TypedArrayObject>();
  11663 
  11664  // Initialize the input operand.
  11665  Int32OperandId argcId = initializeInputOperand();
  11666 
  11667  // Guard callee is the 'length' native function.
  11668  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11669 
  11670  // Guard |this| is a TypedArrayObject.
  11671  ValOperandId thisValId = loadThis(calleeId);
  11672  ObjOperandId objId = writer.guardToObject(thisValId);
  11673  writer.guardShapeForClass(objId, tarr->shape());
  11674 
  11675  size_t length = tarr->length().valueOr(0);
  11676  if (!tarr->is<ResizableTypedArrayObject>()) {
  11677    if (length <= INT32_MAX) {
  11678      writer.loadArrayBufferViewLengthInt32Result(objId);
  11679    } else {
  11680      writer.loadArrayBufferViewLengthDoubleResult(objId);
  11681    }
  11682  } else {
  11683    if (length <= INT32_MAX) {
  11684      writer.resizableTypedArrayLengthInt32Result(objId);
  11685    } else {
  11686      writer.resizableTypedArrayLengthDoubleResult(objId);
  11687    }
  11688  }
  11689 
  11690  writer.returnFromIC();
  11691 
  11692  trackAttached("TypedArrayLength");
  11693  return AttachDecision::Attach;
  11694 }
  11695 
  11696 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteLength() {
  11697  // Expecting no arguments.
  11698  if (args_.length() != 0) {
  11699    return AttachDecision::NoAction;
  11700  }
  11701 
  11702  // Ensure |this| is a TypedArrayObject.
  11703  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11704    return AttachDecision::NoAction;
  11705  }
  11706 
  11707  auto* tarr = &thisval_.toObject().as<TypedArrayObject>();
  11708 
  11709  // Initialize the input operand.
  11710  Int32OperandId argcId = initializeInputOperand();
  11711 
  11712  // Guard callee is the 'byteLength' native function.
  11713  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11714 
  11715  // Guard |this| is a TypedArrayObject.
  11716  ValOperandId thisValId = loadThis(calleeId);
  11717  ObjOperandId objId = writer.guardToObject(thisValId);
  11718  writer.guardShapeForClass(objId, tarr->shape());
  11719 
  11720  size_t byteLength = tarr->byteLength().valueOr(0);
  11721  if (!tarr->is<ResizableTypedArrayObject>()) {
  11722    if (byteLength <= INT32_MAX) {
  11723      writer.typedArrayByteLengthInt32Result(objId);
  11724    } else {
  11725      writer.typedArrayByteLengthDoubleResult(objId);
  11726    }
  11727  } else {
  11728    if (byteLength <= INT32_MAX) {
  11729      writer.resizableTypedArrayByteLengthInt32Result(objId);
  11730    } else {
  11731      writer.resizableTypedArrayByteLengthDoubleResult(objId);
  11732    }
  11733  }
  11734 
  11735  writer.returnFromIC();
  11736 
  11737  trackAttached("TypedArrayByteLength");
  11738  return AttachDecision::Attach;
  11739 }
  11740 
  11741 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() {
  11742  // Expecting no arguments.
  11743  if (args_.length() != 0) {
  11744    return AttachDecision::NoAction;
  11745  }
  11746 
  11747  // Ensure |this| is a TypedArrayObject.
  11748  if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) {
  11749    return AttachDecision::NoAction;
  11750  }
  11751 
  11752  auto* tarr = &thisval_.toObject().as<TypedArrayObject>();
  11753 
  11754  // Initialize the input operand.
  11755  Int32OperandId argcId = initializeInputOperand();
  11756 
  11757  // Guard callee is the 'byteLength' native function.
  11758  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11759 
  11760  // Guard |this| is a TypedArrayObject.
  11761  ValOperandId thisValId = loadThis(calleeId);
  11762  ObjOperandId objId = writer.guardToObject(thisValId);
  11763  writer.guardShapeForClass(objId, tarr->shape());
  11764 
  11765  // byteOffset doesn't need to use different code paths for fixed-length and
  11766  // resizable TypedArrays.
  11767  size_t byteOffset = tarr->byteOffset().valueOr(0);
  11768  if (byteOffset <= INT32_MAX) {
  11769    writer.arrayBufferViewByteOffsetInt32Result(objId);
  11770  } else {
  11771    writer.arrayBufferViewByteOffsetDoubleResult(objId);
  11772  }
  11773 
  11774  writer.returnFromIC();
  11775 
  11776  trackAttached("TypedArrayByteOffset");
  11777  return AttachDecision::Attach;
  11778 }
  11779 
  11780 AttachDecision InlinableNativeIRGenerator::tryAttachIsTypedArray(
  11781    bool isPossiblyWrapped) {
  11782  // Self-hosted code calls this with a single object argument.
  11783  MOZ_ASSERT(args_.length() == 1);
  11784  MOZ_ASSERT(args_[0].isObject());
  11785 
  11786  // Initialize the input operand.
  11787  initializeInputOperand();
  11788 
  11789  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  11790 
  11791  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
  11792  ObjOperandId objArgId = writer.guardToObject(argId);
  11793  writer.isTypedArrayResult(objArgId, isPossiblyWrapped);
  11794  writer.returnFromIC();
  11795 
  11796  trackAttached(isPossiblyWrapped ? "IsPossiblyWrappedTypedArray"
  11797                                  : "IsTypedArray");
  11798  return AttachDecision::Attach;
  11799 }
  11800 
  11801 AttachDecision InlinableNativeIRGenerator::tryAttachIsTypedArrayConstructor() {
  11802  // Self-hosted code calls this with a single object argument.
  11803  MOZ_ASSERT(args_.length() == 1);
  11804  MOZ_ASSERT(args_[0].isObject());
  11805 
  11806  // Initialize the input operand.
  11807  initializeInputOperand();
  11808 
  11809  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  11810 
  11811  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
  11812  ObjOperandId objArgId = writer.guardToObject(argId);
  11813  writer.isTypedArrayConstructorResult(objArgId);
  11814  writer.returnFromIC();
  11815 
  11816  trackAttached("IsTypedArrayConstructor");
  11817  return AttachDecision::Attach;
  11818 }
  11819 
  11820 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength(
  11821    bool isPossiblyWrapped) {
  11822  // Self-hosted code calls this with a single, possibly wrapped,
  11823  // TypedArrayObject argument.
  11824  MOZ_ASSERT(args_.length() == 1);
  11825  MOZ_ASSERT(args_[0].isObject());
  11826 
  11827  // Only optimize when the object isn't a wrapper.
  11828  if (isPossiblyWrapped && IsWrapper(&args_[0].toObject())) {
  11829    return AttachDecision::NoAction;
  11830  }
  11831 
  11832  MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>());
  11833 
  11834  auto* tarr = &args_[0].toObject().as<TypedArrayObject>();
  11835 
  11836  // Don't optimize when a resizable TypedArray is out-of-bounds.
  11837  auto length = tarr->length();
  11838  if (length.isNothing() && !tarr->hasDetachedBuffer()) {
  11839    MOZ_ASSERT(tarr->is<ResizableTypedArrayObject>());
  11840    MOZ_ASSERT(tarr->isOutOfBounds());
  11841 
  11842    return AttachDecision::NoAction;
  11843  }
  11844 
  11845  // Initialize the input operand.
  11846  initializeInputOperand();
  11847 
  11848  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  11849 
  11850  ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
  11851  ObjOperandId objArgId = writer.guardToObject(argId);
  11852 
  11853  if (isPossiblyWrapped) {
  11854    writer.guardIsNotProxy(objArgId);
  11855  }
  11856 
  11857  EmitGuardTypedArray(writer, tarr, objArgId);
  11858 
  11859  if (!tarr->is<ResizableTypedArrayObject>()) {
  11860    if (length.valueOr(0) <= INT32_MAX) {
  11861      writer.loadArrayBufferViewLengthInt32Result(objArgId);
  11862    } else {
  11863      writer.loadArrayBufferViewLengthDoubleResult(objArgId);
  11864    }
  11865  } else {
  11866    writer.guardResizableArrayBufferViewInBoundsOrDetached(objArgId);
  11867 
  11868    if (length.valueOr(0) <= INT32_MAX) {
  11869      writer.resizableTypedArrayLengthInt32Result(objArgId);
  11870    } else {
  11871      writer.resizableTypedArrayLengthDoubleResult(objArgId);
  11872    }
  11873  }
  11874  writer.returnFromIC();
  11875 
  11876  trackAttached("IntrinsicTypedArrayLength");
  11877  return AttachDecision::Attach;
  11878 }
  11879 
  11880 AttachDecision InlinableNativeIRGenerator::tryAttachArrayBufferByteLength() {
  11881  // Expecting no arguments.
  11882  if (args_.length() != 0) {
  11883    return AttachDecision::NoAction;
  11884  }
  11885 
  11886  // Ensure |this| is an ArrayBufferObject.
  11887  if (!thisval_.isObject() || !thisval_.toObject().is<ArrayBufferObject>()) {
  11888    return AttachDecision::NoAction;
  11889  }
  11890 
  11891  auto* buf = &thisval_.toObject().as<ArrayBufferObject>();
  11892 
  11893  // Initialize the input operand.
  11894  Int32OperandId argcId = initializeInputOperand();
  11895 
  11896  // Guard callee is the 'byteLength' native function.
  11897  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11898 
  11899  // Guard |this| is an ArrayBufferObject.
  11900  ValOperandId thisValId = loadThis(calleeId);
  11901  ObjOperandId objId = writer.guardToObject(thisValId);
  11902 
  11903  if (buf->is<FixedLengthArrayBufferObject>()) {
  11904    emitOptimisticClassGuard(objId, buf,
  11905                             GuardClassKind::FixedLengthArrayBuffer);
  11906  } else if (buf->is<ImmutableArrayBufferObject>()) {
  11907    emitOptimisticClassGuard(objId, buf, GuardClassKind::ImmutableArrayBuffer);
  11908  } else {
  11909    emitOptimisticClassGuard(objId, buf, GuardClassKind::ResizableArrayBuffer);
  11910  }
  11911 
  11912  if (buf->byteLength() <= INT32_MAX) {
  11913    writer.loadArrayBufferByteLengthInt32Result(objId);
  11914  } else {
  11915    writer.loadArrayBufferByteLengthDoubleResult(objId);
  11916  }
  11917 
  11918  writer.returnFromIC();
  11919 
  11920  trackAttached("ArrayBufferByteLength");
  11921  return AttachDecision::Attach;
  11922 }
  11923 
  11924 AttachDecision
  11925 InlinableNativeIRGenerator::tryAttachSharedArrayBufferByteLength() {
  11926  // Expecting no arguments.
  11927  if (args_.length() != 0) {
  11928    return AttachDecision::NoAction;
  11929  }
  11930 
  11931  // Ensure |this| is a SharedArrayBufferObject.
  11932  if (!thisval_.isObject() ||
  11933      !thisval_.toObject().is<SharedArrayBufferObject>()) {
  11934    return AttachDecision::NoAction;
  11935  }
  11936 
  11937  auto* buf = &thisval_.toObject().as<SharedArrayBufferObject>();
  11938 
  11939  // Initialize the input operand.
  11940  Int32OperandId argcId = initializeInputOperand();
  11941 
  11942  // Guard callee is the 'byteLength' native function.
  11943  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  11944 
  11945  // Guard |this| is a SharedArrayBufferObject.
  11946  ValOperandId thisValId = loadThis(calleeId);
  11947  ObjOperandId objId = writer.guardToObject(thisValId);
  11948 
  11949  if (buf->is<FixedLengthSharedArrayBufferObject>()) {
  11950    emitOptimisticClassGuard(objId, buf,
  11951                             GuardClassKind::FixedLengthSharedArrayBuffer);
  11952 
  11953    if (buf->byteLength() <= INT32_MAX) {
  11954      writer.loadArrayBufferByteLengthInt32Result(objId);
  11955    } else {
  11956      writer.loadArrayBufferByteLengthDoubleResult(objId);
  11957    }
  11958  } else {
  11959    emitOptimisticClassGuard(objId, buf,
  11960                             GuardClassKind::GrowableSharedArrayBuffer);
  11961 
  11962    if (buf->byteLength() <= INT32_MAX) {
  11963      writer.growableSharedArrayBufferByteLengthInt32Result(objId);
  11964    } else {
  11965      writer.growableSharedArrayBufferByteLengthDoubleResult(objId);
  11966    }
  11967  }
  11968 
  11969  writer.returnFromIC();
  11970 
  11971  trackAttached("SharedArrayBufferByteLength");
  11972  return AttachDecision::Attach;
  11973 }
  11974 
  11975 AttachDecision InlinableNativeIRGenerator::tryAttachIsConstructing() {
  11976  // Self-hosted code calls this with no arguments in function scripts.
  11977  MOZ_ASSERT(args_.length() == 0);
  11978  MOZ_ASSERT(script()->isFunction());
  11979 
  11980  // Initialize the input operand.
  11981  initializeInputOperand();
  11982 
  11983  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  11984 
  11985  writer.frameIsConstructingResult();
  11986  writer.returnFromIC();
  11987 
  11988  trackAttached("IsConstructing");
  11989  return AttachDecision::Attach;
  11990 }
  11991 
  11992 AttachDecision
  11993 InlinableNativeIRGenerator::tryAttachGetNextMapSetEntryForIterator(bool isMap) {
  11994  // Self-hosted code calls this with two objects.
  11995  MOZ_ASSERT(args_.length() == 2);
  11996  if (isMap) {
  11997    MOZ_ASSERT(args_[0].toObject().is<MapIteratorObject>());
  11998  } else {
  11999    MOZ_ASSERT(args_[0].toObject().is<SetIteratorObject>());
  12000  }
  12001  MOZ_ASSERT(args_[1].toObject().is<ArrayObject>());
  12002 
  12003  // Initialize the input operand.
  12004  initializeInputOperand();
  12005 
  12006  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  12007 
  12008  ValOperandId iterId = loadArgumentIntrinsic(ArgumentKind::Arg0);
  12009  ObjOperandId objIterId = writer.guardToObject(iterId);
  12010 
  12011  ValOperandId resultArrId = loadArgumentIntrinsic(ArgumentKind::Arg1);
  12012  ObjOperandId objResultArrId = writer.guardToObject(resultArrId);
  12013 
  12014  writer.getNextMapSetEntryForIteratorResult(objIterId, objResultArrId, isMap);
  12015  writer.returnFromIC();
  12016 
  12017  trackAttached("GetNextMapSetEntryForIterator");
  12018  return AttachDecision::Attach;
  12019 }
  12020 
  12021 AttachDecision InlinableNativeIRGenerator::tryAttachNewArrayIterator() {
  12022  // Self-hosted code calls this without any arguments
  12023  MOZ_ASSERT(args_.length() == 0);
  12024 
  12025  JSObject* templateObj = NewArrayIteratorTemplate(cx_);
  12026  if (!templateObj) {
  12027    cx_->recoverFromOutOfMemory();
  12028    return AttachDecision::NoAction;
  12029  }
  12030 
  12031  // Initialize the input operand.
  12032  initializeInputOperand();
  12033 
  12034  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  12035 
  12036  writer.newArrayIteratorResult(templateObj);
  12037  writer.returnFromIC();
  12038 
  12039  trackAttached("NewArrayIterator");
  12040  return AttachDecision::Attach;
  12041 }
  12042 
  12043 AttachDecision InlinableNativeIRGenerator::tryAttachNewStringIterator() {
  12044  // Self-hosted code calls this without any arguments
  12045  MOZ_ASSERT(args_.length() == 0);
  12046 
  12047  JSObject* templateObj = NewStringIteratorTemplate(cx_);
  12048  if (!templateObj) {
  12049    cx_->recoverFromOutOfMemory();
  12050    return AttachDecision::NoAction;
  12051  }
  12052 
  12053  // Initialize the input operand.
  12054  initializeInputOperand();
  12055 
  12056  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  12057 
  12058  writer.newStringIteratorResult(templateObj);
  12059  writer.returnFromIC();
  12060 
  12061  trackAttached("NewStringIterator");
  12062  return AttachDecision::Attach;
  12063 }
  12064 
  12065 AttachDecision InlinableNativeIRGenerator::tryAttachNewRegExpStringIterator() {
  12066  // Self-hosted code calls this without any arguments
  12067  MOZ_ASSERT(args_.length() == 0);
  12068 
  12069  JSObject* templateObj = NewRegExpStringIteratorTemplate(cx_);
  12070  if (!templateObj) {
  12071    cx_->recoverFromOutOfMemory();
  12072    return AttachDecision::NoAction;
  12073  }
  12074 
  12075  // Initialize the input operand.
  12076  initializeInputOperand();
  12077 
  12078  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  12079 
  12080  writer.newRegExpStringIteratorResult(templateObj);
  12081  writer.returnFromIC();
  12082 
  12083  trackAttached("NewRegExpStringIterator");
  12084  return AttachDecision::Attach;
  12085 }
  12086 
  12087 AttachDecision
  12088 InlinableNativeIRGenerator::tryAttachArrayIteratorPrototypeOptimizable() {
  12089  // Self-hosted code calls this without any arguments
  12090  MOZ_ASSERT(args_.length() == 0);
  12091 
  12092  if (!isFirstStub()) {
  12093    // Attach only once to prevent slowdowns for polymorphic calls.
  12094    return AttachDecision::NoAction;
  12095  }
  12096 
  12097  if (!HasOptimizableArrayIteratorPrototype(cx_)) {
  12098    return AttachDecision::NoAction;
  12099  }
  12100 
  12101  // Initialize the input operand.
  12102  initializeInputOperand();
  12103 
  12104  // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
  12105 
  12106  writer.guardFuse(RealmFuses::FuseIndex::OptimizeArrayIteratorPrototypeFuse);
  12107  writer.loadBooleanResult(true);
  12108  writer.returnFromIC();
  12109 
  12110  trackAttached("ArrayIteratorPrototypeOptimizable");
  12111  return AttachDecision::Attach;
  12112 }
  12113 
  12114 AttachDecision InlinableNativeIRGenerator::tryAttachObjectCreate() {
  12115  // Need a single object-or-null argument.
  12116  if (args_.length() != 1 || !args_[0].isObjectOrNull()) {
  12117    return AttachDecision::NoAction;
  12118  }
  12119 
  12120  if (!isFirstStub()) {
  12121    // Attach only once to prevent slowdowns for polymorphic calls.
  12122    return AttachDecision::NoAction;
  12123  }
  12124 
  12125  RootedObject proto(cx_, args_[0].toObjectOrNull());
  12126  JSObject* templateObj = ObjectCreateImpl(cx_, proto, TenuredObject);
  12127  if (!templateObj) {
  12128    cx_->recoverFromOutOfMemory();
  12129    return AttachDecision::NoAction;
  12130  }
  12131 
  12132  // Initialize the input operand.
  12133  Int32OperandId argcId = initializeInputOperand();
  12134 
  12135  // Guard callee is the 'create' native function.
  12136  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12137 
  12138  // Guard on the proto argument.
  12139  ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  12140  if (proto) {
  12141    ObjOperandId protoId = writer.guardToObject(argId);
  12142    writer.guardSpecificObject(protoId, proto);
  12143  } else {
  12144    writer.guardIsNull(argId);
  12145  }
  12146 
  12147  writer.objectCreateResult(templateObj);
  12148  writer.returnFromIC();
  12149 
  12150  trackAttached("ObjectCreate");
  12151  return AttachDecision::Attach;
  12152 }
  12153 
  12154 AttachDecision InlinableNativeIRGenerator::tryAttachObjectConstructor() {
  12155  // Expecting no arguments or a single object argument.
  12156  // TODO(Warp): Support all or more conversions to object.
  12157  if (args_.length() > 1) {
  12158    return AttachDecision::NoAction;
  12159  }
  12160  if (args_.length() == 1 && !args_[0].isObject()) {
  12161    return AttachDecision::NoAction;
  12162  }
  12163 
  12164  gc::AllocSite* site = nullptr;
  12165  PlainObject* templateObj = nullptr;
  12166  if (args_.length() == 0) {
  12167    // Don't optimize if we can't create an alloc-site.
  12168    if (!BytecodeOpCanHaveAllocSite(op())) {
  12169      return AttachDecision::NoAction;
  12170    }
  12171 
  12172    // Stub doesn't support metadata builder
  12173    if (cx_->realm()->hasAllocationMetadataBuilder()) {
  12174      return AttachDecision::NoAction;
  12175    }
  12176 
  12177    site = generator_.maybeCreateAllocSite();
  12178    if (!site) {
  12179      return AttachDecision::NoAction;
  12180    }
  12181 
  12182    // Create a temporary object to act as the template object.
  12183    templateObj = NewPlainObjectWithAllocKind(cx_, NewObjectGCKind());
  12184    if (!templateObj) {
  12185      cx_->recoverFromOutOfMemory();
  12186      return AttachDecision::NoAction;
  12187    }
  12188  }
  12189 
  12190  // Initialize the input operand.
  12191  Int32OperandId argcId = initializeInputOperand();
  12192 
  12193  // Guard callee and newTarget (if constructing) are this Object constructor
  12194  // function.
  12195  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12196 
  12197  if (args_.length() == 0) {
  12198    uint32_t numFixedSlots = templateObj->numUsedFixedSlots();
  12199    uint32_t numDynamicSlots = templateObj->numDynamicSlots();
  12200    gc::AllocKind allocKind = templateObj->allocKindForTenure();
  12201    Shape* shape = templateObj->shape();
  12202 
  12203    writer.guardNoAllocationMetadataBuilder(
  12204        cx_->realm()->addressOfMetadataBuilder());
  12205    writer.newPlainObjectResult(numFixedSlots, numDynamicSlots, allocKind,
  12206                                shape, site);
  12207  } else {
  12208    // Guard that the argument is an object.
  12209    ValOperandId argId = loadArgument(calleeId, ArgumentKind::Arg0);
  12210    ObjOperandId objId = writer.guardToObject(argId);
  12211 
  12212    // Return the object.
  12213    writer.loadObjectResult(objId);
  12214  }
  12215 
  12216  writer.returnFromIC();
  12217 
  12218  trackAttached("ObjectConstructor");
  12219  return AttachDecision::Attach;
  12220 }
  12221 
  12222 AttachDecision InlinableNativeIRGenerator::tryAttachArrayConstructor() {
  12223  // Don't optimize if we can't create an alloc-site.
  12224  if (!BytecodeOpCanHaveAllocSite(op())) {
  12225    return AttachDecision::NoAction;
  12226  }
  12227 
  12228  // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
  12229  // for now. Note that self-hosted code calls this without |new| via std_Array.
  12230  if (args_.length() > 1) {
  12231    return AttachDecision::NoAction;
  12232  }
  12233  if (args_.length() == 1 && !args_[0].isInt32()) {
  12234    return AttachDecision::NoAction;
  12235  }
  12236 
  12237  int32_t length = (args_.length() == 1) ? args_[0].toInt32() : 0;
  12238  if (length < 0 || uint32_t(length) > ArrayObject::EagerAllocationMaxLength) {
  12239    return AttachDecision::NoAction;
  12240  }
  12241 
  12242  // We allow inlining this function across realms so make sure the template
  12243  // object is allocated in that realm. See CanInlineNativeCrossRealm.
  12244  JSObject* templateObj;
  12245  {
  12246    AutoRealm ar(cx_, target_);
  12247    templateObj = NewDenseFullyAllocatedArray(cx_, length, TenuredObject);
  12248    if (!templateObj) {
  12249      cx_->clearPendingException();
  12250      return AttachDecision::NoAction;
  12251    }
  12252  }
  12253 
  12254  gc::AllocSite* site = generator_.maybeCreateAllocSite();
  12255  if (!site) {
  12256    return AttachDecision::NoAction;
  12257  }
  12258 
  12259  // Initialize the input operand.
  12260  Int32OperandId argcId = initializeInputOperand();
  12261 
  12262  // Guard callee and newTarget (if constructing) are this Array constructor
  12263  // function.
  12264  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12265 
  12266  Int32OperandId lengthId;
  12267  if (args_.length() == 1) {
  12268    ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  12269    lengthId = writer.guardToInt32(arg0Id);
  12270  } else {
  12271    MOZ_ASSERT(args_.length() == 0);
  12272    lengthId = writer.loadInt32Constant(0);
  12273  }
  12274 
  12275  writer.newArrayFromLengthResult(templateObj, lengthId, site);
  12276  writer.returnFromIC();
  12277 
  12278  trackAttached("ArrayConstructor");
  12279  return AttachDecision::Attach;
  12280 }
  12281 
  12282 AttachDecision
  12283 InlinableNativeIRGenerator::tryAttachTypedArrayConstructorFromLength() {
  12284  MOZ_ASSERT(flags_.isConstructing());
  12285  MOZ_ASSERT(args_.length() == 0 || args_[0].isInt32());
  12286 
  12287  // Expected arguments: Optional length (int32)
  12288  if (args_.length() > 1) {
  12289    return AttachDecision::NoAction;
  12290  }
  12291 
  12292  int32_t length = args_.length() > 0 ? args_[0].toInt32() : 0;
  12293 
  12294  Scalar::Type type = TypedArrayConstructorType(target_);
  12295  Rooted<TypedArrayObject*> templateObj(cx_);
  12296  if (!TypedArrayObject::GetTemplateObjectForLength(cx_, type, length,
  12297                                                    &templateObj)) {
  12298    cx_->recoverFromOutOfMemory();
  12299    return AttachDecision::NoAction;
  12300  }
  12301 
  12302  // This can happen for large length values.
  12303  if (!templateObj) {
  12304    return AttachDecision::NoAction;
  12305  }
  12306 
  12307  // Initialize the input operand.
  12308  Int32OperandId argcId = initializeInputOperand();
  12309 
  12310  // Guard callee and newTarget are this TypedArray constructor function.
  12311  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12312 
  12313  Int32OperandId lengthId;
  12314  if (args_.length() > 0) {
  12315    ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  12316    lengthId = writer.guardToInt32(arg0Id);
  12317  } else {
  12318    lengthId = writer.loadInt32Constant(0);
  12319  }
  12320  writer.newTypedArrayFromLengthResult(templateObj, lengthId);
  12321  writer.returnFromIC();
  12322 
  12323  trackAttached("TypedArrayConstructorFromLength");
  12324  return AttachDecision::Attach;
  12325 }
  12326 
  12327 AttachDecision
  12328 InlinableNativeIRGenerator::tryAttachTypedArrayConstructorFromArrayBuffer() {
  12329  MOZ_ASSERT(flags_.isConstructing());
  12330  MOZ_ASSERT(args_.length() > 0);
  12331  MOZ_ASSERT(args_[0].isObject());
  12332 
  12333  // Expected arguments: array buffer, optional byteOffset, optional length
  12334  if (args_.length() > 3) {
  12335    return AttachDecision::NoAction;
  12336  }
  12337 
  12338 #ifdef JS_CODEGEN_X86
  12339  // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
  12340  // we can easily support on 32-bit x86 for now.
  12341  return AttachDecision::NoAction;
  12342 #else
  12343  Scalar::Type type = TypedArrayConstructorType(target_);
  12344 
  12345  Rooted<ArrayBufferObjectMaybeShared*> obj(
  12346      cx_, &args_[0].toObject().as<ArrayBufferObjectMaybeShared>());
  12347 
  12348  Rooted<TypedArrayObject*> templateObj(
  12349      cx_, TypedArrayObject::GetTemplateObjectForBuffer(cx_, type, obj));
  12350  if (!templateObj) {
  12351    cx_->recoverFromOutOfMemory();
  12352    return AttachDecision::NoAction;
  12353  }
  12354 
  12355  // Initialize the input operand.
  12356  Int32OperandId argcId = initializeInputOperand();
  12357 
  12358  // Guard callee and newTarget are this TypedArray constructor function.
  12359  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12360 
  12361  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  12362  ObjOperandId objId = writer.guardToObject(arg0Id);
  12363 
  12364  if (obj->is<FixedLengthArrayBufferObject>()) {
  12365    writer.guardClass(objId, GuardClassKind::FixedLengthArrayBuffer);
  12366  } else if (obj->is<FixedLengthSharedArrayBufferObject>()) {
  12367    writer.guardClass(objId, GuardClassKind::FixedLengthSharedArrayBuffer);
  12368  } else if (obj->is<ResizableArrayBufferObject>()) {
  12369    writer.guardClass(objId, GuardClassKind::ResizableArrayBuffer);
  12370  } else if (obj->is<GrowableSharedArrayBufferObject>()) {
  12371    writer.guardClass(objId, GuardClassKind::GrowableSharedArrayBuffer);
  12372  } else {
  12373    MOZ_ASSERT(obj->is<ImmutableArrayBufferObject>());
  12374    writer.guardClass(objId, GuardClassKind::ImmutableArrayBuffer);
  12375  }
  12376 
  12377  ValOperandId byteOffsetId;
  12378  if (args_.length() > 1) {
  12379    byteOffsetId = loadArgument(calleeId, ArgumentKind::Arg1);
  12380  } else {
  12381    byteOffsetId = writer.loadUndefined();
  12382  }
  12383 
  12384  ValOperandId lengthId;
  12385  if (args_.length() > 2) {
  12386    lengthId = loadArgument(calleeId, ArgumentKind::Arg2);
  12387  } else {
  12388    lengthId = writer.loadUndefined();
  12389  }
  12390 
  12391  writer.newTypedArrayFromArrayBufferResult(templateObj, objId, byteOffsetId,
  12392                                            lengthId);
  12393  writer.returnFromIC();
  12394 
  12395  trackAttached("TypedArrayConstructorFromArrayBuffer");
  12396  return AttachDecision::Attach;
  12397 #endif
  12398 }
  12399 
  12400 AttachDecision
  12401 InlinableNativeIRGenerator::tryAttachTypedArrayConstructorFromArray() {
  12402  MOZ_ASSERT(flags_.isConstructing());
  12403  MOZ_ASSERT(args_.length() > 0);
  12404  MOZ_ASSERT(args_[0].isObject());
  12405 
  12406  // Expected arguments: Array-like object.
  12407  if (args_.length() != 1) {
  12408    return AttachDecision::NoAction;
  12409  }
  12410 
  12411  Rooted<JSObject*> obj(cx_, &args_[0].toObject());
  12412  MOZ_ASSERT(!obj->is<ProxyObject>());
  12413  MOZ_ASSERT(!obj->is<ArrayBufferObjectMaybeShared>());
  12414 
  12415  Scalar::Type type = TypedArrayConstructorType(target_);
  12416 
  12417  Rooted<TypedArrayObject*> templateObj(
  12418      cx_, TypedArrayObject::GetTemplateObjectForArrayLike(cx_, type, obj));
  12419  if (!templateObj) {
  12420    cx_->recoverFromOutOfMemory();
  12421    return AttachDecision::NoAction;
  12422  }
  12423 
  12424  // Initialize the input operand.
  12425  Int32OperandId argcId = initializeInputOperand();
  12426 
  12427  // Guard callee and newTarget are this TypedArray constructor function.
  12428  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12429 
  12430  ValOperandId arg0Id = loadArgument(calleeId, ArgumentKind::Arg0);
  12431  ObjOperandId objId = writer.guardToObject(arg0Id);
  12432 
  12433  writer.guardIsNotArrayBufferMaybeShared(objId);
  12434  writer.guardIsNotProxy(objId);
  12435  writer.newTypedArrayFromArrayResult(templateObj, objId);
  12436  writer.returnFromIC();
  12437 
  12438  trackAttached("TypedArrayConstructorFromArray");
  12439  return AttachDecision::Attach;
  12440 }
  12441 
  12442 AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() {
  12443  MOZ_ASSERT(flags_.isConstructing());
  12444 
  12445  if (!isFirstStub()) {
  12446    // Attach only once to prevent slowdowns for polymorphic calls.
  12447    return AttachDecision::NoAction;
  12448  }
  12449 
  12450  // The first argument, if present, must be int32 or a non-proxy object.
  12451 
  12452  if (args_.length() == 0 || args_[0].isInt32()) {
  12453    return tryAttachTypedArrayConstructorFromLength();
  12454  }
  12455 
  12456  if (args_[0].isObject()) {
  12457    auto* obj = &args_[0].toObject();
  12458 
  12459    // Proxy objects not allowed, because handling Wrappers is complicated.
  12460    if (obj->is<ProxyObject>()) {
  12461      return AttachDecision::NoAction;
  12462    }
  12463 
  12464    if (obj->is<ArrayBufferObjectMaybeShared>()) {
  12465      return tryAttachTypedArrayConstructorFromArrayBuffer();
  12466    }
  12467    return tryAttachTypedArrayConstructorFromArray();
  12468  }
  12469 
  12470  // Other argument types are not supported.
  12471  return AttachDecision::NoAction;
  12472 }
  12473 
  12474 AttachDecision InlinableNativeIRGenerator::tryAttachMapSetConstructor(
  12475    InlinableNative native) {
  12476  MOZ_ASSERT(native == InlinableNative::MapConstructor ||
  12477             native == InlinableNative::SetConstructor);
  12478  MOZ_ASSERT(flags_.isConstructing());
  12479 
  12480  // Must have either no arguments or a single (iterable) argument.
  12481  if (args_.length() > 1) {
  12482    return AttachDecision::NoAction;
  12483  }
  12484 
  12485  if (!isFirstStub()) {
  12486    // Attach only once to prevent slowdowns for polymorphic calls.
  12487    return AttachDecision::NoAction;
  12488  }
  12489 
  12490  JSObject* templateObj;
  12491  if (native == InlinableNative::MapConstructor) {
  12492    templateObj = GlobalObject::getOrCreateMapTemplateObject(cx_);
  12493  } else {
  12494    templateObj = GlobalObject::getOrCreateSetTemplateObject(cx_);
  12495  }
  12496  if (!templateObj) {
  12497    cx_->recoverFromOutOfMemory();
  12498    return AttachDecision::NoAction;
  12499  }
  12500 
  12501  // Initialize the input operand.
  12502  Int32OperandId argcId = initializeInputOperand();
  12503 
  12504  // Guard callee and newTarget are this Map/Set constructor function.
  12505  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12506 
  12507  if (args_.length() == 1) {
  12508    ValOperandId iterableId = loadArgument(calleeId, ArgumentKind::Arg0);
  12509    if (native == InlinableNative::MapConstructor) {
  12510      writer.newMapObjectFromIterableResult(templateObj, iterableId);
  12511    } else {
  12512      writer.newSetObjectFromIterableResult(templateObj, iterableId);
  12513    }
  12514  } else {
  12515    if (native == InlinableNative::MapConstructor) {
  12516      writer.newMapObjectResult(templateObj);
  12517    } else {
  12518      writer.newSetObjectResult(templateObj);
  12519    }
  12520  }
  12521  writer.returnFromIC();
  12522 
  12523  if (native == InlinableNative::MapConstructor) {
  12524    trackAttached("MapConstructor");
  12525  } else {
  12526    trackAttached("SetConstructor");
  12527  }
  12528  return AttachDecision::Attach;
  12529 }
  12530 
  12531 AttachDecision InlinableNativeIRGenerator::tryAttachSpecializedFunctionBind(
  12532    Handle<JSObject*> target, Handle<BoundFunctionObject*> templateObj) {
  12533  // Try to attach a faster stub that's more specialized than what we emit in
  12534  // tryAttachFunctionBind. This lets us allocate and initialize a bound
  12535  // function object in Ion without calling into C++.
  12536  //
  12537  // We can do this if:
  12538  //
  12539  // * The target's prototype is Function.prototype, because that's the proto we
  12540  //   use for the template object.
  12541  // * All bound arguments can be stored inline.
  12542  // * The `.name`, `.length`, and `IsConstructor` values match `target`.
  12543  //
  12544  // We initialize the template object with the bound function's name, length,
  12545  // and flags. At runtime we then only have to clone the template object and
  12546  // initialize the slots for the target, the bound `this` and the bound
  12547  // arguments.
  12548 
  12549  if (!isFirstStub()) {
  12550    return AttachDecision::NoAction;
  12551  }
  12552  if (!target->is<JSFunction>() && !target->is<BoundFunctionObject>()) {
  12553    return AttachDecision::NoAction;
  12554  }
  12555  if (target->staticPrototype() != &cx_->global()->getFunctionPrototype()) {
  12556    return AttachDecision::NoAction;
  12557  }
  12558  size_t numBoundArgs = args_.length() > 0 ? args_.length() - 1 : 0;
  12559  if (numBoundArgs > BoundFunctionObject::MaxInlineBoundArgs) {
  12560    return AttachDecision::NoAction;
  12561  }
  12562 
  12563  const bool targetIsConstructor = target->isConstructor();
  12564  Rooted<JSAtom*> targetName(cx_);
  12565  uint32_t targetLength = 0;
  12566 
  12567  if (target->is<JSFunction>()) {
  12568    Rooted<JSFunction*> fun(cx_, &target->as<JSFunction>());
  12569    if (fun->isNativeFun()) {
  12570      return AttachDecision::NoAction;
  12571    }
  12572    if (fun->hasResolvedLength() || fun->hasResolvedName()) {
  12573      return AttachDecision::NoAction;
  12574    }
  12575    uint16_t len;
  12576    if (!JSFunction::getUnresolvedLength(cx_, fun, &len)) {
  12577      cx_->clearPendingException();
  12578      return AttachDecision::NoAction;
  12579    }
  12580    targetName = fun->getUnresolvedName(cx_);
  12581    if (!targetName) {
  12582      cx_->clearPendingException();
  12583      return AttachDecision::NoAction;
  12584    }
  12585 
  12586    targetLength = len;
  12587  } else {
  12588    BoundFunctionObject* bound = &target->as<BoundFunctionObject>();
  12589    if (!targetIsConstructor) {
  12590      // Only support constructors for now. This lets us use
  12591      // GuardBoundFunctionIsConstructor.
  12592      return AttachDecision::NoAction;
  12593    }
  12594    Shape* initialShape =
  12595        cx_->global()->maybeBoundFunctionShapeWithDefaultProto();
  12596    if (bound->shape() != initialShape) {
  12597      return AttachDecision::NoAction;
  12598    }
  12599    Value lenVal = bound->getLengthForInitialShape();
  12600    Value nameVal = bound->getNameForInitialShape();
  12601    if (!lenVal.isInt32() || lenVal.toInt32() < 0 || !nameVal.isString() ||
  12602        !nameVal.toString()->isAtom()) {
  12603      return AttachDecision::NoAction;
  12604    }
  12605    targetName = &nameVal.toString()->asAtom();
  12606    targetLength = uint32_t(lenVal.toInt32());
  12607  }
  12608 
  12609  if (!templateObj->initTemplateSlotsForSpecializedBind(
  12610          cx_, numBoundArgs, targetIsConstructor, targetLength, targetName)) {
  12611    cx_->recoverFromOutOfMemory();
  12612    return AttachDecision::NoAction;
  12613  }
  12614 
  12615  Int32OperandId argcId = initializeInputOperand();
  12616  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12617 
  12618  ValOperandId thisValId = loadThis(calleeId);
  12619  ObjOperandId targetId = writer.guardToObject(thisValId);
  12620 
  12621  // Ensure the JSClass and proto match, and that the `length` and `name`
  12622  // properties haven't been redefined.
  12623  writer.guardShape(targetId, target->shape());
  12624 
  12625  // Emit guards for the `IsConstructor`, `.length`, and `.name` values.
  12626  if (target->is<JSFunction>()) {
  12627    // Guard on:
  12628    // * The BaseScript (because that's what JSFunction uses for the `length`).
  12629    //   Because MGuardFunctionScript doesn't support self-hosted functions yet,
  12630    //   we use GuardSpecificFunction instead in this case.
  12631    //   See assertion in MGuardFunctionScript::getAliasSet.
  12632    // * The flags slot (for the CONSTRUCTOR, RESOLVED_NAME, RESOLVED_LENGTH,
  12633    //   HAS_INFERRED_NAME, and HAS_GUESSED_ATOM flags).
  12634    // * The atom slot.
  12635    JSFunction* fun = &target->as<JSFunction>();
  12636    if (fun->isSelfHostedBuiltin()) {
  12637      writer.guardSpecificFunction(targetId, fun);
  12638    } else {
  12639      writer.guardFunctionScript(targetId, fun->baseScript());
  12640    }
  12641    writer.guardFixedSlotValue(
  12642        targetId, JSFunction::offsetOfFlagsAndArgCount(),
  12643        fun->getReservedSlot(JSFunction::FlagsAndArgCountSlot));
  12644    writer.guardFixedSlotValue(targetId, JSFunction::offsetOfAtom(),
  12645                               fun->getReservedSlot(JSFunction::AtomSlot));
  12646  } else {
  12647    BoundFunctionObject* bound = &target->as<BoundFunctionObject>();
  12648    writer.guardBoundFunctionIsConstructor(targetId);
  12649    writer.guardFixedSlotValue(targetId,
  12650                               BoundFunctionObject::offsetOfLengthSlot(),
  12651                               bound->getLengthForInitialShape());
  12652    writer.guardFixedSlotValue(targetId,
  12653                               BoundFunctionObject::offsetOfNameSlot(),
  12654                               bound->getNameForInitialShape());
  12655  }
  12656 
  12657  writer.specializedBindFunctionResult(targetId, args_.length(), templateObj);
  12658  writer.returnFromIC();
  12659 
  12660  trackAttached("SpecializedFunctionBind");
  12661  return AttachDecision::Attach;
  12662 }
  12663 
  12664 AttachDecision InlinableNativeIRGenerator::tryAttachFunctionBind() {
  12665  // Ensure |this| (the target) is a function object or a bound function object.
  12666  // We could support other callables too, but note that we rely on the target
  12667  // having a static prototype in BoundFunctionObject::functionBindImpl.
  12668  if (!thisval_.isObject()) {
  12669    return AttachDecision::NoAction;
  12670  }
  12671  Rooted<JSObject*> target(cx_, &thisval_.toObject());
  12672  if (!target->is<JSFunction>() && !target->is<BoundFunctionObject>()) {
  12673    return AttachDecision::NoAction;
  12674  }
  12675 
  12676  // Only support standard, non-spread calls.
  12677  if (flags_.getArgFormat() != CallFlags::Standard) {
  12678    return AttachDecision::NoAction;
  12679  }
  12680 
  12681  // Only support when no additional bound arguments are present.
  12682  if (hasBoundArguments()) {
  12683    return AttachDecision::NoAction;
  12684  }
  12685  MOZ_ASSERT(stackArgc() == args_.length(), "argc matches number of arguments");
  12686 
  12687  // Only optimize if the number of arguments is small. This ensures we don't
  12688  // compile a lot of different stubs (because we bake in argc) and that we
  12689  // don't get anywhere near ARGS_LENGTH_MAX.
  12690  static constexpr size_t MaxArguments = 6;
  12691  if (args_.length() > MaxArguments) {
  12692    return AttachDecision::NoAction;
  12693  }
  12694 
  12695  Rooted<BoundFunctionObject*> templateObj(
  12696      cx_, BoundFunctionObject::createTemplateObject(cx_));
  12697  if (!templateObj) {
  12698    cx_->recoverFromOutOfMemory();
  12699    return AttachDecision::NoAction;
  12700  }
  12701 
  12702  TRY_ATTACH(tryAttachSpecializedFunctionBind(target, templateObj));
  12703 
  12704  Int32OperandId argcId = initializeInputOperand();
  12705 
  12706  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  12707 
  12708  // Guard |this| is a function object or a bound function object.
  12709  ValOperandId thisValId = loadThis(calleeId);
  12710  ObjOperandId targetId = writer.guardToObject(thisValId);
  12711  if (target->is<JSFunction>()) {
  12712    writer.guardClass(targetId, GuardClassKind::JSFunction);
  12713  } else {
  12714    MOZ_ASSERT(target->is<BoundFunctionObject>());
  12715    writer.guardClass(targetId, GuardClassKind::BoundFunction);
  12716  }
  12717 
  12718  writer.bindFunctionResult(targetId, args_.length(), templateObj);
  12719  writer.returnFromIC();
  12720 
  12721  trackAttached("FunctionBind");
  12722  return AttachDecision::Attach;
  12723 }
  12724 
  12725 AttachDecision CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc) {
  12726  MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
  12727 
  12728  if (calleeFunc->native() != fun_apply) {
  12729    return AttachDecision::NoAction;
  12730  }
  12731 
  12732  if (argc_ > 2) {
  12733    return AttachDecision::NoAction;
  12734  }
  12735 
  12736  if (!thisval_.isObject() || !thisval_.toObject().is<JSFunction>()) {
  12737    return AttachDecision::NoAction;
  12738  }
  12739  Rooted<JSFunction*> target(cx_, &thisval_.toObject().as<JSFunction>());
  12740 
  12741  bool isScripted = target->hasJitEntry();
  12742  MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
  12743 
  12744  if (target->isClassConstructor()) {
  12745    return AttachDecision::NoAction;
  12746  }
  12747 
  12748  CallFlags::ArgFormat format = CallFlags::Standard;
  12749  if (argc_ < 2) {
  12750    // |fun.apply()| and |fun.apply(thisValue)| are equivalent to |fun.call()|
  12751    // resp. |fun.call(thisValue)|.
  12752    format = CallFlags::FunCall;
  12753  } else if (args_[1].isNullOrUndefined()) {
  12754    // |fun.apply(thisValue, null)| and |fun.apply(thisValue, undefined)| are
  12755    // also equivalent to |fun.call(thisValue)|, but we can't use FunCall
  12756    // because we have to discard the second argument.
  12757    format = CallFlags::FunApplyNullUndefined;
  12758  } else if (args_[1].isObject() && args_[1].toObject().is<ArgumentsObject>()) {
  12759    auto* argsObj = &args_[1].toObject().as<ArgumentsObject>();
  12760    if (argsObj->hasOverriddenElement() || argsObj->anyArgIsForwarded() ||
  12761        argsObj->hasOverriddenLength() ||
  12762        argsObj->initialLength() > JIT_ARGS_LENGTH_MAX) {
  12763      return AttachDecision::NoAction;
  12764    }
  12765    format = CallFlags::FunApplyArgsObj;
  12766  } else if (args_[1].isObject() && args_[1].toObject().is<ArrayObject>() &&
  12767             IsPackedArray(&args_[1].toObject())) {
  12768    format = CallFlags::FunApplyArray;
  12769  } else {
  12770    return AttachDecision::NoAction;
  12771  }
  12772 
  12773  CallFlags targetFlags(format);
  12774  if (mode_ == ICState::Mode::Specialized) {
  12775    if (cx_->realm() == target->realm()) {
  12776      targetFlags.setIsSameRealm();
  12777    }
  12778  }
  12779 
  12780  if (mode_ == ICState::Mode::Specialized && !isScripted &&
  12781      format == CallFlags::FunApplyArray) {
  12782    HandleValue newTarget = NullHandleValue;
  12783    HandleValue thisValue = args_[0];
  12784    Rooted<ArrayObject*> aobj(cx_, &args_[1].toObject().as<ArrayObject>());
  12785    HandleValueArray args = HandleValueArray::fromMarkedLocation(
  12786        aobj->length(), aobj->getDenseElements());
  12787 
  12788    // Check for specific native-function optimizations.
  12789    InlinableNativeIRGenerator nativeGen(*this, calleeFunc, target, newTarget,
  12790                                         thisValue, args, targetFlags);
  12791    TRY_ATTACH(nativeGen.tryAttachStub());
  12792  }
  12793  if (format == CallFlags::FunApplyArray &&
  12794      args_[1].toObject().as<ArrayObject>().length() > JIT_ARGS_LENGTH_MAX) {
  12795    // We check this after trying to attach inlinable natives, because some
  12796    // inlinable natives can safely ignore the limit.
  12797    return AttachDecision::NoAction;
  12798  }
  12799 
  12800  if (mode_ == ICState::Mode::Specialized && !isScripted &&
  12801      (format == CallFlags::FunCall ||
  12802       format == CallFlags::FunApplyNullUndefined)) {
  12803    HandleValue newTarget = NullHandleValue;
  12804    HandleValue thisValue = argc_ > 0 ? args_[0] : UndefinedHandleValue;
  12805    HandleValueArray args = HandleValueArray::empty();
  12806 
  12807    // Check for specific native-function optimizations.
  12808    InlinableNativeIRGenerator nativeGen(*this, calleeFunc, target, newTarget,
  12809                                         thisValue, args, targetFlags);
  12810    TRY_ATTACH(nativeGen.tryAttachStub());
  12811  }
  12812 
  12813  Int32OperandId argcId(writer.setInputOperandId(0));
  12814  ObjOperandId thisObjId = emitFunApplyGuard(argcId);
  12815 
  12816  uint32_t fixedArgc;
  12817  if (format == CallFlags::FunApplyArray ||
  12818      format == CallFlags::FunApplyArgsObj ||
  12819      format == CallFlags::FunApplyNullUndefined) {
  12820    emitFunApplyArgsGuard(format);
  12821 
  12822    // We always use MaxUnrolledArgCopy here because the fixed argc is
  12823    // meaningless in a FunApply case.
  12824    fixedArgc = MaxUnrolledArgCopy;
  12825  } else {
  12826    MOZ_ASSERT(format == CallFlags::FunCall);
  12827 
  12828    // Whereas for the FunCall case we need to use the actual fixed argc value.
  12829    fixedArgc = ClampFixedArgc(argc_);
  12830  }
  12831 
  12832  if (mode_ == ICState::Mode::Specialized) {
  12833    // Ensure that |this| is the expected target function.
  12834    emitCalleeGuard(thisObjId, target);
  12835 
  12836    if (isScripted) {
  12837      writer.callScriptedFunction(thisObjId, argcId, targetFlags, fixedArgc);
  12838    } else {
  12839      writer.callNativeFunction(thisObjId, argcId, jsop(), target, targetFlags,
  12840                                fixedArgc);
  12841    }
  12842  } else {
  12843    // Guard that |this| is a function.
  12844    writer.guardClass(thisObjId, GuardClassKind::JSFunction);
  12845 
  12846    // Guard that function is not a class constructor.
  12847    writer.guardNotClassConstructor(thisObjId);
  12848 
  12849    if (isScripted) {
  12850      // Guard that function is scripted.
  12851      writer.guardFunctionHasJitEntry(thisObjId);
  12852      writer.callScriptedFunction(thisObjId, argcId, targetFlags, fixedArgc);
  12853    } else {
  12854      // Guard that function is native.
  12855      writer.guardFunctionHasNoJitEntry(thisObjId);
  12856      writer.callAnyNativeFunction(thisObjId, argcId, targetFlags, fixedArgc);
  12857    }
  12858  }
  12859 
  12860  writer.returnFromIC();
  12861 
  12862  if (isScripted) {
  12863    trackAttached("Call.ScriptedFunApply");
  12864  } else {
  12865    trackAttached("Call.NativeFunApply");
  12866  }
  12867 
  12868  return AttachDecision::Attach;
  12869 }
  12870 
  12871 AttachDecision CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc) {
  12872  // Try to optimize calls into Wasm code by emitting the CallWasmFunction
  12873  // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
  12874  // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
  12875  // into Wasm code.
  12876  //
  12877  // Note: some code refers to these optimized Wasm calls as "inlined" calls.
  12878 
  12879  MOZ_ASSERT(calleeFunc->isWasmWithJitEntry());
  12880 
  12881  if (!JitOptions.enableWasmIonFastCalls) {
  12882    return AttachDecision::NoAction;
  12883  }
  12884  if (!isFirstStub_) {
  12885    return AttachDecision::NoAction;
  12886  }
  12887  JSOp op = JSOp(*pc_);
  12888  if (op != JSOp::Call && op != JSOp::CallContent &&
  12889      op != JSOp::CallIgnoresRv) {
  12890    return AttachDecision::NoAction;
  12891  }
  12892  if (cx_->realm() != calleeFunc->realm()) {
  12893    return AttachDecision::NoAction;
  12894  }
  12895 
  12896  wasm::Instance& inst = calleeFunc->wasmInstance();
  12897  uint32_t funcIndex = calleeFunc->wasmFuncIndex();
  12898  const wasm::CodeBlock& codeBlock = inst.code().funcCodeBlock(funcIndex);
  12899  const wasm::FuncExport& funcExport = codeBlock.lookupFuncExport(funcIndex);
  12900  const wasm::FuncType& sig = calleeFunc->wasmTypeDef()->funcType();
  12901 
  12902  MOZ_ASSERT(!IsInsideNursery(inst.object()));
  12903  MOZ_ASSERT(sig.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
  12904 
  12905  // If there are too many arguments, don't optimize (we won't be able to store
  12906  // the arguments in the LIR node).
  12907  static_assert(wasm::MaxArgsForJitInlineCall <= ArgumentKindArgIndexLimit);
  12908  if (sig.args().length() > wasm::MaxArgsForJitInlineCall ||
  12909      argc_ > ArgumentKindArgIndexLimit) {
  12910    return AttachDecision::NoAction;
  12911  }
  12912 
  12913  // If there are too many results, don't optimize as Warp currently doesn't
  12914  // have code to handle this.
  12915  if (sig.results().length() > wasm::MaxResultsForJitInlineCall) {
  12916    return AttachDecision::NoAction;
  12917  }
  12918 
  12919  // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
  12920  // because it is more difficult (because it requires multiple LIR arguments
  12921  // per I64).
  12922  //
  12923  // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
  12924  // spilled to the stack because it causes problems with register allocation.
  12925 #ifdef JS_64BIT
  12926  constexpr bool optimizeWithI64 = true;
  12927 #else
  12928  constexpr bool optimizeWithI64 = false;
  12929 #endif
  12930  ABIArgGenerator abi(ABIKind::Wasm);
  12931  for (const auto& valType : sig.args()) {
  12932    MIRType mirType = valType.toMIRType();
  12933    ABIArg abiArg = abi.next(mirType);
  12934    if (mirType != MIRType::Int64) {
  12935      continue;
  12936    }
  12937    if (!optimizeWithI64 || abiArg.kind() == ABIArg::Stack) {
  12938      return AttachDecision::NoAction;
  12939    }
  12940  }
  12941 
  12942  // Check that all arguments can be converted to the Wasm type in Warp code
  12943  // without bailing out.
  12944  for (size_t i = 0; i < sig.args().length(); i++) {
  12945    Value argVal = i < argc_ ? args_[i] : UndefinedValue();
  12946    switch (sig.args()[i].kind()) {
  12947      case wasm::ValType::I32:
  12948      case wasm::ValType::F32:
  12949      case wasm::ValType::F64:
  12950        if (!argVal.isNumber() && !argVal.isBoolean() &&
  12951            !argVal.isUndefined()) {
  12952          return AttachDecision::NoAction;
  12953        }
  12954        break;
  12955      case wasm::ValType::I64:
  12956        if (!argVal.isBigInt() && !argVal.isBoolean() && !argVal.isString()) {
  12957          return AttachDecision::NoAction;
  12958        }
  12959        break;
  12960      case wasm::ValType::V128:
  12961        MOZ_CRASH("Function should not have a Wasm JitEntry");
  12962      case wasm::ValType::Ref:
  12963        // canHaveJitEntry restricts args to externref, where all JS values are
  12964        // valid and can be boxed.
  12965        MOZ_ASSERT(sig.args()[i].refType().isExtern(),
  12966                   "Unexpected type for Wasm JitEntry");
  12967        break;
  12968    }
  12969  }
  12970 
  12971  CallFlags flags(/* isConstructing = */ false, /* isSpread = */ false,
  12972                  /* isSameRealm = */ true);
  12973 
  12974  // Load argc.
  12975  Int32OperandId argcId(writer.setInputOperandId(0));
  12976 
  12977  // Load the callee and ensure it is an object
  12978  ValOperandId calleeValId =
  12979      writer.loadArgumentFixedSlot(ArgumentKind::Callee, argc_, flags);
  12980  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
  12981 
  12982  // Ensure the callee is this Wasm function.
  12983  emitCalleeGuard(calleeObjId, calleeFunc);
  12984 
  12985  // Guard the argument types.
  12986  uint32_t guardedArgs = std::min<uint32_t>(sig.args().length(), argc_);
  12987  for (uint32_t i = 0; i < guardedArgs; i++) {
  12988    ArgumentKind argKind = ArgumentKindForArgIndex(i);
  12989    ValOperandId argId = writer.loadArgumentFixedSlot(argKind, argc_, flags);
  12990    writer.guardWasmArg(argId, sig.args()[i].kind());
  12991  }
  12992 
  12993  writer.callWasmFunction(calleeObjId, argcId, flags, ClampFixedArgc(argc_),
  12994                          &funcExport, inst.object());
  12995  writer.returnFromIC();
  12996 
  12997  trackAttached("Call.WasmCall");
  12998 
  12999  return AttachDecision::Attach;
  13000 }
  13001 
  13002 AttachDecision CallIRGenerator::tryAttachInlinableNative(HandleFunction callee,
  13003                                                         CallFlags flags) {
  13004  MOZ_ASSERT(mode_ == ICState::Mode::Specialized);
  13005  MOZ_ASSERT(callee->isNativeWithoutJitEntry());
  13006  MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard ||
  13007             flags.getArgFormat() == CallFlags::Spread);
  13008 
  13009  InlinableNativeIRGenerator nativeGen(*this, callee, callee, newTarget_,
  13010                                       thisval_, args_, flags);
  13011  return nativeGen.tryAttachStub();
  13012 }
  13013 
  13014 #ifdef FUZZING_JS_FUZZILLI
  13015 AttachDecision InlinableNativeIRGenerator::tryAttachFuzzilliHash() {
  13016  if (args_.length() != 1) {
  13017    return AttachDecision::NoAction;
  13018  }
  13019 
  13020  // Initialize the input operand.
  13021  Int32OperandId argcId = initializeInputOperand();
  13022 
  13023  // Guard callee is the 'fuzzilli_hash' native function.
  13024  ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
  13025 
  13026  ValOperandId argValId = loadArgument(calleeId, ArgumentKind::Arg0);
  13027 
  13028  writer.fuzzilliHashResult(argValId);
  13029  writer.returnFromIC();
  13030 
  13031  trackAttached("FuzzilliHash");
  13032  return AttachDecision::Attach;
  13033 }
  13034 #endif
  13035 
  13036 AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
  13037  MOZ_ASSERT(generator_.mode_ == ICState::Mode::Specialized);
  13038  MOZ_ASSERT(target_->isNativeWithoutJitEntry());
  13039 
  13040  // Special case functions are only optimized for normal calls.
  13041  if (!BytecodeCallOpCanHaveInlinableNative(op()) &&
  13042      !BytecodeGetOpCanHaveInlinableNative(op())) {
  13043    return AttachDecision::NoAction;
  13044  }
  13045 
  13046  if (!target_->hasJitInfo() ||
  13047      target_->jitInfo()->type() != JSJitInfo::InlinableNative) {
  13048    return AttachDecision::NoAction;
  13049  }
  13050 
  13051  InlinableNative native = target_->jitInfo()->inlinableNative;
  13052 
  13053  // Not all natives can be inlined cross-realm.
  13054  if (cx_->realm() != target_->realm() && !CanInlineNativeCrossRealm(native)) {
  13055    return AttachDecision::NoAction;
  13056  }
  13057 
  13058  // Check for special-cased native constructors.
  13059  if (flags_.isConstructing()) {
  13060    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard);
  13061 
  13062    // newTarget must match the callee. CacheIR for this is emitted in
  13063    // emitNativeCalleeGuard.
  13064    if (ObjectValue(*callee()) != newTarget_) {
  13065      return AttachDecision::NoAction;
  13066    }
  13067    switch (native) {
  13068      case InlinableNative::Array:
  13069        return tryAttachArrayConstructor();
  13070      case InlinableNative::TypedArrayConstructor:
  13071        return tryAttachTypedArrayConstructor();
  13072      case InlinableNative::MapConstructor:
  13073      case InlinableNative::SetConstructor:
  13074        return tryAttachMapSetConstructor(native);
  13075      case InlinableNative::String:
  13076        return tryAttachStringConstructor();
  13077      case InlinableNative::Object:
  13078        return tryAttachObjectConstructor();
  13079      default:
  13080        break;
  13081    }
  13082    return AttachDecision::NoAction;
  13083  }
  13084 
  13085  // Check for special-cased native spread calls.
  13086  if (flags_.getArgFormat() == CallFlags::Spread ||
  13087      flags_.getArgFormat() == CallFlags::FunApplyArray) {
  13088    // Can't inline spread calls when bound arguments are present.
  13089    MOZ_ASSERT(!hasBoundArguments());
  13090 
  13091    // Note: we haven't compared args.length() to JIT_ARGS_LENGTH_MAX
  13092    // yet, to support natives that can handle more arguments without
  13093    // having to push them on the stack. Spread-call inlined natives
  13094    // are responsible for enforcing the limit on themselves if
  13095    // necessary.
  13096    switch (native) {
  13097      case InlinableNative::MathMin:
  13098        return tryAttachSpreadMathMinMax(/*isMax = */ false);
  13099      case InlinableNative::MathMax:
  13100        return tryAttachSpreadMathMinMax(/*isMax = */ true);
  13101      default:
  13102        break;
  13103    }
  13104    return AttachDecision::NoAction;
  13105  }
  13106 
  13107  MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard ||
  13108             flags_.getArgFormat() == CallFlags::FunCall ||
  13109             flags_.getArgFormat() == CallFlags::FunApplyNullUndefined);
  13110 
  13111  // Check for special-cased native functions.
  13112  switch (native) {
  13113    // Array natives.
  13114    case InlinableNative::Array:
  13115      return tryAttachArrayConstructor();
  13116    case InlinableNative::ArrayPush:
  13117      return tryAttachArrayPush();
  13118    case InlinableNative::ArrayPop:
  13119    case InlinableNative::ArrayShift:
  13120      return tryAttachArrayPopShift(native);
  13121    case InlinableNative::ArrayJoin:
  13122      return tryAttachArrayJoin();
  13123    case InlinableNative::ArraySlice:
  13124      return tryAttachArraySlice();
  13125    case InlinableNative::ArrayIsArray:
  13126      return tryAttachArrayIsArray();
  13127 
  13128    // DataView natives.
  13129    case InlinableNative::DataViewGetInt8:
  13130      return tryAttachDataViewGet(Scalar::Int8);
  13131    case InlinableNative::DataViewGetUint8:
  13132      return tryAttachDataViewGet(Scalar::Uint8);
  13133    case InlinableNative::DataViewGetInt16:
  13134      return tryAttachDataViewGet(Scalar::Int16);
  13135    case InlinableNative::DataViewGetUint16:
  13136      return tryAttachDataViewGet(Scalar::Uint16);
  13137    case InlinableNative::DataViewGetInt32:
  13138      return tryAttachDataViewGet(Scalar::Int32);
  13139    case InlinableNative::DataViewGetUint32:
  13140      return tryAttachDataViewGet(Scalar::Uint32);
  13141    case InlinableNative::DataViewGetFloat16:
  13142      return tryAttachDataViewGet(Scalar::Float16);
  13143    case InlinableNative::DataViewGetFloat32:
  13144      return tryAttachDataViewGet(Scalar::Float32);
  13145    case InlinableNative::DataViewGetFloat64:
  13146      return tryAttachDataViewGet(Scalar::Float64);
  13147    case InlinableNative::DataViewGetBigInt64:
  13148      return tryAttachDataViewGet(Scalar::BigInt64);
  13149    case InlinableNative::DataViewGetBigUint64:
  13150      return tryAttachDataViewGet(Scalar::BigUint64);
  13151    case InlinableNative::DataViewSetInt8:
  13152      return tryAttachDataViewSet(Scalar::Int8);
  13153    case InlinableNative::DataViewSetUint8:
  13154      return tryAttachDataViewSet(Scalar::Uint8);
  13155    case InlinableNative::DataViewSetInt16:
  13156      return tryAttachDataViewSet(Scalar::Int16);
  13157    case InlinableNative::DataViewSetUint16:
  13158      return tryAttachDataViewSet(Scalar::Uint16);
  13159    case InlinableNative::DataViewSetInt32:
  13160      return tryAttachDataViewSet(Scalar::Int32);
  13161    case InlinableNative::DataViewSetUint32:
  13162      return tryAttachDataViewSet(Scalar::Uint32);
  13163    case InlinableNative::DataViewSetFloat16:
  13164      return tryAttachDataViewSet(Scalar::Float16);
  13165    case InlinableNative::DataViewSetFloat32:
  13166      return tryAttachDataViewSet(Scalar::Float32);
  13167    case InlinableNative::DataViewSetFloat64:
  13168      return tryAttachDataViewSet(Scalar::Float64);
  13169    case InlinableNative::DataViewSetBigInt64:
  13170      return tryAttachDataViewSet(Scalar::BigInt64);
  13171    case InlinableNative::DataViewSetBigUint64:
  13172      return tryAttachDataViewSet(Scalar::BigUint64);
  13173    case InlinableNative::DataViewByteLength:
  13174      return tryAttachDataViewByteLength();
  13175    case InlinableNative::DataViewByteOffset:
  13176      return tryAttachDataViewByteOffset();
  13177 
  13178    // Function natives.
  13179    case InlinableNative::FunctionBind:
  13180      return tryAttachFunctionBind();
  13181 
  13182    // Intl natives.
  13183    case InlinableNative::IntlGuardToCollator:
  13184    case InlinableNative::IntlGuardToDateTimeFormat:
  13185    case InlinableNative::IntlGuardToDisplayNames:
  13186    case InlinableNative::IntlGuardToDurationFormat:
  13187    case InlinableNative::IntlGuardToListFormat:
  13188    case InlinableNative::IntlGuardToNumberFormat:
  13189    case InlinableNative::IntlGuardToPluralRules:
  13190    case InlinableNative::IntlGuardToRelativeTimeFormat:
  13191    case InlinableNative::IntlGuardToSegmenter:
  13192    case InlinableNative::IntlGuardToSegments:
  13193    case InlinableNative::IntlGuardToSegmentIterator:
  13194      return tryAttachGuardToClass(native);
  13195 
  13196    // Slot intrinsics.
  13197    case InlinableNative::IntrinsicUnsafeGetReservedSlot:
  13198    case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
  13199    case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
  13200    case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
  13201      return tryAttachUnsafeGetReservedSlot(native);
  13202    case InlinableNative::IntrinsicUnsafeSetReservedSlot:
  13203      return tryAttachUnsafeSetReservedSlot();
  13204 
  13205    // Intrinsics.
  13206    case InlinableNative::IntrinsicIsSuspendedGenerator:
  13207      return tryAttachIsSuspendedGenerator();
  13208    case InlinableNative::IntrinsicToObject:
  13209      return tryAttachToObject();
  13210    case InlinableNative::IntrinsicToInteger:
  13211      return tryAttachToInteger();
  13212    case InlinableNative::IntrinsicToLength:
  13213      return tryAttachToLength();
  13214    case InlinableNative::IntrinsicIsObject:
  13215      return tryAttachIsObject();
  13216    case InlinableNative::IntrinsicIsPackedArray:
  13217      return tryAttachIsPackedArray();
  13218    case InlinableNative::IntrinsicIsCallable:
  13219      return tryAttachIsCallable();
  13220    case InlinableNative::IntrinsicIsConstructor:
  13221      return tryAttachIsConstructor();
  13222    case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
  13223      return tryAttachIsCrossRealmArrayConstructor();
  13224    case InlinableNative::IntrinsicCanOptimizeArraySpecies:
  13225      return tryAttachCanOptimizeArraySpecies();
  13226    case InlinableNative::IntrinsicGuardToArrayIterator:
  13227    case InlinableNative::IntrinsicGuardToMapIterator:
  13228    case InlinableNative::IntrinsicGuardToSetIterator:
  13229    case InlinableNative::IntrinsicGuardToStringIterator:
  13230    case InlinableNative::IntrinsicGuardToRegExpStringIterator:
  13231    case InlinableNative::IntrinsicGuardToWrapForValidIterator:
  13232    case InlinableNative::IntrinsicGuardToIteratorHelper:
  13233 #ifdef NIGHTLY_BUILD
  13234    case InlinableNative::IntrinsicGuardToIteratorRange:
  13235 #endif
  13236    case InlinableNative::IntrinsicGuardToAsyncIteratorHelper:
  13237 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  13238    case InlinableNative::IntrinsicGuardToAsyncDisposableStack:
  13239    case InlinableNative::IntrinsicGuardToDisposableStack:
  13240 #endif
  13241      return tryAttachGuardToClass(native);
  13242    case InlinableNative::IntrinsicSubstringKernel:
  13243      return tryAttachSubstringKernel();
  13244    case InlinableNative::IntrinsicIsConstructing:
  13245      return tryAttachIsConstructing();
  13246    case InlinableNative::IntrinsicNewArrayIterator:
  13247      return tryAttachNewArrayIterator();
  13248    case InlinableNative::IntrinsicNewStringIterator:
  13249      return tryAttachNewStringIterator();
  13250    case InlinableNative::IntrinsicNewRegExpStringIterator:
  13251      return tryAttachNewRegExpStringIterator();
  13252    case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
  13253      return tryAttachArrayIteratorPrototypeOptimizable();
  13254 
  13255    // RegExp natives.
  13256    case InlinableNative::RegExpDotAll:
  13257      return tryAttachRegExpFlag(JS::RegExpFlag::DotAll);
  13258    case InlinableNative::RegExpGlobal:
  13259      return tryAttachRegExpFlag(JS::RegExpFlag::Global);
  13260    case InlinableNative::RegExpHasIndices:
  13261      return tryAttachRegExpFlag(JS::RegExpFlag::HasIndices);
  13262    case InlinableNative::RegExpIgnoreCase:
  13263      return tryAttachRegExpFlag(JS::RegExpFlag::IgnoreCase);
  13264    case InlinableNative::RegExpMultiline:
  13265      return tryAttachRegExpFlag(JS::RegExpFlag::Multiline);
  13266    case InlinableNative::RegExpSticky:
  13267      return tryAttachRegExpFlag(JS::RegExpFlag::Sticky);
  13268    case InlinableNative::RegExpUnicode:
  13269      return tryAttachRegExpFlag(JS::RegExpFlag::Unicode);
  13270    case InlinableNative::RegExpUnicodeSets:
  13271      return tryAttachRegExpFlag(JS::RegExpFlag::UnicodeSets);
  13272    case InlinableNative::IsRegExpObject:
  13273      return tryAttachHasClass(&RegExpObject::class_,
  13274                               /* isPossiblyWrapped = */ false);
  13275    case InlinableNative::IsPossiblyWrappedRegExpObject:
  13276      return tryAttachHasClass(&RegExpObject::class_,
  13277                               /* isPossiblyWrapped = */ true);
  13278    case InlinableNative::RegExpMatcher:
  13279    case InlinableNative::RegExpSearcher:
  13280      return tryAttachRegExpMatcherSearcher(native);
  13281    case InlinableNative::RegExpSearcherLastLimit:
  13282      return tryAttachRegExpSearcherLastLimit();
  13283    case InlinableNative::RegExpHasCaptureGroups:
  13284      return tryAttachRegExpHasCaptureGroups();
  13285    case InlinableNative::IsRegExpPrototypeOptimizable:
  13286      return tryAttachIsRegExpPrototypeOptimizable();
  13287    case InlinableNative::IsOptimizableRegExpObject:
  13288      return tryAttachIsOptimizableRegExpObject();
  13289    case InlinableNative::GetFirstDollarIndex:
  13290      return tryAttachGetFirstDollarIndex();
  13291    case InlinableNative::IntrinsicRegExpBuiltinExec:
  13292    case InlinableNative::IntrinsicRegExpBuiltinExecForTest:
  13293      return tryAttachIntrinsicRegExpBuiltinExec(native);
  13294    case InlinableNative::IntrinsicRegExpExec:
  13295    case InlinableNative::IntrinsicRegExpExecForTest:
  13296      return tryAttachIntrinsicRegExpExec(native);
  13297 
  13298    // String natives.
  13299    case InlinableNative::String:
  13300      return tryAttachString();
  13301    case InlinableNative::StringToString:
  13302    case InlinableNative::StringValueOf:
  13303      return tryAttachStringToStringValueOf();
  13304    case InlinableNative::StringCharCodeAt:
  13305      return tryAttachStringCharCodeAt();
  13306    case InlinableNative::StringCodePointAt:
  13307      return tryAttachStringCodePointAt();
  13308    case InlinableNative::StringCharAt:
  13309      return tryAttachStringCharAt();
  13310    case InlinableNative::StringAt:
  13311      return tryAttachStringAt();
  13312    case InlinableNative::StringFromCharCode:
  13313      return tryAttachStringFromCharCode();
  13314    case InlinableNative::StringFromCodePoint:
  13315      return tryAttachStringFromCodePoint();
  13316    case InlinableNative::StringIncludes:
  13317      return tryAttachStringIncludes();
  13318    case InlinableNative::StringIndexOf:
  13319      return tryAttachStringIndexOf();
  13320    case InlinableNative::StringLastIndexOf:
  13321      return tryAttachStringLastIndexOf();
  13322    case InlinableNative::StringStartsWith:
  13323      return tryAttachStringStartsWith();
  13324    case InlinableNative::StringEndsWith:
  13325      return tryAttachStringEndsWith();
  13326    case InlinableNative::StringToLowerCase:
  13327      return tryAttachStringToLowerCase();
  13328    case InlinableNative::StringToUpperCase:
  13329      return tryAttachStringToUpperCase();
  13330    case InlinableNative::StringToLocaleLowerCase:
  13331      return tryAttachStringToLocaleLowerCase();
  13332    case InlinableNative::StringToLocaleUpperCase:
  13333      return tryAttachStringToLocaleUpperCase();
  13334    case InlinableNative::StringTrim:
  13335      return tryAttachStringTrim();
  13336    case InlinableNative::StringTrimStart:
  13337      return tryAttachStringTrimStart();
  13338    case InlinableNative::StringTrimEnd:
  13339      return tryAttachStringTrimEnd();
  13340    case InlinableNative::IntrinsicStringReplaceString:
  13341      return tryAttachStringReplaceString();
  13342    case InlinableNative::IntrinsicStringSplitString:
  13343      return tryAttachStringSplitString();
  13344 
  13345    // Math natives.
  13346    case InlinableNative::MathRandom:
  13347      return tryAttachMathRandom();
  13348    case InlinableNative::MathAbs:
  13349      return tryAttachMathAbs();
  13350    case InlinableNative::MathClz32:
  13351      return tryAttachMathClz32();
  13352    case InlinableNative::MathSign:
  13353      return tryAttachMathSign();
  13354    case InlinableNative::MathImul:
  13355      return tryAttachMathImul();
  13356    case InlinableNative::MathFloor:
  13357      return tryAttachMathFloor();
  13358    case InlinableNative::MathCeil:
  13359      return tryAttachMathCeil();
  13360    case InlinableNative::MathTrunc:
  13361      return tryAttachMathTrunc();
  13362    case InlinableNative::MathRound:
  13363      return tryAttachMathRound();
  13364    case InlinableNative::MathSqrt:
  13365      return tryAttachMathSqrt();
  13366    case InlinableNative::MathFRound:
  13367      return tryAttachMathFRound();
  13368    case InlinableNative::MathF16Round:
  13369      return tryAttachMathF16Round();
  13370    case InlinableNative::MathHypot:
  13371      return tryAttachMathHypot();
  13372    case InlinableNative::MathATan2:
  13373      return tryAttachMathATan2();
  13374    case InlinableNative::MathSin:
  13375      return tryAttachMathFunction(UnaryMathFunction::SinNative);
  13376    case InlinableNative::MathTan:
  13377      return tryAttachMathFunction(UnaryMathFunction::TanNative);
  13378    case InlinableNative::MathCos:
  13379      return tryAttachMathFunction(UnaryMathFunction::CosNative);
  13380    case InlinableNative::MathExp:
  13381      return tryAttachMathFunction(UnaryMathFunction::Exp);
  13382    case InlinableNative::MathLog:
  13383      return tryAttachMathFunction(UnaryMathFunction::Log);
  13384    case InlinableNative::MathASin:
  13385      return tryAttachMathFunction(UnaryMathFunction::ASin);
  13386    case InlinableNative::MathATan:
  13387      return tryAttachMathFunction(UnaryMathFunction::ATan);
  13388    case InlinableNative::MathACos:
  13389      return tryAttachMathFunction(UnaryMathFunction::ACos);
  13390    case InlinableNative::MathLog10:
  13391      return tryAttachMathFunction(UnaryMathFunction::Log10);
  13392    case InlinableNative::MathLog2:
  13393      return tryAttachMathFunction(UnaryMathFunction::Log2);
  13394    case InlinableNative::MathLog1P:
  13395      return tryAttachMathFunction(UnaryMathFunction::Log1P);
  13396    case InlinableNative::MathExpM1:
  13397      return tryAttachMathFunction(UnaryMathFunction::ExpM1);
  13398    case InlinableNative::MathCosH:
  13399      return tryAttachMathFunction(UnaryMathFunction::CosH);
  13400    case InlinableNative::MathSinH:
  13401      return tryAttachMathFunction(UnaryMathFunction::SinH);
  13402    case InlinableNative::MathTanH:
  13403      return tryAttachMathFunction(UnaryMathFunction::TanH);
  13404    case InlinableNative::MathACosH:
  13405      return tryAttachMathFunction(UnaryMathFunction::ACosH);
  13406    case InlinableNative::MathASinH:
  13407      return tryAttachMathFunction(UnaryMathFunction::ASinH);
  13408    case InlinableNative::MathATanH:
  13409      return tryAttachMathFunction(UnaryMathFunction::ATanH);
  13410    case InlinableNative::MathCbrt:
  13411      return tryAttachMathFunction(UnaryMathFunction::Cbrt);
  13412    case InlinableNative::MathPow:
  13413      return tryAttachMathPow();
  13414    case InlinableNative::MathMin:
  13415      return tryAttachMathMinMax(/* isMax = */ false);
  13416    case InlinableNative::MathMax:
  13417      return tryAttachMathMinMax(/* isMax = */ true);
  13418 
  13419    // Map intrinsics.
  13420    case InlinableNative::IntrinsicGuardToMapObject:
  13421      return tryAttachGuardToClass(GuardClassKind::Map);
  13422    case InlinableNative::IntrinsicGetNextMapEntryForIterator:
  13423      return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true);
  13424 
  13425    // Number natives.
  13426    case InlinableNative::Number:
  13427      return tryAttachNumber();
  13428    case InlinableNative::NumberParseInt:
  13429      return tryAttachNumberParseInt();
  13430    case InlinableNative::NumberToString:
  13431      return tryAttachNumberToString();
  13432 
  13433    // Object natives.
  13434    case InlinableNative::Object:
  13435      return tryAttachObjectConstructor();
  13436    case InlinableNative::ObjectCreate:
  13437      return tryAttachObjectCreate();
  13438    case InlinableNative::ObjectIs:
  13439      return tryAttachObjectIs();
  13440    case InlinableNative::ObjectIsPrototypeOf:
  13441      return tryAttachObjectIsPrototypeOf();
  13442    case InlinableNative::ObjectKeys:
  13443      return tryAttachObjectKeys();
  13444    case InlinableNative::ObjectToString:
  13445      return tryAttachObjectToString();
  13446 
  13447    // Set intrinsics.
  13448    case InlinableNative::IntrinsicGuardToSetObject:
  13449      return tryAttachGuardToClass(GuardClassKind::Set);
  13450    case InlinableNative::IntrinsicGetNextSetEntryForIterator:
  13451      return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false);
  13452 
  13453    // ArrayBuffer intrinsics.
  13454    case InlinableNative::IntrinsicGuardToArrayBuffer:
  13455      return tryAttachGuardToArrayBuffer();
  13456 
  13457    // SharedArrayBuffer intrinsics.
  13458    case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
  13459      return tryAttachGuardToSharedArrayBuffer();
  13460 
  13461    // TypedArray natives.
  13462    case InlinableNative::TypedArrayConstructor:
  13463      return AttachDecision::NoAction;  // Not callable.
  13464    case InlinableNative::TypedArrayFill:
  13465      return tryAttachTypedArrayFill();
  13466    case InlinableNative::TypedArraySet:
  13467      return tryAttachTypedArraySet();
  13468    case InlinableNative::TypedArraySubarray:
  13469      return tryAttachTypedArraySubarray();
  13470    case InlinableNative::TypedArrayLength:
  13471      return tryAttachTypedArrayLength();
  13472    case InlinableNative::TypedArrayByteLength:
  13473      return tryAttachTypedArrayByteLength();
  13474    case InlinableNative::TypedArrayByteOffset:
  13475      return tryAttachTypedArrayByteOffset();
  13476 
  13477    // TypedArray intrinsics.
  13478    case InlinableNative::IntrinsicIsTypedArray:
  13479      return tryAttachIsTypedArray(/* isPossiblyWrapped = */ false);
  13480    case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
  13481      return tryAttachIsTypedArray(/* isPossiblyWrapped = */ true);
  13482    case InlinableNative::IntrinsicIsTypedArrayConstructor:
  13483      return tryAttachIsTypedArrayConstructor();
  13484    case InlinableNative::IntrinsicTypedArrayLength:
  13485      return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false);
  13486    case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
  13487      return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true);
  13488 
  13489    // Reflect natives.
  13490    case InlinableNative::ReflectGetPrototypeOf:
  13491      return tryAttachReflectGetPrototypeOf();
  13492 
  13493    // Atomics intrinsics:
  13494    case InlinableNative::AtomicsCompareExchange:
  13495      return tryAttachAtomicsCompareExchange();
  13496    case InlinableNative::AtomicsExchange:
  13497      return tryAttachAtomicsExchange();
  13498    case InlinableNative::AtomicsAdd:
  13499      return tryAttachAtomicsAdd();
  13500    case InlinableNative::AtomicsSub:
  13501      return tryAttachAtomicsSub();
  13502    case InlinableNative::AtomicsAnd:
  13503      return tryAttachAtomicsAnd();
  13504    case InlinableNative::AtomicsOr:
  13505      return tryAttachAtomicsOr();
  13506    case InlinableNative::AtomicsXor:
  13507      return tryAttachAtomicsXor();
  13508    case InlinableNative::AtomicsLoad:
  13509      return tryAttachAtomicsLoad();
  13510    case InlinableNative::AtomicsStore:
  13511      return tryAttachAtomicsStore();
  13512    case InlinableNative::AtomicsIsLockFree:
  13513      return tryAttachAtomicsIsLockFree();
  13514    case InlinableNative::AtomicsPause:
  13515      return tryAttachAtomicsPause();
  13516 
  13517    // BigInt natives.
  13518    case InlinableNative::BigInt:
  13519      return tryAttachBigInt();
  13520    case InlinableNative::BigIntAsIntN:
  13521      return tryAttachBigIntAsIntN();
  13522    case InlinableNative::BigIntAsUintN:
  13523      return tryAttachBigIntAsUintN();
  13524 
  13525    // Boolean natives.
  13526    case InlinableNative::Boolean:
  13527      return tryAttachBoolean();
  13528 
  13529    // Set natives.
  13530    case InlinableNative::SetConstructor:
  13531      return AttachDecision::NoAction;  // Not callable.
  13532    case InlinableNative::SetHas:
  13533      return tryAttachSetHas();
  13534    case InlinableNative::SetDelete:
  13535      return tryAttachSetDelete();
  13536    case InlinableNative::SetAdd:
  13537      return tryAttachSetAdd();
  13538    case InlinableNative::SetSize:
  13539      return tryAttachSetSize();
  13540 
  13541    // Map natives.
  13542    case InlinableNative::MapConstructor:
  13543      return AttachDecision::NoAction;  // Not callable.
  13544    case InlinableNative::MapHas:
  13545      return tryAttachMapHas();
  13546    case InlinableNative::MapGet:
  13547      return tryAttachMapGet();
  13548    case InlinableNative::MapDelete:
  13549      return tryAttachMapDelete();
  13550    case InlinableNative::MapSet:
  13551      return tryAttachMapSet();
  13552    case InlinableNative::MapSize:
  13553      return tryAttachMapSize();
  13554 
  13555    // Date natives and intrinsics.
  13556    case InlinableNative::DateGetTime:
  13557      return tryAttachDateGetTime();
  13558    case InlinableNative::DateGetFullYear:
  13559      return tryAttachDateGet(DateComponent::FullYear);
  13560    case InlinableNative::DateGetMonth:
  13561      return tryAttachDateGet(DateComponent::Month);
  13562    case InlinableNative::DateGetDate:
  13563      return tryAttachDateGet(DateComponent::Date);
  13564    case InlinableNative::DateGetDay:
  13565      return tryAttachDateGet(DateComponent::Day);
  13566    case InlinableNative::DateGetHours:
  13567      return tryAttachDateGet(DateComponent::Hours);
  13568    case InlinableNative::DateGetMinutes:
  13569      return tryAttachDateGet(DateComponent::Minutes);
  13570    case InlinableNative::DateGetSeconds:
  13571      return tryAttachDateGet(DateComponent::Seconds);
  13572 
  13573    // WeakMap/WeakSet natives.
  13574    case InlinableNative::WeakMapGet:
  13575      return tryAttachWeakMapGet();
  13576    case InlinableNative::WeakMapHas:
  13577      return tryAttachWeakMapHas();
  13578    case InlinableNative::WeakSetHas:
  13579      return tryAttachWeakSetHas();
  13580 
  13581    // ArrayBuffer natives.
  13582    case InlinableNative::ArrayBufferByteLength:
  13583      return tryAttachArrayBufferByteLength();
  13584 
  13585    // SharedArrayBuffer natives.
  13586    case InlinableNative::SharedArrayBufferByteLength:
  13587      return tryAttachSharedArrayBufferByteLength();
  13588 
  13589    // Testing functions.
  13590    case InlinableNative::TestBailout:
  13591      if (js::SupportDifferentialTesting()) {
  13592        return AttachDecision::NoAction;
  13593      }
  13594      return tryAttachBailout();
  13595    case InlinableNative::TestAssertFloat32:
  13596      return tryAttachAssertFloat32();
  13597    case InlinableNative::TestAssertRecoveredOnBailout:
  13598      if (js::SupportDifferentialTesting()) {
  13599        return AttachDecision::NoAction;
  13600      }
  13601      return tryAttachAssertRecoveredOnBailout();
  13602 
  13603 #ifdef FUZZING_JS_FUZZILLI
  13604    // Fuzzilli function
  13605    case InlinableNative::FuzzilliHash:
  13606      return tryAttachFuzzilliHash();
  13607 #endif
  13608 
  13609    case InlinableNative::Limit:
  13610      break;
  13611  }
  13612 
  13613  MOZ_CRASH("Shouldn't get here");
  13614 }
  13615 
  13616 // Remember the shape of the this object for any script being called as a
  13617 // constructor, for later use during Ion compilation.
  13618 ScriptedThisResult CallIRGenerator::getThisShapeForScripted(
  13619    HandleFunction calleeFunc, Handle<JSObject*> newTarget,
  13620    MutableHandle<Shape*> result) {
  13621  // Some constructors allocate their own |this| object.
  13622  if (calleeFunc->constructorNeedsUninitializedThis()) {
  13623    return ScriptedThisResult::UninitializedThis;
  13624  }
  13625 
  13626  // Only attach a stub if the newTarget is a function with a
  13627  // nonconfigurable prototype.
  13628  if (!newTarget->is<JSFunction>() ||
  13629      !newTarget->as<JSFunction>().hasNonConfigurablePrototypeDataProperty()) {
  13630    return ScriptedThisResult::NoAction;
  13631  }
  13632 
  13633  AutoRealm ar(cx_, calleeFunc);
  13634  Shape* thisShape = ThisShapeForFunction(cx_, calleeFunc, newTarget);
  13635  if (!thisShape) {
  13636    cx_->clearPendingException();
  13637    return ScriptedThisResult::NoAction;
  13638  }
  13639 
  13640  MOZ_ASSERT(thisShape->realm() == calleeFunc->realm());
  13641  result.set(thisShape);
  13642  return ScriptedThisResult::PlainObjectShape;
  13643 }
  13644 
  13645 static bool CanOptimizeScriptedCall(JSFunction* callee, bool isConstructing) {
  13646  if (!callee->hasJitEntry()) {
  13647    return false;
  13648  }
  13649 
  13650  // If callee is not an interpreted constructor, we have to throw.
  13651  if (isConstructing && !callee->isConstructor()) {
  13652    return false;
  13653  }
  13654 
  13655  // Likewise, if the callee is a class constructor, we have to throw.
  13656  if (!isConstructing && callee->isClassConstructor()) {
  13657    return false;
  13658  }
  13659 
  13660  return true;
  13661 }
  13662 
  13663 void CallIRGenerator::emitCallScriptedGuards(ObjOperandId calleeObjId,
  13664                                             JSFunction* calleeFunc,
  13665                                             Int32OperandId argcId,
  13666                                             CallFlags flags, Shape* thisShape,
  13667                                             bool isBoundFunction) {
  13668  bool isConstructing = flags.isConstructing();
  13669 
  13670  if (mode_ == ICState::Mode::Specialized) {
  13671    MOZ_ASSERT_IF(isConstructing, thisShape || flags.needsUninitializedThis());
  13672 
  13673    // Ensure callee matches this stub's callee
  13674    emitCalleeGuard(calleeObjId, calleeFunc);
  13675    if (thisShape) {
  13676      // Emit guards to ensure the newTarget's .prototype property is what we
  13677      // expect. Note that getThisForScripted checked newTarget is a function
  13678      // with a non-configurable .prototype data property.
  13679 
  13680      JSFunction* newTarget;
  13681      ObjOperandId newTargetObjId;
  13682      if (isBoundFunction) {
  13683        newTarget = calleeFunc;
  13684        newTargetObjId = calleeObjId;
  13685      } else {
  13686        newTarget = &newTarget_.toObject().as<JSFunction>();
  13687        ValOperandId newTargetValId = writer.loadArgumentDynamicSlot(
  13688            ArgumentKind::NewTarget, argcId, flags);
  13689        newTargetObjId = writer.guardToObject(newTargetValId);
  13690      }
  13691 
  13692      Maybe<PropertyInfo> prop = newTarget->lookupPure(cx_->names().prototype);
  13693      MOZ_ASSERT(prop.isSome());
  13694      uint32_t slot = prop->slot();
  13695      MOZ_ASSERT(slot >= newTarget->numFixedSlots(),
  13696                 "Stub code relies on this");
  13697 
  13698      writer.guardShape(newTargetObjId, newTarget->shape());
  13699 
  13700      const Value& value = newTarget->getSlot(slot);
  13701      if (value.isObject()) {
  13702        JSObject* prototypeObject = &value.toObject();
  13703 
  13704        ObjOperandId protoId = writer.loadObject(prototypeObject);
  13705        writer.guardDynamicSlotIsSpecificObject(
  13706            newTargetObjId, protoId, slot - newTarget->numFixedSlots());
  13707      } else {
  13708        writer.guardDynamicSlotIsNotObject(newTargetObjId,
  13709                                           slot - newTarget->numFixedSlots());
  13710      }
  13711 
  13712      // Call metaScriptedThisShape before emitting the call, so that Warp can
  13713      // use the shape to create the |this| object before transpiling the call.
  13714      writer.metaScriptedThisShape(thisShape);
  13715    }
  13716  } else {
  13717    // Guard that object is a scripted function
  13718    writer.guardClass(calleeObjId, GuardClassKind::JSFunction);
  13719    writer.guardFunctionHasJitEntry(calleeObjId);
  13720 
  13721    if (isConstructing) {
  13722      // If callee is not a constructor, we have to throw.
  13723      writer.guardFunctionIsConstructor(calleeObjId);
  13724    } else {
  13725      // If callee is a class constructor, we have to throw.
  13726      writer.guardNotClassConstructor(calleeObjId);
  13727    }
  13728  }
  13729 }
  13730 
  13731 AttachDecision CallIRGenerator::tryAttachCallScripted(
  13732    HandleFunction calleeFunc) {
  13733  MOZ_ASSERT(calleeFunc->hasJitEntry());
  13734 
  13735  if (calleeFunc->isWasmWithJitEntry()) {
  13736    TRY_ATTACH(tryAttachWasmCall(calleeFunc));
  13737  }
  13738 
  13739  bool isSpecialized = mode_ == ICState::Mode::Specialized;
  13740 
  13741  bool isConstructing = IsConstructPC(pc_);
  13742  bool isSpread = IsSpreadPC(pc_);
  13743  bool isSameRealm = isSpecialized && cx_->realm() == calleeFunc->realm();
  13744  CallFlags flags(isConstructing, isSpread, isSameRealm);
  13745 
  13746  if (!CanOptimizeScriptedCall(calleeFunc, isConstructing)) {
  13747    return AttachDecision::NoAction;
  13748  }
  13749 
  13750  // Verify that spread calls have a reasonable number of arguments.
  13751  if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
  13752    return AttachDecision::NoAction;
  13753  }
  13754 
  13755  Rooted<Shape*> thisShape(cx_);
  13756  if (isConstructing && isSpecialized) {
  13757    Rooted<JSObject*> newTarget(cx_, &newTarget_.toObject());
  13758    switch (getThisShapeForScripted(calleeFunc, newTarget, &thisShape)) {
  13759      case ScriptedThisResult::PlainObjectShape:
  13760        break;
  13761      case ScriptedThisResult::UninitializedThis:
  13762        flags.setNeedsUninitializedThis();
  13763        break;
  13764      case ScriptedThisResult::NoAction:
  13765        return AttachDecision::NoAction;
  13766    }
  13767  }
  13768 
  13769  // Load argc.
  13770  Int32OperandId argcId(writer.setInputOperandId(0));
  13771 
  13772  // Load the callee and ensure it is an object
  13773  ValOperandId calleeValId =
  13774      writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
  13775  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
  13776 
  13777  emitCallScriptedGuards(calleeObjId, calleeFunc, argcId, flags, thisShape,
  13778                         /* isBoundFunction = */ false);
  13779 
  13780  writer.callScriptedFunction(calleeObjId, argcId, flags,
  13781                              ClampFixedArgc(argc_));
  13782  writer.returnFromIC();
  13783 
  13784  if (isSpecialized) {
  13785    trackAttached("Call.CallScripted");
  13786  } else {
  13787    trackAttached("Call.CallAnyScripted");
  13788  }
  13789 
  13790  return AttachDecision::Attach;
  13791 }
  13792 
  13793 AttachDecision CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc) {
  13794  MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
  13795 
  13796  bool isSpecialized = mode_ == ICState::Mode::Specialized;
  13797 
  13798  bool isSpread = IsSpreadPC(pc_);
  13799  bool isSameRealm = isSpecialized && cx_->realm() == calleeFunc->realm();
  13800  bool isConstructing = IsConstructPC(pc_);
  13801  CallFlags flags(isConstructing, isSpread, isSameRealm);
  13802 
  13803  if (isConstructing && !calleeFunc->isConstructor()) {
  13804    return AttachDecision::NoAction;
  13805  }
  13806 
  13807  // Check for specific native-function optimizations.
  13808  if (isSpecialized) {
  13809    TRY_ATTACH(tryAttachInlinableNative(calleeFunc, flags));
  13810  }
  13811 
  13812  // Verify that spread calls have a reasonable number of arguments.
  13813  // We check this after trying to attach inlinable natives, because some
  13814  // inlinable natives can safely ignore the limit.
  13815  if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
  13816    return AttachDecision::NoAction;
  13817  }
  13818 
  13819  // Load argc.
  13820  Int32OperandId argcId(writer.setInputOperandId(0));
  13821 
  13822  // Load the callee and ensure it is an object
  13823  ValOperandId calleeValId =
  13824      writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
  13825  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
  13826 
  13827  // DOM calls need an additional guard so only try optimizing the first stub.
  13828  // Can only optimize normal (non-spread) calls.
  13829  if (isFirstStub_ && !isSpread && thisval_.isObject() &&
  13830      CanAttachDOMCall(cx_, JSJitInfo::Method, &thisval_.toObject(), calleeFunc,
  13831                       mode_)) {
  13832    MOZ_ASSERT(!isConstructing, "DOM functions are not constructors");
  13833 
  13834    gc::AllocSite* allocSite = nullptr;
  13835    if (calleeFunc->jitInfo()->returnType() == JSVAL_TYPE_OBJECT &&
  13836        JS::Prefs::dom_alloc_site()) {
  13837      allocSite = maybeCreateAllocSite();
  13838      if (!allocSite) {
  13839        return AttachDecision::NoAction;
  13840      }
  13841    }
  13842 
  13843    // Guard that |this| is an object.
  13844    ValOperandId thisValId =
  13845        writer.loadArgumentDynamicSlot(ArgumentKind::This, argcId, flags);
  13846    ObjOperandId thisObjId = writer.guardToObject(thisValId);
  13847 
  13848    // Guard on the |this| shape to make sure it's the right instance. This also
  13849    // ensures DOM_OBJECT_SLOT is stored in a fixed slot. See CanAttachDOMCall.
  13850    writer.guardShape(thisObjId, thisval_.toObject().shape());
  13851 
  13852    // Ensure callee matches this stub's callee
  13853    writer.guardSpecificFunction(calleeObjId, calleeFunc);
  13854 
  13855    if (allocSite) {
  13856      writer.callDOMFunctionWithAllocSite(calleeObjId, argcId, thisObjId,
  13857                                          calleeFunc, flags,
  13858                                          ClampFixedArgc(argc_), allocSite);
  13859    } else {
  13860      writer.callDOMFunction(calleeObjId, argcId, thisObjId, calleeFunc, flags,
  13861                             ClampFixedArgc(argc_));
  13862    }
  13863 
  13864    trackAttached("Call.CallDOM");
  13865  } else if (isSpecialized) {
  13866    // Ensure callee matches this stub's callee
  13867    writer.guardSpecificFunction(calleeObjId, calleeFunc);
  13868    writer.callNativeFunction(calleeObjId, argcId, jsop(), calleeFunc, flags,
  13869                              ClampFixedArgc(argc_));
  13870 
  13871    trackAttached("Call.CallNative");
  13872  } else {
  13873    // Guard that object is a native function
  13874    writer.guardClass(calleeObjId, GuardClassKind::JSFunction);
  13875    writer.guardFunctionHasNoJitEntry(calleeObjId);
  13876 
  13877    if (isConstructing) {
  13878      // If callee is not a constructor, we have to throw.
  13879      writer.guardFunctionIsConstructor(calleeObjId);
  13880    } else {
  13881      // If callee is a class constructor, we have to throw.
  13882      writer.guardNotClassConstructor(calleeObjId);
  13883    }
  13884    writer.callAnyNativeFunction(calleeObjId, argcId, flags,
  13885                                 ClampFixedArgc(argc_));
  13886 
  13887    trackAttached("Call.CallAnyNative");
  13888  }
  13889 
  13890  writer.returnFromIC();
  13891 
  13892  return AttachDecision::Attach;
  13893 }
  13894 
  13895 AttachDecision CallIRGenerator::tryAttachCallHook(HandleObject calleeObj) {
  13896  if (mode_ != ICState::Mode::Specialized) {
  13897    // We do not have megamorphic call hook stubs.
  13898    // TODO: Should we attach specialized call hook stubs in
  13899    // megamorphic mode to avoid going generic?
  13900    return AttachDecision::NoAction;
  13901  }
  13902 
  13903  bool isSpread = IsSpreadPC(pc_);
  13904  bool isConstructing = IsConstructPC(pc_);
  13905  CallFlags flags(isConstructing, isSpread);
  13906  JSNative hook =
  13907      isConstructing ? calleeObj->constructHook() : calleeObj->callHook();
  13908  if (!hook) {
  13909    return AttachDecision::NoAction;
  13910  }
  13911 
  13912  // Bound functions have a JSClass construct hook but are not always
  13913  // constructors.
  13914  if (isConstructing && !calleeObj->isConstructor()) {
  13915    return AttachDecision::NoAction;
  13916  }
  13917 
  13918  // We don't support spread calls in the transpiler yet.
  13919  if (isSpread) {
  13920    return AttachDecision::NoAction;
  13921  }
  13922 
  13923  // Load argc.
  13924  Int32OperandId argcId(writer.setInputOperandId(0));
  13925 
  13926  // Load the callee and ensure it is an object
  13927  ValOperandId calleeValId =
  13928      writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
  13929  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
  13930 
  13931  // Ensure the callee's class matches the one in this stub.
  13932  writer.guardAnyClass(calleeObjId, calleeObj->getClass());
  13933 
  13934  if (isConstructing && calleeObj->is<BoundFunctionObject>()) {
  13935    writer.guardBoundFunctionIsConstructor(calleeObjId);
  13936  }
  13937 
  13938  writer.callClassHook(calleeObjId, argcId, hook, flags, ClampFixedArgc(argc_));
  13939  writer.returnFromIC();
  13940 
  13941  trackAttached("Call.CallHook");
  13942 
  13943  return AttachDecision::Attach;
  13944 }
  13945 
  13946 AttachDecision CallIRGenerator::tryAttachBoundFunction(
  13947    Handle<BoundFunctionObject*> calleeObj) {
  13948  // The target must be a JSFunction with a JitEntry.
  13949  if (!calleeObj->getTarget()->is<JSFunction>()) {
  13950    return AttachDecision::NoAction;
  13951  }
  13952 
  13953  bool isSpread = IsSpreadPC(pc_);
  13954  bool isConstructing = IsConstructPC(pc_);
  13955 
  13956  // Spread calls are not supported yet.
  13957  if (isSpread) {
  13958    return AttachDecision::NoAction;
  13959  }
  13960 
  13961  Rooted<JSFunction*> target(cx_, &calleeObj->getTarget()->as<JSFunction>());
  13962  if (!CanOptimizeScriptedCall(target, isConstructing)) {
  13963    return AttachDecision::NoAction;
  13964  }
  13965 
  13966  // Limit the number of bound arguments to prevent us from compiling many
  13967  // different stubs (we bake in numBoundArgs and it's usually very small).
  13968  static constexpr size_t MaxBoundArgs = 10;
  13969  size_t numBoundArgs = calleeObj->numBoundArgs();
  13970  if (numBoundArgs > MaxBoundArgs) {
  13971    return AttachDecision::NoAction;
  13972  }
  13973 
  13974  // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
  13975  if (numBoundArgs + argc_ > JIT_ARGS_LENGTH_MAX) {
  13976    return AttachDecision::NoAction;
  13977  }
  13978 
  13979  CallFlags flags(isConstructing, isSpread);
  13980 
  13981  if (mode_ == ICState::Mode::Specialized) {
  13982    if (cx_->realm() == target->realm()) {
  13983      flags.setIsSameRealm();
  13984    }
  13985  }
  13986 
  13987  Rooted<Shape*> thisShape(cx_);
  13988  if (isConstructing) {
  13989    // Only optimize if newTarget == callee. This is the common case and ensures
  13990    // we can always pass the bound function's target as newTarget.
  13991    if (newTarget_ != ObjectValue(*calleeObj)) {
  13992      return AttachDecision::NoAction;
  13993    }
  13994 
  13995    if (mode_ == ICState::Mode::Specialized) {
  13996      Handle<JSFunction*> newTarget = target;
  13997      switch (getThisShapeForScripted(target, newTarget, &thisShape)) {
  13998        case ScriptedThisResult::PlainObjectShape:
  13999          break;
  14000        case ScriptedThisResult::UninitializedThis:
  14001          flags.setNeedsUninitializedThis();
  14002          break;
  14003        case ScriptedThisResult::NoAction:
  14004          return AttachDecision::NoAction;
  14005      }
  14006    }
  14007  }
  14008 
  14009  // Load argc.
  14010  Int32OperandId argcId(writer.setInputOperandId(0));
  14011 
  14012  // Load the callee and ensure it's a bound function.
  14013  ValOperandId calleeValId =
  14014      writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
  14015  ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
  14016  writer.guardClass(calleeObjId, GuardClassKind::BoundFunction);
  14017 
  14018  // Ensure numBoundArgs matches.
  14019  Int32OperandId numBoundArgsId = writer.loadBoundFunctionNumArgs(calleeObjId);
  14020  writer.guardSpecificInt32(numBoundArgsId, numBoundArgs);
  14021 
  14022  if (isConstructing) {
  14023    // Guard newTarget == callee. We depend on this in CallBoundScriptedFunction
  14024    // and in emitCallScriptedGuards by using boundTarget as newTarget.
  14025    ValOperandId newTargetValId =
  14026        writer.loadArgumentDynamicSlot(ArgumentKind::NewTarget, argcId, flags);
  14027    ObjOperandId newTargetObjId = writer.guardToObject(newTargetValId);
  14028    writer.guardObjectIdentity(newTargetObjId, calleeObjId);
  14029  }
  14030 
  14031  ObjOperandId targetId = writer.loadBoundFunctionTarget(calleeObjId);
  14032 
  14033  emitCallScriptedGuards(targetId, target, argcId, flags, thisShape,
  14034                         /* isBoundFunction = */ true);
  14035 
  14036  writer.callBoundScriptedFunction(calleeObjId, targetId, argcId, flags,
  14037                                   numBoundArgs);
  14038  writer.returnFromIC();
  14039 
  14040  trackAttached("Call.BoundFunction");
  14041  return AttachDecision::Attach;
  14042 }
  14043 
  14044 AttachDecision CallIRGenerator::tryAttachBoundNative(
  14045    Handle<BoundFunctionObject*> calleeObj) {
  14046  // The target must be a native JSFunction without a JitEntry.
  14047  Rooted<JSObject*> boundTarget(cx_, calleeObj->getTarget());
  14048  if (!boundTarget->is<JSFunction>()) {
  14049    return AttachDecision::NoAction;
  14050  }
  14051  auto target = boundTarget.as<JSFunction>();
  14052 
  14053  bool isScripted = target->hasJitEntry();
  14054  MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
  14055 
  14056  if (isScripted) {
  14057    return AttachDecision::NoAction;
  14058  }
  14059 
  14060  // Limit the number of bound arguments to prevent us from compiling many
  14061  // different stubs (we bake in numBoundArgs and it's usually very small).
  14062  static constexpr size_t MaxBoundArgs = 10;
  14063  size_t numBoundArgs = calleeObj->numBoundArgs();
  14064  if (numBoundArgs > MaxBoundArgs) {
  14065    return AttachDecision::NoAction;
  14066  }
  14067 
  14068  // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
  14069  if (numBoundArgs + argc_ > JIT_ARGS_LENGTH_MAX) {
  14070    return AttachDecision::NoAction;
  14071  }
  14072 
  14073  // Don't try to optimize when we're already megamorphic.
  14074  if (mode_ != ICState::Mode::Specialized) {
  14075    return AttachDecision::NoAction;
  14076  }
  14077 
  14078  bool isSpread = IsSpreadPC(pc_);
  14079  bool isSameRealm = cx_->realm() == target->realm();
  14080  bool isConstructing = IsConstructPC(pc_);
  14081  CallFlags flags(isConstructing, isSpread, isSameRealm);
  14082 
  14083  if (isConstructing && !target->isConstructor()) {
  14084    return AttachDecision::NoAction;
  14085  }
  14086 
  14087  // Verify that spread calls have a reasonable number of arguments.
  14088  if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
  14089    return AttachDecision::NoAction;
  14090  }
  14091 
  14092  // Spread calls are only supported when we don't have to insert bound args.
  14093  if (isSpread && numBoundArgs != 0) {
  14094    return AttachDecision::NoAction;
  14095  }
  14096 
  14097  // Use the bound |this| value.
  14098  Rooted<Value> thisValue(cx_, calleeObj->getBoundThis());
  14099 
  14100  // Concatenate the bound arguments and the stack arguments.
  14101  JS::RootedVector<Value> concatenatedArgs(cx_);
  14102  if (numBoundArgs != 0) {
  14103    if (!concatenatedArgs.reserve(numBoundArgs + args_.length())) {
  14104      cx_->recoverFromOutOfMemory();
  14105      return AttachDecision::NoAction;
  14106    }
  14107 
  14108    for (size_t i = 0; i < numBoundArgs; i++) {
  14109      concatenatedArgs.infallibleAppend(calleeObj->getBoundArg(i));
  14110    }
  14111    concatenatedArgs.infallibleAppend(args_.begin(), args_.length());
  14112  }
  14113  auto args = numBoundArgs != 0 ? concatenatedArgs : args_;
  14114 
  14115  // Check for specific native-function optimizations.
  14116  InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget_,
  14117                                       thisValue, args, flags);
  14118  return nativeGen.tryAttachStub();
  14119 }
  14120 
  14121 static bool IsInlinableFunCallOrApply(JSOp op) {
  14122  return op == JSOp::Call || op == JSOp::CallContent ||
  14123         op == JSOp::CallIgnoresRv;
  14124 }
  14125 
  14126 AttachDecision CallIRGenerator::tryAttachBoundFunCall(
  14127    Handle<BoundFunctionObject*> calleeObj) {
  14128  // Only optimize fun_call for simple calls.
  14129  if (!IsInlinableFunCallOrApply(jsop())) {
  14130    return AttachDecision::NoAction;
  14131  }
  14132 
  14133  // The target must be a native JSFunction to fun_call.
  14134  JSObject* boundTarget = calleeObj->getTarget();
  14135  if (!boundTarget->is<JSFunction>()) {
  14136    return AttachDecision::NoAction;
  14137  }
  14138  auto* boundTargetFn = &boundTarget->as<JSFunction>();
  14139 
  14140  bool isScripted = boundTargetFn->hasJitEntry();
  14141  MOZ_ASSERT_IF(!isScripted, boundTargetFn->isNativeWithoutJitEntry());
  14142 
  14143  if (isScripted || boundTargetFn->native() != fun_call) {
  14144    return AttachDecision::NoAction;
  14145  }
  14146 
  14147  // Limit the number of bound arguments to prevent us from compiling many
  14148  // different stubs (we bake in numBoundArgs and it's usually very small).
  14149  static constexpr size_t MaxBoundArgs = 10;
  14150  size_t numBoundArgs = calleeObj->numBoundArgs();
  14151  if (numBoundArgs > MaxBoundArgs) {
  14152    return AttachDecision::NoAction;
  14153  }
  14154 
  14155  // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
  14156  if (numBoundArgs + argc_ > JIT_ARGS_LENGTH_MAX) {
  14157    return AttachDecision::NoAction;
  14158  }
  14159 
  14160  // Don't try to optimize when we're already megamorphic.
  14161  if (mode_ != ICState::Mode::Specialized) {
  14162    return AttachDecision::NoAction;
  14163  }
  14164 
  14165  JSFunction* boundThis;
  14166  if (!IsFunctionObject(calleeObj->getBoundThis(), &boundThis)) {
  14167    return AttachDecision::NoAction;
  14168  }
  14169 
  14170  bool boundThisIsScripted = boundThis->hasJitEntry();
  14171  MOZ_ASSERT_IF(!boundThisIsScripted, boundThis->isNativeWithoutJitEntry());
  14172 
  14173  if (boundThisIsScripted) {
  14174    return AttachDecision::NoAction;
  14175  }
  14176 
  14177  CallFlags targetFlags(CallFlags::FunCall);
  14178  if (cx_->realm() == boundThis->realm()) {
  14179    targetFlags.setIsSameRealm();
  14180  }
  14181 
  14182  Rooted<JSFunction*> target(cx_, boundThis);
  14183  HandleValue newTarget = NullHandleValue;
  14184 
  14185  Rooted<Value> thisValue(cx_);
  14186  if (numBoundArgs > 0) {
  14187    thisValue = calleeObj->getBoundArg(0);
  14188  } else if (argc_ > 0) {
  14189    thisValue = args_[0];
  14190  } else {
  14191    MOZ_ASSERT(thisValue.isUndefined());
  14192  }
  14193 
  14194  // Concatenate the bound arguments and the stack arguments.
  14195  JS::RootedVector<Value> concatenatedArgs(cx_);
  14196  if (numBoundArgs > 1) {
  14197    if (!concatenatedArgs.reserve((numBoundArgs - 1) + args_.length())) {
  14198      cx_->recoverFromOutOfMemory();
  14199      return AttachDecision::NoAction;
  14200    }
  14201 
  14202    for (size_t i = 1; i < numBoundArgs; i++) {
  14203      concatenatedArgs.infallibleAppend(calleeObj->getBoundArg(i));
  14204    }
  14205    concatenatedArgs.infallibleAppend(args_.begin(), args_.length());
  14206  }
  14207  auto args = ([&]() -> HandleValueArray {
  14208    if (numBoundArgs > 1) {
  14209      // Return |concatenatedArgs| if there are any bound arguments.
  14210      return concatenatedArgs;
  14211    }
  14212    if (numBoundArgs > 0) {
  14213      // Return |args_| if only the |this| value is bound.
  14214      return args_;
  14215    }
  14216    if (argc_ > 0) {
  14217      // Nothing bound at all, return stack arguments starting from |args[1]|.
  14218      return HandleValueArray::subarray(args_, 1, args_.length() - 1);
  14219    }
  14220    // No arguments at all.
  14221    return HandleValueArray::empty();
  14222  })();
  14223 
  14224  // Check for specific native-function optimizations.
  14225  InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget,
  14226                                       thisValue, args, targetFlags);
  14227  return nativeGen.tryAttachStub();
  14228 }
  14229 
  14230 AttachDecision CallIRGenerator::tryAttachBoundFunApply(
  14231    Handle<BoundFunctionObject*> calleeObj) {
  14232  // Only optimize fun_apply for simple calls.
  14233  if (!IsInlinableFunCallOrApply(jsop())) {
  14234    return AttachDecision::NoAction;
  14235  }
  14236 
  14237  // The target must be a native JSFunction to fun_apply.
  14238  JSObject* boundTarget = calleeObj->getTarget();
  14239  if (!boundTarget->is<JSFunction>()) {
  14240    return AttachDecision::NoAction;
  14241  }
  14242  auto* boundTargetFn = &boundTarget->as<JSFunction>();
  14243 
  14244  bool isScripted = boundTargetFn->hasJitEntry();
  14245  MOZ_ASSERT_IF(!isScripted, boundTargetFn->isNativeWithoutJitEntry());
  14246 
  14247  if (isScripted || boundTargetFn->native() != fun_apply) {
  14248    return AttachDecision::NoAction;
  14249  }
  14250 
  14251  size_t numBoundArgs = calleeObj->numBoundArgs();
  14252  if (numBoundArgs + argc_ > 2) {
  14253    return AttachDecision::NoAction;
  14254  }
  14255 
  14256  // Don't try to optimize when we're already megamorphic.
  14257  if (mode_ != ICState::Mode::Specialized) {
  14258    return AttachDecision::NoAction;
  14259  }
  14260 
  14261  JSFunction* boundThis;
  14262  if (!IsFunctionObject(calleeObj->getBoundThis(), &boundThis)) {
  14263    return AttachDecision::NoAction;
  14264  }
  14265 
  14266  bool boundThisIsScripted = boundThis->hasJitEntry();
  14267  MOZ_ASSERT_IF(!boundThisIsScripted, boundThis->isNativeWithoutJitEntry());
  14268 
  14269  if (boundThisIsScripted) {
  14270    return AttachDecision::NoAction;
  14271  }
  14272 
  14273  // The second argument must be |null| or |undefined|, because we only support
  14274  // |CallFlags::FunCall| and |CallFlags::FunApplyNullUndefined|.
  14275  CallFlags::ArgFormat format;
  14276  if (numBoundArgs + argc_ < 2) {
  14277    format = CallFlags::FunCall;
  14278  } else {
  14279    Value arg;
  14280    if (numBoundArgs == 2) {
  14281      arg = calleeObj->getBoundArg(1);
  14282    } else if (numBoundArgs == 1) {
  14283      arg = args_[0];
  14284    } else {
  14285      arg = args_[1];
  14286    }
  14287    if (!arg.isNullOrUndefined()) {
  14288      return AttachDecision::NoAction;
  14289    }
  14290    format = CallFlags::FunApplyNullUndefined;
  14291  }
  14292 
  14293  CallFlags targetFlags(format);
  14294  if (cx_->realm() == boundThis->realm()) {
  14295    targetFlags.setIsSameRealm();
  14296  }
  14297 
  14298  Rooted<JSFunction*> target(cx_, boundThis);
  14299  HandleValue newTarget = NullHandleValue;
  14300 
  14301  Rooted<Value> thisValue(cx_);
  14302  if (numBoundArgs > 0) {
  14303    thisValue = calleeObj->getBoundArg(0);
  14304  } else if (argc_ > 0) {
  14305    thisValue = args_[0];
  14306  } else {
  14307    MOZ_ASSERT(thisValue.isUndefined());
  14308  }
  14309  HandleValueArray args = HandleValueArray::empty();
  14310 
  14311  // Check for specific native-function optimizations.
  14312  InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget,
  14313                                       thisValue, args, targetFlags);
  14314  return nativeGen.tryAttachStub();
  14315 }
  14316 
  14317 AttachDecision CallIRGenerator::tryAttachFunCallBound(
  14318    Handle<JSFunction*> callee) {
  14319  MOZ_ASSERT(callee->isNativeWithoutJitEntry());
  14320 
  14321  if (callee->native() != fun_call) {
  14322    return AttachDecision::NoAction;
  14323  }
  14324 
  14325  if (!thisval_.isObject() || !thisval_.toObject().is<BoundFunctionObject>()) {
  14326    return AttachDecision::NoAction;
  14327  }
  14328  Rooted<BoundFunctionObject*> bound(
  14329      cx_, &thisval_.toObject().as<BoundFunctionObject>());
  14330 
  14331  // The target must be a native JSFunction without a JitEntry.
  14332  Rooted<JSObject*> boundTarget(cx_, bound->getTarget());
  14333  if (!boundTarget->is<JSFunction>()) {
  14334    return AttachDecision::NoAction;
  14335  }
  14336  auto target = boundTarget.as<JSFunction>();
  14337 
  14338  bool isScripted = target->hasJitEntry();
  14339  MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
  14340 
  14341  // We don't yet supported scripted bound targets.
  14342  if (isScripted) {
  14343    return AttachDecision::NoAction;
  14344  }
  14345 
  14346  // Limit the number of bound arguments to prevent us from compiling many
  14347  // different stubs (we bake in numBoundArgs and it's usually very small).
  14348  static constexpr size_t MaxBoundArgs = 10;
  14349  size_t numBoundArgs = bound->numBoundArgs();
  14350  if (numBoundArgs > MaxBoundArgs) {
  14351    return AttachDecision::NoAction;
  14352  }
  14353 
  14354  // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
  14355  if (numBoundArgs + argc_ > JIT_ARGS_LENGTH_MAX) {
  14356    return AttachDecision::NoAction;
  14357  }
  14358 
  14359  // Don't try to optimize when we're already megamorphic.
  14360  if (mode_ != ICState::Mode::Specialized) {
  14361    return AttachDecision::NoAction;
  14362  }
  14363 
  14364  CallFlags targetFlags(CallFlags::FunCall);
  14365  if (cx_->realm() == target->realm()) {
  14366    targetFlags.setIsSameRealm();
  14367  }
  14368 
  14369  HandleValue newTarget = NullHandleValue;
  14370 
  14371  // Use the bound |this| value.
  14372  Rooted<Value> thisValue(cx_, bound->getBoundThis());
  14373 
  14374  auto callArgs = argc_ > 0
  14375                      ? HandleValueArray::subarray(args_, 1, args_.length() - 1)
  14376                      : HandleValueArray::empty();
  14377 
  14378  // Concatenate the bound arguments and the stack arguments.
  14379  JS::RootedVector<Value> concatenatedArgs(cx_);
  14380  if (numBoundArgs != 0) {
  14381    if (!concatenatedArgs.reserve(numBoundArgs + callArgs.length())) {
  14382      cx_->recoverFromOutOfMemory();
  14383      return AttachDecision::NoAction;
  14384    }
  14385 
  14386    for (size_t i = 0; i < numBoundArgs; i++) {
  14387      concatenatedArgs.infallibleAppend(bound->getBoundArg(i));
  14388    }
  14389    concatenatedArgs.infallibleAppend(callArgs.begin(), callArgs.length());
  14390  }
  14391 
  14392  // Actual args.
  14393  auto args = numBoundArgs != 0 ? concatenatedArgs : callArgs;
  14394 
  14395  // Check for specific native-function optimizations.
  14396  InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget,
  14397                                       thisValue, args, targetFlags, bound);
  14398  return nativeGen.tryAttachStub();
  14399 }
  14400 
  14401 AttachDecision CallIRGenerator::tryAttachFunApplyBound(
  14402    Handle<JSFunction*> callee) {
  14403  MOZ_ASSERT(callee->isNativeWithoutJitEntry());
  14404 
  14405  if (callee->native() != fun_apply) {
  14406    return AttachDecision::NoAction;
  14407  }
  14408 
  14409  if (argc_ > 2) {
  14410    return AttachDecision::NoAction;
  14411  }
  14412 
  14413  if (!thisval_.isObject() || !thisval_.toObject().is<BoundFunctionObject>()) {
  14414    return AttachDecision::NoAction;
  14415  }
  14416  Rooted<BoundFunctionObject*> bound(
  14417      cx_, &thisval_.toObject().as<BoundFunctionObject>());
  14418 
  14419  // The target must be a native JSFunction without a JitEntry.
  14420  Rooted<JSObject*> boundTarget(cx_, bound->getTarget());
  14421  if (!boundTarget->is<JSFunction>()) {
  14422    return AttachDecision::NoAction;
  14423  }
  14424  auto target = boundTarget.as<JSFunction>();
  14425 
  14426  bool isScripted = target->hasJitEntry();
  14427  MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
  14428 
  14429  // We don't yet supported scripted bound targets.
  14430  if (isScripted) {
  14431    return AttachDecision::NoAction;
  14432  }
  14433 
  14434  // Limit the number of bound arguments to prevent us from compiling many
  14435  // different stubs (we bake in numBoundArgs and it's usually very small).
  14436  static constexpr size_t MaxBoundArgs = 10;
  14437  size_t numBoundArgs = bound->numBoundArgs();
  14438  if (numBoundArgs > MaxBoundArgs) {
  14439    return AttachDecision::NoAction;
  14440  }
  14441 
  14442  // The second argument must be |null| or |undefined|, because we only support
  14443  // |CallFlags::FunCall| and |CallFlags::FunApplyNullUndefined|.
  14444  CallFlags::ArgFormat format;
  14445  if (argc_ < 2) {
  14446    format = CallFlags::FunCall;
  14447  } else if (args_[1].isNullOrUndefined()) {
  14448    format = CallFlags::FunApplyNullUndefined;
  14449  } else {
  14450    return AttachDecision::NoAction;
  14451  }
  14452 
  14453  // Don't try to optimize when we're already megamorphic.
  14454  if (mode_ != ICState::Mode::Specialized) {
  14455    return AttachDecision::NoAction;
  14456  }
  14457 
  14458  CallFlags targetFlags(format);
  14459  if (cx_->realm() == target->realm()) {
  14460    targetFlags.setIsSameRealm();
  14461  }
  14462 
  14463  HandleValue newTarget = NullHandleValue;
  14464 
  14465  // Use the bound |this| value.
  14466  Rooted<Value> thisValue(cx_, bound->getBoundThis());
  14467 
  14468  // Collect all bound arguments.
  14469  JS::RootedVector<Value> args(cx_);
  14470  if (numBoundArgs != 0) {
  14471    if (!args.reserve(numBoundArgs)) {
  14472      cx_->recoverFromOutOfMemory();
  14473      return AttachDecision::NoAction;
  14474    }
  14475 
  14476    for (size_t i = 0; i < numBoundArgs; i++) {
  14477      args.infallibleAppend(bound->getBoundArg(i));
  14478    }
  14479  }
  14480 
  14481  // Check for specific native-function optimizations.
  14482  InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget,
  14483                                       thisValue, args, targetFlags, bound);
  14484  return nativeGen.tryAttachStub();
  14485 }
  14486 
  14487 AttachDecision CallIRGenerator::tryAttachStub() {
  14488  AutoAssertNoPendingException aanpe(cx_);
  14489 
  14490  // Some opcodes are not yet supported.
  14491  switch (jsop()) {
  14492    case JSOp::Call:
  14493    case JSOp::CallContent:
  14494    case JSOp::CallIgnoresRv:
  14495    case JSOp::CallIter:
  14496    case JSOp::CallContentIter:
  14497    case JSOp::SpreadCall:
  14498    case JSOp::New:
  14499    case JSOp::NewContent:
  14500    case JSOp::SpreadNew:
  14501    case JSOp::SuperCall:
  14502    case JSOp::SpreadSuperCall:
  14503      break;
  14504    default:
  14505      return AttachDecision::NoAction;
  14506  }
  14507 
  14508  MOZ_ASSERT(mode_ != ICState::Mode::Generic);
  14509 
  14510  // Ensure callee is a function.
  14511  if (!callee_.isObject()) {
  14512    return AttachDecision::NoAction;
  14513  }
  14514 
  14515  RootedObject calleeObj(cx_, &callee_.toObject());
  14516  if (calleeObj->is<BoundFunctionObject>()) {
  14517    auto boundCalleeObj = calleeObj.as<BoundFunctionObject>();
  14518 
  14519    TRY_ATTACH(tryAttachBoundFunction(boundCalleeObj));
  14520    TRY_ATTACH(tryAttachBoundNative(boundCalleeObj));
  14521    TRY_ATTACH(tryAttachBoundFunCall(boundCalleeObj));
  14522    TRY_ATTACH(tryAttachBoundFunApply(boundCalleeObj));
  14523  }
  14524  if (!calleeObj->is<JSFunction>()) {
  14525    return tryAttachCallHook(calleeObj);
  14526  }
  14527 
  14528  HandleFunction calleeFunc = calleeObj.as<JSFunction>();
  14529 
  14530  // Check for scripted optimizations.
  14531  if (calleeFunc->hasJitEntry()) {
  14532    return tryAttachCallScripted(calleeFunc);
  14533  }
  14534 
  14535  // Check for native-function optimizations.
  14536  MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
  14537 
  14538  // Try inlining Function.prototype.{call,apply}. We don't use the
  14539  // InlinableNative mechanism for this because we want to optimize these more
  14540  // aggressively than other natives.
  14541  if (IsInlinableFunCallOrApply(jsop())) {
  14542    TRY_ATTACH(tryAttachFunCall(calleeFunc));
  14543    TRY_ATTACH(tryAttachFunApply(calleeFunc));
  14544    TRY_ATTACH(tryAttachFunCallBound(calleeFunc));
  14545    TRY_ATTACH(tryAttachFunApplyBound(calleeFunc));
  14546  }
  14547 
  14548  return tryAttachCallNative(calleeFunc);
  14549 }
  14550 
  14551 void CallIRGenerator::trackAttached(const char* name) {
  14552  stubName_ = name ? name : "NotAttached";
  14553 #ifdef JS_CACHEIR_SPEW
  14554  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  14555    sp.valueProperty("callee", callee_);
  14556    sp.valueProperty("thisval", thisval_);
  14557    sp.valueProperty("argc", Int32Value(argc_));
  14558 
  14559    // Try to log the first two arguments.
  14560    if (args_.length() >= 1) {
  14561      sp.valueProperty("arg0", args_[0]);
  14562    }
  14563    if (args_.length() >= 2) {
  14564      sp.valueProperty("arg1", args_[1]);
  14565    }
  14566  }
  14567 #endif
  14568 }
  14569 
  14570 // Class which holds a shape pointer for use when caches might reference data in
  14571 // other zones.
  14572 static const JSClass shapeContainerClass = {"ShapeContainer",
  14573                                            JSCLASS_HAS_RESERVED_SLOTS(1)};
  14574 
  14575 static const size_t SHAPE_CONTAINER_SLOT = 0;
  14576 
  14577 static JSObject* NewWrapperWithObjectShape(JSContext* cx,
  14578                                           Handle<NativeObject*> obj) {
  14579  MOZ_ASSERT(cx->compartment() != obj->compartment());
  14580 
  14581  RootedObject wrapper(cx);
  14582  {
  14583    AutoRealm ar(cx, obj);
  14584    wrapper = NewBuiltinClassInstance(cx, &shapeContainerClass);
  14585    if (!wrapper) {
  14586      return nullptr;
  14587    }
  14588    wrapper->as<NativeObject>().setReservedSlot(
  14589        SHAPE_CONTAINER_SLOT, PrivateGCThingValue(obj->shape()));
  14590  }
  14591  if (!JS_WrapObject(cx, &wrapper)) {
  14592    return nullptr;
  14593  }
  14594  MOZ_ASSERT(IsWrapper(wrapper));
  14595  return wrapper;
  14596 }
  14597 
  14598 void jit::LoadShapeWrapperContents(MacroAssembler& masm, Register obj,
  14599                                   Register dst, Label* failure) {
  14600  masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), dst);
  14601  Address privateAddr(dst,
  14602                      js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
  14603  masm.fallibleUnboxObject(privateAddr, dst, failure);
  14604  masm.unboxNonDouble(
  14605      Address(dst, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT)), dst,
  14606      JSVAL_TYPE_PRIVATE_GCTHING);
  14607 }
  14608 
  14609 static bool CanConvertToInt32ForToNumber(const Value& v) {
  14610  return v.isInt32() || v.isBoolean() || v.isNull();
  14611 }
  14612 
  14613 static Int32OperandId EmitGuardToInt32ForToNumber(CacheIRWriter& writer,
  14614                                                  ValOperandId id,
  14615                                                  const Value& v) {
  14616  if (v.isInt32()) {
  14617    return writer.guardToInt32(id);
  14618  }
  14619  if (v.isNull()) {
  14620    writer.guardIsNull(id);
  14621    return writer.loadInt32Constant(0);
  14622  }
  14623  MOZ_ASSERT(v.isBoolean());
  14624  return writer.guardBooleanToInt32(id);
  14625 }
  14626 
  14627 static bool CanConvertToDoubleForToNumber(const Value& v) {
  14628  return v.isNumber() || v.isBoolean() || v.isNullOrUndefined();
  14629 }
  14630 
  14631 static NumberOperandId EmitGuardToDoubleForToNumber(CacheIRWriter& writer,
  14632                                                    ValOperandId id,
  14633                                                    const Value& v) {
  14634  if (v.isNumber()) {
  14635    return writer.guardIsNumber(id);
  14636  }
  14637  if (v.isBoolean()) {
  14638    BooleanOperandId boolId = writer.guardToBoolean(id);
  14639    return writer.booleanToNumber(boolId);
  14640  }
  14641  if (v.isNull()) {
  14642    writer.guardIsNull(id);
  14643    return writer.loadDoubleConstant(0.0);
  14644  }
  14645  MOZ_ASSERT(v.isUndefined());
  14646  writer.guardIsUndefined(id);
  14647  return writer.loadDoubleConstant(JS::GenericNaN());
  14648 }
  14649 
  14650 CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script,
  14651                                       jsbytecode* pc, ICState state, JSOp op,
  14652                                       HandleValue lhsVal, HandleValue rhsVal)
  14653    : IRGenerator(cx, script, pc, CacheKind::Compare, state),
  14654      op_(op),
  14655      lhsVal_(lhsVal),
  14656      rhsVal_(rhsVal) {}
  14657 
  14658 AttachDecision CompareIRGenerator::tryAttachString(ValOperandId lhsId,
  14659                                                   ValOperandId rhsId) {
  14660  if (!lhsVal_.isString() || !rhsVal_.isString()) {
  14661    return AttachDecision::NoAction;
  14662  }
  14663 
  14664  StringOperandId lhsStrId = writer.guardToString(lhsId);
  14665  StringOperandId rhsStrId = writer.guardToString(rhsId);
  14666  writer.compareStringResult(op_, lhsStrId, rhsStrId);
  14667  writer.returnFromIC();
  14668 
  14669  trackAttached("Compare.String");
  14670  return AttachDecision::Attach;
  14671 }
  14672 
  14673 AttachDecision CompareIRGenerator::tryAttachObject(ValOperandId lhsId,
  14674                                                   ValOperandId rhsId) {
  14675  MOZ_ASSERT(IsEqualityOp(op_));
  14676 
  14677  if (!lhsVal_.isObject() || !rhsVal_.isObject()) {
  14678    return AttachDecision::NoAction;
  14679  }
  14680 
  14681  ObjOperandId lhsObjId = writer.guardToObject(lhsId);
  14682  ObjOperandId rhsObjId = writer.guardToObject(rhsId);
  14683  writer.compareObjectResult(op_, lhsObjId, rhsObjId);
  14684  writer.returnFromIC();
  14685 
  14686  trackAttached("Compare.Object");
  14687  return AttachDecision::Attach;
  14688 }
  14689 
  14690 AttachDecision CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId,
  14691                                                   ValOperandId rhsId) {
  14692  MOZ_ASSERT(IsEqualityOp(op_));
  14693 
  14694  if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol()) {
  14695    return AttachDecision::NoAction;
  14696  }
  14697 
  14698  SymbolOperandId lhsSymId = writer.guardToSymbol(lhsId);
  14699  SymbolOperandId rhsSymId = writer.guardToSymbol(rhsId);
  14700  writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
  14701  writer.returnFromIC();
  14702 
  14703  trackAttached("Compare.Symbol");
  14704  return AttachDecision::Attach;
  14705 }
  14706 
  14707 AttachDecision CompareIRGenerator::tryAttachStrictDifferentTypes(
  14708    ValOperandId lhsId, ValOperandId rhsId) {
  14709  MOZ_ASSERT(IsEqualityOp(op_));
  14710 
  14711  if (op_ != JSOp::StrictEq && op_ != JSOp::StrictNe) {
  14712    return AttachDecision::NoAction;
  14713  }
  14714 
  14715  // Probably can't hit some of these.
  14716  if (SameType(lhsVal_, rhsVal_) ||
  14717      (lhsVal_.isNumber() && rhsVal_.isNumber())) {
  14718    return AttachDecision::NoAction;
  14719  }
  14720 
  14721  // Compare tags
  14722  ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
  14723  ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
  14724  writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
  14725 
  14726  // Now that we've passed the guard, we know differing types, so return the
  14727  // bool result.
  14728  writer.loadBooleanResult(op_ == JSOp::StrictNe ? true : false);
  14729  writer.returnFromIC();
  14730 
  14731  trackAttached("Compare.StrictDifferentTypes");
  14732  return AttachDecision::Attach;
  14733 }
  14734 
  14735 AttachDecision CompareIRGenerator::tryAttachInt32(ValOperandId lhsId,
  14736                                                  ValOperandId rhsId) {
  14737  if (!CanConvertToInt32ForToNumber(lhsVal_) ||
  14738      !CanConvertToInt32ForToNumber(rhsVal_)) {
  14739    return AttachDecision::NoAction;
  14740  }
  14741 
  14742  // Strictly different types should have been handed by
  14743  // tryAttachStrictDifferentTypes.
  14744  MOZ_ASSERT_IF(op_ == JSOp::StrictEq || op_ == JSOp::StrictNe,
  14745                lhsVal_.type() == rhsVal_.type());
  14746 
  14747  // Should have been handled by tryAttachAnyNullUndefined.
  14748  MOZ_ASSERT_IF(lhsVal_.isNull() || rhsVal_.isNull(), !IsEqualityOp(op_));
  14749 
  14750  Int32OperandId lhsIntId = EmitGuardToInt32ForToNumber(writer, lhsId, lhsVal_);
  14751  Int32OperandId rhsIntId = EmitGuardToInt32ForToNumber(writer, rhsId, rhsVal_);
  14752 
  14753  writer.compareInt32Result(op_, lhsIntId, rhsIntId);
  14754  writer.returnFromIC();
  14755 
  14756  trackAttached("Compare.Int32");
  14757  return AttachDecision::Attach;
  14758 }
  14759 
  14760 AttachDecision CompareIRGenerator::tryAttachNumber(ValOperandId lhsId,
  14761                                                   ValOperandId rhsId) {
  14762  if (!CanConvertToDoubleForToNumber(lhsVal_) ||
  14763      !CanConvertToDoubleForToNumber(rhsVal_)) {
  14764    return AttachDecision::NoAction;
  14765  }
  14766 
  14767  // Strictly different types should have been handed by
  14768  // tryAttachStrictDifferentTypes.
  14769  MOZ_ASSERT_IF(op_ == JSOp::StrictEq || op_ == JSOp::StrictNe,
  14770                lhsVal_.type() == rhsVal_.type() ||
  14771                    (lhsVal_.isNumber() && rhsVal_.isNumber()));
  14772 
  14773  // Should have been handled by tryAttachAnyNullUndefined.
  14774  MOZ_ASSERT_IF(lhsVal_.isNullOrUndefined() || rhsVal_.isNullOrUndefined(),
  14775                !IsEqualityOp(op_));
  14776 
  14777  NumberOperandId lhs = EmitGuardToDoubleForToNumber(writer, lhsId, lhsVal_);
  14778  NumberOperandId rhs = EmitGuardToDoubleForToNumber(writer, rhsId, rhsVal_);
  14779  writer.compareDoubleResult(op_, lhs, rhs);
  14780  writer.returnFromIC();
  14781 
  14782  trackAttached("Compare.Number");
  14783  return AttachDecision::Attach;
  14784 }
  14785 
  14786 AttachDecision CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId,
  14787                                                   ValOperandId rhsId) {
  14788  if (!lhsVal_.isBigInt() || !rhsVal_.isBigInt()) {
  14789    return AttachDecision::NoAction;
  14790  }
  14791 
  14792  BigIntOperandId lhs = writer.guardToBigInt(lhsId);
  14793  BigIntOperandId rhs = writer.guardToBigInt(rhsId);
  14794 
  14795  writer.compareBigIntResult(op_, lhs, rhs);
  14796  writer.returnFromIC();
  14797 
  14798  trackAttached("Compare.BigInt");
  14799  return AttachDecision::Attach;
  14800 }
  14801 
  14802 AttachDecision CompareIRGenerator::tryAttachAnyNullUndefined(
  14803    ValOperandId lhsId, ValOperandId rhsId) {
  14804  MOZ_ASSERT(IsEqualityOp(op_));
  14805 
  14806  // Either RHS or LHS needs to be null/undefined.
  14807  if (!lhsVal_.isNullOrUndefined() && !rhsVal_.isNullOrUndefined()) {
  14808    return AttachDecision::NoAction;
  14809  }
  14810 
  14811  // We assume that the side with null/undefined is usually constant, in
  14812  // code like `if (x === undefined) { x = {}; }`.
  14813  // That is why we don't attach when both sides are undefined/null,
  14814  // because we would basically need to decide by chance which side is
  14815  // the likely constant.
  14816  // The actual generated code however handles null/undefined of course.
  14817  if (lhsVal_.isNullOrUndefined() && rhsVal_.isNullOrUndefined()) {
  14818    return AttachDecision::NoAction;
  14819  }
  14820 
  14821  if (rhsVal_.isNullOrUndefined()) {
  14822    if (rhsVal_.isNull()) {
  14823      writer.guardIsNull(rhsId);
  14824      writer.compareNullUndefinedResult(op_, /* isUndefined */ false, lhsId);
  14825      trackAttached("Compare.AnyNull");
  14826    } else {
  14827      writer.guardIsUndefined(rhsId);
  14828      writer.compareNullUndefinedResult(op_, /* isUndefined */ true, lhsId);
  14829      trackAttached("Compare.AnyUndefined");
  14830    }
  14831  } else {
  14832    if (lhsVal_.isNull()) {
  14833      writer.guardIsNull(lhsId);
  14834      writer.compareNullUndefinedResult(op_, /* isUndefined */ false, rhsId);
  14835      trackAttached("Compare.NullAny");
  14836    } else {
  14837      writer.guardIsUndefined(lhsId);
  14838      writer.compareNullUndefinedResult(op_, /* isUndefined */ true, rhsId);
  14839      trackAttached("Compare.UndefinedAny");
  14840    }
  14841  }
  14842 
  14843  writer.returnFromIC();
  14844  return AttachDecision::Attach;
  14845 }
  14846 
  14847 // Handle {null/undefined} x {null,undefined} equality comparisons
  14848 AttachDecision CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId,
  14849                                                          ValOperandId rhsId) {
  14850  if (!lhsVal_.isNullOrUndefined() || !rhsVal_.isNullOrUndefined()) {
  14851    return AttachDecision::NoAction;
  14852  }
  14853 
  14854  if (op_ == JSOp::Eq || op_ == JSOp::Ne) {
  14855    writer.guardIsNullOrUndefined(lhsId);
  14856    writer.guardIsNullOrUndefined(rhsId);
  14857    // Sloppy equality means we actually only care about the op:
  14858    writer.loadBooleanResult(op_ == JSOp::Eq);
  14859    trackAttached("Compare.SloppyNullUndefined");
  14860  } else {
  14861    // Strict equality only hits this branch, and only in the
  14862    // undef {!,=}==  undef and null {!,=}== null cases.
  14863    // The other cases should have hit tryAttachStrictDifferentTypes.
  14864    MOZ_ASSERT(lhsVal_.isNull() == rhsVal_.isNull());
  14865    lhsVal_.isNull() ? writer.guardIsNull(lhsId)
  14866                     : writer.guardIsUndefined(lhsId);
  14867    rhsVal_.isNull() ? writer.guardIsNull(rhsId)
  14868                     : writer.guardIsUndefined(rhsId);
  14869    writer.loadBooleanResult(op_ == JSOp::StrictEq);
  14870    trackAttached("Compare.StrictNullUndefinedEquality");
  14871  }
  14872 
  14873  writer.returnFromIC();
  14874  return AttachDecision::Attach;
  14875 }
  14876 
  14877 AttachDecision CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId,
  14878                                                         ValOperandId rhsId) {
  14879  // Ensure String x {Number, Boolean, Null, Undefined}
  14880  if (!(lhsVal_.isString() && CanConvertToDoubleForToNumber(rhsVal_)) &&
  14881      !(rhsVal_.isString() && CanConvertToDoubleForToNumber(lhsVal_))) {
  14882    return AttachDecision::NoAction;
  14883  }
  14884 
  14885  // Case should have been handled by tryAttachStrictDifferentTypes
  14886  MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
  14887 
  14888  auto createGuards = [&](const Value& v, ValOperandId vId) {
  14889    if (v.isString()) {
  14890      StringOperandId strId = writer.guardToString(vId);
  14891      return writer.guardStringToNumber(strId);
  14892    }
  14893    return EmitGuardToDoubleForToNumber(writer, vId, v);
  14894  };
  14895 
  14896  NumberOperandId lhsGuardedId = createGuards(lhsVal_, lhsId);
  14897  NumberOperandId rhsGuardedId = createGuards(rhsVal_, rhsId);
  14898  writer.compareDoubleResult(op_, lhsGuardedId, rhsGuardedId);
  14899  writer.returnFromIC();
  14900 
  14901  trackAttached("Compare.StringNumber");
  14902  return AttachDecision::Attach;
  14903 }
  14904 
  14905 AttachDecision CompareIRGenerator::tryAttachPrimitiveSymbol(
  14906    ValOperandId lhsId, ValOperandId rhsId) {
  14907  MOZ_ASSERT(IsEqualityOp(op_));
  14908 
  14909  // The set of primitive cases we want to handle here (excluding null,
  14910  // undefined, and symbol)
  14911  auto isPrimitive = [](const Value& x) {
  14912    return x.isString() || x.isBoolean() || x.isNumber() || x.isBigInt();
  14913  };
  14914 
  14915  // Ensure Symbol x {String, Bool, Number, BigInt}.
  14916  if (!(lhsVal_.isSymbol() && isPrimitive(rhsVal_)) &&
  14917      !(rhsVal_.isSymbol() && isPrimitive(lhsVal_))) {
  14918    return AttachDecision::NoAction;
  14919  }
  14920 
  14921  auto guardPrimitive = [&](const Value& v, ValOperandId id) {
  14922    MOZ_ASSERT(isPrimitive(v));
  14923    if (v.isNumber()) {
  14924      writer.guardIsNumber(id);
  14925      return;
  14926    }
  14927    switch (v.extractNonDoubleType()) {
  14928      case JSVAL_TYPE_STRING:
  14929        writer.guardToString(id);
  14930        return;
  14931      case JSVAL_TYPE_BOOLEAN:
  14932        writer.guardToBoolean(id);
  14933        return;
  14934      case JSVAL_TYPE_BIGINT:
  14935        writer.guardToBigInt(id);
  14936        return;
  14937      default:
  14938        MOZ_CRASH("unexpected type");
  14939        return;
  14940    }
  14941  };
  14942 
  14943  if (lhsVal_.isSymbol()) {
  14944    writer.guardToSymbol(lhsId);
  14945    guardPrimitive(rhsVal_, rhsId);
  14946  } else {
  14947    guardPrimitive(lhsVal_, lhsId);
  14948    writer.guardToSymbol(rhsId);
  14949  }
  14950 
  14951  // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
  14952  // always be false for other compare ops.
  14953  writer.loadBooleanResult(op_ == JSOp::Ne || op_ == JSOp::StrictNe);
  14954  writer.returnFromIC();
  14955 
  14956  trackAttached("Compare.PrimitiveSymbol");
  14957  return AttachDecision::Attach;
  14958 }
  14959 
  14960 AttachDecision CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId,
  14961                                                        ValOperandId rhsId) {
  14962  // Ensure BigInt x {Int32, Boolean, Null}.
  14963  if (!(lhsVal_.isBigInt() && CanConvertToInt32ForToNumber(rhsVal_)) &&
  14964      !(rhsVal_.isBigInt() && CanConvertToInt32ForToNumber(lhsVal_))) {
  14965    return AttachDecision::NoAction;
  14966  }
  14967 
  14968  // Case should have been handled by tryAttachStrictDifferentTypes
  14969  MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
  14970 
  14971  if (lhsVal_.isBigInt()) {
  14972    BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
  14973    Int32OperandId intId = EmitGuardToInt32ForToNumber(writer, rhsId, rhsVal_);
  14974 
  14975    writer.compareBigIntInt32Result(op_, bigIntId, intId);
  14976  } else {
  14977    Int32OperandId intId = EmitGuardToInt32ForToNumber(writer, lhsId, lhsVal_);
  14978    BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
  14979 
  14980    writer.compareBigIntInt32Result(ReverseCompareOp(op_), bigIntId, intId);
  14981  }
  14982  writer.returnFromIC();
  14983 
  14984  trackAttached("Compare.BigIntInt32");
  14985  return AttachDecision::Attach;
  14986 }
  14987 
  14988 AttachDecision CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId,
  14989                                                         ValOperandId rhsId) {
  14990  // Ensure BigInt x {Number, Undefined}.
  14991  if (!(lhsVal_.isBigInt() && CanConvertToDoubleForToNumber(rhsVal_)) &&
  14992      !(rhsVal_.isBigInt() && CanConvertToDoubleForToNumber(lhsVal_))) {
  14993    return AttachDecision::NoAction;
  14994  }
  14995 
  14996  // Case should have been handled by tryAttachStrictDifferentTypes
  14997  MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
  14998 
  14999  // Case should have been handled by tryAttachBigIntInt32.
  15000  MOZ_ASSERT(!CanConvertToInt32ForToNumber(lhsVal_));
  15001  MOZ_ASSERT(!CanConvertToInt32ForToNumber(rhsVal_));
  15002 
  15003  if (lhsVal_.isBigInt()) {
  15004    BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
  15005    NumberOperandId numId =
  15006        EmitGuardToDoubleForToNumber(writer, rhsId, rhsVal_);
  15007 
  15008    writer.compareBigIntNumberResult(op_, bigIntId, numId);
  15009  } else {
  15010    NumberOperandId numId =
  15011        EmitGuardToDoubleForToNumber(writer, lhsId, lhsVal_);
  15012    BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
  15013 
  15014    writer.compareBigIntNumberResult(ReverseCompareOp(op_), bigIntId, numId);
  15015  }
  15016  writer.returnFromIC();
  15017 
  15018  trackAttached("Compare.BigIntNumber");
  15019  return AttachDecision::Attach;
  15020 }
  15021 
  15022 AttachDecision CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId,
  15023                                                         ValOperandId rhsId) {
  15024  // Ensure BigInt x String.
  15025  if (!(lhsVal_.isBigInt() && rhsVal_.isString()) &&
  15026      !(rhsVal_.isBigInt() && lhsVal_.isString())) {
  15027    return AttachDecision::NoAction;
  15028  }
  15029 
  15030  // Case should have been handled by tryAttachStrictDifferentTypes
  15031  MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
  15032 
  15033  if (lhsVal_.isBigInt()) {
  15034    BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
  15035    StringOperandId strId = writer.guardToString(rhsId);
  15036 
  15037    writer.compareBigIntStringResult(op_, bigIntId, strId);
  15038  } else {
  15039    StringOperandId strId = writer.guardToString(lhsId);
  15040    BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
  15041 
  15042    writer.compareBigIntStringResult(ReverseCompareOp(op_), bigIntId, strId);
  15043  }
  15044  writer.returnFromIC();
  15045 
  15046  trackAttached("Compare.BigIntString");
  15047  return AttachDecision::Attach;
  15048 }
  15049 
  15050 AttachDecision CompareIRGenerator::tryAttachStub() {
  15051  MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
  15052  MOZ_ASSERT(IsEqualityOp(op_) || IsRelationalOp(op_));
  15053 
  15054  AutoAssertNoPendingException aanpe(cx_);
  15055 
  15056  constexpr uint8_t lhsIndex = 0;
  15057  constexpr uint8_t rhsIndex = 1;
  15058 
  15059  ValOperandId lhsId(writer.setInputOperandId(lhsIndex));
  15060  ValOperandId rhsId(writer.setInputOperandId(rhsIndex));
  15061 
  15062  // For sloppy equality ops, there are cases this IC does not handle:
  15063  // - {Object} x {String, Symbol, Bool, Number, BigInt}.
  15064  //
  15065  // For relational comparison ops, these cases aren't handled:
  15066  // - Object x {String, Bool, Number, BigInt, Object, Null, Undefined}.
  15067  // Note: |Symbol x any| always throws, so it doesn't need to be handled.
  15068  //
  15069  // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
  15070  // already present.)
  15071 
  15072  if (IsEqualityOp(op_)) {
  15073    TRY_ATTACH(tryAttachObject(lhsId, rhsId));
  15074    TRY_ATTACH(tryAttachSymbol(lhsId, rhsId));
  15075 
  15076    // Handles any (non null or undefined) comparison with null/undefined.
  15077    TRY_ATTACH(tryAttachAnyNullUndefined(lhsId, rhsId));
  15078 
  15079    // This covers -strict- equality/inequality using a type tag check, so
  15080    // catches all different type pairs outside of Numbers, which cannot be
  15081    // checked on tags alone.
  15082    TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId, rhsId));
  15083 
  15084    TRY_ATTACH(tryAttachNullUndefined(lhsId, rhsId));
  15085 
  15086    TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId, rhsId));
  15087  }
  15088 
  15089  // We want these to be last, to allow us to bypass the
  15090  // strictly-different-types cases in the below attachment code
  15091  TRY_ATTACH(tryAttachInt32(lhsId, rhsId));
  15092  TRY_ATTACH(tryAttachNumber(lhsId, rhsId));
  15093  TRY_ATTACH(tryAttachBigInt(lhsId, rhsId));
  15094  TRY_ATTACH(tryAttachString(lhsId, rhsId));
  15095 
  15096  TRY_ATTACH(tryAttachStringNumber(lhsId, rhsId));
  15097 
  15098  TRY_ATTACH(tryAttachBigIntInt32(lhsId, rhsId));
  15099  TRY_ATTACH(tryAttachBigIntNumber(lhsId, rhsId));
  15100  TRY_ATTACH(tryAttachBigIntString(lhsId, rhsId));
  15101 
  15102  // Strict equality is always supported.
  15103  MOZ_ASSERT(!IsStrictEqualityOp(op_));
  15104 
  15105  // Other operations are unsupported iff at least one operand is an object.
  15106  MOZ_ASSERT(lhsVal_.isObject() || rhsVal_.isObject());
  15107 
  15108  trackAttached(IRGenerator::NotAttached);
  15109  return AttachDecision::NoAction;
  15110 }
  15111 
  15112 void CompareIRGenerator::trackAttached(const char* name) {
  15113  stubName_ = name ? name : "NotAttached";
  15114 #ifdef JS_CACHEIR_SPEW
  15115  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15116    sp.valueProperty("lhs", lhsVal_);
  15117    sp.valueProperty("rhs", rhsVal_);
  15118    sp.opcodeProperty("op", op_);
  15119  }
  15120 #endif
  15121 }
  15122 
  15123 ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script,
  15124                                     jsbytecode* pc, ICState state,
  15125                                     HandleValue val)
  15126    : IRGenerator(cx, script, pc, CacheKind::ToBool, state), val_(val) {}
  15127 
  15128 void ToBoolIRGenerator::trackAttached(const char* name) {
  15129  stubName_ = name ? name : "NotAttached";
  15130 #ifdef JS_CACHEIR_SPEW
  15131  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15132    sp.valueProperty("val", val_);
  15133  }
  15134 #endif
  15135 }
  15136 
  15137 AttachDecision ToBoolIRGenerator::tryAttachStub() {
  15138  AutoAssertNoPendingException aanpe(cx_);
  15139  writer.setTypeData(TypeData(JSValueType(val_.type())));
  15140 
  15141  TRY_ATTACH(tryAttachBool());
  15142  TRY_ATTACH(tryAttachInt32());
  15143  TRY_ATTACH(tryAttachNumber());
  15144  TRY_ATTACH(tryAttachString());
  15145  TRY_ATTACH(tryAttachNullOrUndefined());
  15146  TRY_ATTACH(tryAttachObject());
  15147  TRY_ATTACH(tryAttachSymbol());
  15148  TRY_ATTACH(tryAttachBigInt());
  15149 
  15150  trackAttached(IRGenerator::NotAttached);
  15151  return AttachDecision::NoAction;
  15152 }
  15153 
  15154 AttachDecision ToBoolIRGenerator::tryAttachBool() {
  15155  if (!val_.isBoolean()) {
  15156    return AttachDecision::NoAction;
  15157  }
  15158 
  15159  ValOperandId valId(writer.setInputOperandId(0));
  15160  writer.guardNonDoubleType(valId, ValueType::Boolean);
  15161  writer.loadOperandResult(valId);
  15162  writer.returnFromIC();
  15163  trackAttached("ToBool.Bool");
  15164  return AttachDecision::Attach;
  15165 }
  15166 
  15167 AttachDecision ToBoolIRGenerator::tryAttachInt32() {
  15168  if (!val_.isInt32()) {
  15169    return AttachDecision::NoAction;
  15170  }
  15171 
  15172  ValOperandId valId(writer.setInputOperandId(0));
  15173  writer.guardNonDoubleType(valId, ValueType::Int32);
  15174  writer.loadInt32TruthyResult(valId);
  15175  writer.returnFromIC();
  15176  trackAttached("ToBool.Int32");
  15177  return AttachDecision::Attach;
  15178 }
  15179 
  15180 AttachDecision ToBoolIRGenerator::tryAttachNumber() {
  15181  if (!val_.isNumber()) {
  15182    return AttachDecision::NoAction;
  15183  }
  15184 
  15185  ValOperandId valId(writer.setInputOperandId(0));
  15186  NumberOperandId numId = writer.guardIsNumber(valId);
  15187  writer.loadDoubleTruthyResult(numId);
  15188  writer.returnFromIC();
  15189  trackAttached("ToBool.Number");
  15190  return AttachDecision::Attach;
  15191 }
  15192 
  15193 AttachDecision ToBoolIRGenerator::tryAttachSymbol() {
  15194  if (!val_.isSymbol()) {
  15195    return AttachDecision::NoAction;
  15196  }
  15197 
  15198  ValOperandId valId(writer.setInputOperandId(0));
  15199  writer.guardNonDoubleType(valId, ValueType::Symbol);
  15200  writer.loadBooleanResult(true);
  15201  writer.returnFromIC();
  15202  trackAttached("ToBool.Symbol");
  15203  return AttachDecision::Attach;
  15204 }
  15205 
  15206 AttachDecision ToBoolIRGenerator::tryAttachString() {
  15207  if (!val_.isString()) {
  15208    return AttachDecision::NoAction;
  15209  }
  15210 
  15211  ValOperandId valId(writer.setInputOperandId(0));
  15212  StringOperandId strId = writer.guardToString(valId);
  15213  writer.loadStringTruthyResult(strId);
  15214  writer.returnFromIC();
  15215  trackAttached("ToBool.String");
  15216  return AttachDecision::Attach;
  15217 }
  15218 
  15219 AttachDecision ToBoolIRGenerator::tryAttachNullOrUndefined() {
  15220  if (!val_.isNullOrUndefined()) {
  15221    return AttachDecision::NoAction;
  15222  }
  15223 
  15224  ValOperandId valId(writer.setInputOperandId(0));
  15225  writer.guardIsNullOrUndefined(valId);
  15226  writer.loadBooleanResult(false);
  15227  writer.returnFromIC();
  15228  trackAttached("ToBool.NullOrUndefined");
  15229  return AttachDecision::Attach;
  15230 }
  15231 
  15232 AttachDecision ToBoolIRGenerator::tryAttachObject() {
  15233  if (!val_.isObject()) {
  15234    return AttachDecision::NoAction;
  15235  }
  15236 
  15237  ValOperandId valId(writer.setInputOperandId(0));
  15238  ObjOperandId objId = writer.guardToObject(valId);
  15239  writer.loadObjectTruthyResult(objId);
  15240  writer.returnFromIC();
  15241  trackAttached("ToBool.Object");
  15242  return AttachDecision::Attach;
  15243 }
  15244 
  15245 AttachDecision ToBoolIRGenerator::tryAttachBigInt() {
  15246  if (!val_.isBigInt()) {
  15247    return AttachDecision::NoAction;
  15248  }
  15249 
  15250  ValOperandId valId(writer.setInputOperandId(0));
  15251  BigIntOperandId bigIntId = writer.guardToBigInt(valId);
  15252  writer.loadBigIntTruthyResult(bigIntId);
  15253  writer.returnFromIC();
  15254  trackAttached("ToBool.BigInt");
  15255  return AttachDecision::Attach;
  15256 }
  15257 
  15258 LazyConstantIRGenerator::LazyConstantIRGenerator(JSContext* cx,
  15259                                                 HandleScript script,
  15260                                                 jsbytecode* pc, ICState state,
  15261                                                 HandleValue val)
  15262    : IRGenerator(cx, script, pc, CacheKind::LazyConstant, state), val_(val) {}
  15263 
  15264 void LazyConstantIRGenerator::trackAttached(const char* name) {
  15265  stubName_ = name ? name : "NotAttached";
  15266 #ifdef JS_CACHEIR_SPEW
  15267  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15268    sp.valueProperty("val", val_);
  15269  }
  15270 #endif
  15271 }
  15272 
  15273 AttachDecision LazyConstantIRGenerator::tryAttachStub() {
  15274  AutoAssertNoPendingException aanpe(cx_);
  15275  writer.loadValueResult(val_);
  15276  writer.returnFromIC();
  15277  trackAttached("LazyConstant");
  15278  return AttachDecision::Attach;
  15279 }
  15280 
  15281 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext* cx, HandleScript script,
  15282                                             jsbytecode* pc, ICState state,
  15283                                             JSOp op, HandleValue val,
  15284                                             HandleValue res)
  15285    : IRGenerator(cx, script, pc, CacheKind::UnaryArith, state),
  15286      op_(op),
  15287      val_(val),
  15288      res_(res) {}
  15289 
  15290 void UnaryArithIRGenerator::trackAttached(const char* name) {
  15291  stubName_ = name ? name : "NotAttached";
  15292 #ifdef JS_CACHEIR_SPEW
  15293  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15294    sp.valueProperty("val", val_);
  15295    sp.valueProperty("res", res_);
  15296  }
  15297 #endif
  15298 }
  15299 
  15300 AttachDecision UnaryArithIRGenerator::tryAttachStub() {
  15301  AutoAssertNoPendingException aanpe(cx_);
  15302  TRY_ATTACH(tryAttachInt32());
  15303  TRY_ATTACH(tryAttachNumber());
  15304  TRY_ATTACH(tryAttachBitwise());
  15305  TRY_ATTACH(tryAttachBigIntPtr());
  15306  TRY_ATTACH(tryAttachBigInt());
  15307  TRY_ATTACH(tryAttachStringInt32());
  15308  TRY_ATTACH(tryAttachStringNumber());
  15309 
  15310  trackAttached(IRGenerator::NotAttached);
  15311  return AttachDecision::NoAction;
  15312 }
  15313 
  15314 AttachDecision UnaryArithIRGenerator::tryAttachInt32() {
  15315  if (op_ == JSOp::BitNot) {
  15316    return AttachDecision::NoAction;
  15317  }
  15318  if (!CanConvertToInt32ForToNumber(val_) || !res_.isInt32()) {
  15319    return AttachDecision::NoAction;
  15320  }
  15321 
  15322  ValOperandId valId(writer.setInputOperandId(0));
  15323 
  15324  Int32OperandId intId = EmitGuardToInt32ForToNumber(writer, valId, val_);
  15325  switch (op_) {
  15326    case JSOp::Pos:
  15327      writer.loadInt32Result(intId);
  15328      trackAttached("UnaryArith.Int32Pos");
  15329      break;
  15330    case JSOp::Neg:
  15331      writer.int32NegationResult(intId);
  15332      trackAttached("UnaryArith.Int32Neg");
  15333      break;
  15334    case JSOp::Inc:
  15335      writer.int32IncResult(intId);
  15336      trackAttached("UnaryArith.Int32Inc");
  15337      break;
  15338    case JSOp::Dec:
  15339      writer.int32DecResult(intId);
  15340      trackAttached("UnaryArith.Int32Dec");
  15341      break;
  15342    case JSOp::ToNumeric:
  15343      writer.loadInt32Result(intId);
  15344      trackAttached("UnaryArith.Int32ToNumeric");
  15345      break;
  15346    default:
  15347      MOZ_CRASH("unexpected OP");
  15348  }
  15349 
  15350  writer.returnFromIC();
  15351  return AttachDecision::Attach;
  15352 }
  15353 
  15354 AttachDecision UnaryArithIRGenerator::tryAttachNumber() {
  15355  if (op_ == JSOp::BitNot) {
  15356    return AttachDecision::NoAction;
  15357  }
  15358  if (!CanConvertToDoubleForToNumber(val_)) {
  15359    return AttachDecision::NoAction;
  15360  }
  15361  MOZ_ASSERT(res_.isNumber());
  15362 
  15363  ValOperandId valId(writer.setInputOperandId(0));
  15364  NumberOperandId numId = EmitGuardToDoubleForToNumber(writer, valId, val_);
  15365 
  15366  switch (op_) {
  15367    case JSOp::Pos:
  15368      writer.loadDoubleResult(numId);
  15369      trackAttached("UnaryArith.DoublePos");
  15370      break;
  15371    case JSOp::Neg:
  15372      writer.doubleNegationResult(numId);
  15373      trackAttached("UnaryArith.DoubleNeg");
  15374      break;
  15375    case JSOp::Inc:
  15376      writer.doubleIncResult(numId);
  15377      trackAttached("UnaryArith.DoubleInc");
  15378      break;
  15379    case JSOp::Dec:
  15380      writer.doubleDecResult(numId);
  15381      trackAttached("UnaryArith.DoubleDec");
  15382      break;
  15383    case JSOp::ToNumeric:
  15384      writer.loadDoubleResult(numId);
  15385      trackAttached("UnaryArith.DoubleToNumeric");
  15386      break;
  15387    default:
  15388      MOZ_CRASH("Unexpected OP");
  15389  }
  15390 
  15391  writer.returnFromIC();
  15392  return AttachDecision::Attach;
  15393 }
  15394 
  15395 static bool CanTruncateToInt32(const Value& val) {
  15396  return val.isNumber() || val.isBoolean() || val.isNullOrUndefined() ||
  15397         val.isString();
  15398 }
  15399 
  15400 // Convert type into int32 for the bitwise/shift operands.
  15401 static Int32OperandId EmitTruncateToInt32Guard(CacheIRWriter& writer,
  15402                                               ValOperandId id,
  15403                                               const Value& val) {
  15404  MOZ_ASSERT(CanTruncateToInt32(val));
  15405  if (val.isInt32()) {
  15406    return writer.guardToInt32(id);
  15407  }
  15408  if (val.isBoolean()) {
  15409    return writer.guardBooleanToInt32(id);
  15410  }
  15411  if (val.isNullOrUndefined()) {
  15412    writer.guardIsNullOrUndefined(id);
  15413    return writer.loadInt32Constant(0);
  15414  }
  15415  NumberOperandId numId;
  15416  if (val.isString()) {
  15417    StringOperandId strId = writer.guardToString(id);
  15418    numId = writer.guardStringToNumber(strId);
  15419  } else {
  15420    MOZ_ASSERT(val.isDouble());
  15421    numId = writer.guardIsNumber(id);
  15422  }
  15423  return writer.truncateDoubleToUInt32(numId);
  15424 }
  15425 
  15426 AttachDecision UnaryArithIRGenerator::tryAttachBitwise() {
  15427  // Only bitwise operators.
  15428  if (op_ != JSOp::BitNot) {
  15429    return AttachDecision::NoAction;
  15430  }
  15431 
  15432  // Check guard conditions
  15433  if (!CanTruncateToInt32(val_)) {
  15434    return AttachDecision::NoAction;
  15435  }
  15436 
  15437  // Bitwise operators always produce Int32 values.
  15438  MOZ_ASSERT(res_.isInt32());
  15439 
  15440  ValOperandId valId(writer.setInputOperandId(0));
  15441  Int32OperandId intId = EmitTruncateToInt32Guard(writer, valId, val_);
  15442  writer.int32NotResult(intId);
  15443  trackAttached("UnaryArith.BitwiseBitNot");
  15444 
  15445  writer.returnFromIC();
  15446  return AttachDecision::Attach;
  15447 }
  15448 
  15449 AttachDecision UnaryArithIRGenerator::tryAttachBigInt() {
  15450  if (!val_.isBigInt()) {
  15451    return AttachDecision::NoAction;
  15452  }
  15453  MOZ_ASSERT(res_.isBigInt());
  15454 
  15455  MOZ_ASSERT(op_ != JSOp::Pos,
  15456             "Applying the unary + operator on BigInt values throws an error");
  15457 
  15458  ValOperandId valId(writer.setInputOperandId(0));
  15459  BigIntOperandId bigIntId = writer.guardToBigInt(valId);
  15460  switch (op_) {
  15461    case JSOp::BitNot:
  15462      writer.bigIntNotResult(bigIntId);
  15463      trackAttached("UnaryArith.BigIntNot");
  15464      break;
  15465    case JSOp::Neg:
  15466      writer.bigIntNegationResult(bigIntId);
  15467      trackAttached("UnaryArith.BigIntNeg");
  15468      break;
  15469    case JSOp::Inc:
  15470      writer.bigIntIncResult(bigIntId);
  15471      trackAttached("UnaryArith.BigIntInc");
  15472      break;
  15473    case JSOp::Dec:
  15474      writer.bigIntDecResult(bigIntId);
  15475      trackAttached("UnaryArith.BigIntDec");
  15476      break;
  15477    case JSOp::ToNumeric:
  15478      writer.loadBigIntResult(bigIntId);
  15479      trackAttached("UnaryArith.BigIntToNumeric");
  15480      break;
  15481    default:
  15482      MOZ_CRASH("Unexpected OP");
  15483  }
  15484 
  15485  writer.returnFromIC();
  15486  return AttachDecision::Attach;
  15487 }
  15488 
  15489 AttachDecision UnaryArithIRGenerator::tryAttachBigIntPtr() {
  15490  if (!val_.isBigInt()) {
  15491    return AttachDecision::NoAction;
  15492  }
  15493  MOZ_ASSERT(res_.isBigInt());
  15494 
  15495  MOZ_ASSERT(op_ != JSOp::Pos,
  15496             "Applying the unary + operator on BigInt values throws an error");
  15497 
  15498  switch (op_) {
  15499    case JSOp::BitNot:
  15500    case JSOp::Neg:
  15501    case JSOp::Inc:
  15502    case JSOp::Dec:
  15503      break;
  15504    case JSOp::ToNumeric:
  15505      return AttachDecision::NoAction;
  15506    default:
  15507      MOZ_CRASH("Unexpected OP");
  15508  }
  15509 
  15510  intptr_t val;
  15511  if (!BigInt::isIntPtr(val_.toBigInt(), &val)) {
  15512    return AttachDecision::NoAction;
  15513  }
  15514 
  15515  using CheckedIntPtr = mozilla::CheckedInt<intptr_t>;
  15516 
  15517  switch (op_) {
  15518    case JSOp::BitNot: {
  15519      // Bitwise operations always return an intptr-sized result.
  15520      break;
  15521    }
  15522    case JSOp::Neg: {
  15523      auto result = -CheckedIntPtr(val);
  15524      if (result.isValid()) {
  15525        break;
  15526      }
  15527      return AttachDecision::NoAction;
  15528    }
  15529    case JSOp::Inc: {
  15530      auto result = CheckedIntPtr(val) + intptr_t(1);
  15531      if (result.isValid()) {
  15532        break;
  15533      }
  15534      return AttachDecision::NoAction;
  15535    }
  15536    case JSOp::Dec: {
  15537      auto result = CheckedIntPtr(val) - intptr_t(1);
  15538      if (result.isValid()) {
  15539        break;
  15540      }
  15541      return AttachDecision::NoAction;
  15542    }
  15543    default:
  15544      MOZ_CRASH("Unexpected OP");
  15545  }
  15546 
  15547  ValOperandId valId(writer.setInputOperandId(0));
  15548  BigIntOperandId bigIntId = writer.guardToBigInt(valId);
  15549  IntPtrOperandId intPtrId = writer.bigIntToIntPtr(bigIntId);
  15550  IntPtrOperandId resultId;
  15551  switch (op_) {
  15552    case JSOp::BitNot:
  15553      resultId = writer.bigIntPtrNot(intPtrId);
  15554      trackAttached("UnaryArith.BigIntPtrNot");
  15555      break;
  15556    case JSOp::Neg:
  15557      resultId = writer.bigIntPtrNegation(intPtrId);
  15558      trackAttached("UnaryArith.BigIntPtrNeg");
  15559      break;
  15560    case JSOp::Inc:
  15561      resultId = writer.bigIntPtrInc(intPtrId);
  15562      trackAttached("UnaryArith.BigIntPtrInc");
  15563      break;
  15564    case JSOp::Dec:
  15565      resultId = writer.bigIntPtrDec(intPtrId);
  15566      trackAttached("UnaryArith.BigIntPtrDec");
  15567      break;
  15568    default:
  15569      MOZ_CRASH("Unexpected OP");
  15570  }
  15571 
  15572  writer.intPtrToBigIntResult(resultId);
  15573  writer.returnFromIC();
  15574  return AttachDecision::Attach;
  15575 }
  15576 
  15577 AttachDecision UnaryArithIRGenerator::tryAttachStringInt32() {
  15578  if (!val_.isString()) {
  15579    return AttachDecision::NoAction;
  15580  }
  15581  MOZ_ASSERT(res_.isNumber());
  15582 
  15583  // Case should have been handled by tryAttachBitwise.
  15584  MOZ_ASSERT(op_ != JSOp::BitNot);
  15585 
  15586  if (!res_.isInt32()) {
  15587    return AttachDecision::NoAction;
  15588  }
  15589 
  15590  ValOperandId valId(writer.setInputOperandId(0));
  15591  StringOperandId stringId = writer.guardToString(valId);
  15592  Int32OperandId intId = writer.guardStringToInt32(stringId);
  15593 
  15594  switch (op_) {
  15595    case JSOp::Pos:
  15596      writer.loadInt32Result(intId);
  15597      trackAttached("UnaryArith.StringInt32Pos");
  15598      break;
  15599    case JSOp::Neg:
  15600      writer.int32NegationResult(intId);
  15601      trackAttached("UnaryArith.StringInt32Neg");
  15602      break;
  15603    case JSOp::Inc:
  15604      writer.int32IncResult(intId);
  15605      trackAttached("UnaryArith.StringInt32Inc");
  15606      break;
  15607    case JSOp::Dec:
  15608      writer.int32DecResult(intId);
  15609      trackAttached("UnaryArith.StringInt32Dec");
  15610      break;
  15611    case JSOp::ToNumeric:
  15612      writer.loadInt32Result(intId);
  15613      trackAttached("UnaryArith.StringInt32ToNumeric");
  15614      break;
  15615    default:
  15616      MOZ_CRASH("Unexpected OP");
  15617  }
  15618 
  15619  writer.returnFromIC();
  15620  return AttachDecision::Attach;
  15621 }
  15622 
  15623 AttachDecision UnaryArithIRGenerator::tryAttachStringNumber() {
  15624  if (!val_.isString()) {
  15625    return AttachDecision::NoAction;
  15626  }
  15627  MOZ_ASSERT(res_.isNumber());
  15628 
  15629  // Case should have been handled by tryAttachBitwise.
  15630  MOZ_ASSERT(op_ != JSOp::BitNot);
  15631 
  15632  ValOperandId valId(writer.setInputOperandId(0));
  15633  StringOperandId stringId = writer.guardToString(valId);
  15634  NumberOperandId numId = writer.guardStringToNumber(stringId);
  15635 
  15636  Int32OperandId truncatedId;
  15637  switch (op_) {
  15638    case JSOp::Pos:
  15639      writer.loadDoubleResult(numId);
  15640      trackAttached("UnaryArith.StringNumberPos");
  15641      break;
  15642    case JSOp::Neg:
  15643      writer.doubleNegationResult(numId);
  15644      trackAttached("UnaryArith.StringNumberNeg");
  15645      break;
  15646    case JSOp::Inc:
  15647      writer.doubleIncResult(numId);
  15648      trackAttached("UnaryArith.StringNumberInc");
  15649      break;
  15650    case JSOp::Dec:
  15651      writer.doubleDecResult(numId);
  15652      trackAttached("UnaryArith.StringNumberDec");
  15653      break;
  15654    case JSOp::ToNumeric:
  15655      writer.loadDoubleResult(numId);
  15656      trackAttached("UnaryArith.StringNumberToNumeric");
  15657      break;
  15658    default:
  15659      MOZ_CRASH("Unexpected OP");
  15660  }
  15661 
  15662  writer.returnFromIC();
  15663  return AttachDecision::Attach;
  15664 }
  15665 
  15666 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext* cx,
  15667                                                   HandleScript script,
  15668                                                   jsbytecode* pc,
  15669                                                   ICState state,
  15670                                                   HandleValue val)
  15671    : IRGenerator(cx, script, pc, CacheKind::ToPropertyKey, state), val_(val) {}
  15672 
  15673 void ToPropertyKeyIRGenerator::trackAttached(const char* name) {
  15674  stubName_ = name ? name : "NotAttached";
  15675 #ifdef JS_CACHEIR_SPEW
  15676  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15677    sp.valueProperty("val", val_);
  15678  }
  15679 #endif
  15680 }
  15681 
  15682 AttachDecision ToPropertyKeyIRGenerator::tryAttachStub() {
  15683  AutoAssertNoPendingException aanpe(cx_);
  15684  TRY_ATTACH(tryAttachInt32());
  15685  TRY_ATTACH(tryAttachNumber());
  15686  TRY_ATTACH(tryAttachString());
  15687  TRY_ATTACH(tryAttachSymbol());
  15688 
  15689  trackAttached(IRGenerator::NotAttached);
  15690  return AttachDecision::NoAction;
  15691 }
  15692 
  15693 AttachDecision ToPropertyKeyIRGenerator::tryAttachInt32() {
  15694  if (!val_.isInt32()) {
  15695    return AttachDecision::NoAction;
  15696  }
  15697 
  15698  ValOperandId valId(writer.setInputOperandId(0));
  15699 
  15700  Int32OperandId intId = writer.guardToInt32(valId);
  15701  writer.loadInt32Result(intId);
  15702  writer.returnFromIC();
  15703 
  15704  trackAttached("ToPropertyKey.Int32");
  15705  return AttachDecision::Attach;
  15706 }
  15707 
  15708 AttachDecision ToPropertyKeyIRGenerator::tryAttachNumber() {
  15709  if (!val_.isNumber()) {
  15710    return AttachDecision::NoAction;
  15711  }
  15712 
  15713  // We allow negative zero here because ToPropertyKey(-0.0) is 0.
  15714  int32_t unused;
  15715  if (!mozilla::NumberEqualsInt32(val_.toNumber(), &unused)) {
  15716    return AttachDecision::NoAction;
  15717  }
  15718 
  15719  ValOperandId valId(writer.setInputOperandId(0));
  15720 
  15721  Int32OperandId intId = EmitGuardToInt32Index(writer, val_, valId);
  15722  writer.loadInt32Result(intId);
  15723  writer.returnFromIC();
  15724 
  15725  trackAttached("ToPropertyKey.Number");
  15726  return AttachDecision::Attach;
  15727 }
  15728 
  15729 AttachDecision ToPropertyKeyIRGenerator::tryAttachString() {
  15730  if (!val_.isString()) {
  15731    return AttachDecision::NoAction;
  15732  }
  15733 
  15734  ValOperandId valId(writer.setInputOperandId(0));
  15735 
  15736  StringOperandId strId = writer.guardToString(valId);
  15737  writer.loadStringResult(strId);
  15738  writer.returnFromIC();
  15739 
  15740  trackAttached("ToPropertyKey.String");
  15741  return AttachDecision::Attach;
  15742 }
  15743 
  15744 AttachDecision ToPropertyKeyIRGenerator::tryAttachSymbol() {
  15745  if (!val_.isSymbol()) {
  15746    return AttachDecision::NoAction;
  15747  }
  15748 
  15749  ValOperandId valId(writer.setInputOperandId(0));
  15750 
  15751  SymbolOperandId strId = writer.guardToSymbol(valId);
  15752  writer.loadSymbolResult(strId);
  15753  writer.returnFromIC();
  15754 
  15755  trackAttached("ToPropertyKey.Symbol");
  15756  return AttachDecision::Attach;
  15757 }
  15758 
  15759 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext* cx,
  15760                                               HandleScript script,
  15761                                               jsbytecode* pc, ICState state,
  15762                                               JSOp op, HandleValue lhs,
  15763                                               HandleValue rhs, HandleValue res)
  15764    : IRGenerator(cx, script, pc, CacheKind::BinaryArith, state),
  15765      op_(op),
  15766      lhs_(lhs),
  15767      rhs_(rhs),
  15768      res_(res) {}
  15769 
  15770 void BinaryArithIRGenerator::trackAttached(const char* name) {
  15771  stubName_ = name ? name : "NotAttached";
  15772 #ifdef JS_CACHEIR_SPEW
  15773  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  15774    sp.opcodeProperty("op", op_);
  15775    sp.valueProperty("rhs", rhs_);
  15776    sp.valueProperty("lhs", lhs_);
  15777  }
  15778 #endif
  15779 }
  15780 
  15781 AttachDecision BinaryArithIRGenerator::tryAttachStub() {
  15782  AutoAssertNoPendingException aanpe(cx_);
  15783  // Arithmetic operations with Int32 operands
  15784  TRY_ATTACH(tryAttachInt32());
  15785 
  15786  // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
  15787  // operands.
  15788  TRY_ATTACH(tryAttachBitwise());
  15789 
  15790  // Arithmetic operations with Double operands. This needs to come after
  15791  // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
  15792  // more specialized Int32 IC if it is possible.
  15793  TRY_ATTACH(tryAttachDouble());
  15794 
  15795  // String x {String,Number,Boolean,Null,Undefined}
  15796  TRY_ATTACH(tryAttachStringConcat());
  15797 
  15798  // String x Object
  15799  TRY_ATTACH(tryAttachStringObjectConcat());
  15800 
  15801  // Arithmetic operations or bitwise operations with intptr-sized BigInt
  15802  // operands.
  15803  TRY_ATTACH(tryAttachBigIntPtr());
  15804 
  15805  // Arithmetic operations or bitwise operations with BigInt operands
  15806  TRY_ATTACH(tryAttachBigInt());
  15807 
  15808  // Arithmetic operations (without addition) with String x Int32.
  15809  TRY_ATTACH(tryAttachStringInt32Arith());
  15810 
  15811  // Arithmetic operations (without addition) with String x Number. This needs
  15812  // to come after tryAttachStringInt32Arith, as the guards overlap, and we'd
  15813  // prefer to attach the more specialized Int32 IC if it is possible.
  15814  TRY_ATTACH(tryAttachStringNumberArith());
  15815 
  15816  TRY_ATTACH(tryAttachDateArith());
  15817 
  15818  trackAttached(IRGenerator::NotAttached);
  15819  return AttachDecision::NoAction;
  15820 }
  15821 
  15822 AttachDecision BinaryArithIRGenerator::tryAttachBitwise() {
  15823  // Only bit-wise and shifts.
  15824  if (op_ != JSOp::BitOr && op_ != JSOp::BitXor && op_ != JSOp::BitAnd &&
  15825      op_ != JSOp::Lsh && op_ != JSOp::Rsh && op_ != JSOp::Ursh) {
  15826    return AttachDecision::NoAction;
  15827  }
  15828 
  15829  // Check guard conditions
  15830  if (!CanTruncateToInt32(lhs_) || !CanTruncateToInt32(rhs_)) {
  15831    return AttachDecision::NoAction;
  15832  }
  15833 
  15834  // All ops, with the exception of Ursh, produce Int32 values.
  15835  MOZ_ASSERT_IF(op_ != JSOp::Ursh, res_.isInt32());
  15836 
  15837  ValOperandId lhsId(writer.setInputOperandId(0));
  15838  ValOperandId rhsId(writer.setInputOperandId(1));
  15839 
  15840  Int32OperandId lhsIntId = EmitTruncateToInt32Guard(writer, lhsId, lhs_);
  15841  Int32OperandId rhsIntId = EmitTruncateToInt32Guard(writer, rhsId, rhs_);
  15842 
  15843  switch (op_) {
  15844    case JSOp::BitOr:
  15845      writer.int32BitOrResult(lhsIntId, rhsIntId);
  15846      trackAttached("BinaryArith.BitwiseBitOr");
  15847      break;
  15848    case JSOp::BitXor:
  15849      writer.int32BitXorResult(lhsIntId, rhsIntId);
  15850      trackAttached("BinaryArith.BitwiseBitXor");
  15851      break;
  15852    case JSOp::BitAnd:
  15853      writer.int32BitAndResult(lhsIntId, rhsIntId);
  15854      trackAttached("BinaryArith.BitwiseBitAnd");
  15855      break;
  15856    case JSOp::Lsh:
  15857      writer.int32LeftShiftResult(lhsIntId, rhsIntId);
  15858      trackAttached("BinaryArith.BitwiseLeftShift");
  15859      break;
  15860    case JSOp::Rsh:
  15861      writer.int32RightShiftResult(lhsIntId, rhsIntId);
  15862      trackAttached("BinaryArith.BitwiseRightShift");
  15863      break;
  15864    case JSOp::Ursh:
  15865      writer.int32URightShiftResult(lhsIntId, rhsIntId, res_.isDouble());
  15866      trackAttached("BinaryArith.BitwiseUnsignedRightShift");
  15867      break;
  15868    default:
  15869      MOZ_CRASH("Unhandled op in tryAttachBitwise");
  15870  }
  15871 
  15872  writer.returnFromIC();
  15873  return AttachDecision::Attach;
  15874 }
  15875 
  15876 AttachDecision BinaryArithIRGenerator::tryAttachDouble() {
  15877  // Check valid opcodes
  15878  if (op_ != JSOp::Add && op_ != JSOp::Sub && op_ != JSOp::Mul &&
  15879      op_ != JSOp::Div && op_ != JSOp::Mod && op_ != JSOp::Pow) {
  15880    return AttachDecision::NoAction;
  15881  }
  15882 
  15883  // Check guard conditions.
  15884  if (!CanConvertToDoubleForToNumber(lhs_) ||
  15885      !CanConvertToDoubleForToNumber(rhs_)) {
  15886    return AttachDecision::NoAction;
  15887  }
  15888 
  15889  ValOperandId lhsId(writer.setInputOperandId(0));
  15890  ValOperandId rhsId(writer.setInputOperandId(1));
  15891 
  15892  NumberOperandId lhs = EmitGuardToDoubleForToNumber(writer, lhsId, lhs_);
  15893  NumberOperandId rhs = EmitGuardToDoubleForToNumber(writer, rhsId, rhs_);
  15894 
  15895  switch (op_) {
  15896    case JSOp::Add:
  15897      writer.doubleAddResult(lhs, rhs);
  15898      trackAttached("BinaryArith.DoubleAdd");
  15899      break;
  15900    case JSOp::Sub:
  15901      writer.doubleSubResult(lhs, rhs);
  15902      trackAttached("BinaryArith.DoubleSub");
  15903      break;
  15904    case JSOp::Mul:
  15905      writer.doubleMulResult(lhs, rhs);
  15906      trackAttached("BinaryArith.DoubleMul");
  15907      break;
  15908    case JSOp::Div:
  15909      writer.doubleDivResult(lhs, rhs);
  15910      trackAttached("BinaryArith.DoubleDiv");
  15911      break;
  15912    case JSOp::Mod:
  15913      writer.doubleModResult(lhs, rhs);
  15914      trackAttached("BinaryArith.DoubleMod");
  15915      break;
  15916    case JSOp::Pow:
  15917      writer.doublePowResult(lhs, rhs);
  15918      trackAttached("BinaryArith.DoublePow");
  15919      break;
  15920    default:
  15921      MOZ_CRASH("Unhandled Op");
  15922  }
  15923  writer.returnFromIC();
  15924  return AttachDecision::Attach;
  15925 }
  15926 
  15927 AttachDecision BinaryArithIRGenerator::tryAttachInt32() {
  15928  // Check guard conditions.
  15929  if (!CanConvertToInt32ForToNumber(lhs_) ||
  15930      !CanConvertToInt32ForToNumber(rhs_)) {
  15931    return AttachDecision::NoAction;
  15932  }
  15933 
  15934  // These ICs will failure() if result can't be encoded in an Int32:
  15935  // If sample result is not Int32, we should avoid IC.
  15936  if (!res_.isInt32()) {
  15937    return AttachDecision::NoAction;
  15938  }
  15939 
  15940  if (op_ != JSOp::Add && op_ != JSOp::Sub && op_ != JSOp::Mul &&
  15941      op_ != JSOp::Div && op_ != JSOp::Mod && op_ != JSOp::Pow) {
  15942    return AttachDecision::NoAction;
  15943  }
  15944 
  15945  if (op_ == JSOp::Pow && !CanAttachInt32Pow(lhs_, rhs_)) {
  15946    return AttachDecision::NoAction;
  15947  }
  15948 
  15949  ValOperandId lhsId(writer.setInputOperandId(0));
  15950  ValOperandId rhsId(writer.setInputOperandId(1));
  15951 
  15952  Int32OperandId lhsIntId = EmitGuardToInt32ForToNumber(writer, lhsId, lhs_);
  15953  Int32OperandId rhsIntId = EmitGuardToInt32ForToNumber(writer, rhsId, rhs_);
  15954 
  15955  switch (op_) {
  15956    case JSOp::Add:
  15957      writer.int32AddResult(lhsIntId, rhsIntId);
  15958      trackAttached("BinaryArith.Int32Add");
  15959      break;
  15960    case JSOp::Sub:
  15961      writer.int32SubResult(lhsIntId, rhsIntId);
  15962      trackAttached("BinaryArith.Int32Sub");
  15963      break;
  15964    case JSOp::Mul:
  15965      writer.int32MulResult(lhsIntId, rhsIntId);
  15966      trackAttached("BinaryArith.Int32Mul");
  15967      break;
  15968    case JSOp::Div:
  15969      writer.int32DivResult(lhsIntId, rhsIntId);
  15970      trackAttached("BinaryArith.Int32Div");
  15971      break;
  15972    case JSOp::Mod:
  15973      writer.int32ModResult(lhsIntId, rhsIntId);
  15974      trackAttached("BinaryArith.Int32Mod");
  15975      break;
  15976    case JSOp::Pow:
  15977      writer.int32PowResult(lhsIntId, rhsIntId);
  15978      trackAttached("BinaryArith.Int32Pow");
  15979      break;
  15980    default:
  15981      MOZ_CRASH("Unhandled op in tryAttachInt32");
  15982  }
  15983 
  15984  writer.returnFromIC();
  15985  return AttachDecision::Attach;
  15986 }
  15987 
  15988 AttachDecision BinaryArithIRGenerator::tryAttachStringConcat() {
  15989  // Only Addition
  15990  if (op_ != JSOp::Add) {
  15991    return AttachDecision::NoAction;
  15992  }
  15993 
  15994  // One side must be a string, the other side a primitive value we can easily
  15995  // convert to a string.
  15996  if (!(lhs_.isString() && CanConvertToString(rhs_)) &&
  15997      !(CanConvertToString(lhs_) && rhs_.isString())) {
  15998    return AttachDecision::NoAction;
  15999  }
  16000 
  16001  JitCode* code = cx_->zone()->jitZone()->ensureStubExists(
  16002      cx_, JitZone::StubKind::StringConcat);
  16003  if (!code) {
  16004    cx_->recoverFromOutOfMemory();
  16005    return AttachDecision::NoAction;
  16006  }
  16007 
  16008  ValOperandId lhsId(writer.setInputOperandId(0));
  16009  ValOperandId rhsId(writer.setInputOperandId(1));
  16010 
  16011  StringOperandId lhsStrId = emitToStringGuard(lhsId, lhs_);
  16012  StringOperandId rhsStrId = emitToStringGuard(rhsId, rhs_);
  16013 
  16014  writer.concatStringsResult(lhsStrId, rhsStrId, code);
  16015 
  16016  writer.returnFromIC();
  16017  trackAttached("BinaryArith.StringConcat");
  16018  return AttachDecision::Attach;
  16019 }
  16020 
  16021 AttachDecision BinaryArithIRGenerator::tryAttachStringObjectConcat() {
  16022  // Only Addition
  16023  if (op_ != JSOp::Add) {
  16024    return AttachDecision::NoAction;
  16025  }
  16026 
  16027  // Check Guards
  16028  if (!(lhs_.isObject() && rhs_.isString()) &&
  16029      !(lhs_.isString() && rhs_.isObject()))
  16030    return AttachDecision::NoAction;
  16031 
  16032  ValOperandId lhsId(writer.setInputOperandId(0));
  16033  ValOperandId rhsId(writer.setInputOperandId(1));
  16034 
  16035  // This guard is actually overly tight, as the runtime
  16036  // helper can handle lhs or rhs being a string, so long
  16037  // as the other is an object.
  16038  if (lhs_.isString()) {
  16039    writer.guardToString(lhsId);
  16040    writer.guardToObject(rhsId);
  16041  } else {
  16042    writer.guardToObject(lhsId);
  16043    writer.guardToString(rhsId);
  16044  }
  16045 
  16046  writer.callStringObjectConcatResult(lhsId, rhsId);
  16047 
  16048  writer.returnFromIC();
  16049  trackAttached("BinaryArith.StringObjectConcat");
  16050  return AttachDecision::Attach;
  16051 }
  16052 
  16053 AttachDecision BinaryArithIRGenerator::tryAttachBigInt() {
  16054  // Check Guards
  16055  if (!lhs_.isBigInt() || !rhs_.isBigInt()) {
  16056    return AttachDecision::NoAction;
  16057  }
  16058 
  16059  switch (op_) {
  16060    case JSOp::Add:
  16061    case JSOp::Sub:
  16062    case JSOp::Mul:
  16063    case JSOp::Div:
  16064    case JSOp::Mod:
  16065    case JSOp::Pow:
  16066      // Arithmetic operations.
  16067      break;
  16068 
  16069    case JSOp::BitOr:
  16070    case JSOp::BitXor:
  16071    case JSOp::BitAnd:
  16072    case JSOp::Lsh:
  16073    case JSOp::Rsh:
  16074      // Bitwise operations.
  16075      break;
  16076 
  16077    default:
  16078      return AttachDecision::NoAction;
  16079  }
  16080 
  16081  ValOperandId lhsId(writer.setInputOperandId(0));
  16082  ValOperandId rhsId(writer.setInputOperandId(1));
  16083 
  16084  BigIntOperandId lhsBigIntId = writer.guardToBigInt(lhsId);
  16085  BigIntOperandId rhsBigIntId = writer.guardToBigInt(rhsId);
  16086 
  16087  switch (op_) {
  16088    case JSOp::Add:
  16089      writer.bigIntAddResult(lhsBigIntId, rhsBigIntId);
  16090      trackAttached("BinaryArith.BigIntAdd");
  16091      break;
  16092    case JSOp::Sub:
  16093      writer.bigIntSubResult(lhsBigIntId, rhsBigIntId);
  16094      trackAttached("BinaryArith.BigIntSub");
  16095      break;
  16096    case JSOp::Mul:
  16097      writer.bigIntMulResult(lhsBigIntId, rhsBigIntId);
  16098      trackAttached("BinaryArith.BigIntMul");
  16099      break;
  16100    case JSOp::Div:
  16101      writer.bigIntDivResult(lhsBigIntId, rhsBigIntId);
  16102      trackAttached("BinaryArith.BigIntDiv");
  16103      break;
  16104    case JSOp::Mod:
  16105      writer.bigIntModResult(lhsBigIntId, rhsBigIntId);
  16106      trackAttached("BinaryArith.BigIntMod");
  16107      break;
  16108    case JSOp::Pow:
  16109      writer.bigIntPowResult(lhsBigIntId, rhsBigIntId);
  16110      trackAttached("BinaryArith.BigIntPow");
  16111      break;
  16112    case JSOp::BitOr:
  16113      writer.bigIntBitOrResult(lhsBigIntId, rhsBigIntId);
  16114      trackAttached("BinaryArith.BigIntBitOr");
  16115      break;
  16116    case JSOp::BitXor:
  16117      writer.bigIntBitXorResult(lhsBigIntId, rhsBigIntId);
  16118      trackAttached("BinaryArith.BigIntBitXor");
  16119      break;
  16120    case JSOp::BitAnd:
  16121      writer.bigIntBitAndResult(lhsBigIntId, rhsBigIntId);
  16122      trackAttached("BinaryArith.BigIntBitAnd");
  16123      break;
  16124    case JSOp::Lsh:
  16125      writer.bigIntLeftShiftResult(lhsBigIntId, rhsBigIntId);
  16126      trackAttached("BinaryArith.BigIntLeftShift");
  16127      break;
  16128    case JSOp::Rsh:
  16129      writer.bigIntRightShiftResult(lhsBigIntId, rhsBigIntId);
  16130      trackAttached("BinaryArith.BigIntRightShift");
  16131      break;
  16132    default:
  16133      MOZ_CRASH("Unhandled op in tryAttachBigInt");
  16134  }
  16135 
  16136  writer.returnFromIC();
  16137  return AttachDecision::Attach;
  16138 }
  16139 
  16140 AttachDecision BinaryArithIRGenerator::tryAttachBigIntPtr() {
  16141  // Check Guards
  16142  if (!lhs_.isBigInt() || !rhs_.isBigInt()) {
  16143    return AttachDecision::NoAction;
  16144  }
  16145 
  16146  switch (op_) {
  16147    case JSOp::Add:
  16148    case JSOp::Sub:
  16149    case JSOp::Mul:
  16150    case JSOp::Div:
  16151    case JSOp::Mod:
  16152    case JSOp::Pow:
  16153      // Arithmetic operations.
  16154      break;
  16155 
  16156    case JSOp::BitOr:
  16157    case JSOp::BitXor:
  16158    case JSOp::BitAnd:
  16159    case JSOp::Lsh:
  16160    case JSOp::Rsh:
  16161      // Bitwise operations.
  16162      break;
  16163 
  16164    default:
  16165      return AttachDecision::NoAction;
  16166  }
  16167 
  16168  intptr_t lhs;
  16169  intptr_t rhs;
  16170  if (!BigInt::isIntPtr(lhs_.toBigInt(), &lhs) ||
  16171      !BigInt::isIntPtr(rhs_.toBigInt(), &rhs)) {
  16172    return AttachDecision::NoAction;
  16173  }
  16174 
  16175  using CheckedIntPtr = mozilla::CheckedInt<intptr_t>;
  16176 
  16177  switch (op_) {
  16178    case JSOp::Add: {
  16179      auto result = CheckedIntPtr(lhs) + rhs;
  16180      if (result.isValid()) {
  16181        break;
  16182      }
  16183      return AttachDecision::NoAction;
  16184    }
  16185    case JSOp::Sub: {
  16186      auto result = CheckedIntPtr(lhs) - rhs;
  16187      if (result.isValid()) {
  16188        break;
  16189      }
  16190      return AttachDecision::NoAction;
  16191    }
  16192    case JSOp::Mul: {
  16193      auto result = CheckedIntPtr(lhs) * rhs;
  16194      if (result.isValid()) {
  16195        break;
  16196      }
  16197      return AttachDecision::NoAction;
  16198    }
  16199    case JSOp::Div: {
  16200      auto result = CheckedIntPtr(lhs) / rhs;
  16201      if (result.isValid()) {
  16202        break;
  16203      }
  16204      return AttachDecision::NoAction;
  16205    }
  16206    case JSOp::Mod: {
  16207      // We can't use mozilla::CheckedInt here, because it disallows negative
  16208      // inputs.
  16209      if (rhs != 0) {
  16210        break;
  16211      }
  16212      return AttachDecision::NoAction;
  16213    }
  16214    case JSOp::Pow: {
  16215      intptr_t result;
  16216      if (BigInt::powIntPtr(lhs, rhs, &result)) {
  16217        break;
  16218      }
  16219      return AttachDecision::NoAction;
  16220    }
  16221    case JSOp::BitOr:
  16222    case JSOp::BitXor:
  16223    case JSOp::BitAnd: {
  16224      // Bitwise operations always return an intptr-sized result.
  16225      break;
  16226    }
  16227    case JSOp::Lsh: {
  16228      if (lhs == 0 || rhs <= 0) {
  16229        break;
  16230      }
  16231      if (size_t(rhs) < BigInt::DigitBits) {
  16232        intptr_t result = lhs << rhs;
  16233        if ((result >> rhs) == lhs) {
  16234          break;
  16235        }
  16236      }
  16237      return AttachDecision::NoAction;
  16238    }
  16239    case JSOp::Rsh: {
  16240      if (lhs == 0 || rhs >= 0) {
  16241        break;
  16242      }
  16243      if (rhs > -intptr_t(BigInt::DigitBits)) {
  16244        intptr_t result = lhs << -rhs;
  16245        if ((result >> -rhs) == lhs) {
  16246          break;
  16247        }
  16248      }
  16249      return AttachDecision::NoAction;
  16250    }
  16251    default:
  16252      MOZ_CRASH("Unexpected OP");
  16253  }
  16254 
  16255  ValOperandId lhsId(writer.setInputOperandId(0));
  16256  ValOperandId rhsId(writer.setInputOperandId(1));
  16257 
  16258  BigIntOperandId lhsBigIntId = writer.guardToBigInt(lhsId);
  16259  BigIntOperandId rhsBigIntId = writer.guardToBigInt(rhsId);
  16260 
  16261  IntPtrOperandId lhsIntPtrId = writer.bigIntToIntPtr(lhsBigIntId);
  16262  IntPtrOperandId rhsIntPtrId = writer.bigIntToIntPtr(rhsBigIntId);
  16263 
  16264  IntPtrOperandId resultId;
  16265  switch (op_) {
  16266    case JSOp::Add: {
  16267      resultId = writer.bigIntPtrAdd(lhsIntPtrId, rhsIntPtrId);
  16268      trackAttached("BinaryArith.BigIntPtr.Add");
  16269      break;
  16270    }
  16271    case JSOp::Sub: {
  16272      resultId = writer.bigIntPtrSub(lhsIntPtrId, rhsIntPtrId);
  16273      trackAttached("BinaryArith.BigIntPtr.Sub");
  16274      break;
  16275    }
  16276    case JSOp::Mul: {
  16277      resultId = writer.bigIntPtrMul(lhsIntPtrId, rhsIntPtrId);
  16278      trackAttached("BinaryArith.BigIntPtr.Mul");
  16279      break;
  16280    }
  16281    case JSOp::Div: {
  16282      resultId = writer.bigIntPtrDiv(lhsIntPtrId, rhsIntPtrId);
  16283      trackAttached("BinaryArith.BigIntPtr.Div");
  16284      break;
  16285    }
  16286    case JSOp::Mod: {
  16287      resultId = writer.bigIntPtrMod(lhsIntPtrId, rhsIntPtrId);
  16288      trackAttached("BinaryArith.BigIntPtr.Mod");
  16289      break;
  16290    }
  16291    case JSOp::Pow: {
  16292      resultId = writer.bigIntPtrPow(lhsIntPtrId, rhsIntPtrId);
  16293      trackAttached("BinaryArith.BigIntPtr.Pow");
  16294      break;
  16295    }
  16296    case JSOp::BitOr: {
  16297      resultId = writer.bigIntPtrBitOr(lhsIntPtrId, rhsIntPtrId);
  16298      trackAttached("BinaryArith.BigIntPtr.BitOr");
  16299      break;
  16300    }
  16301    case JSOp::BitXor: {
  16302      resultId = writer.bigIntPtrBitXor(lhsIntPtrId, rhsIntPtrId);
  16303      trackAttached("BinaryArith.BigIntPtr.BitXor");
  16304      break;
  16305    }
  16306    case JSOp::BitAnd: {
  16307      resultId = writer.bigIntPtrBitAnd(lhsIntPtrId, rhsIntPtrId);
  16308      trackAttached("BinaryArith.BigIntPtr.BitAnd");
  16309      break;
  16310    }
  16311    case JSOp::Lsh: {
  16312      resultId = writer.bigIntPtrLeftShift(lhsIntPtrId, rhsIntPtrId);
  16313      trackAttached("BinaryArith.BigIntPtr.LeftShift");
  16314      break;
  16315    }
  16316    case JSOp::Rsh: {
  16317      resultId = writer.bigIntPtrRightShift(lhsIntPtrId, rhsIntPtrId);
  16318      trackAttached("BinaryArith.BigIntPtr.RightShift");
  16319      break;
  16320    }
  16321    default:
  16322      MOZ_CRASH("Unexpected OP");
  16323  }
  16324 
  16325  writer.intPtrToBigIntResult(resultId);
  16326  writer.returnFromIC();
  16327  return AttachDecision::Attach;
  16328 }
  16329 
  16330 AttachDecision BinaryArithIRGenerator::tryAttachStringInt32Arith() {
  16331  // Check for either int32 x string or string x int32.
  16332  if (!(lhs_.isInt32() && rhs_.isString()) &&
  16333      !(lhs_.isString() && rhs_.isInt32())) {
  16334    return AttachDecision::NoAction;
  16335  }
  16336 
  16337  // The created ICs will fail if the result can't be encoded as as int32.
  16338  // Thus skip this IC, if the sample result is not an int32.
  16339  if (!res_.isInt32()) {
  16340    return AttachDecision::NoAction;
  16341  }
  16342 
  16343  // Must _not_ support Add, because it would be string concatenation instead.
  16344  // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
  16345  // reject that as well.
  16346  if (op_ != JSOp::Sub && op_ != JSOp::Mul && op_ != JSOp::Div &&
  16347      op_ != JSOp::Mod) {
  16348    return AttachDecision::NoAction;
  16349  }
  16350 
  16351  // The string operand must be convertable to an int32 value.
  16352  JSString* str = lhs_.isString() ? lhs_.toString() : rhs_.toString();
  16353 
  16354  double num;
  16355  if (!StringToNumber(cx_, str, &num)) {
  16356    cx_->recoverFromOutOfMemory();
  16357    return AttachDecision::NoAction;
  16358  }
  16359 
  16360  int32_t unused;
  16361  if (!mozilla::NumberIsInt32(num, &unused)) {
  16362    return AttachDecision::NoAction;
  16363  }
  16364 
  16365  ValOperandId lhsId(writer.setInputOperandId(0));
  16366  ValOperandId rhsId(writer.setInputOperandId(1));
  16367 
  16368  auto guardToInt32 = [&](ValOperandId id, const Value& v) {
  16369    if (v.isInt32()) {
  16370      return writer.guardToInt32(id);
  16371    }
  16372 
  16373    MOZ_ASSERT(v.isString());
  16374    StringOperandId strId = writer.guardToString(id);
  16375    return writer.guardStringToInt32(strId);
  16376  };
  16377 
  16378  Int32OperandId lhsIntId = guardToInt32(lhsId, lhs_);
  16379  Int32OperandId rhsIntId = guardToInt32(rhsId, rhs_);
  16380 
  16381  switch (op_) {
  16382    case JSOp::Sub:
  16383      writer.int32SubResult(lhsIntId, rhsIntId);
  16384      trackAttached("BinaryArith.StringInt32Sub");
  16385      break;
  16386    case JSOp::Mul:
  16387      writer.int32MulResult(lhsIntId, rhsIntId);
  16388      trackAttached("BinaryArith.StringInt32Mul");
  16389      break;
  16390    case JSOp::Div:
  16391      writer.int32DivResult(lhsIntId, rhsIntId);
  16392      trackAttached("BinaryArith.StringInt32Div");
  16393      break;
  16394    case JSOp::Mod:
  16395      writer.int32ModResult(lhsIntId, rhsIntId);
  16396      trackAttached("BinaryArith.StringInt32Mod");
  16397      break;
  16398    default:
  16399      MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
  16400  }
  16401 
  16402  writer.returnFromIC();
  16403  return AttachDecision::Attach;
  16404 }
  16405 
  16406 AttachDecision BinaryArithIRGenerator::tryAttachStringNumberArith() {
  16407  // Check for either number x string or string x number.
  16408  if (!(lhs_.isNumber() && rhs_.isString()) &&
  16409      !(lhs_.isString() && rhs_.isNumber())) {
  16410    return AttachDecision::NoAction;
  16411  }
  16412 
  16413  // Must _not_ support Add, because it would be string concatenation instead.
  16414  if (op_ != JSOp::Sub && op_ != JSOp::Mul && op_ != JSOp::Div &&
  16415      op_ != JSOp::Mod && op_ != JSOp::Pow) {
  16416    return AttachDecision::NoAction;
  16417  }
  16418 
  16419  ValOperandId lhsId(writer.setInputOperandId(0));
  16420  ValOperandId rhsId(writer.setInputOperandId(1));
  16421 
  16422  auto guardToNumber = [&](ValOperandId id, const Value& v) {
  16423    if (v.isNumber()) {
  16424      return writer.guardIsNumber(id);
  16425    }
  16426 
  16427    MOZ_ASSERT(v.isString());
  16428    StringOperandId strId = writer.guardToString(id);
  16429    return writer.guardStringToNumber(strId);
  16430  };
  16431 
  16432  NumberOperandId lhsIntId = guardToNumber(lhsId, lhs_);
  16433  NumberOperandId rhsIntId = guardToNumber(rhsId, rhs_);
  16434 
  16435  switch (op_) {
  16436    case JSOp::Sub:
  16437      writer.doubleSubResult(lhsIntId, rhsIntId);
  16438      trackAttached("BinaryArith.StringNumberSub");
  16439      break;
  16440    case JSOp::Mul:
  16441      writer.doubleMulResult(lhsIntId, rhsIntId);
  16442      trackAttached("BinaryArith.StringNumberMul");
  16443      break;
  16444    case JSOp::Div:
  16445      writer.doubleDivResult(lhsIntId, rhsIntId);
  16446      trackAttached("BinaryArith.StringNumberDiv");
  16447      break;
  16448    case JSOp::Mod:
  16449      writer.doubleModResult(lhsIntId, rhsIntId);
  16450      trackAttached("BinaryArith.StringNumberMod");
  16451      break;
  16452    case JSOp::Pow:
  16453      writer.doublePowResult(lhsIntId, rhsIntId);
  16454      trackAttached("BinaryArith.StringNumberPow");
  16455      break;
  16456    default:
  16457      MOZ_CRASH("Unhandled op in tryAttachStringNumberArith");
  16458  }
  16459 
  16460  writer.returnFromIC();
  16461  return AttachDecision::Attach;
  16462 }
  16463 
  16464 static bool CheckPropertyIsNativeFunction(JSContext* cx, JSObject* obj,
  16465                                          jsbytecode* pc, PropertyKey propKey,
  16466                                          JSNative nativeFn, JSFunction** fn,
  16467                                          NativeObject** holder, size_t* slot) {
  16468  Maybe<PropertyInfo> prop;
  16469  NativeGetPropKind kind =
  16470      CanAttachNativeGetProp(cx, obj, propKey, holder, &prop, pc);
  16471  if (kind != NativeGetPropKind::Slot) {
  16472    return false;
  16473  }
  16474 
  16475  MOZ_ASSERT(holder);
  16476  MOZ_ASSERT(prop->isDataProperty());
  16477 
  16478  *slot = prop->slot();
  16479  Value calleeVal = (*holder)->getSlot(*slot);
  16480  if (!calleeVal.isObject() || !calleeVal.toObject().is<JSFunction>()) {
  16481    return false;
  16482  }
  16483 
  16484  if (!IsNativeFunction(calleeVal, nativeFn)) {
  16485    return false;
  16486  }
  16487 
  16488  *fn = &calleeVal.toObject().as<JSFunction>();
  16489  return true;
  16490 }
  16491 
  16492 static void EmitGuardPropertyIsNativeFunction(CacheIRWriter& writer,
  16493                                              JSObject* dateObj, JSFunction* fn,
  16494                                              NativeObject* holder, size_t slot,
  16495                                              ObjOperandId objId) {
  16496  MOZ_ASSERT(holder);
  16497  ObjOperandId holderId =
  16498      EmitReadSlotGuard(writer, &dateObj->as<NativeObject>(), holder, objId);
  16499  ValOperandId calleeValId = EmitLoadSlot(writer, holder, holderId, slot);
  16500  ObjOperandId calleeId = writer.guardToObject(calleeValId);
  16501  writer.guardSpecificFunction(calleeId, fn);
  16502 }
  16503 
  16504 AttachDecision BinaryArithIRGenerator::tryAttachDateArith() {
  16505  // Only support subtractions
  16506  if (op_ != JSOp::Sub) {
  16507    return AttachDecision::NoAction;
  16508  }
  16509 
  16510  // At least one side must be an object.
  16511  if (!lhs_.isObject() && !rhs_.isObject()) {
  16512    return AttachDecision::NoAction;
  16513  }
  16514 
  16515  // Must be either object or numbers.
  16516  if (!lhs_.isObject() && !lhs_.isNumber()) {
  16517    return AttachDecision::NoAction;
  16518  }
  16519 
  16520  if (!rhs_.isObject() && !rhs_.isNumber()) {
  16521    return AttachDecision::NoAction;
  16522  }
  16523 
  16524  // We can only operate on Date objects.
  16525  if (lhs_.isObject() && !lhs_.toObject().is<DateObject>()) {
  16526    return AttachDecision::NoAction;
  16527  }
  16528 
  16529  if (rhs_.isObject() && !rhs_.toObject().is<DateObject>()) {
  16530    return AttachDecision::NoAction;
  16531  }
  16532 
  16533  JSFunction* lhsDateValueOfFn = nullptr;
  16534  NativeObject* lhsDateValueOfHolder = nullptr;
  16535  size_t lhsDateValueOfSlot;
  16536 
  16537  JSFunction* lhsToPrimitiveFn = nullptr;
  16538  NativeObject* lhsToPrimitiveHolder = nullptr;
  16539  size_t lhsToPrimitiveSlot;
  16540 
  16541  if (lhs_.isObject()) {
  16542    if (!CheckPropertyIsNativeFunction(
  16543            cx_, &lhs_.toObject(), pc_, NameToId(cx_->names().valueOf),
  16544            date_valueOf, &lhsDateValueOfFn, &lhsDateValueOfHolder,
  16545            &lhsDateValueOfSlot)) {
  16546      return AttachDecision::NoAction;
  16547    }
  16548 
  16549    if (!CheckPropertyIsNativeFunction(
  16550            cx_, &lhs_.toObject(), pc_,
  16551            PropertyKey::Symbol(cx_->wellKnownSymbols().toPrimitive),
  16552            date_toPrimitive, &lhsToPrimitiveFn, &lhsToPrimitiveHolder,
  16553            &lhsToPrimitiveSlot)) {
  16554      return AttachDecision::NoAction;
  16555    }
  16556  }
  16557 
  16558  JSFunction* rhsDateValueOfFn = nullptr;
  16559  NativeObject* rhsDateValueOfHolder = nullptr;
  16560  size_t rhsDateValueOfSlot;
  16561 
  16562  JSFunction* rhsToPrimitiveFn = nullptr;
  16563  NativeObject* rhsToPrimitiveHolder = nullptr;
  16564  size_t rhsToPrimitiveSlot;
  16565 
  16566  if (rhs_.isObject()) {
  16567    if (!CheckPropertyIsNativeFunction(
  16568            cx_, &rhs_.toObject(), pc_, NameToId(cx_->names().valueOf),
  16569            date_valueOf, &rhsDateValueOfFn, &rhsDateValueOfHolder,
  16570            &rhsDateValueOfSlot)) {
  16571      return AttachDecision::NoAction;
  16572    }
  16573 
  16574    if (!CheckPropertyIsNativeFunction(
  16575            cx_, &rhs_.toObject(), pc_,
  16576            PropertyKey::Symbol(cx_->wellKnownSymbols().toPrimitive),
  16577            date_toPrimitive, &rhsToPrimitiveFn, &rhsToPrimitiveHolder,
  16578            &rhsToPrimitiveSlot)) {
  16579      return AttachDecision::NoAction;
  16580    }
  16581  }
  16582 
  16583  ValOperandId lhsId(writer.setInputOperandId(0));
  16584  ValOperandId rhsId(writer.setInputOperandId(1));
  16585 
  16586  NumberOperandId lhsNumId;
  16587  NumberOperandId rhsNumId;
  16588 
  16589  if (lhs_.isObject()) {
  16590    ObjOperandId lhsObjId = writer.guardToObject(lhsId);
  16591    // The shape guard in EmitGuardPropertyIsNativeFunction ensures the object
  16592    // is a Date object.
  16593    EmitGuardPropertyIsNativeFunction(writer, &lhs_.toObject(),
  16594                                      lhsDateValueOfFn, lhsDateValueOfHolder,
  16595                                      lhsDateValueOfSlot, lhsObjId);
  16596    EmitGuardPropertyIsNativeFunction(writer, &lhs_.toObject(),
  16597                                      lhsToPrimitiveFn, lhsToPrimitiveHolder,
  16598                                      lhsToPrimitiveSlot, lhsObjId);
  16599 
  16600    ValOperandId lhsUtcValId =
  16601        writer.loadFixedSlot(lhsObjId, DateObject::offsetOfUTCTimeSlot());
  16602    lhsNumId = writer.guardIsNumber(lhsUtcValId);
  16603  } else {
  16604    MOZ_ASSERT(lhs_.isNumber());
  16605    lhsNumId = writer.guardIsNumber(lhsId);
  16606  }
  16607 
  16608  if (rhs_.isObject()) {
  16609    ObjOperandId rhsObjId = writer.guardToObject(rhsId);
  16610    EmitGuardPropertyIsNativeFunction(writer, &rhs_.toObject(),
  16611                                      rhsDateValueOfFn, rhsDateValueOfHolder,
  16612                                      rhsDateValueOfSlot, rhsObjId);
  16613    EmitGuardPropertyIsNativeFunction(writer, &rhs_.toObject(),
  16614                                      rhsToPrimitiveFn, rhsToPrimitiveHolder,
  16615                                      rhsToPrimitiveSlot, rhsObjId);
  16616 
  16617    ValOperandId rhsUtcValId =
  16618        writer.loadFixedSlot(rhsObjId, DateObject::offsetOfUTCTimeSlot());
  16619    rhsNumId = writer.guardIsNumber(rhsUtcValId);
  16620  } else {
  16621    MOZ_ASSERT(rhs_.isNumber());
  16622    rhsNumId = writer.guardIsNumber(rhsId);
  16623  }
  16624 
  16625  writer.doubleSubResult(lhsNumId, rhsNumId);
  16626  trackAttached("BinaryArith.DateSub");
  16627 
  16628  writer.returnFromIC();
  16629  return AttachDecision::Attach;
  16630 }
  16631 
  16632 NewArrayIRGenerator::NewArrayIRGenerator(JSContext* cx, HandleScript script,
  16633                                         jsbytecode* pc, ICState state, JSOp op,
  16634                                         HandleObject templateObj,
  16635                                         BaselineFrame* frame)
  16636    : IRGenerator(cx, script, pc, CacheKind::NewArray, state, frame),
  16637 #ifdef JS_CACHEIR_SPEW
  16638      op_(op),
  16639 #endif
  16640      templateObject_(templateObj) {
  16641  MOZ_ASSERT(templateObject_);
  16642 }
  16643 
  16644 void NewArrayIRGenerator::trackAttached(const char* name) {
  16645  stubName_ = name ? name : "NotAttached";
  16646 #ifdef JS_CACHEIR_SPEW
  16647  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  16648    sp.opcodeProperty("op", op_);
  16649  }
  16650 #endif
  16651 }
  16652 
  16653 AttachDecision NewArrayIRGenerator::tryAttachArrayObject() {
  16654  ArrayObject* arrayObj = &templateObject_->as<ArrayObject>();
  16655 
  16656  MOZ_ASSERT(arrayObj->numUsedFixedSlots() == 0);
  16657  MOZ_ASSERT(arrayObj->numDynamicSlots() == 0);
  16658  MOZ_ASSERT(!arrayObj->isSharedMemory());
  16659 
  16660  // The macro assembler only supports creating arrays with fixed elements.
  16661  if (arrayObj->hasDynamicElements()) {
  16662    return AttachDecision::NoAction;
  16663  }
  16664 
  16665  // Stub doesn't support metadata builder
  16666  if (cx_->realm()->hasAllocationMetadataBuilder()) {
  16667    return AttachDecision::NoAction;
  16668  }
  16669 
  16670  writer.guardNoAllocationMetadataBuilder(
  16671      cx_->realm()->addressOfMetadataBuilder());
  16672 
  16673  gc::AllocSite* site = maybeCreateAllocSite();
  16674  if (!site) {
  16675    return AttachDecision::NoAction;
  16676  }
  16677 
  16678  Shape* shape = arrayObj->shape();
  16679  uint32_t length = arrayObj->length();
  16680 
  16681  writer.newArrayObjectResult(length, shape, site);
  16682 
  16683  writer.returnFromIC();
  16684 
  16685  trackAttached("NewArray.Object");
  16686  return AttachDecision::Attach;
  16687 }
  16688 
  16689 AttachDecision NewArrayIRGenerator::tryAttachStub() {
  16690  AutoAssertNoPendingException aanpe(cx_);
  16691 
  16692  TRY_ATTACH(tryAttachArrayObject());
  16693 
  16694  trackAttached(IRGenerator::NotAttached);
  16695  return AttachDecision::NoAction;
  16696 }
  16697 
  16698 NewObjectIRGenerator::NewObjectIRGenerator(JSContext* cx, HandleScript script,
  16699                                           jsbytecode* pc, ICState state,
  16700                                           JSOp op, HandleObject templateObj,
  16701                                           BaselineFrame* frame)
  16702    : IRGenerator(cx, script, pc, CacheKind::NewObject, state, frame),
  16703 #ifdef JS_CACHEIR_SPEW
  16704      op_(op),
  16705 #endif
  16706      templateObject_(templateObj) {
  16707  MOZ_ASSERT(templateObject_);
  16708 }
  16709 
  16710 void NewObjectIRGenerator::trackAttached(const char* name) {
  16711  stubName_ = name ? name : "NotAttached";
  16712 #ifdef JS_CACHEIR_SPEW
  16713  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  16714    sp.opcodeProperty("op", op_);
  16715  }
  16716 #endif
  16717 }
  16718 
  16719 AttachDecision NewObjectIRGenerator::tryAttachPlainObject() {
  16720  // Don't optimize allocations with too many dynamic slots. We use an unrolled
  16721  // loop when initializing slots and this avoids generating too much code.
  16722  static const uint32_t MaxDynamicSlotsToOptimize = 64;
  16723 
  16724  NativeObject* nativeObj = &templateObject_->as<NativeObject>();
  16725  MOZ_ASSERT(nativeObj->is<PlainObject>());
  16726 
  16727  // Stub doesn't support metadata builder
  16728  if (cx_->realm()->hasAllocationMetadataBuilder()) {
  16729    return AttachDecision::NoAction;
  16730  }
  16731 
  16732  if (nativeObj->numDynamicSlots() > MaxDynamicSlotsToOptimize) {
  16733    return AttachDecision::NoAction;
  16734  }
  16735 
  16736  MOZ_ASSERT(!nativeObj->hasDynamicElements());
  16737  MOZ_ASSERT(!nativeObj->isSharedMemory());
  16738 
  16739  gc::AllocSite* site = maybeCreateAllocSite();
  16740  if (!site) {
  16741    return AttachDecision::NoAction;
  16742  }
  16743 
  16744  uint32_t numFixedSlots = nativeObj->numUsedFixedSlots();
  16745  uint32_t numDynamicSlots = nativeObj->numDynamicSlots();
  16746  gc::AllocKind allocKind = nativeObj->allocKindForTenure();
  16747  Shape* shape = nativeObj->shape();
  16748 
  16749  writer.guardNoAllocationMetadataBuilder(
  16750      cx_->realm()->addressOfMetadataBuilder());
  16751  writer.newPlainObjectResult(numFixedSlots, numDynamicSlots, allocKind, shape,
  16752                              site);
  16753 
  16754  writer.returnFromIC();
  16755 
  16756  trackAttached("NewObject.PlainObject");
  16757  return AttachDecision::Attach;
  16758 }
  16759 
  16760 AttachDecision NewObjectIRGenerator::tryAttachStub() {
  16761  AutoAssertNoPendingException aanpe(cx_);
  16762 
  16763  TRY_ATTACH(tryAttachPlainObject());
  16764 
  16765  trackAttached(IRGenerator::NotAttached);
  16766  return AttachDecision::NoAction;
  16767 }
  16768 
  16769 LambdaIRGenerator::LambdaIRGenerator(JSContext* cx, HandleScript script,
  16770                                     jsbytecode* pc, ICState state, JSOp op,
  16771                                     Handle<JSFunction*> canonicalFunction,
  16772                                     BaselineFrame* frame)
  16773    : IRGenerator(cx, script, pc, CacheKind::Lambda, state, frame),
  16774 #ifdef JS_CACHEIR_SPEW
  16775      op_(op),
  16776 #endif
  16777      canonicalFunction_(canonicalFunction) {
  16778  MOZ_ASSERT(canonicalFunction_);
  16779 }
  16780 
  16781 void LambdaIRGenerator::trackAttached(const char* name) {
  16782  stubName_ = name ? name : "NotAttached";
  16783 #ifdef JS_CACHEIR_SPEW
  16784  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  16785    sp.opcodeProperty("op", op_);
  16786  }
  16787 #endif
  16788 }
  16789 
  16790 AttachDecision LambdaIRGenerator::tryAttachFunctionClone() {
  16791  // Don't optimize asm.js module functions.
  16792  if (canonicalFunction_->isNativeFun()) {
  16793    MOZ_ASSERT(IsAsmJSModule(canonicalFunction_));
  16794    return AttachDecision::NoAction;
  16795  }
  16796 
  16797  // Stub doesn't support metadata builder.
  16798  if (cx_->realm()->hasAllocationMetadataBuilder()) {
  16799    return AttachDecision::NoAction;
  16800  }
  16801 
  16802  gc::AllocSite* site = maybeCreateAllocSite();
  16803  if (!site) {
  16804    return AttachDecision::NoAction;
  16805  }
  16806 
  16807  writer.guardNoAllocationMetadataBuilder(
  16808      cx_->realm()->addressOfMetadataBuilder());
  16809 
  16810  gc::AllocKind allocKind = canonicalFunction_->getAllocKind();
  16811  MOZ_ASSERT(allocKind == gc::AllocKind::FUNCTION ||
  16812             allocKind == gc::AllocKind::FUNCTION_EXTENDED);
  16813  writer.newFunctionCloneResult(canonicalFunction_, allocKind, site);
  16814  writer.returnFromIC();
  16815 
  16816  trackAttached("Lambda.FunctionClone");
  16817  return AttachDecision::Attach;
  16818 }
  16819 
  16820 AttachDecision LambdaIRGenerator::tryAttachStub() {
  16821  AutoAssertNoPendingException aanpe(cx_);
  16822 
  16823  TRY_ATTACH(tryAttachFunctionClone());
  16824 
  16825  trackAttached(IRGenerator::NotAttached);
  16826  return AttachDecision::NoAction;
  16827 }
  16828 
  16829 CloseIterIRGenerator::CloseIterIRGenerator(JSContext* cx, HandleScript script,
  16830                                           jsbytecode* pc, ICState state,
  16831                                           HandleObject iter,
  16832                                           CompletionKind kind)
  16833    : IRGenerator(cx, script, pc, CacheKind::CloseIter, state),
  16834      iter_(iter),
  16835      kind_(kind) {}
  16836 
  16837 void CloseIterIRGenerator::trackAttached(const char* name) {
  16838 #ifdef JS_CACHEIR_SPEW
  16839  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  16840    sp.valueProperty("iter", ObjectValue(*iter_));
  16841  }
  16842 #endif
  16843 }
  16844 
  16845 AttachDecision CloseIterIRGenerator::tryAttachNoReturnMethod() {
  16846  Maybe<PropertyInfo> prop;
  16847  NativeObject* holder = nullptr;
  16848 
  16849  // If we can guard that the iterator does not have a |return| method,
  16850  // then this CloseIter is a no-op.
  16851  NativeGetPropKind kind = CanAttachNativeGetProp(
  16852      cx_, iter_, NameToId(cx_->names().return_), &holder, &prop, pc_);
  16853  if (kind != NativeGetPropKind::Missing) {
  16854    return AttachDecision::NoAction;
  16855  }
  16856  MOZ_ASSERT(!holder);
  16857 
  16858  ObjOperandId objId(writer.setInputOperandId(0));
  16859 
  16860  EmitMissingPropGuard(writer, &iter_->as<NativeObject>(), objId);
  16861 
  16862  // There is no return method, so we don't have to do anything.
  16863  writer.returnFromIC();
  16864 
  16865  trackAttached("CloseIter.NoReturn");
  16866  return AttachDecision::Attach;
  16867 }
  16868 
  16869 AttachDecision CloseIterIRGenerator::tryAttachScriptedReturn() {
  16870  if (kind_ == CompletionKind::Throw) {
  16871    return AttachDecision::NoAction;
  16872  }
  16873 
  16874  Maybe<PropertyInfo> prop;
  16875  NativeObject* holder = nullptr;
  16876 
  16877  NativeGetPropKind kind = CanAttachNativeGetProp(
  16878      cx_, iter_, NameToId(cx_->names().return_), &holder, &prop, pc_);
  16879  if (kind != NativeGetPropKind::Slot) {
  16880    return AttachDecision::NoAction;
  16881  }
  16882  MOZ_ASSERT(holder);
  16883  MOZ_ASSERT(prop->isDataProperty());
  16884 
  16885  size_t slot = prop->slot();
  16886  Value calleeVal = holder->getSlot(slot);
  16887  if (!calleeVal.isObject() || !calleeVal.toObject().is<JSFunction>()) {
  16888    return AttachDecision::NoAction;
  16889  }
  16890 
  16891  JSFunction* callee = &calleeVal.toObject().as<JSFunction>();
  16892  if (!callee->hasJitEntry()) {
  16893    return AttachDecision::NoAction;
  16894  }
  16895  if (callee->isClassConstructor()) {
  16896    return AttachDecision::NoAction;
  16897  }
  16898 
  16899  // We don't support cross-realm |return|.
  16900  if (cx_->realm() != callee->realm()) {
  16901    return AttachDecision::NoAction;
  16902  }
  16903 
  16904  ObjOperandId objId(writer.setInputOperandId(0));
  16905 
  16906  ObjOperandId holderId =
  16907      EmitReadSlotGuard(writer, &iter_->as<NativeObject>(), holder, objId);
  16908 
  16909  ValOperandId calleeValId = EmitLoadSlot(writer, holder, holderId, slot);
  16910  ObjOperandId calleeId = writer.guardToObject(calleeValId);
  16911  emitCalleeGuard(calleeId, callee);
  16912 
  16913  writer.closeIterScriptedResult(objId, calleeId, callee->nargs());
  16914 
  16915  writer.returnFromIC();
  16916  trackAttached("CloseIter.ScriptedReturn");
  16917 
  16918  return AttachDecision::Attach;
  16919 }
  16920 
  16921 AttachDecision CloseIterIRGenerator::tryAttachStub() {
  16922  AutoAssertNoPendingException aanpe(cx_);
  16923 
  16924  TRY_ATTACH(tryAttachNoReturnMethod());
  16925  TRY_ATTACH(tryAttachScriptedReturn());
  16926 
  16927  trackAttached(IRGenerator::NotAttached);
  16928  return AttachDecision::NoAction;
  16929 }
  16930 
  16931 OptimizeGetIteratorIRGenerator::OptimizeGetIteratorIRGenerator(
  16932    JSContext* cx, HandleScript script, jsbytecode* pc, ICState state,
  16933    HandleValue value)
  16934    : IRGenerator(cx, script, pc, CacheKind::OptimizeGetIterator, state),
  16935      val_(value) {}
  16936 
  16937 AttachDecision OptimizeGetIteratorIRGenerator::tryAttachStub() {
  16938  MOZ_ASSERT(cacheKind_ == CacheKind::OptimizeGetIterator);
  16939 
  16940  AutoAssertNoPendingException aanpe(cx_);
  16941 
  16942  TRY_ATTACH(tryAttachArray());
  16943  TRY_ATTACH(tryAttachNotOptimizable());
  16944 
  16945  MOZ_CRASH("Failed to attach unoptimizable case.");
  16946 }
  16947 
  16948 AttachDecision OptimizeGetIteratorIRGenerator::tryAttachArray() {
  16949  if (!isFirstStub_) {
  16950    return AttachDecision::NoAction;
  16951  }
  16952 
  16953  // The value must be a packed array.
  16954  if (!val_.isObject()) {
  16955    return AttachDecision::NoAction;
  16956  }
  16957  Rooted<JSObject*> obj(cx_, &val_.toObject());
  16958  if (!IsArrayWithDefaultIterator<MustBePacked::Yes>(obj, cx_)) {
  16959    return AttachDecision::NoAction;
  16960  }
  16961 
  16962  ValOperandId valId(writer.setInputOperandId(0));
  16963  ObjOperandId objId = writer.guardToObject(valId);
  16964 
  16965  // Guard the object is a packed array with Array.prototype as proto.
  16966  MOZ_ASSERT(obj->is<ArrayObject>());
  16967  writer.guardShape(objId, obj->shape());
  16968  writer.guardArrayIsPacked(objId);
  16969 
  16970  // Guard on Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next.
  16971  // This fuse also ensures the prototype chain for Array Iterator is
  16972  // maintained and that no return method is added.
  16973  writer.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse);
  16974 
  16975  writer.loadBooleanResult(true);
  16976  writer.returnFromIC();
  16977 
  16978  trackAttached("OptimizeGetIterator.Array.Fuse");
  16979  return AttachDecision::Attach;
  16980 }
  16981 
  16982 AttachDecision OptimizeGetIteratorIRGenerator::tryAttachNotOptimizable() {
  16983  ValOperandId valId(writer.setInputOperandId(0));
  16984 
  16985  writer.loadBooleanResult(false);
  16986  writer.returnFromIC();
  16987 
  16988  trackAttached("OptimizeGetIterator.NotOptimizable");
  16989  return AttachDecision::Attach;
  16990 }
  16991 
  16992 void OptimizeGetIteratorIRGenerator::trackAttached(const char* name) {
  16993  stubName_ = name ? name : "NotAttached";
  16994 
  16995 #ifdef JS_CACHEIR_SPEW
  16996  if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
  16997    sp.valueProperty("val", val_);
  16998  }
  16999 #endif
  17000 }
  17001 
  17002 GetImportIRGenerator::GetImportIRGenerator(JSContext* cx, HandleScript script,
  17003                                           jsbytecode* pc, ICState state)
  17004    : IRGenerator(cx, script, pc, CacheKind::GetImport, state) {}
  17005 
  17006 void GetImportIRGenerator::trackAttached(const char* name) {
  17007 #ifdef JS_CACHEIR_SPEW
  17008  const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name);
  17009  (void)sp;  // Silence unused warning
  17010 #endif
  17011 }
  17012 
  17013 AttachDecision GetImportIRGenerator::tryAttachInitialized() {
  17014  ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script_);
  17015  MOZ_ASSERT(env);
  17016 
  17017  jsid id = NameToId(script_->getName(pc_));
  17018  ModuleEnvironmentObject* holderEnv;
  17019  Maybe<PropertyInfo> prop;
  17020  MOZ_ALWAYS_TRUE(env->lookupImport(id, &holderEnv, &prop));
  17021 
  17022  // Imports are initialized by this point except in rare circumstances.
  17023  if (holderEnv->getSlot(prop->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
  17024    return AttachDecision::NoAction;
  17025  }
  17026 
  17027  ObjOperandId holderEnvId = writer.loadObject(holderEnv);
  17028  EmitLoadSlotResult(writer, holderEnvId, holderEnv, *prop);
  17029  writer.returnFromIC();
  17030 
  17031  trackAttached("GetImport.Initialized");
  17032  return AttachDecision::Attach;
  17033 }
  17034 
  17035 AttachDecision GetImportIRGenerator::tryAttachStub() {
  17036  AutoAssertNoPendingException aanpe(cx_);
  17037 
  17038  TRY_ATTACH(tryAttachInitialized());
  17039 
  17040  trackAttached(IRGenerator::NotAttached);
  17041  return AttachDecision::NoAction;
  17042 }
  17043 
  17044 #ifdef JS_SIMULATOR
  17045 bool js::jit::CallAnyNative(JSContext* cx, unsigned argc, Value* vp) {
  17046  CallArgs args = CallArgsFromVp(argc, vp);
  17047  JSObject* calleeObj = &args.callee();
  17048 
  17049  MOZ_ASSERT(calleeObj->is<JSFunction>());
  17050  auto* calleeFunc = &calleeObj->as<JSFunction>();
  17051  MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
  17052 
  17053  JSNative native = calleeFunc->native();
  17054  return native(cx, args.length(), args.base());
  17055 }
  17056 
  17057 const void* js::jit::RedirectedCallAnyNative() {
  17058  // The simulator requires native calls to be redirected to a
  17059  // special swi instruction. If we are calling an arbitrary native
  17060  // function, we can't wrap the real target ahead of time, so we
  17061  // call a wrapper function (CallAnyNative) that calls the target
  17062  // itself, and redirect that wrapper.
  17063  JSNative target = CallAnyNative;
  17064  void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, target);
  17065  void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3);
  17066  return redirected;
  17067 }
  17068 #endif