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/DebugOnly.h"
10 #include "mozilla/FloatingPoint.h"
11 
12 #include "jsapi.h"
13 #include "jsmath.h"
14 
15 #include "builtin/DataViewObject.h"
16 #include "builtin/MapObject.h"
17 #include "builtin/ModuleObject.h"
18 #include "builtin/Object.h"
19 #include "jit/BaselineIC.h"
20 #include "jit/CacheIRCloner.h"
21 #include "jit/CacheIRCompiler.h"
22 #include "jit/CacheIRGenerator.h"
23 #include "jit/CacheIRSpewer.h"
24 #include "jit/CacheIRWriter.h"
25 #include "jit/InlinableNatives.h"
26 #include "jit/JitContext.h"
27 #include "js/experimental/JitInfo.h"  // JSJitInfo
28 #include "js/friend/DOMProxy.h"       // JS::ExpandoAndGeneration
29 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy, js::ToWindowIfWindowProxy
30 #include "js/friend/XrayJitInfo.h"  // js::jit::GetXrayJitInfo, JS::XrayJitInfo
31 #include "js/GCAPI.h"               // JS::AutoSuppressGCAnalysis
32 #include "js/RegExpFlags.h"         // JS::RegExpFlags
33 #include "js/ScalarType.h"          // js::Scalar::Type
34 #include "js/Wrapper.h"
35 #include "proxy/DOMProxy.h"  // js::GetDOMProxyHandlerFamily
36 #include "util/Unicode.h"
37 #include "vm/ArrayBufferObject.h"
38 #include "vm/BytecodeUtil.h"
39 #include "vm/Iteration.h"
40 #include "vm/PlainObject.h"  // js::PlainObject
41 #include "vm/ProxyObject.h"
42 #include "vm/RegExpObject.h"
43 #include "vm/SelfHosting.h"
44 #include "vm/ThrowMsgKind.h"  // ThrowCondition
45 #include "vm/Watchtower.h"
46 #include "wasm/WasmInstance.h"
47 
48 #include "jit/BaselineFrame-inl.h"
49 #include "jit/MacroAssembler-inl.h"
50 #include "vm/ArrayBufferObject-inl.h"
51 #include "vm/BytecodeUtil-inl.h"
52 #include "vm/EnvironmentObject-inl.h"
53 #include "vm/JSContext-inl.h"
54 #include "vm/JSObject-inl.h"
55 #include "vm/JSScript-inl.h"
56 #include "vm/NativeObject-inl.h"
57 #include "vm/StringObject-inl.h"
58 
59 using namespace js;
60 using namespace js::jit;
61 
62 using mozilla::DebugOnly;
63 using mozilla::Maybe;
64 
65 using JS::DOMProxyShadowsResult;
66 using JS::ExpandoAndGeneration;
67 
68 const char* const js::jit::CacheKindNames[] = {
69 #define DEFINE_KIND(kind) #kind,
70     CACHE_IR_KINDS(DEFINE_KIND)
71 #undef DEFINE_KIND
72 };
73 
74 const char* const js::jit::CacheIROpNames[] = {
75 #define OPNAME(op, ...) #op,
76     CACHE_IR_OPS(OPNAME)
77 #undef OPNAME
78 };
79 
80 const CacheIROpInfo js::jit::CacheIROpInfos[] = {
81 #define OPINFO(op, len, transpile, ...) {len, transpile},
82     CACHE_IR_OPS(OPINFO)
83 #undef OPINFO
84 };
85 
86 const uint32_t js::jit::CacheIROpHealth[] = {
87 #define OPHEALTH(op, len, transpile, health) health,
88     CACHE_IR_OPS(OPHEALTH)
89 #undef OPHEALTH
90 };
91 
92 #ifdef DEBUG
NumInputsForCacheKind(CacheKind kind)93 size_t js::jit::NumInputsForCacheKind(CacheKind kind) {
94   switch (kind) {
95     case CacheKind::NewArray:
96     case CacheKind::NewObject:
97     case CacheKind::GetIntrinsic:
98       return 0;
99     case CacheKind::GetProp:
100     case CacheKind::TypeOf:
101     case CacheKind::ToPropertyKey:
102     case CacheKind::GetIterator:
103     case CacheKind::ToBool:
104     case CacheKind::UnaryArith:
105     case CacheKind::GetName:
106     case CacheKind::BindName:
107     case CacheKind::Call:
108     case CacheKind::OptimizeSpreadCall:
109       return 1;
110     case CacheKind::Compare:
111     case CacheKind::GetElem:
112     case CacheKind::GetPropSuper:
113     case CacheKind::SetProp:
114     case CacheKind::In:
115     case CacheKind::HasOwn:
116     case CacheKind::CheckPrivateField:
117     case CacheKind::InstanceOf:
118     case CacheKind::BinaryArith:
119       return 2;
120     case CacheKind::GetElemSuper:
121     case CacheKind::SetElem:
122       return 3;
123   }
124   MOZ_CRASH("Invalid kind");
125 }
126 #endif
127 
128 #ifdef DEBUG
assertSameCompartment(JSObject * obj)129 void CacheIRWriter::assertSameCompartment(JSObject* obj) {
130   cx_->debugOnlyCheck(obj);
131 }
assertSameZone(Shape * shape)132 void CacheIRWriter::assertSameZone(Shape* shape) {
133   MOZ_ASSERT(cx_->zone() == shape->zone());
134 }
135 #endif
136 
readStubFieldForIon(uint32_t offset,StubField::Type type) const137 StubField CacheIRWriter::readStubFieldForIon(uint32_t offset,
138                                              StubField::Type type) const {
139   size_t index = 0;
140   size_t currentOffset = 0;
141 
142   // If we've seen an offset earlier than this before, we know we can start the
143   // search there at least, otherwise, we start the search from the beginning.
144   if (lastOffset_ < offset) {
145     currentOffset = lastOffset_;
146     index = lastIndex_;
147   }
148 
149   while (currentOffset != offset) {
150     currentOffset += StubField::sizeInBytes(stubFields_[index].type());
151     index++;
152     MOZ_ASSERT(index < stubFields_.length());
153   }
154 
155   MOZ_ASSERT(stubFields_[index].type() == type);
156 
157   lastOffset_ = currentOffset;
158   lastIndex_ = index;
159 
160   return stubFields_[index];
161 }
162 
CacheIRCloner(ICCacheIRStub * stub)163 CacheIRCloner::CacheIRCloner(ICCacheIRStub* stub)
164     : stubInfo_(stub->stubInfo()), stubData_(stub->stubDataStart()) {}
165 
cloneOp(CacheOp op,CacheIRReader & reader,CacheIRWriter & writer)166 void CacheIRCloner::cloneOp(CacheOp op, CacheIRReader& reader,
167                             CacheIRWriter& writer) {
168   switch (op) {
169 #define DEFINE_OP(op, ...)     \
170   case CacheOp::op:            \
171     clone##op(reader, writer); \
172     break;
173     CACHE_IR_OPS(DEFINE_OP)
174 #undef DEFINE_OP
175     default:
176       MOZ_CRASH("Invalid op");
177   }
178 }
179 
readStubWord(uint32_t offset)180 uintptr_t CacheIRCloner::readStubWord(uint32_t offset) {
181   return stubInfo_->getStubRawWord(stubData_, offset);
182 }
readStubInt64(uint32_t offset)183 int64_t CacheIRCloner::readStubInt64(uint32_t offset) {
184   return stubInfo_->getStubRawInt64(stubData_, offset);
185 }
186 
getShapeField(uint32_t stubOffset)187 Shape* CacheIRCloner::getShapeField(uint32_t stubOffset) {
188   return reinterpret_cast<Shape*>(readStubWord(stubOffset));
189 }
getGetterSetterField(uint32_t stubOffset)190 GetterSetter* CacheIRCloner::getGetterSetterField(uint32_t stubOffset) {
191   return reinterpret_cast<GetterSetter*>(readStubWord(stubOffset));
192 }
getObjectField(uint32_t stubOffset)193 JSObject* CacheIRCloner::getObjectField(uint32_t stubOffset) {
194   return reinterpret_cast<JSObject*>(readStubWord(stubOffset));
195 }
getStringField(uint32_t stubOffset)196 JSString* CacheIRCloner::getStringField(uint32_t stubOffset) {
197   return reinterpret_cast<JSString*>(readStubWord(stubOffset));
198 }
getAtomField(uint32_t stubOffset)199 JSAtom* CacheIRCloner::getAtomField(uint32_t stubOffset) {
200   return reinterpret_cast<JSAtom*>(readStubWord(stubOffset));
201 }
getPropertyNameField(uint32_t stubOffset)202 PropertyName* CacheIRCloner::getPropertyNameField(uint32_t stubOffset) {
203   return reinterpret_cast<PropertyName*>(readStubWord(stubOffset));
204 }
getSymbolField(uint32_t stubOffset)205 JS::Symbol* CacheIRCloner::getSymbolField(uint32_t stubOffset) {
206   return reinterpret_cast<JS::Symbol*>(readStubWord(stubOffset));
207 }
getBaseScriptField(uint32_t stubOffset)208 BaseScript* CacheIRCloner::getBaseScriptField(uint32_t stubOffset) {
209   return reinterpret_cast<BaseScript*>(readStubWord(stubOffset));
210 }
getRawInt32Field(uint32_t stubOffset)211 uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset) {
212   return uint32_t(reinterpret_cast<uintptr_t>(readStubWord(stubOffset)));
213 }
getRawPointerField(uint32_t stubOffset)214 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset) {
215   return reinterpret_cast<const void*>(readStubWord(stubOffset));
216 }
getRawInt64Field(uint32_t stubOffset)217 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset) {
218   return static_cast<uint64_t>(readStubInt64(stubOffset));
219 }
getAllocSiteField(uint32_t stubOffset)220 gc::AllocSite* CacheIRCloner::getAllocSiteField(uint32_t stubOffset) {
221   return reinterpret_cast<gc::AllocSite*>(readStubWord(stubOffset));
222 }
223 
getIdField(uint32_t stubOffset)224 jsid CacheIRCloner::getIdField(uint32_t stubOffset) {
225   return jsid::fromRawBits(readStubWord(stubOffset));
226 }
getValueField(uint32_t stubOffset)227 const Value CacheIRCloner::getValueField(uint32_t stubOffset) {
228   return Value::fromRawBits(uint64_t(readStubInt64(stubOffset)));
229 }
getDoubleField(uint32_t stubOffset)230 double CacheIRCloner::getDoubleField(uint32_t stubOffset) {
231   uint64_t bits = uint64_t(readStubInt64(stubOffset));
232   return mozilla::BitwiseCast<double>(bits);
233 }
234 
IRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,CacheKind cacheKind,ICState state)235 IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
236                          CacheKind cacheKind, ICState state)
237     : writer(cx),
238       cx_(cx),
239       script_(script),
240       pc_(pc),
241       cacheKind_(cacheKind),
242       mode_(state.mode()),
243       isFirstStub_(state.newStubIsFirstStub()) {}
244 
GetPropIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,CacheKind cacheKind,HandleValue val,HandleValue idVal)245 GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script,
246                                        jsbytecode* pc, ICState state,
247                                        CacheKind cacheKind, HandleValue val,
248                                        HandleValue idVal)
249     : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {}
250 
EmitLoadSlotResult(CacheIRWriter & writer,ObjOperandId holderId,NativeObject * holder,PropertyInfo prop)251 static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderId,
252                                NativeObject* holder, PropertyInfo prop) {
253   if (holder->isFixedSlot(prop.slot())) {
254     writer.loadFixedSlotResult(holderId,
255                                NativeObject::getFixedSlotOffset(prop.slot()));
256   } else {
257     size_t dynamicSlotOffset =
258         holder->dynamicSlotIndex(prop.slot()) * sizeof(Value);
259     writer.loadDynamicSlotResult(holderId, dynamicSlotOffset);
260   }
261 }
262 
263 // DOM proxies
264 // -----------
265 //
266 // DOM proxies are proxies that are used to implement various DOM objects like
267 // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
268 // object that stores extra properties added to the object. The following
269 // CacheIR instructions are only used with DOM proxies:
270 //
271 // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
272 //   returns either an UndefinedValue (no expando), ObjectValue (the expando
273 //   object), or PrivateValue(ExpandoAndGeneration*).
274 //
275 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
276 //   slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
277 //   generation, then returns expandoAndGeneration->expando. This Value is
278 //   either an UndefinedValue or ObjectValue.
279 //
280 // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
281 //   expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
282 //   returns the expandoAndGeneration->expando Value.
283 //
284 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
285 //   guards it's either UndefinedValue or an object with the expected shape.
286 
287 enum class ProxyStubType {
288   None,
289   DOMExpando,
290   DOMShadowed,
291   DOMUnshadowed,
292   Generic
293 };
294 
IsCacheableDOMProxy(ProxyObject * obj)295 static bool IsCacheableDOMProxy(ProxyObject* obj) {
296   const BaseProxyHandler* handler = obj->handler();
297   if (handler->family() != GetDOMProxyHandlerFamily()) {
298     return false;
299   }
300 
301   // Some DOM proxies have dynamic prototypes.  We can't really cache those very
302   // well.
303   return obj->hasStaticPrototype();
304 }
305 
GetProxyStubType(JSContext * cx,HandleObject obj,HandleId id)306 static ProxyStubType GetProxyStubType(JSContext* cx, HandleObject obj,
307                                       HandleId id) {
308   if (!obj->is<ProxyObject>()) {
309     return ProxyStubType::None;
310   }
311   auto proxy = obj.as<ProxyObject>();
312 
313   if (!IsCacheableDOMProxy(proxy)) {
314     return ProxyStubType::Generic;
315   }
316 
317   DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, proxy, id);
318   if (shadows == DOMProxyShadowsResult::ShadowCheckFailed) {
319     cx->clearPendingException();
320     return ProxyStubType::None;
321   }
322 
323   if (DOMProxyIsShadowing(shadows)) {
324     if (shadows == DOMProxyShadowsResult::ShadowsViaDirectExpando ||
325         shadows == DOMProxyShadowsResult::ShadowsViaIndirectExpando) {
326       return ProxyStubType::DOMExpando;
327     }
328     return ProxyStubType::DOMShadowed;
329   }
330 
331   MOZ_ASSERT(shadows == DOMProxyShadowsResult::DoesntShadow ||
332              shadows == DOMProxyShadowsResult::DoesntShadowUnique);
333   return ProxyStubType::DOMUnshadowed;
334 }
335 
ValueToNameOrSymbolId(JSContext * cx,HandleValue idVal,MutableHandleId id,bool * nameOrSymbol)336 static bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idVal,
337                                   MutableHandleId id, bool* nameOrSymbol) {
338   *nameOrSymbol = false;
339 
340   if (!idVal.isString() && !idVal.isSymbol() && !idVal.isUndefined() &&
341       !idVal.isNull()) {
342     return true;
343   }
344 
345   if (!PrimitiveValueToId<CanGC>(cx, idVal, id)) {
346     return false;
347   }
348 
349   if (!id.isAtom() && !id.isSymbol()) {
350     id.set(JS::PropertyKey::Void());
351     return true;
352   }
353 
354   if (id.isAtom() && id.toAtom()->isIndex()) {
355     id.set(JS::PropertyKey::Void());
356     return true;
357   }
358 
359   *nameOrSymbol = true;
360   return true;
361 }
362 
tryAttachStub()363 AttachDecision GetPropIRGenerator::tryAttachStub() {
364   AutoAssertNoPendingException aanpe(cx_);
365 
366   ValOperandId valId(writer.setInputOperandId(0));
367   if (cacheKind_ != CacheKind::GetProp) {
368     MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper,
369                   getSuperReceiverValueId().id() == 1);
370     MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper,
371                   getElemKeyValueId().id() == 1);
372     writer.setInputOperandId(1);
373   }
374   if (cacheKind_ == CacheKind::GetElemSuper) {
375     MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
376     writer.setInputOperandId(2);
377   }
378 
379   RootedId id(cx_);
380   bool nameOrSymbol;
381   if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
382     cx_->clearPendingException();
383     return AttachDecision::NoAction;
384   }
385 
386   // |super.prop| getter calls use a |this| value that differs from lookup
387   // object.
388   ValOperandId receiverId = isSuper() ? getSuperReceiverValueId() : valId;
389 
390   if (val_.isObject()) {
391     RootedObject obj(cx_, &val_.toObject());
392     ObjOperandId objId = writer.guardToObject(valId);
393     if (nameOrSymbol) {
394       TRY_ATTACH(tryAttachObjectLength(obj, objId, id));
395       TRY_ATTACH(tryAttachTypedArray(obj, objId, id));
396       TRY_ATTACH(tryAttachDataView(obj, objId, id));
397       TRY_ATTACH(tryAttachArrayBufferMaybeShared(obj, objId, id));
398       TRY_ATTACH(tryAttachRegExp(obj, objId, id));
399       TRY_ATTACH(tryAttachNative(obj, objId, id, receiverId));
400       TRY_ATTACH(tryAttachModuleNamespace(obj, objId, id));
401       TRY_ATTACH(tryAttachWindowProxy(obj, objId, id));
402       TRY_ATTACH(tryAttachCrossCompartmentWrapper(obj, objId, id));
403       TRY_ATTACH(
404           tryAttachXrayCrossCompartmentWrapper(obj, objId, id, receiverId));
405       TRY_ATTACH(tryAttachFunction(obj, objId, id));
406       TRY_ATTACH(tryAttachArgumentsObjectIterator(obj, objId, id));
407       TRY_ATTACH(tryAttachArgumentsObjectCallee(obj, objId, id));
408       TRY_ATTACH(tryAttachProxy(obj, objId, id, receiverId));
409 
410       trackAttached(IRGenerator::NotAttached);
411       return AttachDecision::NoAction;
412     }
413 
414     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
415                cacheKind_ == CacheKind::GetElemSuper);
416 
417     TRY_ATTACH(tryAttachProxyElement(obj, objId));
418     TRY_ATTACH(tryAttachTypedArrayElement(obj, objId));
419 
420     uint32_t index;
421     Int32OperandId indexId;
422     if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
423       TRY_ATTACH(tryAttachDenseElement(obj, objId, index, indexId));
424       TRY_ATTACH(tryAttachDenseElementHole(obj, objId, index, indexId));
425       TRY_ATTACH(tryAttachSparseElement(obj, objId, index, indexId));
426       TRY_ATTACH(tryAttachArgumentsObjectArg(obj, objId, index, indexId));
427       TRY_ATTACH(tryAttachArgumentsObjectArgHole(obj, objId, index, indexId));
428       TRY_ATTACH(tryAttachGenericElement(obj, objId, index, indexId));
429 
430       trackAttached(IRGenerator::NotAttached);
431       return AttachDecision::NoAction;
432     }
433 
434     trackAttached(IRGenerator::NotAttached);
435     return AttachDecision::NoAction;
436   }
437 
438   if (nameOrSymbol) {
439     TRY_ATTACH(tryAttachPrimitive(valId, id));
440     TRY_ATTACH(tryAttachStringLength(valId, id));
441 
442     trackAttached(IRGenerator::NotAttached);
443     return AttachDecision::NoAction;
444   }
445 
446   if (idVal_.isInt32()) {
447     ValOperandId indexId = getElemKeyValueId();
448     TRY_ATTACH(tryAttachStringChar(valId, indexId));
449 
450     trackAttached(IRGenerator::NotAttached);
451     return AttachDecision::NoAction;
452   }
453 
454   trackAttached(IRGenerator::NotAttached);
455   return AttachDecision::NoAction;
456 }
457 
458 #ifdef DEBUG
459 // Any property lookups performed when trying to attach ICs must be pure, i.e.
460 // must use LookupPropertyPure() or similar functions. Pure lookups are
461 // guaranteed to never modify the prototype chain. This ensures that the holder
462 // object can always be found on the prototype chain.
IsCacheableProtoChain(NativeObject * obj,NativeObject * holder)463 static bool IsCacheableProtoChain(NativeObject* obj, NativeObject* holder) {
464   while (obj != holder) {
465     JSObject* proto = obj->staticPrototype();
466     if (!proto || !proto->is<NativeObject>()) {
467       return false;
468     }
469     obj = &proto->as<NativeObject>();
470   }
471   return true;
472 }
473 #endif
474 
IsCacheableGetPropReadSlot(NativeObject * obj,NativeObject * holder,PropertyInfo prop)475 static bool IsCacheableGetPropReadSlot(NativeObject* obj, NativeObject* holder,
476                                        PropertyInfo prop) {
477   MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
478 
479   return prop.isDataProperty();
480 }
481 
482 enum NativeGetPropCacheability {
483   CanAttachNone,
484   CanAttachReadSlot,
485   CanAttachNativeGetter,
486   CanAttachScriptedGetter,
487 };
488 
IsCacheableGetPropCall(NativeObject * obj,NativeObject * holder,PropertyInfo prop)489 static NativeGetPropCacheability IsCacheableGetPropCall(NativeObject* obj,
490                                                         NativeObject* holder,
491                                                         PropertyInfo prop) {
492   MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
493 
494   if (!prop.isAccessorProperty()) {
495     return CanAttachNone;
496   }
497 
498   JSObject* getterObject = holder->getGetter(prop);
499   if (!getterObject || !getterObject->is<JSFunction>()) {
500     return CanAttachNone;
501   }
502 
503   JSFunction& getter = getterObject->as<JSFunction>();
504 
505   if (getter.isClassConstructor()) {
506     return CanAttachNone;
507   }
508 
509   // For getters that need the WindowProxy (instead of the Window) as this
510   // object, don't cache if obj is the Window, since our cache will pass that
511   // instead of the WindowProxy.
512   if (IsWindow(obj)) {
513     // Check for a getter that has jitinfo and whose jitinfo says it's
514     // OK with both inner and outer objects.
515     if (!getter.hasJitInfo() || getter.jitInfo()->needsOuterizedThisObject()) {
516       return CanAttachNone;
517     }
518   }
519 
520   // Scripted functions and natives with JIT entry can use the scripted path.
521   if (getter.hasJitEntry()) {
522     return CanAttachScriptedGetter;
523   }
524 
525   MOZ_ASSERT(getter.isNativeWithoutJitEntry());
526   return CanAttachNativeGetter;
527 }
528 
CheckHasNoSuchOwnProperty(JSContext * cx,JSObject * obj,jsid id)529 static bool CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id) {
530   if (!obj->is<NativeObject>()) {
531     return false;
532   }
533   // Don't handle objects with resolve hooks.
534   if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
535     return false;
536   }
537   if (obj->as<NativeObject>().contains(cx, id)) {
538     return false;
539   }
540   return true;
541 }
542 
CheckHasNoSuchProperty(JSContext * cx,JSObject * obj,jsid id)543 static bool CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id) {
544   JSObject* curObj = obj;
545   do {
546     if (!CheckHasNoSuchOwnProperty(cx, curObj, id)) {
547       return false;
548     }
549 
550     curObj = curObj->staticPrototype();
551   } while (curObj);
552 
553   return true;
554 }
555 
IsCacheableNoProperty(JSContext * cx,NativeObject * obj,NativeObject * holder,jsid id,jsbytecode * pc)556 static bool IsCacheableNoProperty(JSContext* cx, NativeObject* obj,
557                                   NativeObject* holder, jsid id,
558                                   jsbytecode* pc) {
559   MOZ_ASSERT(!holder);
560 
561   // If we're doing a name lookup, we have to throw a ReferenceError.
562   if (JSOp(*pc) == JSOp::GetBoundName) {
563     return false;
564   }
565 
566   return CheckHasNoSuchProperty(cx, obj, id);
567 }
568 
CanAttachNativeGetProp(JSContext * cx,JSObject * obj,PropertyKey id,NativeObject ** holder,Maybe<PropertyInfo> * propInfo,jsbytecode * pc)569 static NativeGetPropCacheability CanAttachNativeGetProp(
570     JSContext* cx, JSObject* obj, PropertyKey id, NativeObject** holder,
571     Maybe<PropertyInfo>* propInfo, jsbytecode* pc) {
572   MOZ_ASSERT(id.isString() || id.isSymbol());
573   MOZ_ASSERT(!*holder);
574 
575   // The lookup needs to be universally pure, otherwise we risk calling hooks
576   // out of turn. We don't mind doing this even when purity isn't required,
577   // because we only miss out on shape hashification, which is only a temporary
578   // perf cost. The limits were arbitrarily set, anyways.
579   NativeObject* baseHolder = nullptr;
580   PropertyResult prop;
581   if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop)) {
582     return CanAttachNone;
583   }
584   auto* nobj = &obj->as<NativeObject>();
585 
586   if (prop.isNativeProperty()) {
587     MOZ_ASSERT(baseHolder);
588     *holder = baseHolder;
589     *propInfo = mozilla::Some(prop.propertyInfo());
590 
591     if (IsCacheableGetPropReadSlot(nobj, *holder, propInfo->ref())) {
592       return CanAttachReadSlot;
593     }
594 
595     return IsCacheableGetPropCall(nobj, *holder, propInfo->ref());
596   }
597 
598   if (!prop.isFound()) {
599     if (IsCacheableNoProperty(cx, nobj, *holder, id, pc)) {
600       return CanAttachReadSlot;
601     }
602   }
603 
604   return CanAttachNone;
605 }
606 
GuardReceiverProto(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId)607 static void GuardReceiverProto(CacheIRWriter& writer, NativeObject* obj,
608                                ObjOperandId objId) {
609   // Note: we guard on the actual prototype and not on the shape because this is
610   // used for sparse elements where we expect shape changes.
611 
612   if (JSObject* proto = obj->staticPrototype()) {
613     writer.guardProto(objId, proto);
614   } else {
615     writer.guardNullProto(objId);
616   }
617 }
618 
619 // Guard that a given object has same class and same OwnProperties (excluding
620 // dense elements and dynamic properties).
TestMatchingNativeReceiver(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId)621 static void TestMatchingNativeReceiver(CacheIRWriter& writer, NativeObject* obj,
622                                        ObjOperandId objId) {
623   writer.guardShapeForOwnProperties(objId, obj->shape());
624 }
625 
626 // Similar to |TestMatchingNativeReceiver|, but specialized for ProxyObject.
TestMatchingProxyReceiver(CacheIRWriter & writer,ProxyObject * obj,ObjOperandId objId)627 static void TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj,
628                                       ObjOperandId objId) {
629   writer.guardShapeForClass(objId, obj->shape());
630 }
631 
GeneratePrototypeGuards(CacheIRWriter & writer,JSObject * obj,NativeObject * holder,ObjOperandId objId)632 static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
633                                     NativeObject* holder, ObjOperandId objId) {
634   // Assuming target property is on |holder|, generate appropriate guards to
635   // ensure |holder| is still on the prototype chain of |obj| and we haven't
636   // introduced any shadowing definitions.
637   //
638   // For each item in the proto chain before holder, we must ensure that
639   // [[GetPrototypeOf]] still has the expected result, and that
640   // [[GetOwnProperty]] has no definition of the target property.
641   //
642   //
643   // [SMDOC] Shape Teleporting Optimization
644   // --------------------------------------
645   //
646   // Starting with the assumption (and guideline to developers) that mutating
647   // prototypes is an uncommon and fair-to-penalize operation we move cost
648   // from the access side to the mutation side.
649   //
650   // Consider the following proto chain, with B defining a property 'x':
651   //
652   //      D  ->  C  ->  B{x: 3}  ->  A  -> null
653   //
654   // When accessing |D.x| we refer to D as the "receiver", and B as the
655   // "holder". To optimize this access we need to ensure that neither D nor C
656   // has since defined a shadowing property 'x'. Since C is a prototype that
657   // we assume is rarely mutated we would like to avoid checking each time if
658   // new properties are added. To do this we require that whenever C starts
659   // shadowing a property on its proto chain, we invalidate (and opt out of) the
660   // teleporting optimization by setting the InvalidatedTeleporting flag on the
661   // object we're shadowing, triggering a shape change of that object. As a
662   // result, checking the shape of D and B is sufficient. Note that we do not
663   // care if the shape or properties of A change since the lookup of 'x' will
664   // stop at B.
665   //
666   // The second condition we must verify is that the prototype chain was not
667   // mutated. The same mechanism as above is used. When the prototype link is
668   // changed, we generate a new shape for the object. If the object whose
669   // link we are mutating is itself a prototype, we regenerate shapes down
670   // the chain by setting the InvalidatedTeleporting flag on them. This means
671   // the same two shape checks as above are sufficient.
672   //
673   // Once the InvalidatedTeleporting flag is set, it means the shape will no
674   // longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
675   // In this case we can no longer apply the optimization.
676   //
677   // See:
678   //  - ReshapeForProtoMutation
679   //  - ReshapeForShadowedProp
680 
681   MOZ_ASSERT(holder);
682   MOZ_ASSERT(obj != holder);
683 
684   // Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
685   // unchanged so peel off the receiver.
686   JSObject* pobj = obj->staticPrototype();
687   MOZ_ASSERT(pobj->isUsedAsPrototype());
688 
689   // If teleporting is supported for this holder, we are done.
690   if (!holder->hasInvalidatedTeleporting()) {
691     return;
692   }
693 
694   // If already at the holder, no further proto checks are needed.
695   if (pobj == holder) {
696     return;
697   }
698 
699   // Synchronize pobj and protoId.
700   MOZ_ASSERT(pobj == obj->staticPrototype());
701   ObjOperandId protoId = writer.loadProto(objId);
702 
703   // Shape guard each prototype object between receiver and holder. This guards
704   // against both proto changes and shadowing properties.
705   while (pobj != holder) {
706     writer.guardShape(protoId, pobj->shape());
707 
708     pobj = pobj->staticPrototype();
709     protoId = writer.loadProto(protoId);
710   }
711 }
712 
GeneratePrototypeHoleGuards(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId,bool alwaysGuardFirstProto)713 static void GeneratePrototypeHoleGuards(CacheIRWriter& writer,
714                                         NativeObject* obj, ObjOperandId objId,
715                                         bool alwaysGuardFirstProto) {
716   if (alwaysGuardFirstProto) {
717     GuardReceiverProto(writer, obj, objId);
718   }
719 
720   JSObject* pobj = obj->staticPrototype();
721   while (pobj) {
722     ObjOperandId protoId = writer.loadObject(pobj);
723 
724     // Make sure the shape matches, to ensure the proto is unchanged and to
725     // avoid non-dense elements or anything else that is being checked by
726     // CanAttachDenseElementHole.
727     MOZ_ASSERT(pobj->is<NativeObject>());
728     writer.guardShape(protoId, pobj->shape());
729 
730     // Also make sure there are no dense elements.
731     writer.guardNoDenseElements(protoId);
732 
733     pobj = pobj->staticPrototype();
734   }
735 }
736 
737 // Similar to |TestMatchingReceiver|, but for the holder object (when it
738 // differs from the receiver). The holder may also be the expando of the
739 // receiver if it exists.
TestMatchingHolder(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId)740 static void TestMatchingHolder(CacheIRWriter& writer, NativeObject* obj,
741                                ObjOperandId objId) {
742   // The GeneratePrototypeGuards + TestMatchingHolder checks only support
743   // prototype chains composed of NativeObject (excluding the receiver
744   // itself).
745   writer.guardShapeForOwnProperties(objId, obj->shape());
746 }
747 
748 // Emit a shape guard for all objects on the proto chain. This does NOT include
749 // the receiver; callers must ensure the receiver's proto is the first proto by
750 // either emitting a shape guard or a prototype guard for |objId|.
751 //
752 // Note: this relies on shape implying proto.
ShapeGuardProtoChain(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId)753 static void ShapeGuardProtoChain(CacheIRWriter& writer, NativeObject* obj,
754                                  ObjOperandId objId) {
755   while (true) {
756     JSObject* proto = obj->staticPrototype();
757     if (!proto) {
758       return;
759     }
760 
761     obj = &proto->as<NativeObject>();
762     objId = writer.loadProto(objId);
763 
764     writer.guardShape(objId, obj->shape());
765   }
766 }
767 
768 // For cross compartment guards we shape-guard the prototype chain to avoid
769 // referencing the holder object.
770 //
771 // This peels off the first layer because it's guarded against obj == holder.
ShapeGuardProtoChainForCrossCompartmentHolder(CacheIRWriter & writer,NativeObject * obj,ObjOperandId objId,NativeObject * holder,Maybe<ObjOperandId> * holderId)772 static void ShapeGuardProtoChainForCrossCompartmentHolder(
773     CacheIRWriter& writer, NativeObject* obj, ObjOperandId objId,
774     NativeObject* holder, Maybe<ObjOperandId>* holderId) {
775   MOZ_ASSERT(obj != holder);
776   MOZ_ASSERT(holder);
777   while (true) {
778     MOZ_ASSERT(obj->staticPrototype());
779     obj = &obj->staticPrototype()->as<NativeObject>();
780 
781     objId = writer.loadProto(objId);
782     if (obj == holder) {
783       TestMatchingHolder(writer, obj, objId);
784       holderId->emplace(objId);
785       return;
786     } else {
787       writer.guardShapeForOwnProperties(objId, obj->shape());
788     }
789   }
790 }
791 
792 enum class SlotReadType { Normal, CrossCompartment };
793 
794 template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
EmitReadSlotGuard(CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,ObjOperandId objId,Maybe<ObjOperandId> * holderId)795 static void EmitReadSlotGuard(CacheIRWriter& writer, NativeObject* obj,
796                               NativeObject* holder, ObjOperandId objId,
797                               Maybe<ObjOperandId>* holderId) {
798   TestMatchingNativeReceiver(writer, obj, objId);
799 
800   if (obj != holder) {
801     if (holder) {
802       if (MaybeCrossCompartment == SlotReadType::CrossCompartment) {
803         // Guard proto chain integrity.
804         // We use a variant of guards that avoid baking in any cross-compartment
805         // object pointers.
806         ShapeGuardProtoChainForCrossCompartmentHolder(writer, obj, objId,
807                                                       holder, holderId);
808       } else {
809         // Guard proto chain integrity.
810         GeneratePrototypeGuards(writer, obj, holder, objId);
811 
812         // Guard on the holder's shape.
813         holderId->emplace(writer.loadObject(holder));
814         TestMatchingHolder(writer, holder, holderId->ref());
815       }
816     } else {
817       // The property does not exist. Guard on everything in the prototype
818       // chain. This is guaranteed to see only Native objects because of
819       // CanAttachNativeGetProp().
820       ShapeGuardProtoChain(writer, obj, objId);
821     }
822   } else {
823     holderId->emplace(objId);
824   }
825 }
826 
827 template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
EmitReadSlotResult(CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,Maybe<PropertyInfo> prop,ObjOperandId objId)828 static void EmitReadSlotResult(CacheIRWriter& writer, NativeObject* obj,
829                                NativeObject* holder, Maybe<PropertyInfo> prop,
830                                ObjOperandId objId) {
831   Maybe<ObjOperandId> holderId;
832   EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId,
833                                            &holderId);
834 
835   // Slot access.
836   if (holder) {
837     MOZ_ASSERT(holderId->valid());
838     EmitLoadSlotResult(writer, *holderId, holder, *prop);
839   } else {
840     MOZ_ASSERT(holderId.isNothing());
841     writer.loadUndefinedResult();
842   }
843 }
844 
EmitCallGetterResultNoGuards(JSContext * cx,CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,PropertyInfo prop,ValOperandId receiverId)845 static void EmitCallGetterResultNoGuards(JSContext* cx, CacheIRWriter& writer,
846                                          NativeObject* obj,
847                                          NativeObject* holder,
848                                          PropertyInfo prop,
849                                          ValOperandId receiverId) {
850   JSFunction* target = &holder->getGetter(prop)->as<JSFunction>();
851   bool sameRealm = cx->realm() == target->realm();
852 
853   switch (IsCacheableGetPropCall(obj, holder, prop)) {
854     case CanAttachNativeGetter: {
855       writer.callNativeGetterResult(receiverId, target, sameRealm);
856       writer.returnFromIC();
857       break;
858     }
859     case CanAttachScriptedGetter: {
860       writer.callScriptedGetterResult(receiverId, target, sameRealm);
861       writer.returnFromIC();
862       break;
863     }
864     default:
865       // CanAttachNativeGetProp guarantees that the getter is either a native or
866       // a scripted function.
867       MOZ_ASSERT_UNREACHABLE("Can't attach getter");
868       break;
869   }
870 }
871 
872 // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
873 // properties
EmitGuardGetterSetterSlot(CacheIRWriter & writer,NativeObject * holder,PropertyInfo prop,ObjOperandId holderId,bool holderIsConstant=false)874 static void EmitGuardGetterSetterSlot(CacheIRWriter& writer,
875                                       NativeObject* holder, PropertyInfo prop,
876                                       ObjOperandId holderId,
877                                       bool holderIsConstant = false) {
878   // If the holder is guaranteed to be the same object, and it never had a
879   // slot holding a GetterSetter mutated or deleted, its Shape will change when
880   // that does happen so we don't need to guard on the GetterSetter.
881   if (holderIsConstant && !holder->hadGetterSetterChange()) {
882     return;
883   }
884 
885   size_t slot = prop.slot();
886   Value slotVal = holder->getSlot(slot);
887   MOZ_ASSERT(slotVal.isPrivateGCThing());
888 
889   if (holder->isFixedSlot(slot)) {
890     size_t offset = NativeObject::getFixedSlotOffset(slot);
891     writer.guardFixedSlotValue(holderId, offset, slotVal);
892   } else {
893     size_t offset = holder->dynamicSlotIndex(slot) * sizeof(Value);
894     writer.guardDynamicSlotValue(holderId, offset, slotVal);
895   }
896 }
897 
EmitCallGetterResultGuards(CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,HandleId id,PropertyInfo prop,ObjOperandId objId,ICState::Mode mode)898 static void EmitCallGetterResultGuards(CacheIRWriter& writer, NativeObject* obj,
899                                        NativeObject* holder, HandleId id,
900                                        PropertyInfo prop, ObjOperandId objId,
901                                        ICState::Mode mode) {
902   // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
903   // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
904   // require outerizing).
905 
906   MOZ_ASSERT(holder->containsPure(id, prop));
907 
908   if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
909     TestMatchingNativeReceiver(writer, obj, objId);
910 
911     if (obj != holder) {
912       GeneratePrototypeGuards(writer, obj, holder, objId);
913 
914       // Guard on the holder's shape.
915       ObjOperandId holderId = writer.loadObject(holder);
916       TestMatchingHolder(writer, holder, holderId);
917 
918       EmitGuardGetterSetterSlot(writer, holder, prop, holderId,
919                                 /* holderIsConstant = */ true);
920     } else {
921       EmitGuardGetterSetterSlot(writer, holder, prop, objId);
922     }
923   } else {
924     GetterSetter* gs = holder->getGetterSetter(prop);
925     writer.guardHasGetterSetter(objId, id, gs);
926   }
927 }
928 
EmitCallGetterResult(JSContext * cx,CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,HandleId id,PropertyInfo prop,ObjOperandId objId,ValOperandId receiverId,ICState::Mode mode)929 static void EmitCallGetterResult(JSContext* cx, CacheIRWriter& writer,
930                                  NativeObject* obj, NativeObject* holder,
931                                  HandleId id, PropertyInfo prop,
932                                  ObjOperandId objId, ValOperandId receiverId,
933                                  ICState::Mode mode) {
934   EmitCallGetterResultGuards(writer, obj, holder, id, prop, objId, mode);
935   EmitCallGetterResultNoGuards(cx, writer, obj, holder, prop, receiverId);
936 }
937 
CanAttachDOMCall(JSContext * cx,JSJitInfo::OpType type,JSObject * obj,JSFunction * fun,ICState::Mode mode)938 static bool CanAttachDOMCall(JSContext* cx, JSJitInfo::OpType type,
939                              JSObject* obj, JSFunction* fun,
940                              ICState::Mode mode) {
941   MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter ||
942              type == JSJitInfo::Method);
943 
944   if (mode != ICState::Mode::Specialized) {
945     return false;
946   }
947 
948   if (!fun->hasJitInfo()) {
949     return false;
950   }
951 
952   if (cx->realm() != fun->realm()) {
953     return false;
954   }
955 
956   const JSJitInfo* jitInfo = fun->jitInfo();
957   MOZ_ASSERT_IF(IsWindow(obj), !jitInfo->needsOuterizedThisObject());
958   if (jitInfo->type() != type) {
959     return false;
960   }
961 
962   const JSClass* clasp = obj->getClass();
963   if (!clasp->isDOMClass()) {
964     return false;
965   }
966 
967   if (type != JSJitInfo::Method && clasp->isProxyObject()) {
968     return false;
969   }
970 
971   // Tell the analysis the |DOMInstanceClassHasProtoAtDepth| hook can't GC.
972   JS::AutoSuppressGCAnalysis nogc;
973 
974   DOMInstanceClassHasProtoAtDepth instanceChecker =
975       cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
976   return instanceChecker(clasp, jitInfo->protoID, jitInfo->depth);
977 }
978 
CanAttachDOMGetterSetter(JSContext * cx,JSJitInfo::OpType type,NativeObject * obj,NativeObject * holder,PropertyInfo prop,ICState::Mode mode)979 static bool CanAttachDOMGetterSetter(JSContext* cx, JSJitInfo::OpType type,
980                                      NativeObject* obj, NativeObject* holder,
981                                      PropertyInfo prop, ICState::Mode mode) {
982   MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter);
983 
984   JSObject* accessor = type == JSJitInfo::Getter ? holder->getGetter(prop)
985                                                  : holder->getSetter(prop);
986   JSFunction* fun = &accessor->as<JSFunction>();
987 
988   return CanAttachDOMCall(cx, type, obj, fun, mode);
989 }
990 
EmitCallDOMGetterResultNoGuards(CacheIRWriter & writer,NativeObject * holder,PropertyInfo prop,ObjOperandId objId)991 static void EmitCallDOMGetterResultNoGuards(CacheIRWriter& writer,
992                                             NativeObject* holder,
993                                             PropertyInfo prop,
994                                             ObjOperandId objId) {
995   JSFunction* getter = &holder->getGetter(prop)->as<JSFunction>();
996   writer.callDOMGetterResult(objId, getter->jitInfo());
997   writer.returnFromIC();
998 }
999 
EmitCallDOMGetterResult(JSContext * cx,CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,HandleId id,PropertyInfo prop,ObjOperandId objId)1000 static void EmitCallDOMGetterResult(JSContext* cx, CacheIRWriter& writer,
1001                                     NativeObject* obj, NativeObject* holder,
1002                                     HandleId id, PropertyInfo prop,
1003                                     ObjOperandId objId) {
1004   // Note: this relies on EmitCallGetterResultGuards emitting a shape guard
1005   // for specialized stubs.
1006   // The shape guard ensures the receiver's Class is valid for this DOM getter.
1007   EmitCallGetterResultGuards(writer, obj, holder, id, prop, objId,
1008                              ICState::Mode::Specialized);
1009   EmitCallDOMGetterResultNoGuards(writer, holder, prop, objId);
1010 }
1011 
attachMegamorphicNativeSlot(ObjOperandId objId,jsid id)1012 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId,
1013                                                      jsid id) {
1014   MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
1015 
1016   if (cacheKind_ == CacheKind::GetProp ||
1017       cacheKind_ == CacheKind::GetPropSuper) {
1018     writer.megamorphicLoadSlotResult(objId, id.toAtom()->asPropertyName());
1019   } else {
1020     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
1021                cacheKind_ == CacheKind::GetElemSuper);
1022     writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId());
1023   }
1024   writer.returnFromIC();
1025 
1026   trackAttached("MegamorphicNativeSlot");
1027 }
1028 
tryAttachNative(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId receiverId)1029 AttachDecision GetPropIRGenerator::tryAttachNative(HandleObject obj,
1030                                                    ObjOperandId objId,
1031                                                    HandleId id,
1032                                                    ValOperandId receiverId) {
1033   Maybe<PropertyInfo> prop;
1034   NativeObject* holder = nullptr;
1035 
1036   NativeGetPropCacheability type =
1037       CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
1038   switch (type) {
1039     case CanAttachNone:
1040       return AttachDecision::NoAction;
1041     case CanAttachReadSlot: {
1042       auto* nobj = &obj->as<NativeObject>();
1043 
1044       if (mode_ == ICState::Mode::Megamorphic) {
1045         attachMegamorphicNativeSlot(objId, id);
1046         return AttachDecision::Attach;
1047       }
1048 
1049       maybeEmitIdGuard(id);
1050       EmitReadSlotResult(writer, nobj, holder, prop, objId);
1051       writer.returnFromIC();
1052 
1053       trackAttached("NativeSlot");
1054       return AttachDecision::Attach;
1055     }
1056     case CanAttachScriptedGetter:
1057     case CanAttachNativeGetter: {
1058       auto* nobj = &obj->as<NativeObject>();
1059 
1060       maybeEmitIdGuard(id);
1061 
1062       if (!isSuper() && CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, nobj,
1063                                                  holder, *prop, mode_)) {
1064         EmitCallDOMGetterResult(cx_, writer, nobj, holder, id, *prop, objId);
1065 
1066         trackAttached("DOMGetter");
1067         return AttachDecision::Attach;
1068       }
1069 
1070       EmitCallGetterResult(cx_, writer, nobj, holder, id, *prop, objId,
1071                            receiverId, mode_);
1072 
1073       trackAttached("NativeGetter");
1074       return AttachDecision::Attach;
1075     }
1076   }
1077 
1078   MOZ_CRASH("Bad NativeGetPropCacheability");
1079 }
1080 
1081 // Returns whether obj is a WindowProxy wrapping the script's global.
IsWindowProxyForScriptGlobal(JSScript * script,JSObject * obj)1082 static bool IsWindowProxyForScriptGlobal(JSScript* script, JSObject* obj) {
1083   if (!IsWindowProxy(obj)) {
1084     return false;
1085   }
1086 
1087   MOZ_ASSERT(obj->getClass() ==
1088              script->runtimeFromMainThread()->maybeWindowProxyClass());
1089 
1090   JSObject* window = ToWindowIfWindowProxy(obj);
1091 
1092   // Ion relies on the WindowProxy's group changing (and the group getting
1093   // marked as having unknown properties) on navigation. If we ever stop
1094   // transplanting same-compartment WindowProxies, this assert will fail and we
1095   // need to fix that code.
1096   MOZ_ASSERT(window == &obj->nonCCWGlobal());
1097 
1098   // This must be a WindowProxy for a global in this compartment. Else it would
1099   // be a cross-compartment wrapper and IsWindowProxy returns false for
1100   // those.
1101   MOZ_ASSERT(script->compartment() == obj->compartment());
1102 
1103   // Only optimize lookups on the WindowProxy for the current global. Other
1104   // WindowProxies in the compartment may require security checks (based on
1105   // mutable document.domain). See bug 1516775.
1106   return window == &script->global();
1107 }
1108 
1109 // Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
GuardAndLoadWindowProxyWindow(CacheIRWriter & writer,ObjOperandId objId,GlobalObject * windowObj)1110 static ObjOperandId GuardAndLoadWindowProxyWindow(CacheIRWriter& writer,
1111                                                   ObjOperandId objId,
1112                                                   GlobalObject* windowObj) {
1113   // Note: update AddCacheIRGetPropFunction in BaselineInspector.cpp when making
1114   // changes here.
1115   writer.guardClass(objId, GuardClassKind::WindowProxy);
1116   ObjOperandId windowObjId = writer.loadWrapperTarget(objId);
1117   writer.guardSpecificObject(windowObjId, windowObj);
1118   return windowObjId;
1119 }
1120 
tryAttachWindowProxy(HandleObject obj,ObjOperandId objId,HandleId id)1121 AttachDecision GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
1122                                                         ObjOperandId objId,
1123                                                         HandleId id) {
1124   // Attach a stub when the receiver is a WindowProxy and we can do the lookup
1125   // on the Window (the global object).
1126 
1127   if (!IsWindowProxyForScriptGlobal(script_, obj)) {
1128     return AttachDecision::NoAction;
1129   }
1130 
1131   // If we're megamorphic prefer a generic proxy stub that handles a lot more
1132   // cases.
1133   if (mode_ == ICState::Mode::Megamorphic) {
1134     return AttachDecision::NoAction;
1135   }
1136 
1137   // Now try to do the lookup on the Window (the current global).
1138   GlobalObject* windowObj = cx_->global();
1139   NativeObject* holder = nullptr;
1140   Maybe<PropertyInfo> prop;
1141   NativeGetPropCacheability type =
1142       CanAttachNativeGetProp(cx_, windowObj, id, &holder, &prop, pc_);
1143   switch (type) {
1144     case CanAttachNone:
1145       return AttachDecision::NoAction;
1146 
1147     case CanAttachReadSlot: {
1148       maybeEmitIdGuard(id);
1149       ObjOperandId windowObjId =
1150           GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
1151       EmitReadSlotResult(writer, windowObj, holder, prop, windowObjId);
1152       writer.returnFromIC();
1153 
1154       trackAttached("WindowProxySlot");
1155       return AttachDecision::Attach;
1156     }
1157 
1158     case CanAttachNativeGetter: {
1159       // Make sure the native getter is okay with the IC passing the Window
1160       // instead of the WindowProxy as |this| value.
1161       JSFunction* callee = &holder->getGetter(*prop)->as<JSFunction>();
1162       MOZ_ASSERT(callee->isNativeWithoutJitEntry());
1163       if (!callee->hasJitInfo() ||
1164           callee->jitInfo()->needsOuterizedThisObject()) {
1165         return AttachDecision::NoAction;
1166       }
1167 
1168       // If a |super| access, it is not worth the complexity to attach an IC.
1169       if (isSuper()) {
1170         return AttachDecision::NoAction;
1171       }
1172 
1173       // Guard the incoming object is a WindowProxy and inline a getter call
1174       // based on the Window object.
1175       maybeEmitIdGuard(id);
1176       ObjOperandId windowObjId =
1177           GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
1178 
1179       if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, windowObj, holder,
1180                                    *prop, mode_)) {
1181         EmitCallDOMGetterResult(cx_, writer, windowObj, holder, id, *prop,
1182                                 windowObjId);
1183         trackAttached("WindowProxyDOMGetter");
1184       } else {
1185         ValOperandId receiverId = writer.boxObject(windowObjId);
1186         EmitCallGetterResult(cx_, writer, windowObj, holder, id, *prop,
1187                              windowObjId, receiverId, mode_);
1188         trackAttached("WindowProxyGetter");
1189       }
1190 
1191       return AttachDecision::Attach;
1192     }
1193 
1194     case CanAttachScriptedGetter:
1195       MOZ_ASSERT_UNREACHABLE("Not possible for window proxies");
1196   }
1197 
1198   MOZ_CRASH("Unreachable");
1199 }
1200 
tryAttachCrossCompartmentWrapper(HandleObject obj,ObjOperandId objId,HandleId id)1201 AttachDecision GetPropIRGenerator::tryAttachCrossCompartmentWrapper(
1202     HandleObject obj, ObjOperandId objId, HandleId id) {
1203   // We can only optimize this very wrapper-handler, because others might
1204   // have a security policy.
1205   if (!IsWrapper(obj) ||
1206       Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton) {
1207     return AttachDecision::NoAction;
1208   }
1209 
1210   // If we're megamorphic prefer a generic proxy stub that handles a lot more
1211   // cases.
1212   if (mode_ == ICState::Mode::Megamorphic) {
1213     return AttachDecision::NoAction;
1214   }
1215 
1216   RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
1217   MOZ_ASSERT(unwrapped == UnwrapOneCheckedStatic(obj));
1218   MOZ_ASSERT(!IsCrossCompartmentWrapper(unwrapped),
1219              "CCWs must not wrap other CCWs");
1220 
1221   // If we allowed different zones we would have to wrap strings.
1222   if (unwrapped->compartment()->zone() != cx_->compartment()->zone()) {
1223     return AttachDecision::NoAction;
1224   }
1225 
1226   // Take the unwrapped object's global, and wrap in a
1227   // this-compartment wrapper. This is what will be stored in the IC
1228   // keep the compartment alive.
1229   RootedObject wrappedTargetGlobal(cx_, &unwrapped->nonCCWGlobal());
1230   if (!cx_->compartment()->wrap(cx_, &wrappedTargetGlobal)) {
1231     cx_->clearPendingException();
1232     return AttachDecision::NoAction;
1233   }
1234 
1235   NativeObject* holder = nullptr;
1236   Maybe<PropertyInfo> prop;
1237 
1238   // Enter realm of target to prevent failing compartment assertions when doing
1239   // the lookup.
1240   {
1241     AutoRealm ar(cx_, unwrapped);
1242 
1243     NativeGetPropCacheability canCache =
1244         CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &prop, pc_);
1245     if (canCache != CanAttachReadSlot) {
1246       return AttachDecision::NoAction;
1247     }
1248   }
1249   auto* unwrappedNative = &unwrapped->as<NativeObject>();
1250 
1251   maybeEmitIdGuard(id);
1252   writer.guardIsProxy(objId);
1253   writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj));
1254 
1255   // Load the object wrapped by the CCW
1256   ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
1257 
1258   // If the compartment of the wrapped object is different we should fail.
1259   writer.guardCompartment(wrapperTargetId, wrappedTargetGlobal,
1260                           unwrappedNative->compartment());
1261 
1262   ObjOperandId unwrappedId = wrapperTargetId;
1263   EmitReadSlotResult<SlotReadType::CrossCompartment>(writer, unwrappedNative,
1264                                                      holder, prop, unwrappedId);
1265   writer.wrapResult();
1266   writer.returnFromIC();
1267 
1268   trackAttached("CCWSlot");
1269   return AttachDecision::Attach;
1270 }
1271 
1272 static JSObject* NewWrapperWithObjectShape(JSContext* cx,
1273                                            HandleNativeObject obj);
1274 
GetXrayExpandoShapeWrapper(JSContext * cx,HandleObject xray,MutableHandleObject wrapper)1275 static bool GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray,
1276                                        MutableHandleObject wrapper) {
1277   Value v = GetProxyReservedSlot(xray, GetXrayJitInfo()->xrayHolderSlot);
1278   if (v.isObject()) {
1279     NativeObject* holder = &v.toObject().as<NativeObject>();
1280     v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot);
1281     if (v.isObject()) {
1282       RootedNativeObject expando(
1283           cx, &UncheckedUnwrap(&v.toObject())->as<NativeObject>());
1284       wrapper.set(NewWrapperWithObjectShape(cx, expando));
1285       return wrapper != nullptr;
1286     }
1287   }
1288   wrapper.set(nullptr);
1289   return true;
1290 }
1291 
tryAttachXrayCrossCompartmentWrapper(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId receiverId)1292 AttachDecision GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(
1293     HandleObject obj, ObjOperandId objId, HandleId id,
1294     ValOperandId receiverId) {
1295   if (!obj->is<ProxyObject>()) {
1296     return AttachDecision::NoAction;
1297   }
1298 
1299   JS::XrayJitInfo* info = GetXrayJitInfo();
1300   if (!info || !info->isCrossCompartmentXray(GetProxyHandler(obj))) {
1301     return AttachDecision::NoAction;
1302   }
1303 
1304   if (!info->compartmentHasExclusiveExpandos(obj)) {
1305     return AttachDecision::NoAction;
1306   }
1307 
1308   RootedObject target(cx_, UncheckedUnwrap(obj));
1309 
1310   RootedObject expandoShapeWrapper(cx_);
1311   if (!GetXrayExpandoShapeWrapper(cx_, obj, &expandoShapeWrapper)) {
1312     cx_->recoverFromOutOfMemory();
1313     return AttachDecision::NoAction;
1314   }
1315 
1316   // Look for a getter we can call on the xray or its prototype chain.
1317   Rooted<Maybe<PropertyDescriptor>> desc(cx_);
1318   RootedObject holder(cx_, obj);
1319   RootedObjectVector prototypes(cx_);
1320   RootedObjectVector prototypeExpandoShapeWrappers(cx_);
1321   while (true) {
1322     if (!GetOwnPropertyDescriptor(cx_, holder, id, &desc)) {
1323       cx_->clearPendingException();
1324       return AttachDecision::NoAction;
1325     }
1326     if (desc.isSome()) {
1327       break;
1328     }
1329     if (!GetPrototype(cx_, holder, &holder)) {
1330       cx_->clearPendingException();
1331       return AttachDecision::NoAction;
1332     }
1333     if (!holder || !holder->is<ProxyObject>() ||
1334         !info->isCrossCompartmentXray(GetProxyHandler(holder))) {
1335       return AttachDecision::NoAction;
1336     }
1337     RootedObject prototypeExpandoShapeWrapper(cx_);
1338     if (!GetXrayExpandoShapeWrapper(cx_, holder,
1339                                     &prototypeExpandoShapeWrapper) ||
1340         !prototypes.append(holder) ||
1341         !prototypeExpandoShapeWrappers.append(prototypeExpandoShapeWrapper)) {
1342       cx_->recoverFromOutOfMemory();
1343       return AttachDecision::NoAction;
1344     }
1345   }
1346   if (!desc->isAccessorDescriptor()) {
1347     return AttachDecision::NoAction;
1348   }
1349 
1350   RootedObject getter(cx_, desc->getter());
1351   if (!getter || !getter->is<JSFunction>() ||
1352       !getter->as<JSFunction>().isNativeWithoutJitEntry()) {
1353     return AttachDecision::NoAction;
1354   }
1355 
1356   maybeEmitIdGuard(id);
1357   writer.guardIsProxy(objId);
1358   writer.guardHasProxyHandler(objId, GetProxyHandler(obj));
1359 
1360   // Load the object wrapped by the CCW
1361   ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
1362 
1363   // Test the wrapped object's class. The properties held by xrays or their
1364   // prototypes will be invariant for objects of a given class, except for
1365   // changes due to xray expandos or xray prototype mutations.
1366   writer.guardAnyClass(wrapperTargetId, target->getClass());
1367 
1368   // Make sure the expandos on the xray and its prototype chain match up with
1369   // what we expect. The expando shape needs to be consistent, to ensure it
1370   // has not had any shadowing properties added, and the expando cannot have
1371   // any custom prototype (xray prototypes are stable otherwise).
1372   //
1373   // We can only do this for xrays with exclusive access to their expandos
1374   // (as we checked earlier), which store a pointer to their expando
1375   // directly. Xrays in other compartments may share their expandos with each
1376   // other and a VM call is needed just to find the expando.
1377   if (expandoShapeWrapper) {
1378     writer.guardXrayExpandoShapeAndDefaultProto(objId, expandoShapeWrapper);
1379   } else {
1380     writer.guardXrayNoExpando(objId);
1381   }
1382   for (size_t i = 0; i < prototypes.length(); i++) {
1383     JSObject* proto = prototypes[i];
1384     ObjOperandId protoId = writer.loadObject(proto);
1385     if (JSObject* protoShapeWrapper = prototypeExpandoShapeWrappers[i]) {
1386       writer.guardXrayExpandoShapeAndDefaultProto(protoId, protoShapeWrapper);
1387     } else {
1388       writer.guardXrayNoExpando(protoId);
1389     }
1390   }
1391 
1392   bool sameRealm = cx_->realm() == getter->as<JSFunction>().realm();
1393   writer.callNativeGetterResult(receiverId, &getter->as<JSFunction>(),
1394                                 sameRealm);
1395   writer.returnFromIC();
1396 
1397   trackAttached("XrayGetter");
1398   return AttachDecision::Attach;
1399 }
1400 
tryAttachGenericProxy(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,bool handleDOMProxies)1401 AttachDecision GetPropIRGenerator::tryAttachGenericProxy(
1402     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
1403     bool handleDOMProxies) {
1404   writer.guardIsProxy(objId);
1405 
1406   if (!handleDOMProxies) {
1407     // Ensure that the incoming object is not a DOM proxy, so that we can get to
1408     // the specialized stubs
1409     writer.guardIsNotDOMProxy(objId);
1410   }
1411 
1412   if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
1413     MOZ_ASSERT(!isSuper());
1414     maybeEmitIdGuard(id);
1415     writer.proxyGetResult(objId, id);
1416   } else {
1417     // Attach a stub that handles every id.
1418     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
1419     MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
1420     MOZ_ASSERT(!isSuper());
1421     writer.proxyGetByValueResult(objId, getElemKeyValueId());
1422   }
1423 
1424   writer.returnFromIC();
1425 
1426   trackAttached("GenericProxy");
1427   return AttachDecision::Attach;
1428 }
1429 
ValueIsInt64Index(const Value & val,int64_t * index)1430 static bool ValueIsInt64Index(const Value& val, int64_t* index) {
1431   // Try to convert the Value to a TypedArray index or DataView offset.
1432 
1433   if (val.isInt32()) {
1434     *index = val.toInt32();
1435     return true;
1436   }
1437 
1438   if (val.isDouble()) {
1439     // Use NumberEqualsInt64 because ToPropertyKey(-0) is 0.
1440     return mozilla::NumberEqualsInt64(val.toDouble(), index);
1441   }
1442 
1443   return false;
1444 }
1445 
guardToIntPtrIndex(const Value & index,ValOperandId indexId,bool supportOOB)1446 IntPtrOperandId IRGenerator::guardToIntPtrIndex(const Value& index,
1447                                                 ValOperandId indexId,
1448                                                 bool supportOOB) {
1449 #ifdef DEBUG
1450   int64_t indexInt64;
1451   MOZ_ASSERT_IF(!supportOOB, ValueIsInt64Index(index, &indexInt64));
1452 #endif
1453 
1454   if (index.isInt32()) {
1455     Int32OperandId int32IndexId = writer.guardToInt32(indexId);
1456     return writer.int32ToIntPtr(int32IndexId);
1457   }
1458 
1459   MOZ_ASSERT(index.isNumber());
1460   NumberOperandId numberIndexId = writer.guardIsNumber(indexId);
1461   return writer.guardNumberToIntPtrIndex(numberIndexId, supportOOB);
1462 }
1463 
guardDOMProxyExpandoObjectAndShape(ProxyObject * obj,ObjOperandId objId,const Value & expandoVal,NativeObject * expandoObj)1464 ObjOperandId IRGenerator::guardDOMProxyExpandoObjectAndShape(
1465     ProxyObject* obj, ObjOperandId objId, const Value& expandoVal,
1466     NativeObject* expandoObj) {
1467   MOZ_ASSERT(IsCacheableDOMProxy(obj));
1468 
1469   TestMatchingProxyReceiver(writer, obj, objId);
1470 
1471   // Shape determines Class, so now it must be a DOM proxy.
1472   ValOperandId expandoValId;
1473   if (expandoVal.isObject()) {
1474     expandoValId = writer.loadDOMExpandoValue(objId);
1475   } else {
1476     expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
1477   }
1478 
1479   // Guard the expando is an object and shape guard.
1480   ObjOperandId expandoObjId = writer.guardToObject(expandoValId);
1481   TestMatchingHolder(writer, expandoObj, expandoObjId);
1482   return expandoObjId;
1483 }
1484 
tryAttachDOMProxyExpando(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId receiverId)1485 AttachDecision GetPropIRGenerator::tryAttachDOMProxyExpando(
1486     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
1487     ValOperandId receiverId) {
1488   MOZ_ASSERT(IsCacheableDOMProxy(obj));
1489 
1490   Value expandoVal = GetProxyPrivate(obj);
1491   JSObject* expandoObj;
1492   if (expandoVal.isObject()) {
1493     expandoObj = &expandoVal.toObject();
1494   } else {
1495     MOZ_ASSERT(!expandoVal.isUndefined(),
1496                "How did a missing expando manage to shadow things?");
1497     auto expandoAndGeneration =
1498         static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
1499     MOZ_ASSERT(expandoAndGeneration);
1500     expandoObj = &expandoAndGeneration->expando.toObject();
1501   }
1502 
1503   // Try to do the lookup on the expando object.
1504   NativeObject* holder = nullptr;
1505   Maybe<PropertyInfo> prop;
1506   NativeGetPropCacheability canCache =
1507       CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &prop, pc_);
1508   if (canCache == CanAttachNone) {
1509     return AttachDecision::NoAction;
1510   }
1511   if (!holder) {
1512     return AttachDecision::NoAction;
1513   }
1514   auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
1515 
1516   MOZ_ASSERT(holder == nativeExpandoObj);
1517 
1518   maybeEmitIdGuard(id);
1519   ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
1520       obj, objId, expandoVal, nativeExpandoObj);
1521 
1522   if (canCache == CanAttachReadSlot) {
1523     // Load from the expando's slots.
1524     EmitLoadSlotResult(writer, expandoObjId, nativeExpandoObj, *prop);
1525     writer.returnFromIC();
1526   } else {
1527     // Call the getter. Note that we pass objId, the DOM proxy, as |this|
1528     // and not the expando object.
1529     MOZ_ASSERT(canCache == CanAttachNativeGetter ||
1530                canCache == CanAttachScriptedGetter);
1531     EmitGuardGetterSetterSlot(writer, nativeExpandoObj, *prop, expandoObjId);
1532     EmitCallGetterResultNoGuards(cx_, writer, nativeExpandoObj,
1533                                  nativeExpandoObj, *prop, receiverId);
1534   }
1535 
1536   trackAttached("DOMProxyExpando");
1537   return AttachDecision::Attach;
1538 }
1539 
tryAttachDOMProxyShadowed(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id)1540 AttachDecision GetPropIRGenerator::tryAttachDOMProxyShadowed(
1541     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id) {
1542   MOZ_ASSERT(!isSuper());
1543   MOZ_ASSERT(IsCacheableDOMProxy(obj));
1544 
1545   maybeEmitIdGuard(id);
1546   TestMatchingProxyReceiver(writer, obj, objId);
1547   writer.proxyGetResult(objId, id);
1548   writer.returnFromIC();
1549 
1550   trackAttached("DOMProxyShadowed");
1551   return AttachDecision::Attach;
1552 }
1553 
1554 // Callers are expected to have already guarded on the shape of the
1555 // object, which guarantees the object is a DOM proxy.
CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter & writer,ProxyObject * obj,jsid id,ObjOperandId objId)1556 static void CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer,
1557                                               ProxyObject* obj, jsid id,
1558                                               ObjOperandId objId) {
1559   MOZ_ASSERT(IsCacheableDOMProxy(obj));
1560 
1561   Value expandoVal = GetProxyPrivate(obj);
1562 
1563   ValOperandId expandoId;
1564   if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
1565     auto expandoAndGeneration =
1566         static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
1567     uint64_t generation = expandoAndGeneration->generation;
1568     expandoId = writer.loadDOMExpandoValueGuardGeneration(
1569         objId, expandoAndGeneration, generation);
1570     expandoVal = expandoAndGeneration->expando;
1571   } else {
1572     expandoId = writer.loadDOMExpandoValue(objId);
1573   }
1574 
1575   if (expandoVal.isUndefined()) {
1576     // Guard there's no expando object.
1577     writer.guardNonDoubleType(expandoId, ValueType::Undefined);
1578   } else if (expandoVal.isObject()) {
1579     // Guard the proxy either has no expando object or, if it has one, that
1580     // the shape matches the current expando object.
1581     NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
1582     MOZ_ASSERT(!expandoObj.containsPure(id));
1583     writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.shape());
1584   } else {
1585     MOZ_CRASH("Invalid expando value");
1586   }
1587 }
1588 
tryAttachDOMProxyUnshadowed(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId receiverId)1589 AttachDecision GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
1590     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
1591     ValOperandId receiverId) {
1592   MOZ_ASSERT(IsCacheableDOMProxy(obj));
1593 
1594   JSObject* checkObj = obj->staticPrototype();
1595   if (!checkObj) {
1596     return AttachDecision::NoAction;
1597   }
1598 
1599   NativeObject* holder = nullptr;
1600   Maybe<PropertyInfo> prop;
1601   NativeGetPropCacheability canCache =
1602       CanAttachNativeGetProp(cx_, checkObj, id, &holder, &prop, pc_);
1603   if (canCache == CanAttachNone) {
1604     return AttachDecision::NoAction;
1605   }
1606   auto* nativeCheckObj = &checkObj->as<NativeObject>();
1607 
1608   maybeEmitIdGuard(id);
1609 
1610   // Guard that our expando object hasn't started shadowing this property.
1611   TestMatchingProxyReceiver(writer, obj, objId);
1612   CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
1613 
1614   if (holder) {
1615     // Found the property on the prototype chain. Treat it like a native
1616     // getprop.
1617     GeneratePrototypeGuards(writer, obj, holder, objId);
1618 
1619     // Guard on the holder of the property.
1620     ObjOperandId holderId = writer.loadObject(holder);
1621     TestMatchingHolder(writer, holder, holderId);
1622 
1623     if (canCache == CanAttachReadSlot) {
1624       EmitLoadSlotResult(writer, holderId, holder, *prop);
1625       writer.returnFromIC();
1626     } else {
1627       // EmitCallGetterResultNoGuards expects |obj| to be the object the
1628       // property is on to do some checks. Since we actually looked at
1629       // checkObj, and no extra guards will be generated, we can just
1630       // pass that instead.
1631       MOZ_ASSERT(canCache == CanAttachNativeGetter ||
1632                  canCache == CanAttachScriptedGetter);
1633       MOZ_ASSERT(!isSuper());
1634       EmitGuardGetterSetterSlot(writer, holder, *prop, holderId,
1635                                 /* holderIsConstant = */ true);
1636       EmitCallGetterResultNoGuards(cx_, writer, nativeCheckObj, holder, *prop,
1637                                    receiverId);
1638     }
1639   } else {
1640     // Property was not found on the prototype chain. Deoptimize down to
1641     // proxy get call.
1642     MOZ_ASSERT(!isSuper());
1643     writer.proxyGetResult(objId, id);
1644     writer.returnFromIC();
1645   }
1646 
1647   trackAttached("DOMProxyUnshadowed");
1648   return AttachDecision::Attach;
1649 }
1650 
tryAttachProxy(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId receiverId)1651 AttachDecision GetPropIRGenerator::tryAttachProxy(HandleObject obj,
1652                                                   ObjOperandId objId,
1653                                                   HandleId id,
1654                                                   ValOperandId receiverId) {
1655   ProxyStubType type = GetProxyStubType(cx_, obj, id);
1656   if (type == ProxyStubType::None) {
1657     return AttachDecision::NoAction;
1658   }
1659   auto proxy = obj.as<ProxyObject>();
1660 
1661   // The proxy stubs don't currently support |super| access.
1662   if (isSuper()) {
1663     return AttachDecision::NoAction;
1664   }
1665 
1666   if (mode_ == ICState::Mode::Megamorphic) {
1667     return tryAttachGenericProxy(proxy, objId, id,
1668                                  /* handleDOMProxies = */ true);
1669   }
1670 
1671   switch (type) {
1672     case ProxyStubType::None:
1673       break;
1674     case ProxyStubType::DOMExpando:
1675       TRY_ATTACH(tryAttachDOMProxyExpando(proxy, objId, id, receiverId));
1676       [[fallthrough]];  // Fall through to the generic shadowed case.
1677     case ProxyStubType::DOMShadowed:
1678       return tryAttachDOMProxyShadowed(proxy, objId, id);
1679     case ProxyStubType::DOMUnshadowed:
1680       TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy, objId, id, receiverId));
1681       return tryAttachGenericProxy(proxy, objId, id,
1682                                    /* handleDOMProxies = */ true);
1683     case ProxyStubType::Generic:
1684       return tryAttachGenericProxy(proxy, objId, id,
1685                                    /* handleDOMProxies = */ false);
1686   }
1687 
1688   MOZ_CRASH("Unexpected ProxyStubType");
1689 }
1690 
tryAttachObjectLength(HandleObject obj,ObjOperandId objId,HandleId id)1691 AttachDecision GetPropIRGenerator::tryAttachObjectLength(HandleObject obj,
1692                                                          ObjOperandId objId,
1693                                                          HandleId id) {
1694   if (!id.isAtom(cx_->names().length)) {
1695     return AttachDecision::NoAction;
1696   }
1697 
1698   if (obj->is<ArrayObject>()) {
1699     if (obj->as<ArrayObject>().length() > INT32_MAX) {
1700       return AttachDecision::NoAction;
1701     }
1702 
1703     maybeEmitIdGuard(id);
1704     writer.guardClass(objId, GuardClassKind::Array);
1705     writer.loadInt32ArrayLengthResult(objId);
1706     writer.returnFromIC();
1707 
1708     trackAttached("ArrayLength");
1709     return AttachDecision::Attach;
1710   }
1711 
1712   if (obj->is<ArgumentsObject>() &&
1713       !obj->as<ArgumentsObject>().hasOverriddenLength()) {
1714     maybeEmitIdGuard(id);
1715     if (obj->is<MappedArgumentsObject>()) {
1716       writer.guardClass(objId, GuardClassKind::MappedArguments);
1717     } else {
1718       MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
1719       writer.guardClass(objId, GuardClassKind::UnmappedArguments);
1720     }
1721     writer.loadArgumentsObjectLengthResult(objId);
1722     writer.returnFromIC();
1723 
1724     trackAttached("ArgumentsObjectLength");
1725     return AttachDecision::Attach;
1726   }
1727 
1728   return AttachDecision::NoAction;
1729 }
1730 
tryAttachTypedArray(HandleObject obj,ObjOperandId objId,HandleId id)1731 AttachDecision GetPropIRGenerator::tryAttachTypedArray(HandleObject obj,
1732                                                        ObjOperandId objId,
1733                                                        HandleId id) {
1734   if (!obj->is<TypedArrayObject>()) {
1735     return AttachDecision::NoAction;
1736   }
1737 
1738   if (mode_ != ICState::Mode::Specialized) {
1739     return AttachDecision::NoAction;
1740   }
1741 
1742   // Receiver should be the object.
1743   if (isSuper()) {
1744     return AttachDecision::NoAction;
1745   }
1746 
1747   bool isLength = id.isAtom(cx_->names().length);
1748   bool isByteOffset = id.isAtom(cx_->names().byteOffset);
1749   if (!isLength && !isByteOffset && !id.isAtom(cx_->names().byteLength)) {
1750     return AttachDecision::NoAction;
1751   }
1752 
1753   NativeObject* holder = nullptr;
1754   Maybe<PropertyInfo> prop;
1755   NativeGetPropCacheability type =
1756       CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
1757   if (type != CanAttachNativeGetter) {
1758     return AttachDecision::NoAction;
1759   }
1760 
1761   JSFunction& fun = holder->getGetter(*prop)->as<JSFunction>();
1762   if (isLength) {
1763     if (!TypedArrayObject::isOriginalLengthGetter(fun.native())) {
1764       return AttachDecision::NoAction;
1765     }
1766   } else if (isByteOffset) {
1767     if (!TypedArrayObject::isOriginalByteOffsetGetter(fun.native())) {
1768       return AttachDecision::NoAction;
1769     }
1770   } else {
1771     if (!TypedArrayObject::isOriginalByteLengthGetter(fun.native())) {
1772       return AttachDecision::NoAction;
1773     }
1774   }
1775 
1776   auto* tarr = &obj->as<TypedArrayObject>();
1777 
1778   maybeEmitIdGuard(id);
1779   // Emit all the normal guards for calling this native, but specialize
1780   // callNativeGetterResult.
1781   EmitCallGetterResultGuards(writer, tarr, holder, id, *prop, objId, mode_);
1782   if (isLength) {
1783     if (tarr->length() <= INT32_MAX) {
1784       writer.loadArrayBufferViewLengthInt32Result(objId);
1785     } else {
1786       writer.loadArrayBufferViewLengthDoubleResult(objId);
1787     }
1788     trackAttached("TypedArrayLength");
1789   } else if (isByteOffset) {
1790     if (tarr->byteOffset() <= INT32_MAX) {
1791       writer.arrayBufferViewByteOffsetInt32Result(objId);
1792     } else {
1793       writer.arrayBufferViewByteOffsetDoubleResult(objId);
1794     }
1795     trackAttached("TypedArrayByteOffset");
1796   } else {
1797     if (tarr->byteLength() <= INT32_MAX) {
1798       writer.typedArrayByteLengthInt32Result(objId);
1799     } else {
1800       writer.typedArrayByteLengthDoubleResult(objId);
1801     }
1802     trackAttached("TypedArrayByteLength");
1803   }
1804   writer.returnFromIC();
1805 
1806   return AttachDecision::Attach;
1807 }
1808 
tryAttachDataView(HandleObject obj,ObjOperandId objId,HandleId id)1809 AttachDecision GetPropIRGenerator::tryAttachDataView(HandleObject obj,
1810                                                      ObjOperandId objId,
1811                                                      HandleId id) {
1812   if (!obj->is<DataViewObject>()) {
1813     return AttachDecision::NoAction;
1814   }
1815   auto* dv = &obj->as<DataViewObject>();
1816 
1817   if (mode_ != ICState::Mode::Specialized) {
1818     return AttachDecision::NoAction;
1819   }
1820 
1821   // Receiver should be the object.
1822   if (isSuper()) {
1823     return AttachDecision::NoAction;
1824   }
1825 
1826   bool isByteOffset = id.isAtom(cx_->names().byteOffset);
1827   if (!isByteOffset && !id.isAtom(cx_->names().byteLength)) {
1828     return AttachDecision::NoAction;
1829   }
1830 
1831   // byteOffset and byteLength both throw when the ArrayBuffer is detached.
1832   if (dv->hasDetachedBuffer()) {
1833     return AttachDecision::NoAction;
1834   }
1835 
1836   NativeObject* holder = nullptr;
1837   Maybe<PropertyInfo> prop;
1838   NativeGetPropCacheability type =
1839       CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
1840   if (type != CanAttachNativeGetter) {
1841     return AttachDecision::NoAction;
1842   }
1843 
1844   auto& fun = holder->getGetter(*prop)->as<JSFunction>();
1845   if (isByteOffset) {
1846     if (!DataViewObject::isOriginalByteOffsetGetter(fun.native())) {
1847       return AttachDecision::NoAction;
1848     }
1849   } else {
1850     if (!DataViewObject::isOriginalByteLengthGetter(fun.native())) {
1851       return AttachDecision::NoAction;
1852     }
1853   }
1854 
1855   maybeEmitIdGuard(id);
1856   // Emit all the normal guards for calling this native, but specialize
1857   // callNativeGetterResult.
1858   EmitCallGetterResultGuards(writer, dv, holder, id, *prop, objId, mode_);
1859   writer.guardHasAttachedArrayBuffer(objId);
1860   if (isByteOffset) {
1861     if (dv->byteOffset() <= INT32_MAX) {
1862       writer.arrayBufferViewByteOffsetInt32Result(objId);
1863     } else {
1864       writer.arrayBufferViewByteOffsetDoubleResult(objId);
1865     }
1866     trackAttached("DataViewByteOffset");
1867   } else {
1868     if (dv->byteLength() <= INT32_MAX) {
1869       writer.loadArrayBufferViewLengthInt32Result(objId);
1870     } else {
1871       writer.loadArrayBufferViewLengthDoubleResult(objId);
1872     }
1873     trackAttached("DataViewByteLength");
1874   }
1875   writer.returnFromIC();
1876 
1877   return AttachDecision::Attach;
1878 }
1879 
tryAttachArrayBufferMaybeShared(HandleObject obj,ObjOperandId objId,HandleId id)1880 AttachDecision GetPropIRGenerator::tryAttachArrayBufferMaybeShared(
1881     HandleObject obj, ObjOperandId objId, HandleId id) {
1882   if (!obj->is<ArrayBufferObjectMaybeShared>()) {
1883     return AttachDecision::NoAction;
1884   }
1885   auto* buf = &obj->as<ArrayBufferObjectMaybeShared>();
1886 
1887   if (mode_ != ICState::Mode::Specialized) {
1888     return AttachDecision::NoAction;
1889   }
1890 
1891   // Receiver should be the object.
1892   if (isSuper()) {
1893     return AttachDecision::NoAction;
1894   }
1895 
1896   if (!id.isAtom(cx_->names().byteLength)) {
1897     return AttachDecision::NoAction;
1898   }
1899 
1900   NativeObject* holder = nullptr;
1901   Maybe<PropertyInfo> prop;
1902   NativeGetPropCacheability type =
1903       CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
1904   if (type != CanAttachNativeGetter) {
1905     return AttachDecision::NoAction;
1906   }
1907 
1908   auto& fun = holder->getGetter(*prop)->as<JSFunction>();
1909   if (buf->is<ArrayBufferObject>()) {
1910     if (!ArrayBufferObject::isOriginalByteLengthGetter(fun.native())) {
1911       return AttachDecision::NoAction;
1912     }
1913   } else {
1914     if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun.native())) {
1915       return AttachDecision::NoAction;
1916     }
1917   }
1918 
1919   maybeEmitIdGuard(id);
1920   // Emit all the normal guards for calling this native, but specialize
1921   // callNativeGetterResult.
1922   EmitCallGetterResultGuards(writer, buf, holder, id, *prop, objId, mode_);
1923   if (buf->byteLength() <= INT32_MAX) {
1924     writer.loadArrayBufferByteLengthInt32Result(objId);
1925   } else {
1926     writer.loadArrayBufferByteLengthDoubleResult(objId);
1927   }
1928   writer.returnFromIC();
1929 
1930   trackAttached("ArrayBufferMaybeSharedByteLength");
1931   return AttachDecision::Attach;
1932 }
1933 
tryAttachRegExp(HandleObject obj,ObjOperandId objId,HandleId id)1934 AttachDecision GetPropIRGenerator::tryAttachRegExp(HandleObject obj,
1935                                                    ObjOperandId objId,
1936                                                    HandleId id) {
1937   if (!obj->is<RegExpObject>()) {
1938     return AttachDecision::NoAction;
1939   }
1940   auto* regExp = &obj->as<RegExpObject>();
1941 
1942   if (mode_ != ICState::Mode::Specialized) {
1943     return AttachDecision::NoAction;
1944   }
1945 
1946   // Receiver should be the object.
1947   if (isSuper()) {
1948     return AttachDecision::NoAction;
1949   }
1950 
1951   NativeObject* holder = nullptr;
1952   Maybe<PropertyInfo> prop;
1953   NativeGetPropCacheability type =
1954       CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
1955   if (type != CanAttachNativeGetter) {
1956     return AttachDecision::NoAction;
1957   }
1958 
1959   auto& fun = holder->getGetter(*prop)->as<JSFunction>();
1960   JS::RegExpFlags flags = JS::RegExpFlag::NoFlags;
1961   if (!RegExpObject::isOriginalFlagGetter(fun.native(), &flags)) {
1962     return AttachDecision::NoAction;
1963   }
1964 
1965   maybeEmitIdGuard(id);
1966   // Emit all the normal guards for calling this native, but specialize
1967   // callNativeGetterResult.
1968   EmitCallGetterResultGuards(writer, regExp, holder, id, *prop, objId, mode_);
1969 
1970   writer.regExpFlagResult(objId, flags.value());
1971   writer.returnFromIC();
1972 
1973   trackAttached("RegExpFlag");
1974   return AttachDecision::Attach;
1975 }
1976 
tryAttachFunction(HandleObject obj,ObjOperandId objId,HandleId id)1977 AttachDecision GetPropIRGenerator::tryAttachFunction(HandleObject obj,
1978                                                      ObjOperandId objId,
1979                                                      HandleId id) {
1980   // Function properties are lazily resolved so they might not be defined yet.
1981   // And we might end up in a situation where we always have a fresh function
1982   // object during the IC generation.
1983   if (!obj->is<JSFunction>()) {
1984     return AttachDecision::NoAction;
1985   }
1986 
1987   bool isLength = id.isAtom(cx_->names().length);
1988   if (!isLength && !id.isAtom(cx_->names().name)) {
1989     return AttachDecision::NoAction;
1990   }
1991 
1992   NativeObject* holder = nullptr;
1993   PropertyResult prop;
1994   // If this property exists already, don't attach the stub.
1995   if (LookupPropertyPure(cx_, obj, id, &holder, &prop)) {
1996     return AttachDecision::NoAction;
1997   }
1998 
1999   JSFunction* fun = &obj->as<JSFunction>();
2000 
2001   if (isLength) {
2002     // length was probably deleted from the function.
2003     if (fun->hasResolvedLength()) {
2004       return AttachDecision::NoAction;
2005     }
2006 
2007     // Lazy functions don't store the length.
2008     if (!fun->hasBytecode()) {
2009       return AttachDecision::NoAction;
2010     }
2011 
2012     // Length can be non-int32 for bound functions.
2013     if (fun->isBoundFunction()) {
2014       constexpr auto lengthSlot = FunctionExtended::BOUND_FUNCTION_LENGTH_SLOT;
2015       if (!fun->getExtendedSlot(lengthSlot).isInt32()) {
2016         return AttachDecision::NoAction;
2017       }
2018     }
2019   } else {
2020     // name was probably deleted from the function.
2021     if (fun->hasResolvedName()) {
2022       return AttachDecision::NoAction;
2023     }
2024 
2025     // Unless the bound function name prefix is present, we need to call into
2026     // the VM to compute the full name.
2027     if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) {
2028       return AttachDecision::NoAction;
2029     }
2030   }
2031 
2032   maybeEmitIdGuard(id);
2033   writer.guardClass(objId, GuardClassKind::JSFunction);
2034   if (isLength) {
2035     writer.loadFunctionLengthResult(objId);
2036     writer.returnFromIC();
2037     trackAttached("FunctionLength");
2038   } else {
2039     writer.loadFunctionNameResult(objId);
2040     writer.returnFromIC();
2041     trackAttached("FunctionName");
2042   }
2043   return AttachDecision::Attach;
2044 }
2045 
tryAttachArgumentsObjectIterator(HandleObject obj,ObjOperandId objId,HandleId id)2046 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectIterator(
2047     HandleObject obj, ObjOperandId objId, HandleId id) {
2048   if (!obj->is<ArgumentsObject>()) {
2049     return AttachDecision::NoAction;
2050   }
2051 
2052   if (!id.isWellKnownSymbol(JS::SymbolCode::iterator)) {
2053     return AttachDecision::NoAction;
2054   }
2055 
2056   Handle<ArgumentsObject*> args = obj.as<ArgumentsObject>();
2057   if (args->hasOverriddenIterator()) {
2058     return AttachDecision::NoAction;
2059   }
2060 
2061   RootedValue iterator(cx_);
2062   if (!ArgumentsObject::getArgumentsIterator(cx_, &iterator)) {
2063     cx_->recoverFromOutOfMemory();
2064     return AttachDecision::NoAction;
2065   }
2066   MOZ_ASSERT(iterator.isObject());
2067 
2068   maybeEmitIdGuard(id);
2069   if (args->is<MappedArgumentsObject>()) {
2070     writer.guardClass(objId, GuardClassKind::MappedArguments);
2071   } else {
2072     MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
2073     writer.guardClass(objId, GuardClassKind::UnmappedArguments);
2074   }
2075   uint32_t flags = ArgumentsObject::ITERATOR_OVERRIDDEN_BIT;
2076   writer.guardArgumentsObjectFlags(objId, flags);
2077 
2078   ObjOperandId iterId = writer.loadObject(&iterator.toObject());
2079   writer.loadObjectResult(iterId);
2080   writer.returnFromIC();
2081 
2082   trackAttached("ArgumentsObjectIterator");
2083   return AttachDecision::Attach;
2084 }
2085 
tryAttachModuleNamespace(HandleObject obj,ObjOperandId objId,HandleId id)2086 AttachDecision GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj,
2087                                                             ObjOperandId objId,
2088                                                             HandleId id) {
2089   if (!obj->is<ModuleNamespaceObject>()) {
2090     return AttachDecision::NoAction;
2091   }
2092 
2093   auto* ns = &obj->as<ModuleNamespaceObject>();
2094   ModuleEnvironmentObject* env = nullptr;
2095   Maybe<PropertyInfo> prop;
2096   if (!ns->bindings().lookup(id, &env, &prop)) {
2097     return AttachDecision::NoAction;
2098   }
2099 
2100   // Don't emit a stub until the target binding has been initialized.
2101   if (env->getSlot(prop->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
2102     return AttachDecision::NoAction;
2103   }
2104 
2105   // Check for the specific namespace object.
2106   maybeEmitIdGuard(id);
2107   writer.guardSpecificObject(objId, ns);
2108 
2109   ObjOperandId envId = writer.loadObject(env);
2110   EmitLoadSlotResult(writer, envId, env, *prop);
2111   writer.returnFromIC();
2112 
2113   trackAttached("ModuleNamespace");
2114   return AttachDecision::Attach;
2115 }
2116 
tryAttachPrimitive(ValOperandId valId,HandleId id)2117 AttachDecision GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId,
2118                                                       HandleId id) {
2119   MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
2120 
2121   JSProtoKey protoKey;
2122   switch (val_.type()) {
2123     case ValueType::String:
2124       if (id.isAtom(cx_->names().length)) {
2125         // String length is special-cased, see js::GetProperty.
2126         return AttachDecision::NoAction;
2127       }
2128       protoKey = JSProto_String;
2129       break;
2130     case ValueType::Int32:
2131     case ValueType::Double:
2132       protoKey = JSProto_Number;
2133       break;
2134     case ValueType::Boolean:
2135       protoKey = JSProto_Boolean;
2136       break;
2137     case ValueType::Symbol:
2138       protoKey = JSProto_Symbol;
2139       break;
2140     case ValueType::BigInt:
2141       protoKey = JSProto_BigInt;
2142       break;
2143     case ValueType::Null:
2144     case ValueType::Undefined:
2145     case ValueType::Magic:
2146       return AttachDecision::NoAction;
2147 #ifdef ENABLE_RECORD_TUPLE
2148     case ValueType::ExtendedPrimitive:
2149 #endif
2150     case ValueType::Object:
2151     case ValueType::PrivateGCThing:
2152       MOZ_CRASH("unexpected type");
2153   }
2154 
2155   JSObject* proto = cx_->global()->maybeGetPrototype(protoKey);
2156   if (!proto) {
2157     return AttachDecision::NoAction;
2158   }
2159 
2160   NativeObject* holder = nullptr;
2161   Maybe<PropertyInfo> prop;
2162   NativeGetPropCacheability type =
2163       CanAttachNativeGetProp(cx_, proto, id, &holder, &prop, pc_);
2164   switch (type) {
2165     case CanAttachNone:
2166       return AttachDecision::NoAction;
2167     case CanAttachReadSlot: {
2168       auto* nproto = &proto->as<NativeObject>();
2169 
2170       if (val_.isNumber()) {
2171         writer.guardIsNumber(valId);
2172       } else {
2173         writer.guardNonDoubleType(valId, val_.type());
2174       }
2175       maybeEmitIdGuard(id);
2176 
2177       ObjOperandId protoId = writer.loadObject(nproto);
2178       EmitReadSlotResult(writer, nproto, holder, prop, protoId);
2179       writer.returnFromIC();
2180 
2181       trackAttached("PrimitiveSlot");
2182       return AttachDecision::Attach;
2183     }
2184     case CanAttachScriptedGetter:
2185     case CanAttachNativeGetter: {
2186       auto* nproto = &proto->as<NativeObject>();
2187 
2188       if (val_.isNumber()) {
2189         writer.guardIsNumber(valId);
2190       } else {
2191         writer.guardNonDoubleType(valId, val_.type());
2192       }
2193       maybeEmitIdGuard(id);
2194 
2195       ObjOperandId protoId = writer.loadObject(nproto);
2196       EmitCallGetterResult(cx_, writer, nproto, holder, id, *prop, protoId,
2197                            valId, mode_);
2198 
2199       trackAttached("PrimitiveGetter");
2200       return AttachDecision::Attach;
2201     }
2202   }
2203 
2204   MOZ_CRASH("Bad NativeGetPropCacheability");
2205 }
2206 
tryAttachStringLength(ValOperandId valId,HandleId id)2207 AttachDecision GetPropIRGenerator::tryAttachStringLength(ValOperandId valId,
2208                                                          HandleId id) {
2209   if (!val_.isString() || !id.isAtom(cx_->names().length)) {
2210     return AttachDecision::NoAction;
2211   }
2212 
2213   StringOperandId strId = writer.guardToString(valId);
2214   maybeEmitIdGuard(id);
2215   writer.loadStringLengthResult(strId);
2216   writer.returnFromIC();
2217 
2218   trackAttached("StringLength");
2219   return AttachDecision::Attach;
2220 }
2221 
CanAttachStringChar(const Value & val,const Value & idVal)2222 static bool CanAttachStringChar(const Value& val, const Value& idVal) {
2223   if (!val.isString() || !idVal.isInt32()) {
2224     return false;
2225   }
2226 
2227   int32_t index = idVal.toInt32();
2228   if (index < 0) {
2229     return false;
2230   }
2231 
2232   JSString* str = val.toString();
2233   if (size_t(index) >= str->length()) {
2234     return false;
2235   }
2236 
2237   // This follows JSString::getChar, otherwise we fail to attach getChar in a
2238   // lot of cases.
2239   if (str->isRope()) {
2240     JSRope* rope = &str->asRope();
2241 
2242     // Make sure the left side contains the index.
2243     if (size_t(index) >= rope->leftChild()->length()) {
2244       return false;
2245     }
2246 
2247     str = rope->leftChild();
2248   }
2249 
2250   if (!str->isLinear()) {
2251     return false;
2252   }
2253 
2254   return true;
2255 }
2256 
tryAttachStringChar(ValOperandId valId,ValOperandId indexId)2257 AttachDecision GetPropIRGenerator::tryAttachStringChar(ValOperandId valId,
2258                                                        ValOperandId indexId) {
2259   MOZ_ASSERT(idVal_.isInt32());
2260 
2261   if (!CanAttachStringChar(val_, idVal_)) {
2262     return AttachDecision::NoAction;
2263   }
2264 
2265   StringOperandId strId = writer.guardToString(valId);
2266   Int32OperandId int32IndexId = writer.guardToInt32Index(indexId);
2267   writer.loadStringCharResult(strId, int32IndexId);
2268   writer.returnFromIC();
2269 
2270   trackAttached("StringChar");
2271   return AttachDecision::Attach;
2272 }
2273 
ClassCanHaveExtraProperties(const JSClass * clasp)2274 static bool ClassCanHaveExtraProperties(const JSClass* clasp) {
2275   return clasp->getResolve() || clasp->getOpsLookupProperty() ||
2276          clasp->getOpsGetProperty() || IsTypedArrayClass(clasp);
2277 }
2278 
2279 enum class OwnProperty : bool { No, Yes };
2280 enum class AllowIndexedReceiver : bool { No, Yes };
2281 enum class AllowExtraReceiverProperties : bool { No, Yes };
2282 
CanAttachDenseElementHole(NativeObject * obj,OwnProperty ownProp,AllowIndexedReceiver allowIndexedReceiver=AllowIndexedReceiver::No,AllowExtraReceiverProperties allowExtraReceiverProperties=AllowExtraReceiverProperties::No)2283 static bool CanAttachDenseElementHole(
2284     NativeObject* obj, OwnProperty ownProp,
2285     AllowIndexedReceiver allowIndexedReceiver = AllowIndexedReceiver::No,
2286     AllowExtraReceiverProperties allowExtraReceiverProperties =
2287         AllowExtraReceiverProperties::No) {
2288   // Make sure the objects on the prototype don't have any indexed properties
2289   // or that such properties can't appear without a shape change.
2290   // Otherwise returning undefined for holes would obviously be incorrect,
2291   // because we would have to lookup a property on the prototype instead.
2292   do {
2293     // The first two checks are also relevant to the receiver object.
2294     if (allowIndexedReceiver == AllowIndexedReceiver::No && obj->isIndexed()) {
2295       return false;
2296     }
2297     allowIndexedReceiver = AllowIndexedReceiver::No;
2298 
2299     if (allowExtraReceiverProperties == AllowExtraReceiverProperties::No &&
2300         ClassCanHaveExtraProperties(obj->getClass())) {
2301       return false;
2302     }
2303     allowExtraReceiverProperties = AllowExtraReceiverProperties::No;
2304 
2305     // Don't need to check prototype for OwnProperty checks
2306     if (ownProp == OwnProperty::Yes) {
2307       return true;
2308     }
2309 
2310     JSObject* proto = obj->staticPrototype();
2311     if (!proto) {
2312       break;
2313     }
2314 
2315     if (!proto->is<NativeObject>()) {
2316       return false;
2317     }
2318 
2319     // Make sure objects on the prototype don't have dense elements.
2320     if (proto->as<NativeObject>().getDenseInitializedLength() != 0) {
2321       return false;
2322     }
2323 
2324     obj = &proto->as<NativeObject>();
2325   } while (true);
2326 
2327   return true;
2328 }
2329 
tryAttachArgumentsObjectArg(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2330 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectArg(
2331     HandleObject obj, ObjOperandId objId, uint32_t index,
2332     Int32OperandId indexId) {
2333   if (!obj->is<ArgumentsObject>()) {
2334     return AttachDecision::NoAction;
2335   }
2336   auto* args = &obj->as<ArgumentsObject>();
2337 
2338   // No elements must have been overridden or deleted.
2339   if (args->hasOverriddenElement()) {
2340     return AttachDecision::NoAction;
2341   }
2342 
2343   // Check bounds.
2344   if (index >= args->initialLength()) {
2345     return AttachDecision::NoAction;
2346   }
2347 
2348   // And finally also check that the argument isn't forwarded.
2349   if (args->argIsForwarded(index)) {
2350     return AttachDecision::NoAction;
2351   }
2352 
2353   if (args->is<MappedArgumentsObject>()) {
2354     writer.guardClass(objId, GuardClassKind::MappedArguments);
2355   } else {
2356     MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
2357     writer.guardClass(objId, GuardClassKind::UnmappedArguments);
2358   }
2359 
2360   writer.loadArgumentsObjectArgResult(objId, indexId);
2361   writer.returnFromIC();
2362 
2363   trackAttached("ArgumentsObjectArg");
2364   return AttachDecision::Attach;
2365 }
2366 
tryAttachArgumentsObjectArgHole(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2367 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
2368     HandleObject obj, ObjOperandId objId, uint32_t index,
2369     Int32OperandId indexId) {
2370   if (!obj->is<ArgumentsObject>()) {
2371     return AttachDecision::NoAction;
2372   }
2373   auto* args = &obj->as<ArgumentsObject>();
2374 
2375   // No elements must have been overridden or deleted.
2376   if (args->hasOverriddenElement()) {
2377     return AttachDecision::NoAction;
2378   }
2379 
2380   // And also check that the argument isn't forwarded.
2381   if (index < args->initialLength() && args->argIsForwarded(index)) {
2382     return AttachDecision::NoAction;
2383   }
2384 
2385   if (!CanAttachDenseElementHole(args, OwnProperty::No,
2386                                  AllowIndexedReceiver::Yes,
2387                                  AllowExtraReceiverProperties::Yes)) {
2388     return AttachDecision::NoAction;
2389   }
2390 
2391   // We don't need to guard on the shape, because we check if any element is
2392   // overridden. Elements are marked as overridden iff any element is defined,
2393   // irrespective of whether the element is in-bounds or out-of-bounds. So when
2394   // that flag isn't set, we can guarantee that the arguments object doesn't
2395   // have any additional own elements.
2396 
2397   if (args->is<MappedArgumentsObject>()) {
2398     writer.guardClass(objId, GuardClassKind::MappedArguments);
2399   } else {
2400     MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
2401     writer.guardClass(objId, GuardClassKind::UnmappedArguments);
2402   }
2403 
2404   GeneratePrototypeHoleGuards(writer, args, objId,
2405                               /* alwaysGuardFirstProto = */ true);
2406 
2407   writer.loadArgumentsObjectArgHoleResult(objId, indexId);
2408   writer.returnFromIC();
2409 
2410   trackAttached("ArgumentsObjectArgHole");
2411   return AttachDecision::Attach;
2412 }
2413 
tryAttachArgumentsObjectCallee(HandleObject obj,ObjOperandId objId,HandleId id)2414 AttachDecision GetPropIRGenerator::tryAttachArgumentsObjectCallee(
2415     HandleObject obj, ObjOperandId objId, HandleId id) {
2416   // Only mapped arguments objects have a `callee` property.
2417   if (!obj->is<MappedArgumentsObject>()) {
2418     return AttachDecision::NoAction;
2419   }
2420 
2421   if (!id.isAtom(cx_->names().callee)) {
2422     return AttachDecision::NoAction;
2423   }
2424 
2425   // The callee must not have been overridden or deleted.
2426   if (obj->as<MappedArgumentsObject>().hasOverriddenCallee()) {
2427     return AttachDecision::NoAction;
2428   }
2429 
2430   maybeEmitIdGuard(id);
2431   writer.guardClass(objId, GuardClassKind::MappedArguments);
2432 
2433   uint32_t flags = ArgumentsObject::CALLEE_OVERRIDDEN_BIT;
2434   writer.guardArgumentsObjectFlags(objId, flags);
2435 
2436   writer.loadFixedSlotResult(objId,
2437                              MappedArgumentsObject::getCalleeSlotOffset());
2438   writer.returnFromIC();
2439 
2440   trackAttached("ArgumentsObjectCallee");
2441   return AttachDecision::Attach;
2442 }
2443 
tryAttachDenseElement(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2444 AttachDecision GetPropIRGenerator::tryAttachDenseElement(
2445     HandleObject obj, ObjOperandId objId, uint32_t index,
2446     Int32OperandId indexId) {
2447   if (!obj->is<NativeObject>()) {
2448     return AttachDecision::NoAction;
2449   }
2450 
2451   NativeObject* nobj = &obj->as<NativeObject>();
2452   if (!nobj->containsDenseElement(index)) {
2453     return AttachDecision::NoAction;
2454   }
2455 
2456   TestMatchingNativeReceiver(writer, nobj, objId);
2457   writer.loadDenseElementResult(objId, indexId);
2458   writer.returnFromIC();
2459 
2460   trackAttached("DenseElement");
2461   return AttachDecision::Attach;
2462 }
2463 
tryAttachDenseElementHole(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2464 AttachDecision GetPropIRGenerator::tryAttachDenseElementHole(
2465     HandleObject obj, ObjOperandId objId, uint32_t index,
2466     Int32OperandId indexId) {
2467   if (!obj->is<NativeObject>()) {
2468     return AttachDecision::NoAction;
2469   }
2470 
2471   NativeObject* nobj = &obj->as<NativeObject>();
2472   if (nobj->containsDenseElement(index)) {
2473     return AttachDecision::NoAction;
2474   }
2475   if (!CanAttachDenseElementHole(nobj, OwnProperty::No)) {
2476     return AttachDecision::NoAction;
2477   }
2478 
2479   // Guard on the shape, to prevent non-dense elements from appearing.
2480   TestMatchingNativeReceiver(writer, nobj, objId);
2481   GeneratePrototypeHoleGuards(writer, nobj, objId,
2482                               /* alwaysGuardFirstProto = */ false);
2483   writer.loadDenseElementHoleResult(objId, indexId);
2484   writer.returnFromIC();
2485 
2486   trackAttached("DenseElementHole");
2487   return AttachDecision::Attach;
2488 }
2489 
tryAttachSparseElement(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2490 AttachDecision GetPropIRGenerator::tryAttachSparseElement(
2491     HandleObject obj, ObjOperandId objId, uint32_t index,
2492     Int32OperandId indexId) {
2493   if (!obj->is<NativeObject>()) {
2494     return AttachDecision::NoAction;
2495   }
2496   NativeObject* nobj = &obj->as<NativeObject>();
2497 
2498   // Stub doesn't handle negative indices.
2499   if (index > INT_MAX) {
2500     return AttachDecision::NoAction;
2501   }
2502 
2503   // We also need to be past the end of the dense capacity, to ensure sparse.
2504   if (index < nobj->getDenseInitializedLength()) {
2505     return AttachDecision::NoAction;
2506   }
2507 
2508   // Only handle Array objects in this stub.
2509   if (!nobj->is<ArrayObject>()) {
2510     return AttachDecision::NoAction;
2511   }
2512 
2513   // Here, we ensure that the prototype chain does not define any sparse
2514   // indexed properties on the shape lineage. This allows us to guard on
2515   // the shapes up the prototype chain to ensure that no indexed properties
2516   // exist outside of the dense elements.
2517   //
2518   // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
2519   // as well as ensure that no prototypes contain dense elements, allowing
2520   // us to perform a pure shape-search for out-of-bounds integer-indexed
2521   // properties on the recevier object.
2522   if ((nobj->staticPrototype() != nullptr) &&
2523       ObjectMayHaveExtraIndexedProperties(nobj->staticPrototype())) {
2524     return AttachDecision::NoAction;
2525   }
2526 
2527   // Ensure that obj is an Array.
2528   writer.guardClass(objId, GuardClassKind::Array);
2529 
2530   // The helper we are going to call only applies to non-dense elements.
2531   writer.guardIndexGreaterThanDenseInitLength(objId, indexId);
2532 
2533   // Ensures we are able to efficiently able to map to an integral jsid.
2534   writer.guardInt32IsNonNegative(indexId);
2535 
2536   // Shape guard the prototype chain to avoid shadowing indexes from appearing.
2537   // The helper function also ensures that the index does not appear within the
2538   // dense element set of the prototypes.
2539   GeneratePrototypeHoleGuards(writer, nobj, objId,
2540                               /* alwaysGuardFirstProto = */ true);
2541 
2542   // At this point, we are guaranteed that the indexed property will not
2543   // be found on one of the prototypes. We are assured that we only have
2544   // to check that the receiving object has the property.
2545 
2546   writer.callGetSparseElementResult(objId, indexId);
2547   writer.returnFromIC();
2548 
2549   trackAttached("GetSparseElement");
2550   return AttachDecision::Attach;
2551 }
2552 
2553 // For Uint32Array we let the stub return an Int32 if we have not seen a
2554 // double, to allow better codegen in Warp while avoiding bailout loops.
ForceDoubleForUint32Array(TypedArrayObject * tarr,uint64_t index)2555 static bool ForceDoubleForUint32Array(TypedArrayObject* tarr, uint64_t index) {
2556   MOZ_ASSERT(index < tarr->length());
2557 
2558   if (tarr->type() != Scalar::Type::Uint32) {
2559     // Return value is only relevant for Uint32Array.
2560     return false;
2561   }
2562 
2563   Value res;
2564   MOZ_ALWAYS_TRUE(tarr->getElementPure(index, &res));
2565   MOZ_ASSERT(res.isNumber());
2566   return res.isDouble();
2567 }
2568 
tryAttachTypedArrayElement(HandleObject obj,ObjOperandId objId)2569 AttachDecision GetPropIRGenerator::tryAttachTypedArrayElement(
2570     HandleObject obj, ObjOperandId objId) {
2571   if (!obj->is<TypedArrayObject>()) {
2572     return AttachDecision::NoAction;
2573   }
2574 
2575   if (!idVal_.isNumber()) {
2576     return AttachDecision::NoAction;
2577   }
2578 
2579   TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2580 
2581   bool handleOOB = false;
2582   int64_t indexInt64;
2583   if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 ||
2584       uint64_t(indexInt64) >= tarr->length()) {
2585     handleOOB = true;
2586   }
2587 
2588   // If the number is not representable as an integer the result will be
2589   // |undefined| so we leave |forceDoubleForUint32| as false.
2590   bool forceDoubleForUint32 = false;
2591   if (!handleOOB) {
2592     uint64_t index = uint64_t(indexInt64);
2593     forceDoubleForUint32 = ForceDoubleForUint32Array(tarr, index);
2594   }
2595 
2596   writer.guardShapeForClass(objId, tarr->shape());
2597 
2598   ValOperandId keyId = getElemKeyValueId();
2599   IntPtrOperandId intPtrIndexId = guardToIntPtrIndex(idVal_, keyId, handleOOB);
2600 
2601   writer.loadTypedArrayElementResult(objId, intPtrIndexId, tarr->type(),
2602                                      handleOOB, forceDoubleForUint32);
2603   writer.returnFromIC();
2604 
2605   trackAttached("TypedElement");
2606   return AttachDecision::Attach;
2607 }
2608 
tryAttachGenericElement(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)2609 AttachDecision GetPropIRGenerator::tryAttachGenericElement(
2610     HandleObject obj, ObjOperandId objId, uint32_t index,
2611     Int32OperandId indexId) {
2612   if (!obj->is<NativeObject>()) {
2613     return AttachDecision::NoAction;
2614   }
2615 
2616   // To allow other types to attach in the non-megamorphic case we test the
2617   // specific matching native receiver; however, once megamorphic we can attach
2618   // for any native
2619   if (mode_ == ICState::Mode::Megamorphic) {
2620     writer.guardIsNativeObject(objId);
2621   } else {
2622     NativeObject* nobj = &obj->as<NativeObject>();
2623     TestMatchingNativeReceiver(writer, nobj, objId);
2624   }
2625   writer.guardIndexGreaterThanDenseInitLength(objId, indexId);
2626   writer.callNativeGetElementResult(objId, indexId);
2627   writer.returnFromIC();
2628 
2629   trackAttached(mode_ == ICState::Mode::Megamorphic
2630                     ? "GenericElementMegamorphic"
2631                     : "GenericElement");
2632   return AttachDecision::Attach;
2633 }
2634 
tryAttachProxyElement(HandleObject obj,ObjOperandId objId)2635 AttachDecision GetPropIRGenerator::tryAttachProxyElement(HandleObject obj,
2636                                                          ObjOperandId objId) {
2637   if (!obj->is<ProxyObject>()) {
2638     return AttachDecision::NoAction;
2639   }
2640 
2641   // The proxy stubs don't currently support |super| access.
2642   if (isSuper()) {
2643     return AttachDecision::NoAction;
2644   }
2645 
2646   writer.guardIsProxy(objId);
2647 
2648   // We are not guarding against DOM proxies here, because there is no other
2649   // specialized DOM IC we could attach.
2650   // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
2651   // but for GetElem we prefer to attach a stub that can handle any Value
2652   // so we don't attach a new stub for every id.
2653   MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
2654   MOZ_ASSERT(!isSuper());
2655   writer.proxyGetByValueResult(objId, getElemKeyValueId());
2656   writer.returnFromIC();
2657 
2658   trackAttached("ProxyElement");
2659   return AttachDecision::Attach;
2660 }
2661 
trackAttached(const char * name)2662 void GetPropIRGenerator::trackAttached(const char* name) {
2663 #ifdef JS_CACHEIR_SPEW
2664   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2665     sp.valueProperty("base", val_);
2666     sp.valueProperty("property", idVal_);
2667   }
2668 #endif
2669 }
2670 
emitIdGuard(ValOperandId valId,const Value & idVal,jsid id)2671 void IRGenerator::emitIdGuard(ValOperandId valId, const Value& idVal, jsid id) {
2672   if (id.isSymbol()) {
2673     MOZ_ASSERT(idVal.toSymbol() == id.toSymbol());
2674     SymbolOperandId symId = writer.guardToSymbol(valId);
2675     writer.guardSpecificSymbol(symId, id.toSymbol());
2676   } else {
2677     MOZ_ASSERT(id.isAtom());
2678     if (idVal.isUndefined()) {
2679       MOZ_ASSERT(id.isAtom(cx_->names().undefined));
2680       writer.guardIsUndefined(valId);
2681     } else if (idVal.isNull()) {
2682       MOZ_ASSERT(id.isAtom(cx_->names().null));
2683       writer.guardIsNull(valId);
2684     } else {
2685       MOZ_ASSERT(idVal.isString());
2686       StringOperandId strId = writer.guardToString(valId);
2687       writer.guardSpecificAtom(strId, id.toAtom());
2688     }
2689   }
2690 }
2691 
maybeEmitIdGuard(jsid id)2692 void GetPropIRGenerator::maybeEmitIdGuard(jsid id) {
2693   if (cacheKind_ == CacheKind::GetProp ||
2694       cacheKind_ == CacheKind::GetPropSuper) {
2695     // Constant PropertyName, no guards necessary.
2696     MOZ_ASSERT(&idVal_.toString()->asAtom() == id.toAtom());
2697     return;
2698   }
2699 
2700   MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
2701              cacheKind_ == CacheKind::GetElemSuper);
2702   emitIdGuard(getElemKeyValueId(), idVal_, id);
2703 }
2704 
maybeEmitIdGuard(jsid id)2705 void SetPropIRGenerator::maybeEmitIdGuard(jsid id) {
2706   if (cacheKind_ == CacheKind::SetProp) {
2707     // Constant PropertyName, no guards necessary.
2708     MOZ_ASSERT(&idVal_.toString()->asAtom() == id.toAtom());
2709     return;
2710   }
2711 
2712   MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
2713   emitIdGuard(setElemKeyValueId(), idVal_, id);
2714 }
2715 
GetNameIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleObject env,HandlePropertyName name)2716 GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script,
2717                                        jsbytecode* pc, ICState state,
2718                                        HandleObject env,
2719                                        HandlePropertyName name)
2720     : IRGenerator(cx, script, pc, CacheKind::GetName, state),
2721       env_(env),
2722       name_(name) {}
2723 
tryAttachStub()2724 AttachDecision GetNameIRGenerator::tryAttachStub() {
2725   MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
2726 
2727   AutoAssertNoPendingException aanpe(cx_);
2728 
2729   ObjOperandId envId(writer.setInputOperandId(0));
2730   RootedId id(cx_, NameToId(name_));
2731 
2732   TRY_ATTACH(tryAttachGlobalNameValue(envId, id));
2733   TRY_ATTACH(tryAttachGlobalNameGetter(envId, id));
2734   TRY_ATTACH(tryAttachEnvironmentName(envId, id));
2735 
2736   trackAttached(IRGenerator::NotAttached);
2737   return AttachDecision::NoAction;
2738 }
2739 
CanAttachGlobalName(JSContext * cx,GlobalLexicalEnvironmentObject * globalLexical,PropertyKey id,NativeObject ** holder,Maybe<PropertyInfo> * prop)2740 static bool CanAttachGlobalName(JSContext* cx,
2741                                 GlobalLexicalEnvironmentObject* globalLexical,
2742                                 PropertyKey id, NativeObject** holder,
2743                                 Maybe<PropertyInfo>* prop) {
2744   // The property must be found, and it must be found as a normal data property.
2745   NativeObject* current = globalLexical;
2746   while (true) {
2747     *prop = current->lookup(cx, id);
2748     if (prop->isSome()) {
2749       break;
2750     }
2751 
2752     if (current == globalLexical) {
2753       current = &globalLexical->global();
2754     } else {
2755       // In the browser the global prototype chain should be immutable.
2756       if (!current->staticPrototypeIsImmutable()) {
2757         return false;
2758       }
2759 
2760       JSObject* proto = current->staticPrototype();
2761       if (!proto || !proto->is<NativeObject>()) {
2762         return false;
2763       }
2764 
2765       current = &proto->as<NativeObject>();
2766     }
2767   }
2768 
2769   *holder = current;
2770   return true;
2771 }
2772 
tryAttachGlobalNameValue(ObjOperandId objId,HandleId id)2773 AttachDecision GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId,
2774                                                             HandleId id) {
2775   if (!IsGlobalOp(JSOp(*pc_))) {
2776     return AttachDecision::NoAction;
2777   }
2778   MOZ_ASSERT(!script_->hasNonSyntacticScope());
2779 
2780   auto* globalLexical = &env_->as<GlobalLexicalEnvironmentObject>();
2781 
2782   NativeObject* holder = nullptr;
2783   Maybe<PropertyInfo> prop;
2784   if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) {
2785     return AttachDecision::NoAction;
2786   }
2787 
2788   // The property must be found, and it must be found as a normal data property.
2789   if (!prop->isDataProperty()) {
2790     return AttachDecision::NoAction;
2791   }
2792 
2793   // This might still be an uninitialized lexical.
2794   if (holder->getSlot(prop->slot()).isMagic()) {
2795     return AttachDecision::NoAction;
2796   }
2797 
2798   if (holder == globalLexical) {
2799     // There is no need to guard on the shape. Lexical bindings are
2800     // non-configurable, and this stub cannot be shared across globals.
2801     size_t dynamicSlotOffset =
2802         holder->dynamicSlotIndex(prop->slot()) * sizeof(Value);
2803     writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
2804   } else {
2805     // Check the prototype chain from the global to the holder
2806     // prototype. Ignore the global lexical scope as it doesn't figure
2807     // into the prototype chain. We guard on the global lexical
2808     // scope's shape independently.
2809     if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, *prop)) {
2810       return AttachDecision::NoAction;
2811     }
2812 
2813     // Shape guard for global lexical.
2814     writer.guardShape(objId, globalLexical->shape());
2815 
2816     // Guard on the shape of the GlobalObject.
2817     ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2818     writer.guardShape(globalId, globalLexical->global().shape());
2819 
2820     ObjOperandId holderId = globalId;
2821     if (holder != &globalLexical->global()) {
2822       // Shape guard holder.
2823       holderId = writer.loadObject(holder);
2824       writer.guardShape(holderId, holder->shape());
2825     }
2826 
2827     EmitLoadSlotResult(writer, holderId, holder, *prop);
2828   }
2829 
2830   writer.returnFromIC();
2831 
2832   trackAttached("GlobalNameValue");
2833   return AttachDecision::Attach;
2834 }
2835 
tryAttachGlobalNameGetter(ObjOperandId objId,HandleId id)2836 AttachDecision GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId,
2837                                                              HandleId id) {
2838   if (!IsGlobalOp(JSOp(*pc_))) {
2839     return AttachDecision::NoAction;
2840   }
2841   MOZ_ASSERT(!script_->hasNonSyntacticScope());
2842 
2843   Handle<GlobalLexicalEnvironmentObject*> globalLexical =
2844       env_.as<GlobalLexicalEnvironmentObject>();
2845   MOZ_ASSERT(globalLexical->isGlobal());
2846 
2847   NativeObject* holder = nullptr;
2848   Maybe<PropertyInfo> prop;
2849   if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) {
2850     return AttachDecision::NoAction;
2851   }
2852 
2853   if (holder == globalLexical) {
2854     return AttachDecision::NoAction;
2855   }
2856 
2857   GlobalObject* global = &globalLexical->global();
2858 
2859   if (IsCacheableGetPropCall(global, holder, *prop) != CanAttachNativeGetter) {
2860     return AttachDecision::NoAction;
2861   }
2862 
2863   // Shape guard for global lexical.
2864   writer.guardShape(objId, globalLexical->shape());
2865 
2866   // Guard on the shape of the GlobalObject.
2867   ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2868   writer.guardShape(globalId, global->shape());
2869 
2870   if (holder != global) {
2871     // Shape guard holder.
2872     ObjOperandId holderId = writer.loadObject(holder);
2873     writer.guardShape(holderId, holder->shape());
2874     EmitGuardGetterSetterSlot(writer, holder, *prop, holderId,
2875                               /* holderIsConstant = */ true);
2876   } else {
2877     // Note: pass true for |holderIsConstant| because the holder must be the
2878     // current global object.
2879     EmitGuardGetterSetterSlot(writer, holder, *prop, globalId,
2880                               /* holderIsConstant = */ true);
2881   }
2882 
2883   if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, global, holder, *prop,
2884                                mode_)) {
2885     // The global shape guard above ensures the instance JSClass is correct.
2886     EmitCallDOMGetterResultNoGuards(writer, holder, *prop, globalId);
2887     trackAttached("GlobalNameDOMGetter");
2888   } else {
2889     ValOperandId receiverId = writer.boxObject(globalId);
2890     EmitCallGetterResultNoGuards(cx_, writer, global, holder, *prop,
2891                                  receiverId);
2892     trackAttached("GlobalNameGetter");
2893   }
2894 
2895   return AttachDecision::Attach;
2896 }
2897 
NeedEnvironmentShapeGuard(JSObject * envObj)2898 static bool NeedEnvironmentShapeGuard(JSObject* envObj) {
2899   if (!envObj->is<CallObject>()) {
2900     return true;
2901   }
2902 
2903   // We can skip a guard on the call object if the script's bindings are
2904   // guaranteed to be immutable (and thus cannot introduce shadowing variables).
2905   // If the function is a relazified self-hosted function it has no BaseScript
2906   // and we pessimistically create the guard.
2907   CallObject* callObj = &envObj->as<CallObject>();
2908   JSFunction* fun = &callObj->callee();
2909   if (!fun->hasBaseScript() || fun->baseScript()->funHasExtensibleScope()) {
2910     return true;
2911   }
2912 
2913   return false;
2914 }
2915 
tryAttachEnvironmentName(ObjOperandId objId,HandleId id)2916 AttachDecision GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId,
2917                                                             HandleId id) {
2918   if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) {
2919     return AttachDecision::NoAction;
2920   }
2921 
2922   JSObject* env = env_;
2923   Maybe<PropertyInfo> prop;
2924   NativeObject* holder = nullptr;
2925 
2926   while (env) {
2927     if (env->is<GlobalObject>()) {
2928       prop = env->as<GlobalObject>().lookup(cx_, id);
2929       if (prop.isSome()) {
2930         break;
2931       }
2932       return AttachDecision::NoAction;
2933     }
2934 
2935     if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>()) {
2936       return AttachDecision::NoAction;
2937     }
2938 
2939     // Check for an 'own' property on the env. There is no need to
2940     // check the prototype as non-with scopes do not inherit properties
2941     // from any prototype.
2942     prop = env->as<NativeObject>().lookup(cx_, id);
2943     if (prop.isSome()) {
2944       break;
2945     }
2946 
2947     env = env->enclosingEnvironment();
2948   }
2949 
2950   holder = &env->as<NativeObject>();
2951   if (!IsCacheableGetPropReadSlot(holder, holder, *prop)) {
2952     return AttachDecision::NoAction;
2953   }
2954   if (holder->getSlot(prop->slot()).isMagic()) {
2955     return AttachDecision::NoAction;
2956   }
2957 
2958   ObjOperandId lastObjId = objId;
2959   env = env_;
2960   while (env) {
2961     if (NeedEnvironmentShapeGuard(env)) {
2962       writer.guardShape(lastObjId, env->shape());
2963     }
2964 
2965     if (env == holder) {
2966       break;
2967     }
2968 
2969     lastObjId = writer.loadEnclosingEnvironment(lastObjId);
2970     env = env->enclosingEnvironment();
2971   }
2972 
2973   if (holder->isFixedSlot(prop->slot())) {
2974     writer.loadEnvironmentFixedSlotResult(
2975         lastObjId, NativeObject::getFixedSlotOffset(prop->slot()));
2976   } else {
2977     size_t dynamicSlotOffset =
2978         holder->dynamicSlotIndex(prop->slot()) * sizeof(Value);
2979     writer.loadEnvironmentDynamicSlotResult(lastObjId, dynamicSlotOffset);
2980   }
2981   writer.returnFromIC();
2982 
2983   trackAttached("EnvironmentName");
2984   return AttachDecision::Attach;
2985 }
2986 
trackAttached(const char * name)2987 void GetNameIRGenerator::trackAttached(const char* name) {
2988 #ifdef JS_CACHEIR_SPEW
2989   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2990     sp.valueProperty("base", ObjectValue(*env_));
2991     sp.valueProperty("property", StringValue(name_));
2992   }
2993 #endif
2994 }
2995 
BindNameIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleObject env,HandlePropertyName name)2996 BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script,
2997                                          jsbytecode* pc, ICState state,
2998                                          HandleObject env,
2999                                          HandlePropertyName name)
3000     : IRGenerator(cx, script, pc, CacheKind::BindName, state),
3001       env_(env),
3002       name_(name) {}
3003 
tryAttachStub()3004 AttachDecision BindNameIRGenerator::tryAttachStub() {
3005   MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
3006 
3007   AutoAssertNoPendingException aanpe(cx_);
3008 
3009   ObjOperandId envId(writer.setInputOperandId(0));
3010   RootedId id(cx_, NameToId(name_));
3011 
3012   TRY_ATTACH(tryAttachGlobalName(envId, id));
3013   TRY_ATTACH(tryAttachEnvironmentName(envId, id));
3014 
3015   trackAttached(IRGenerator::NotAttached);
3016   return AttachDecision::NoAction;
3017 }
3018 
tryAttachGlobalName(ObjOperandId objId,HandleId id)3019 AttachDecision BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId,
3020                                                         HandleId id) {
3021   if (!IsGlobalOp(JSOp(*pc_))) {
3022     return AttachDecision::NoAction;
3023   }
3024   MOZ_ASSERT(!script_->hasNonSyntacticScope());
3025 
3026   Handle<GlobalLexicalEnvironmentObject*> globalLexical =
3027       env_.as<GlobalLexicalEnvironmentObject>();
3028   MOZ_ASSERT(globalLexical->isGlobal());
3029 
3030   JSObject* result = nullptr;
3031   if (Maybe<PropertyInfo> prop = globalLexical->lookup(cx_, id)) {
3032     // If this is an uninitialized lexical or a const, we need to return a
3033     // RuntimeLexicalErrorObject.
3034     if (globalLexical->getSlot(prop->slot()).isMagic() || !prop->writable()) {
3035       return AttachDecision::NoAction;
3036     }
3037     result = globalLexical;
3038   } else {
3039     result = &globalLexical->global();
3040   }
3041 
3042   if (result == globalLexical) {
3043     // Lexical bindings are non-configurable so we can just return the
3044     // global lexical.
3045     writer.loadObjectResult(objId);
3046   } else {
3047     // If the property exists on the global and is non-configurable, it cannot
3048     // be shadowed by the lexical scope so we can just return the global without
3049     // a shape guard.
3050     Maybe<PropertyInfo> prop = result->as<GlobalObject>().lookup(cx_, id);
3051     if (prop.isNothing() || prop->configurable()) {
3052       writer.guardShape(objId, globalLexical->shape());
3053     }
3054     ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
3055     writer.loadObjectResult(globalId);
3056   }
3057   writer.returnFromIC();
3058 
3059   trackAttached("GlobalName");
3060   return AttachDecision::Attach;
3061 }
3062 
tryAttachEnvironmentName(ObjOperandId objId,HandleId id)3063 AttachDecision BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId,
3064                                                              HandleId id) {
3065   if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) {
3066     return AttachDecision::NoAction;
3067   }
3068 
3069   JSObject* env = env_;
3070   Maybe<PropertyInfo> prop;
3071   while (true) {
3072     if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>()) {
3073       return AttachDecision::NoAction;
3074     }
3075     if (env->is<WithEnvironmentObject>()) {
3076       return AttachDecision::NoAction;
3077     }
3078 
3079     // When we reach an unqualified variables object (like the global) we
3080     // have to stop looking and return that object.
3081     if (env->isUnqualifiedVarObj()) {
3082       break;
3083     }
3084 
3085     // Check for an 'own' property on the env. There is no need to
3086     // check the prototype as non-with scopes do not inherit properties
3087     // from any prototype.
3088     prop = env->as<NativeObject>().lookup(cx_, id);
3089     if (prop.isSome()) {
3090       break;
3091     }
3092 
3093     env = env->enclosingEnvironment();
3094   }
3095 
3096   // If this is an uninitialized lexical or a const, we need to return a
3097   // RuntimeLexicalErrorObject.
3098   auto* holder = &env->as<NativeObject>();
3099   if (prop.isSome() && holder->is<EnvironmentObject>() &&
3100       (holder->getSlot(prop->slot()).isMagic() || !prop->writable())) {
3101     return AttachDecision::NoAction;
3102   }
3103 
3104   ObjOperandId lastObjId = objId;
3105   env = env_;
3106   while (env) {
3107     if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>()) {
3108       writer.guardShape(lastObjId, env->shape());
3109     }
3110 
3111     if (env == holder) {
3112       break;
3113     }
3114 
3115     lastObjId = writer.loadEnclosingEnvironment(lastObjId);
3116     env = env->enclosingEnvironment();
3117   }
3118   writer.loadObjectResult(lastObjId);
3119   writer.returnFromIC();
3120 
3121   trackAttached("EnvironmentName");
3122   return AttachDecision::Attach;
3123 }
3124 
trackAttached(const char * name)3125 void BindNameIRGenerator::trackAttached(const char* name) {
3126 #ifdef JS_CACHEIR_SPEW
3127   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
3128     sp.valueProperty("base", ObjectValue(*env_));
3129     sp.valueProperty("property", StringValue(name_));
3130   }
3131 #endif
3132 }
3133 
HasPropIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,CacheKind cacheKind,HandleValue idVal,HandleValue val)3134 HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script,
3135                                        jsbytecode* pc, ICState state,
3136                                        CacheKind cacheKind, HandleValue idVal,
3137                                        HandleValue val)
3138     : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {}
3139 
tryAttachDense(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)3140 AttachDecision HasPropIRGenerator::tryAttachDense(HandleObject obj,
3141                                                   ObjOperandId objId,
3142                                                   uint32_t index,
3143                                                   Int32OperandId indexId) {
3144   if (!obj->is<NativeObject>()) {
3145     return AttachDecision::NoAction;
3146   }
3147 
3148   NativeObject* nobj = &obj->as<NativeObject>();
3149   if (!nobj->containsDenseElement(index)) {
3150     return AttachDecision::NoAction;
3151   }
3152 
3153   // Guard shape to ensure object class is NativeObject.
3154   TestMatchingNativeReceiver(writer, nobj, objId);
3155   writer.loadDenseElementExistsResult(objId, indexId);
3156   writer.returnFromIC();
3157 
3158   trackAttached("DenseHasProp");
3159   return AttachDecision::Attach;
3160 }
3161 
tryAttachDenseHole(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId)3162 AttachDecision HasPropIRGenerator::tryAttachDenseHole(HandleObject obj,
3163                                                       ObjOperandId objId,
3164                                                       uint32_t index,
3165                                                       Int32OperandId indexId) {
3166   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3167   OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
3168 
3169   if (!obj->is<NativeObject>()) {
3170     return AttachDecision::NoAction;
3171   }
3172 
3173   NativeObject* nobj = &obj->as<NativeObject>();
3174   if (nobj->containsDenseElement(index)) {
3175     return AttachDecision::NoAction;
3176   }
3177   if (!CanAttachDenseElementHole(nobj, ownProp)) {
3178     return AttachDecision::NoAction;
3179   }
3180 
3181   // Guard shape to ensure class is NativeObject and to prevent non-dense
3182   // elements being added. Also ensures prototype doesn't change if dynamic
3183   // checks aren't emitted.
3184   TestMatchingNativeReceiver(writer, nobj, objId);
3185 
3186   // Generate prototype guards if needed. This includes monitoring that
3187   // properties were not added in the chain.
3188   if (!hasOwn) {
3189     GeneratePrototypeHoleGuards(writer, nobj, objId,
3190                                 /* alwaysGuardFirstProto = */ false);
3191   }
3192 
3193   writer.loadDenseElementHoleExistsResult(objId, indexId);
3194   writer.returnFromIC();
3195 
3196   trackAttached("DenseHasPropHole");
3197   return AttachDecision::Attach;
3198 }
3199 
tryAttachSparse(HandleObject obj,ObjOperandId objId,Int32OperandId indexId)3200 AttachDecision HasPropIRGenerator::tryAttachSparse(HandleObject obj,
3201                                                    ObjOperandId objId,
3202                                                    Int32OperandId indexId) {
3203   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3204   OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
3205 
3206   if (!obj->is<NativeObject>()) {
3207     return AttachDecision::NoAction;
3208   }
3209   auto* nobj = &obj->as<NativeObject>();
3210 
3211   if (!nobj->isIndexed()) {
3212     return AttachDecision::NoAction;
3213   }
3214   if (!CanAttachDenseElementHole(nobj, ownProp, AllowIndexedReceiver::Yes)) {
3215     return AttachDecision::NoAction;
3216   }
3217 
3218   // Guard that this is a native object.
3219   writer.guardIsNativeObject(objId);
3220 
3221   // Generate prototype guards if needed. This includes monitoring that
3222   // properties were not added in the chain.
3223   if (!hasOwn) {
3224     GeneratePrototypeHoleGuards(writer, nobj, objId,
3225                                 /* alwaysGuardFirstProto = */ true);
3226   }
3227 
3228   // Because of the prototype guard we know that the prototype chain
3229   // does not include any dense or sparse (i.e indexed) properties.
3230   writer.callObjectHasSparseElementResult(objId, indexId);
3231   writer.returnFromIC();
3232 
3233   trackAttached("Sparse");
3234   return AttachDecision::Attach;
3235 }
3236 
tryAttachArgumentsObjectArg(HandleObject obj,ObjOperandId objId,Int32OperandId indexId)3237 AttachDecision HasPropIRGenerator::tryAttachArgumentsObjectArg(
3238     HandleObject obj, ObjOperandId objId, Int32OperandId indexId) {
3239   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3240   OwnProperty ownProp = hasOwn ? OwnProperty::Yes : OwnProperty::No;
3241 
3242   if (!obj->is<ArgumentsObject>()) {
3243     return AttachDecision::NoAction;
3244   }
3245   auto* args = &obj->as<ArgumentsObject>();
3246 
3247   // No elements must have been overridden or deleted.
3248   if (args->hasOverriddenElement()) {
3249     return AttachDecision::NoAction;
3250   }
3251 
3252   if (!CanAttachDenseElementHole(args, ownProp, AllowIndexedReceiver::Yes,
3253                                  AllowExtraReceiverProperties::Yes)) {
3254     return AttachDecision::NoAction;
3255   }
3256 
3257   if (args->is<MappedArgumentsObject>()) {
3258     writer.guardClass(objId, GuardClassKind::MappedArguments);
3259   } else {
3260     MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
3261     writer.guardClass(objId, GuardClassKind::UnmappedArguments);
3262   }
3263 
3264   if (!hasOwn) {
3265     GeneratePrototypeHoleGuards(writer, args, objId,
3266                                 /* alwaysGuardFirstProto = */ true);
3267   }
3268 
3269   writer.loadArgumentsObjectArgExistsResult(objId, indexId);
3270   writer.returnFromIC();
3271 
3272   trackAttached("ArgumentsObjectArg");
3273   return AttachDecision::Attach;
3274 }
3275 
tryAttachNamedProp(HandleObject obj,ObjOperandId objId,HandleId key,ValOperandId keyId)3276 AttachDecision HasPropIRGenerator::tryAttachNamedProp(HandleObject obj,
3277                                                       ObjOperandId objId,
3278                                                       HandleId key,
3279                                                       ValOperandId keyId) {
3280   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3281 
3282   NativeObject* holder = nullptr;
3283   PropertyResult prop;
3284 
3285   if (hasOwn) {
3286     if (!LookupOwnPropertyPure(cx_, obj, key, &prop)) {
3287       return AttachDecision::NoAction;
3288     }
3289 
3290     holder = &obj->as<NativeObject>();
3291   } else {
3292     if (!LookupPropertyPure(cx_, obj, key, &holder, &prop)) {
3293       return AttachDecision::NoAction;
3294     }
3295   }
3296   if (prop.isNotFound()) {
3297     return AttachDecision::NoAction;
3298   }
3299   auto* nobj = &obj->as<NativeObject>();
3300 
3301   TRY_ATTACH(tryAttachMegamorphic(objId, keyId));
3302   TRY_ATTACH(tryAttachNative(nobj, objId, key, keyId, prop, holder));
3303 
3304   return AttachDecision::NoAction;
3305 }
3306 
tryAttachMegamorphic(ObjOperandId objId,ValOperandId keyId)3307 AttachDecision HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId,
3308                                                         ValOperandId keyId) {
3309   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3310 
3311   if (mode_ != ICState::Mode::Megamorphic) {
3312     return AttachDecision::NoAction;
3313   }
3314 
3315   writer.megamorphicHasPropResult(objId, keyId, hasOwn);
3316   writer.returnFromIC();
3317   trackAttached("MegamorphicHasProp");
3318   return AttachDecision::Attach;
3319 }
3320 
tryAttachNative(NativeObject * obj,ObjOperandId objId,jsid key,ValOperandId keyId,PropertyResult prop,NativeObject * holder)3321 AttachDecision HasPropIRGenerator::tryAttachNative(NativeObject* obj,
3322                                                    ObjOperandId objId, jsid key,
3323                                                    ValOperandId keyId,
3324                                                    PropertyResult prop,
3325                                                    NativeObject* holder) {
3326   MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
3327 
3328   if (!prop.isNativeProperty()) {
3329     return AttachDecision::NoAction;
3330   }
3331 
3332   Maybe<ObjOperandId> tempId;
3333   emitIdGuard(keyId, idVal_, key);
3334   EmitReadSlotGuard(writer, obj, holder, objId, &tempId);
3335   writer.loadBooleanResult(true);
3336   writer.returnFromIC();
3337 
3338   trackAttached("NativeHasProp");
3339   return AttachDecision::Attach;
3340 }
3341 
tryAttachTypedArray(HandleObject obj,ObjOperandId objId,ValOperandId keyId)3342 AttachDecision HasPropIRGenerator::tryAttachTypedArray(HandleObject obj,
3343                                                        ObjOperandId objId,
3344                                                        ValOperandId keyId) {
3345   if (!obj->is<TypedArrayObject>()) {
3346     return AttachDecision::NoAction;
3347   }
3348 
3349   int64_t index;
3350   if (!ValueIsInt64Index(idVal_, &index)) {
3351     return AttachDecision::NoAction;
3352   }
3353 
3354   writer.guardIsTypedArray(objId);
3355   IntPtrOperandId intPtrIndexId =
3356       guardToIntPtrIndex(idVal_, keyId, /* supportOOB = */ true);
3357   writer.loadTypedArrayElementExistsResult(objId, intPtrIndexId);
3358   writer.returnFromIC();
3359 
3360   trackAttached("TypedArrayObject");
3361   return AttachDecision::Attach;
3362 }
3363 
tryAttachSlotDoesNotExist(NativeObject * obj,ObjOperandId objId,jsid key,ValOperandId keyId)3364 AttachDecision HasPropIRGenerator::tryAttachSlotDoesNotExist(
3365     NativeObject* obj, ObjOperandId objId, jsid key, ValOperandId keyId) {
3366   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3367 
3368   emitIdGuard(keyId, idVal_, key);
3369   if (hasOwn) {
3370     TestMatchingNativeReceiver(writer, obj, objId);
3371   } else {
3372     Maybe<ObjOperandId> tempId;
3373     EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
3374   }
3375   writer.loadBooleanResult(false);
3376   writer.returnFromIC();
3377 
3378   trackAttached("DoesNotExist");
3379   return AttachDecision::Attach;
3380 }
3381 
tryAttachDoesNotExist(HandleObject obj,ObjOperandId objId,HandleId key,ValOperandId keyId)3382 AttachDecision HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj,
3383                                                          ObjOperandId objId,
3384                                                          HandleId key,
3385                                                          ValOperandId keyId) {
3386   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3387 
3388   // Check that property doesn't exist on |obj| or it's prototype chain. These
3389   // checks allow NativeObjects with a NativeObject prototype chain. They return
3390   // NoAction if unknown such as resolve hooks or proxies.
3391   if (hasOwn) {
3392     if (!CheckHasNoSuchOwnProperty(cx_, obj, key)) {
3393       return AttachDecision::NoAction;
3394     }
3395   } else {
3396     if (!CheckHasNoSuchProperty(cx_, obj, key)) {
3397       return AttachDecision::NoAction;
3398     }
3399   }
3400   auto* nobj = &obj->as<NativeObject>();
3401 
3402   TRY_ATTACH(tryAttachMegamorphic(objId, keyId));
3403   TRY_ATTACH(tryAttachSlotDoesNotExist(nobj, objId, key, keyId));
3404 
3405   return AttachDecision::NoAction;
3406 }
3407 
tryAttachProxyElement(HandleObject obj,ObjOperandId objId,ValOperandId keyId)3408 AttachDecision HasPropIRGenerator::tryAttachProxyElement(HandleObject obj,
3409                                                          ObjOperandId objId,
3410                                                          ValOperandId keyId) {
3411   bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
3412 
3413   if (!obj->is<ProxyObject>()) {
3414     return AttachDecision::NoAction;
3415   }
3416 
3417   writer.guardIsProxy(objId);
3418   writer.proxyHasPropResult(objId, keyId, hasOwn);
3419   writer.returnFromIC();
3420 
3421   trackAttached("ProxyHasProp");
3422   return AttachDecision::Attach;
3423 }
3424 
tryAttachStub()3425 AttachDecision HasPropIRGenerator::tryAttachStub() {
3426   MOZ_ASSERT(cacheKind_ == CacheKind::In || cacheKind_ == CacheKind::HasOwn);
3427 
3428   AutoAssertNoPendingException aanpe(cx_);
3429 
3430   // NOTE: Argument order is PROPERTY, OBJECT
3431   ValOperandId keyId(writer.setInputOperandId(0));
3432   ValOperandId valId(writer.setInputOperandId(1));
3433 
3434   if (!val_.isObject()) {
3435     trackAttached(IRGenerator::NotAttached);
3436     return AttachDecision::NoAction;
3437   }
3438   RootedObject obj(cx_, &val_.toObject());
3439   ObjOperandId objId = writer.guardToObject(valId);
3440 
3441   // Optimize Proxies
3442   TRY_ATTACH(tryAttachProxyElement(obj, objId, keyId));
3443 
3444   RootedId id(cx_);
3445   bool nameOrSymbol;
3446   if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
3447     cx_->clearPendingException();
3448     return AttachDecision::NoAction;
3449   }
3450 
3451   if (nameOrSymbol) {
3452     TRY_ATTACH(tryAttachNamedProp(obj, objId, id, keyId));
3453     TRY_ATTACH(tryAttachDoesNotExist(obj, objId, id, keyId));
3454 
3455     trackAttached(IRGenerator::NotAttached);
3456     return AttachDecision::NoAction;
3457   }
3458 
3459   TRY_ATTACH(tryAttachTypedArray(obj, objId, keyId));
3460 
3461   uint32_t index;
3462   Int32OperandId indexId;
3463   if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
3464     TRY_ATTACH(tryAttachDense(obj, objId, index, indexId));
3465     TRY_ATTACH(tryAttachDenseHole(obj, objId, index, indexId));
3466     TRY_ATTACH(tryAttachSparse(obj, objId, indexId));
3467     TRY_ATTACH(tryAttachArgumentsObjectArg(obj, objId, indexId));
3468 
3469     trackAttached(IRGenerator::NotAttached);
3470     return AttachDecision::NoAction;
3471   }
3472 
3473   trackAttached(IRGenerator::NotAttached);
3474   return AttachDecision::NoAction;
3475 }
3476 
trackAttached(const char * name)3477 void HasPropIRGenerator::trackAttached(const char* name) {
3478 #ifdef JS_CACHEIR_SPEW
3479   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
3480     sp.valueProperty("base", val_);
3481     sp.valueProperty("property", idVal_);
3482   }
3483 #endif
3484 }
3485 
CheckPrivateFieldIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,CacheKind cacheKind,HandleValue idVal,HandleValue val)3486 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
3487     JSContext* cx, HandleScript script, jsbytecode* pc, ICState state,
3488     CacheKind cacheKind, HandleValue idVal, HandleValue val)
3489     : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {
3490   MOZ_ASSERT(idVal.isSymbol() && idVal.toSymbol()->isPrivateName());
3491 }
3492 
tryAttachStub()3493 AttachDecision CheckPrivateFieldIRGenerator::tryAttachStub() {
3494   AutoAssertNoPendingException aanpe(cx_);
3495 
3496   ValOperandId valId(writer.setInputOperandId(0));
3497   ValOperandId keyId(writer.setInputOperandId(1));
3498 
3499   if (!val_.isObject()) {
3500     trackAttached(IRGenerator::NotAttached);
3501     return AttachDecision::NoAction;
3502   }
3503   JSObject* obj = &val_.toObject();
3504   ObjOperandId objId = writer.guardToObject(valId);
3505   PropertyKey key = PropertyKey::Symbol(idVal_.toSymbol());
3506 
3507   ThrowCondition condition;
3508   ThrowMsgKind msgKind;
3509   GetCheckPrivateFieldOperands(pc_, &condition, &msgKind);
3510 
3511   bool hasOwn = false;
3512   if (!HasOwnDataPropertyPure(cx_, obj, key, &hasOwn)) {
3513     // Can't determine if HasOwnProperty purely.
3514     return AttachDecision::NoAction;
3515   }
3516 
3517   if (CheckPrivateFieldWillThrow(condition, hasOwn)) {
3518     // Don't attach a stub if the operation will throw.
3519     return AttachDecision::NoAction;
3520   }
3521 
3522   TRY_ATTACH(tryAttachNative(obj, objId, key, keyId, hasOwn));
3523 
3524   return AttachDecision::NoAction;
3525 }
3526 
tryAttachNative(JSObject * obj,ObjOperandId objId,jsid key,ValOperandId keyId,bool hasOwn)3527 AttachDecision CheckPrivateFieldIRGenerator::tryAttachNative(JSObject* obj,
3528                                                              ObjOperandId objId,
3529                                                              jsid key,
3530                                                              ValOperandId keyId,
3531                                                              bool hasOwn) {
3532   if (!obj->is<NativeObject>()) {
3533     return AttachDecision::NoAction;
3534   }
3535   auto* nobj = &obj->as<NativeObject>();
3536 
3537   Maybe<ObjOperandId> tempId;
3538   emitIdGuard(keyId, idVal_, key);
3539   EmitReadSlotGuard(writer, nobj, nobj, objId, &tempId);
3540   writer.loadBooleanResult(hasOwn);
3541   writer.returnFromIC();
3542 
3543   trackAttached("NativeCheckPrivateField");
3544   return AttachDecision::Attach;
3545 }
3546 
trackAttached(const char * name)3547 void CheckPrivateFieldIRGenerator::trackAttached(const char* name) {
3548 #ifdef JS_CACHEIR_SPEW
3549   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
3550     sp.valueProperty("base", val_);
3551     sp.valueProperty("property", idVal_);
3552   }
3553 #endif
3554 }
3555 
maybeGuardInt32Index(const Value & index,ValOperandId indexId,uint32_t * int32Index,Int32OperandId * int32IndexId)3556 bool IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
3557                                        uint32_t* int32Index,
3558                                        Int32OperandId* int32IndexId) {
3559   if (index.isNumber()) {
3560     int32_t indexSigned;
3561     if (index.isInt32()) {
3562       indexSigned = index.toInt32();
3563     } else {
3564       // We allow negative zero here.
3565       if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned)) {
3566         return false;
3567       }
3568     }
3569 
3570     if (indexSigned < 0) {
3571       return false;
3572     }
3573 
3574     *int32Index = uint32_t(indexSigned);
3575     *int32IndexId = writer.guardToInt32Index(indexId);
3576     return true;
3577   }
3578 
3579   if (index.isString()) {
3580     int32_t indexSigned = GetIndexFromString(index.toString());
3581     if (indexSigned < 0) {
3582       return false;
3583     }
3584 
3585     StringOperandId strId = writer.guardToString(indexId);
3586     *int32Index = uint32_t(indexSigned);
3587     *int32IndexId = writer.guardStringToIndex(strId);
3588     return true;
3589   }
3590 
3591   return false;
3592 }
3593 
SetPropIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,CacheKind cacheKind,ICState state,HandleValue lhsVal,HandleValue idVal,HandleValue rhsVal)3594 SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script,
3595                                        jsbytecode* pc, CacheKind cacheKind,
3596                                        ICState state, HandleValue lhsVal,
3597                                        HandleValue idVal, HandleValue rhsVal)
3598     : IRGenerator(cx, script, pc, cacheKind, state),
3599       lhsVal_(lhsVal),
3600       idVal_(idVal),
3601       rhsVal_(rhsVal) {}
3602 
tryAttachStub()3603 AttachDecision SetPropIRGenerator::tryAttachStub() {
3604   AutoAssertNoPendingException aanpe(cx_);
3605 
3606   ValOperandId objValId(writer.setInputOperandId(0));
3607   ValOperandId rhsValId;
3608   if (cacheKind_ == CacheKind::SetProp) {
3609     rhsValId = ValOperandId(writer.setInputOperandId(1));
3610   } else {
3611     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3612     MOZ_ASSERT(setElemKeyValueId().id() == 1);
3613     writer.setInputOperandId(1);
3614     rhsValId = ValOperandId(writer.setInputOperandId(2));
3615   }
3616 
3617   RootedId id(cx_);
3618   bool nameOrSymbol;
3619   if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
3620     cx_->clearPendingException();
3621     return AttachDecision::NoAction;
3622   }
3623 
3624   if (lhsVal_.isObject()) {
3625     RootedObject obj(cx_, &lhsVal_.toObject());
3626 
3627     ObjOperandId objId = writer.guardToObject(objValId);
3628     if (IsPropertySetOp(JSOp(*pc_))) {
3629       TRY_ATTACH(tryAttachMegamorphicSetElement(obj, objId, rhsValId));
3630     }
3631     if (nameOrSymbol) {
3632       TRY_ATTACH(tryAttachNativeSetSlot(obj, objId, id, rhsValId));
3633       if (IsPropertySetOp(JSOp(*pc_))) {
3634         TRY_ATTACH(tryAttachSetArrayLength(obj, objId, id, rhsValId));
3635         TRY_ATTACH(tryAttachSetter(obj, objId, id, rhsValId));
3636         TRY_ATTACH(tryAttachWindowProxy(obj, objId, id, rhsValId));
3637         TRY_ATTACH(tryAttachProxy(obj, objId, id, rhsValId));
3638       }
3639       if (canAttachAddSlotStub(obj, id)) {
3640         deferType_ = DeferType::AddSlot;
3641         return AttachDecision::Deferred;
3642       }
3643       return AttachDecision::NoAction;
3644     }
3645 
3646     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3647 
3648     if (IsPropertySetOp(JSOp(*pc_))) {
3649       TRY_ATTACH(tryAttachProxyElement(obj, objId, rhsValId));
3650     }
3651 
3652     TRY_ATTACH(tryAttachSetTypedArrayElement(obj, objId, rhsValId));
3653 
3654     uint32_t index;
3655     Int32OperandId indexId;
3656     if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
3657       TRY_ATTACH(
3658           tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId));
3659       TRY_ATTACH(
3660           tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId));
3661       TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj, objId, index, indexId,
3662                                                    rhsValId));
3663       return AttachDecision::NoAction;
3664     }
3665   }
3666   return AttachDecision::NoAction;
3667 }
3668 
EmitStoreSlotAndReturn(CacheIRWriter & writer,ObjOperandId objId,NativeObject * nobj,PropertyInfo prop,ValOperandId rhsId)3669 static void EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId,
3670                                    NativeObject* nobj, PropertyInfo prop,
3671                                    ValOperandId rhsId) {
3672   if (nobj->isFixedSlot(prop.slot())) {
3673     size_t offset = NativeObject::getFixedSlotOffset(prop.slot());
3674     writer.storeFixedSlot(objId, offset, rhsId);
3675   } else {
3676     size_t offset = nobj->dynamicSlotIndex(prop.slot()) * sizeof(Value);
3677     writer.storeDynamicSlot(objId, offset, rhsId);
3678   }
3679   writer.returnFromIC();
3680 }
3681 
LookupShapeForSetSlot(JSOp op,NativeObject * obj,jsid id)3682 static Maybe<PropertyInfo> LookupShapeForSetSlot(JSOp op, NativeObject* obj,
3683                                                  jsid id) {
3684   Maybe<PropertyInfo> prop = obj->lookupPure(id);
3685   if (prop.isNothing() || !prop->isDataProperty() || !prop->writable()) {
3686     return mozilla::Nothing();
3687   }
3688 
3689   // If this is an op like JSOp::InitElem / [[DefineOwnProperty]], the
3690   // property's attributes may have to be changed too, so make sure it's a
3691   // simple data property.
3692   if (IsPropertyInitOp(op) && (!prop->configurable() || !prop->enumerable())) {
3693     return mozilla::Nothing();
3694   }
3695 
3696   return prop;
3697 }
3698 
CanAttachNativeSetSlot(JSOp op,JSObject * obj,PropertyKey id,Maybe<PropertyInfo> * prop)3699 static bool CanAttachNativeSetSlot(JSOp op, JSObject* obj, PropertyKey id,
3700                                    Maybe<PropertyInfo>* prop) {
3701   if (!obj->is<NativeObject>()) {
3702     return false;
3703   }
3704 
3705   *prop = LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id);
3706   return prop->isSome();
3707 }
3708 
3709 // There is no need to guard on the shape. Global lexical bindings are
3710 // non-configurable and can not be shadowed.
IsGlobalLexicalSetGName(JSOp op,NativeObject * obj,PropertyInfo prop)3711 static bool IsGlobalLexicalSetGName(JSOp op, NativeObject* obj,
3712                                     PropertyInfo prop) {
3713   // Ensure that the env can't change.
3714   if (op != JSOp::SetGName && op != JSOp::StrictSetGName) {
3715     return false;
3716   }
3717 
3718   if (!obj->is<GlobalLexicalEnvironmentObject>()) {
3719     return false;
3720   }
3721 
3722   // Uninitialized let bindings use a RuntimeLexicalErrorObject.
3723   MOZ_ASSERT(!obj->getSlot(prop.slot()).isMagic());
3724   MOZ_ASSERT(prop.writable());
3725   MOZ_ASSERT(!prop.configurable());
3726   return true;
3727 }
3728 
tryAttachNativeSetSlot(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)3729 AttachDecision SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj,
3730                                                           ObjOperandId objId,
3731                                                           HandleId id,
3732                                                           ValOperandId rhsId) {
3733   Maybe<PropertyInfo> prop;
3734   if (!CanAttachNativeSetSlot(JSOp(*pc_), obj, id, &prop)) {
3735     return AttachDecision::NoAction;
3736   }
3737 
3738   // Don't attach a megamorphic store slot stub for ops like JSOp::InitElem.
3739   if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp &&
3740       IsPropertySetOp(JSOp(*pc_))) {
3741     writer.megamorphicStoreSlot(objId, id.toAtom()->asPropertyName(), rhsId);
3742     writer.returnFromIC();
3743     trackAttached("MegamorphicNativeSlot");
3744     return AttachDecision::Attach;
3745   }
3746 
3747   maybeEmitIdGuard(id);
3748 
3749   NativeObject* nobj = &obj->as<NativeObject>();
3750   if (!IsGlobalLexicalSetGName(JSOp(*pc_), nobj, *prop)) {
3751     TestMatchingNativeReceiver(writer, nobj, objId);
3752   }
3753 
3754   EmitStoreSlotAndReturn(writer, objId, nobj, *prop, rhsId);
3755 
3756   trackAttached("NativeSlot");
3757   return AttachDecision::Attach;
3758 }
3759 
emitNumericGuard(ValOperandId valId,Scalar::Type type)3760 OperandId IRGenerator::emitNumericGuard(ValOperandId valId, Scalar::Type type) {
3761   switch (type) {
3762     case Scalar::Int8:
3763     case Scalar::Uint8:
3764     case Scalar::Int16:
3765     case Scalar::Uint16:
3766     case Scalar::Int32:
3767     case Scalar::Uint32:
3768       return writer.guardToInt32ModUint32(valId);
3769 
3770     case Scalar::Float32:
3771     case Scalar::Float64:
3772       return writer.guardIsNumber(valId);
3773 
3774     case Scalar::Uint8Clamped:
3775       return writer.guardToUint8Clamped(valId);
3776 
3777     case Scalar::BigInt64:
3778     case Scalar::BigUint64:
3779       return writer.guardToBigInt(valId);
3780 
3781     case Scalar::MaxTypedArrayViewType:
3782     case Scalar::Int64:
3783     case Scalar::Simd128:
3784       break;
3785   }
3786   MOZ_CRASH("Unsupported TypedArray type");
3787 }
3788 
ValueIsNumeric(Scalar::Type type,const Value & val)3789 static bool ValueIsNumeric(Scalar::Type type, const Value& val) {
3790   if (Scalar::isBigIntType(type)) {
3791     return val.isBigInt();
3792   }
3793   return val.isNumber();
3794 }
3795 
trackAttached(const char * name)3796 void SetPropIRGenerator::trackAttached(const char* name) {
3797 #ifdef JS_CACHEIR_SPEW
3798   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
3799     sp.opcodeProperty("op", JSOp(*pc_));
3800     sp.valueProperty("base", lhsVal_);
3801     sp.valueProperty("property", idVal_);
3802     sp.valueProperty("value", rhsVal_);
3803   }
3804 #endif
3805 }
3806 
IsCacheableSetPropCallNative(NativeObject * obj,NativeObject * holder,PropertyInfo prop)3807 static bool IsCacheableSetPropCallNative(NativeObject* obj,
3808                                          NativeObject* holder,
3809                                          PropertyInfo prop) {
3810   MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
3811 
3812   if (!prop.isAccessorProperty()) {
3813     return false;
3814   }
3815 
3816   JSObject* setterObject = holder->getSetter(prop);
3817   if (!setterObject || !setterObject->is<JSFunction>()) {
3818     return false;
3819   }
3820 
3821   JSFunction& setter = setterObject->as<JSFunction>();
3822   if (!setter.isNativeWithoutJitEntry()) {
3823     return false;
3824   }
3825 
3826   if (setter.isClassConstructor()) {
3827     return false;
3828   }
3829 
3830   if (setter.hasJitInfo() && !setter.jitInfo()->needsOuterizedThisObject()) {
3831     return true;
3832   }
3833 
3834   return !IsWindow(obj);
3835 }
3836 
IsCacheableSetPropCallScripted(NativeObject * obj,NativeObject * holder,PropertyInfo prop)3837 static bool IsCacheableSetPropCallScripted(NativeObject* obj,
3838                                            NativeObject* holder,
3839                                            PropertyInfo prop) {
3840   MOZ_ASSERT(IsCacheableProtoChain(obj, holder));
3841 
3842   if (IsWindow(obj)) {
3843     return false;
3844   }
3845 
3846   if (!prop.isAccessorProperty()) {
3847     return false;
3848   }
3849 
3850   JSObject* setterObject = holder->getSetter(prop);
3851   if (!setterObject || !setterObject->is<JSFunction>()) {
3852     return false;
3853   }
3854 
3855   JSFunction& setter = setterObject->as<JSFunction>();
3856   if (setter.isClassConstructor()) {
3857     return false;
3858   }
3859 
3860   // Scripted functions and natives with JIT entry can use the scripted path.
3861   return setter.hasJitEntry();
3862 }
3863 
CanAttachSetter(JSContext * cx,jsbytecode * pc,JSObject * obj,PropertyKey id,NativeObject ** holder,Maybe<PropertyInfo> * propInfo)3864 static bool CanAttachSetter(JSContext* cx, jsbytecode* pc, JSObject* obj,
3865                             PropertyKey id, NativeObject** holder,
3866                             Maybe<PropertyInfo>* propInfo) {
3867   // Don't attach a setter stub for ops like JSOp::InitElem.
3868   MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
3869 
3870   PropertyResult prop;
3871   if (!LookupPropertyPure(cx, obj, id, holder, &prop)) {
3872     return false;
3873   }
3874   auto* nobj = &obj->as<NativeObject>();
3875 
3876   if (!prop.isNativeProperty()) {
3877     return false;
3878   }
3879 
3880   if (!IsCacheableSetPropCallScripted(nobj, *holder, prop.propertyInfo()) &&
3881       !IsCacheableSetPropCallNative(nobj, *holder, prop.propertyInfo())) {
3882     return false;
3883   }
3884 
3885   *propInfo = mozilla::Some(prop.propertyInfo());
3886   return true;
3887 }
3888 
EmitCallSetterNoGuards(JSContext * cx,CacheIRWriter & writer,NativeObject * obj,NativeObject * holder,PropertyInfo prop,ObjOperandId objId,ValOperandId rhsId)3889 static void EmitCallSetterNoGuards(JSContext* cx, CacheIRWriter& writer,
3890                                    NativeObject* obj, NativeObject* holder,
3891                                    PropertyInfo prop, ObjOperandId objId,
3892                                    ValOperandId rhsId) {
3893   JSFunction* target = &holder->getSetter(prop)->as<JSFunction>();
3894   bool sameRealm = cx->realm() == target->realm();
3895 
3896   if (target->isNativeWithoutJitEntry()) {
3897     MOZ_ASSERT(IsCacheableSetPropCallNative(obj, holder, prop));
3898     writer.callNativeSetter(objId, target, rhsId, sameRealm);
3899     writer.returnFromIC();
3900     return;
3901   }
3902 
3903   MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, prop));
3904   writer.callScriptedSetter(objId, target, rhsId, sameRealm);
3905   writer.returnFromIC();
3906 }
3907 
EmitCallDOMSetterNoGuards(JSContext * cx,CacheIRWriter & writer,NativeObject * holder,PropertyInfo prop,ObjOperandId objId,ValOperandId rhsId)3908 static void EmitCallDOMSetterNoGuards(JSContext* cx, CacheIRWriter& writer,
3909                                       NativeObject* holder, PropertyInfo prop,
3910                                       ObjOperandId objId, ValOperandId rhsId) {
3911   JSFunction* setter = &holder->getSetter(prop)->as<JSFunction>();
3912   MOZ_ASSERT(cx->realm() == setter->realm());
3913 
3914   writer.callDOMSetter(objId, setter->jitInfo(), rhsId);
3915   writer.returnFromIC();
3916 }
3917 
tryAttachSetter(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)3918 AttachDecision SetPropIRGenerator::tryAttachSetter(HandleObject obj,
3919                                                    ObjOperandId objId,
3920                                                    HandleId id,
3921                                                    ValOperandId rhsId) {
3922   NativeObject* holder = nullptr;
3923   Maybe<PropertyInfo> prop;
3924   if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &prop)) {
3925     return AttachDecision::NoAction;
3926   }
3927   auto* nobj = &obj->as<NativeObject>();
3928 
3929   maybeEmitIdGuard(id);
3930 
3931   // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
3932   // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
3933   // require outerizing).
3934   if (mode_ == ICState::Mode::Specialized || IsWindow(nobj)) {
3935     TestMatchingNativeReceiver(writer, nobj, objId);
3936 
3937     if (nobj != holder) {
3938       GeneratePrototypeGuards(writer, nobj, holder, objId);
3939 
3940       // Guard on the holder's shape.
3941       ObjOperandId holderId = writer.loadObject(holder);
3942       TestMatchingHolder(writer, holder, holderId);
3943 
3944       EmitGuardGetterSetterSlot(writer, holder, *prop, holderId,
3945                                 /* holderIsConstant = */ true);
3946     } else {
3947       EmitGuardGetterSetterSlot(writer, holder, *prop, objId);
3948     }
3949   } else {
3950     GetterSetter* gs = holder->getGetterSetter(*prop);
3951     writer.guardHasGetterSetter(objId, id, gs);
3952   }
3953 
3954   if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Setter, nobj, holder, *prop,
3955                                mode_)) {
3956     EmitCallDOMSetterNoGuards(cx_, writer, holder, *prop, objId, rhsId);
3957 
3958     trackAttached("DOMSetter");
3959     return AttachDecision::Attach;
3960   }
3961 
3962   EmitCallSetterNoGuards(cx_, writer, nobj, holder, *prop, objId, rhsId);
3963 
3964   trackAttached("Setter");
3965   return AttachDecision::Attach;
3966 }
3967 
tryAttachSetArrayLength(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)3968 AttachDecision SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj,
3969                                                            ObjOperandId objId,
3970                                                            HandleId id,
3971                                                            ValOperandId rhsId) {
3972   // Don't attach an array length stub for ops like JSOp::InitElem.
3973   MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3974 
3975   if (!obj->is<ArrayObject>() || !id.isAtom(cx_->names().length) ||
3976       !obj->as<ArrayObject>().lengthIsWritable()) {
3977     return AttachDecision::NoAction;
3978   }
3979 
3980   maybeEmitIdGuard(id);
3981   writer.guardClass(objId, GuardClassKind::Array);
3982   writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
3983   writer.returnFromIC();
3984 
3985   trackAttached("SetArrayLength");
3986   return AttachDecision::Attach;
3987 }
3988 
tryAttachSetDenseElement(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId,ValOperandId rhsId)3989 AttachDecision SetPropIRGenerator::tryAttachSetDenseElement(
3990     HandleObject obj, ObjOperandId objId, uint32_t index,
3991     Int32OperandId indexId, ValOperandId rhsId) {
3992   if (!obj->is<NativeObject>()) {
3993     return AttachDecision::NoAction;
3994   }
3995 
3996   NativeObject* nobj = &obj->as<NativeObject>();
3997   if (!nobj->containsDenseElement(index) || nobj->denseElementsAreFrozen()) {
3998     return AttachDecision::NoAction;
3999   }
4000 
4001   // Setting holes requires extra code for marking the elements non-packed.
4002   MOZ_ASSERT(!rhsVal_.isMagic(JS_ELEMENTS_HOLE));
4003 
4004   // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
4005   // the elements are sealed, we have to throw an exception. Note that we have
4006   // to check !isExtensible instead of denseElementsAreSealed because sealing
4007   // a (non-extensible) object does not necessarily trigger a Shape change.
4008   if (IsPropertyInitOp(JSOp(*pc_)) && !nobj->isExtensible()) {
4009     return AttachDecision::NoAction;
4010   }
4011 
4012   TestMatchingNativeReceiver(writer, nobj, objId);
4013 
4014   writer.storeDenseElement(objId, indexId, rhsId);
4015   writer.returnFromIC();
4016 
4017   trackAttached("SetDenseElement");
4018   return AttachDecision::Attach;
4019 }
4020 
CanAttachAddElement(NativeObject * obj,bool isInit)4021 static bool CanAttachAddElement(NativeObject* obj, bool isInit) {
4022   // Make sure the objects on the prototype don't have any indexed properties
4023   // or that such properties can't appear without a shape change.
4024   do {
4025     // The first two checks are also relevant to the receiver object.
4026     if (obj->isIndexed()) {
4027       return false;
4028     }
4029 
4030     const JSClass* clasp = obj->getClass();
4031     if (clasp != &ArrayObject::class_ &&
4032         (clasp->getAddProperty() || clasp->getResolve() ||
4033          clasp->getOpsLookupProperty() || clasp->getOpsSetProperty())) {
4034       return false;
4035     }
4036 
4037     // If we're initializing a property instead of setting one, the objects
4038     // on the prototype are not relevant.
4039     if (isInit) {
4040       break;
4041     }
4042 
4043     JSObject* proto = obj->staticPrototype();
4044     if (!proto) {
4045       break;
4046     }
4047 
4048     if (!proto->is<NativeObject>()) {
4049       return false;
4050     }
4051 
4052     // We have to make sure the proto has no non-writable (frozen) elements
4053     // because we're not allowed to shadow them.
4054     NativeObject* nproto = &proto->as<NativeObject>();
4055     if (nproto->denseElementsAreFrozen() &&
4056         nproto->getDenseInitializedLength() > 0) {
4057       return false;
4058     }
4059 
4060     obj = nproto;
4061   } while (true);
4062 
4063   return true;
4064 }
4065 
tryAttachSetDenseElementHole(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId,ValOperandId rhsId)4066 AttachDecision SetPropIRGenerator::tryAttachSetDenseElementHole(
4067     HandleObject obj, ObjOperandId objId, uint32_t index,
4068     Int32OperandId indexId, ValOperandId rhsId) {
4069   if (!obj->is<NativeObject>()) {
4070     return AttachDecision::NoAction;
4071   }
4072 
4073   // Setting holes requires extra code for marking the elements non-packed.
4074   if (rhsVal_.isMagic(JS_ELEMENTS_HOLE)) {
4075     return AttachDecision::NoAction;
4076   }
4077 
4078   JSOp op = JSOp(*pc_);
4079   MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
4080 
4081   if (op == JSOp::InitHiddenElem) {
4082     return AttachDecision::NoAction;
4083   }
4084 
4085   NativeObject* nobj = &obj->as<NativeObject>();
4086   if (!nobj->isExtensible()) {
4087     return AttachDecision::NoAction;
4088   }
4089 
4090   MOZ_ASSERT(!nobj->denseElementsAreFrozen(),
4091              "Extensible objects should not have frozen elements");
4092 
4093   uint32_t initLength = nobj->getDenseInitializedLength();
4094 
4095   // Optimize if we're adding an element at initLength or writing to a hole.
4096   //
4097   // In the case where index > initLength, we need noteHasDenseAdd to be called
4098   // to ensure Ion is aware that writes have occurred to-out-of-bound indexes
4099   // before.
4100   bool isAdd = index == initLength;
4101   bool isHoleInBounds =
4102       index < initLength && !nobj->containsDenseElement(index);
4103   if (!isAdd && !isHoleInBounds) {
4104     return AttachDecision::NoAction;
4105   }
4106 
4107   // Can't add new elements to arrays with non-writable length.
4108   if (isAdd && nobj->is<ArrayObject>() &&
4109       !nobj->as<ArrayObject>().lengthIsWritable()) {
4110     return AttachDecision::NoAction;
4111   }
4112 
4113   // Typed arrays don't have dense elements.
4114   if (nobj->is<TypedArrayObject>()) {
4115     return AttachDecision::NoAction;
4116   }
4117 
4118   // Check for other indexed properties or class hooks.
4119   if (!CanAttachAddElement(nobj, IsPropertyInitOp(op))) {
4120     return AttachDecision::NoAction;
4121   }
4122 
4123   TestMatchingNativeReceiver(writer, nobj, objId);
4124 
4125   // Also shape guard the proto chain, unless this is an InitElem.
4126   if (IsPropertySetOp(op)) {
4127     ShapeGuardProtoChain(writer, nobj, objId);
4128   }
4129 
4130   writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
4131   writer.returnFromIC();
4132 
4133   trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
4134   return AttachDecision::Attach;
4135 }
4136 
4137 // Add an IC for adding or updating a sparse array element.
tryAttachAddOrUpdateSparseElement(HandleObject obj,ObjOperandId objId,uint32_t index,Int32OperandId indexId,ValOperandId rhsId)4138 AttachDecision SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
4139     HandleObject obj, ObjOperandId objId, uint32_t index,
4140     Int32OperandId indexId, ValOperandId rhsId) {
4141   JSOp op = JSOp(*pc_);
4142   MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
4143 
4144   if (op != JSOp::SetElem && op != JSOp::StrictSetElem) {
4145     return AttachDecision::NoAction;
4146   }
4147 
4148   if (!obj->is<NativeObject>()) {
4149     return AttachDecision::NoAction;
4150   }
4151   NativeObject* nobj = &obj->as<NativeObject>();
4152 
4153   // We cannot attach a stub to a non-extensible object
4154   if (!nobj->isExtensible()) {
4155     return AttachDecision::NoAction;
4156   }
4157 
4158   // Stub doesn't handle negative indices.
4159   if (index > INT_MAX) {
4160     return AttachDecision::NoAction;
4161   }
4162 
4163   // We also need to be past the end of the dense capacity, to ensure sparse.
4164   if (index < nobj->getDenseInitializedLength()) {
4165     return AttachDecision::NoAction;
4166   }
4167 
4168   // Only handle Array objects in this stub.
4169   if (!nobj->is<ArrayObject>()) {
4170     return AttachDecision::NoAction;
4171   }
4172   ArrayObject* aobj = &nobj->as<ArrayObject>();
4173 
4174   // Don't attach if we're adding to an array with non-writable length.
4175   bool isAdd = (index >= aobj->length());
4176   if (isAdd && !aobj->lengthIsWritable()) {
4177     return AttachDecision::NoAction;
4178   }
4179 
4180   // Indexed properties on the prototype chain aren't handled by the helper.
4181   if ((aobj->staticPrototype() != nullptr) &&
4182       ObjectMayHaveExtraIndexedProperties(aobj->staticPrototype())) {
4183     return AttachDecision::NoAction;
4184   }
4185 
4186   // Ensure we are still talking about an array class.
4187   writer.guardClass(objId, GuardClassKind::Array);
4188 
4189   // The helper we are going to call only applies to non-dense elements.
4190   writer.guardIndexGreaterThanDenseInitLength(objId, indexId);
4191 
4192   // Guard extensible: We may be trying to add a new element, and so we'd best
4193   // be able to do so safely.
4194   writer.guardIsExtensible(objId);
4195 
4196   // Ensures we are able to efficiently able to map to an integral jsid.
4197   writer.guardInt32IsNonNegative(indexId);
4198 
4199   // Shape guard the prototype chain to avoid shadowing indexes from appearing.
4200   // Guard the prototype of the receiver explicitly, because the receiver's
4201   // shape is not being guarded as a proxy for that.
4202   GuardReceiverProto(writer, aobj, objId);
4203 
4204   // Dense elements may appear on the prototype chain (and prototypes may
4205   // have a different notion of which elements are dense), but they can
4206   // only be data properties, so our specialized Set handler is ok to bind
4207   // to them.
4208   ShapeGuardProtoChain(writer, aobj, objId);
4209 
4210   // Ensure that if we're adding an element to the object, the object's
4211   // length is writable.
4212   writer.guardIndexIsValidUpdateOrAdd(objId, indexId);
4213 
4214   writer.callAddOrUpdateSparseElementHelper(
4215       objId, indexId, rhsId,
4216       /* strict = */ op == JSOp::StrictSetElem);
4217   writer.returnFromIC();
4218 
4219   trackAttached("AddOrUpdateSparseElement");
4220   return AttachDecision::Attach;
4221 }
4222 
tryAttachSetTypedArrayElement(HandleObject obj,ObjOperandId objId,ValOperandId rhsId)4223 AttachDecision SetPropIRGenerator::tryAttachSetTypedArrayElement(
4224     HandleObject obj, ObjOperandId objId, ValOperandId rhsId) {
4225   if (!obj->is<TypedArrayObject>()) {
4226     return AttachDecision::NoAction;
4227   }
4228   if (!idVal_.isNumber()) {
4229     return AttachDecision::NoAction;
4230   }
4231 
4232   TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
4233   Scalar::Type elementType = tarr->type();
4234 
4235   // Don't attach if the input type doesn't match the guard added below.
4236   if (!ValueIsNumeric(elementType, rhsVal_)) {
4237     return AttachDecision::NoAction;
4238   }
4239 
4240   bool handleOOB = false;
4241   int64_t indexInt64;
4242   if (!ValueIsInt64Index(idVal_, &indexInt64) || indexInt64 < 0 ||
4243       uint64_t(indexInt64) >= tarr->length()) {
4244     handleOOB = true;
4245   }
4246 
4247   // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
4248   if (handleOOB && IsPropertyInitOp(JSOp(*pc_))) {
4249     return AttachDecision::NoAction;
4250   }
4251 
4252   writer.guardShapeForClass(objId, tarr->shape());
4253 
4254   OperandId rhsValId = emitNumericGuard(rhsId, elementType);
4255 
4256   ValOperandId keyId = setElemKeyValueId();
4257   IntPtrOperandId indexId = guardToIntPtrIndex(idVal_, keyId, handleOOB);
4258 
4259   writer.storeTypedArrayElement(objId, elementType, indexId, rhsValId,
4260                                 handleOOB);
4261   writer.returnFromIC();
4262 
4263   trackAttached(handleOOB ? "SetTypedElementOOB" : "SetTypedElement");
4264   return AttachDecision::Attach;
4265 }
4266 
tryAttachGenericProxy(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId rhsId,bool handleDOMProxies)4267 AttachDecision SetPropIRGenerator::tryAttachGenericProxy(
4268     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
4269     ValOperandId rhsId, bool handleDOMProxies) {
4270   writer.guardIsProxy(objId);
4271 
4272   if (!handleDOMProxies) {
4273     // Ensure that the incoming object is not a DOM proxy, so that we can
4274     // get to the specialized stubs. If handleDOMProxies is true, we were
4275     // unable to attach a specialized DOM stub, so we just handle all
4276     // proxies here.
4277     writer.guardIsNotDOMProxy(objId);
4278   }
4279 
4280   if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
4281     maybeEmitIdGuard(id);
4282     writer.proxySet(objId, id, rhsId, IsStrictSetPC(pc_));
4283   } else {
4284     // Attach a stub that handles every id.
4285     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
4286     MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
4287     writer.proxySetByValue(objId, setElemKeyValueId(), rhsId,
4288                            IsStrictSetPC(pc_));
4289   }
4290 
4291   writer.returnFromIC();
4292 
4293   trackAttached("GenericProxy");
4294   return AttachDecision::Attach;
4295 }
4296 
tryAttachDOMProxyShadowed(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)4297 AttachDecision SetPropIRGenerator::tryAttachDOMProxyShadowed(
4298     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
4299     ValOperandId rhsId) {
4300   MOZ_ASSERT(IsCacheableDOMProxy(obj));
4301 
4302   maybeEmitIdGuard(id);
4303   TestMatchingProxyReceiver(writer, obj, objId);
4304   writer.proxySet(objId, id, rhsId, IsStrictSetPC(pc_));
4305   writer.returnFromIC();
4306 
4307   trackAttached("DOMProxyShadowed");
4308   return AttachDecision::Attach;
4309 }
4310 
tryAttachDOMProxyUnshadowed(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)4311 AttachDecision SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
4312     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
4313     ValOperandId rhsId) {
4314   MOZ_ASSERT(IsCacheableDOMProxy(obj));
4315 
4316   JSObject* proto = obj->staticPrototype();
4317   if (!proto) {
4318     return AttachDecision::NoAction;
4319   }
4320 
4321   NativeObject* holder = nullptr;
4322   Maybe<PropertyInfo> prop;
4323   if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &prop)) {
4324     return AttachDecision::NoAction;
4325   }
4326   auto* nproto = &proto->as<NativeObject>();
4327 
4328   maybeEmitIdGuard(id);
4329 
4330   // Guard that our expando object hasn't started shadowing this property.
4331   TestMatchingProxyReceiver(writer, obj, objId);
4332   CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
4333 
4334   GeneratePrototypeGuards(writer, obj, holder, objId);
4335 
4336   // Guard on the holder of the property.
4337   ObjOperandId holderId = writer.loadObject(holder);
4338   TestMatchingHolder(writer, holder, holderId);
4339 
4340   EmitGuardGetterSetterSlot(writer, holder, *prop, holderId,
4341                             /* holderIsConstant = */ true);
4342 
4343   // EmitCallSetterNoGuards expects |obj| to be the object the property is
4344   // on to do some checks. Since we actually looked at proto, and no extra
4345   // guards will be generated, we can just pass that instead.
4346   EmitCallSetterNoGuards(cx_, writer, nproto, holder, *prop, objId, rhsId);
4347 
4348   trackAttached("DOMProxyUnshadowed");
4349   return AttachDecision::Attach;
4350 }
4351 
tryAttachDOMProxyExpando(Handle<ProxyObject * > obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)4352 AttachDecision SetPropIRGenerator::tryAttachDOMProxyExpando(
4353     Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id,
4354     ValOperandId rhsId) {
4355   MOZ_ASSERT(IsCacheableDOMProxy(obj));
4356 
4357   Value expandoVal = GetProxyPrivate(obj);
4358   JSObject* expandoObj;
4359   if (expandoVal.isObject()) {
4360     expandoObj = &expandoVal.toObject();
4361   } else {
4362     MOZ_ASSERT(!expandoVal.isUndefined(),
4363                "How did a missing expando manage to shadow things?");
4364     auto expandoAndGeneration =
4365         static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
4366     MOZ_ASSERT(expandoAndGeneration);
4367     expandoObj = &expandoAndGeneration->expando.toObject();
4368   }
4369 
4370   Maybe<PropertyInfo> prop;
4371   if (CanAttachNativeSetSlot(JSOp(*pc_), expandoObj, id, &prop)) {
4372     auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
4373 
4374     maybeEmitIdGuard(id);
4375     ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
4376         obj, objId, expandoVal, nativeExpandoObj);
4377 
4378     EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, *prop,
4379                            rhsId);
4380     trackAttached("DOMProxyExpandoSlot");
4381     return AttachDecision::Attach;
4382   }
4383 
4384   NativeObject* holder = nullptr;
4385   if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &prop)) {
4386     auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
4387 
4388     // Call the setter. Note that we pass objId, the DOM proxy, as |this|
4389     // and not the expando object.
4390     maybeEmitIdGuard(id);
4391     ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
4392         obj, objId, expandoVal, nativeExpandoObj);
4393 
4394     MOZ_ASSERT(holder == nativeExpandoObj);
4395     EmitGuardGetterSetterSlot(writer, nativeExpandoObj, *prop, expandoObjId);
4396     EmitCallSetterNoGuards(cx_, writer, nativeExpandoObj, nativeExpandoObj,
4397                            *prop, objId, rhsId);
4398     trackAttached("DOMProxyExpandoSetter");
4399     return AttachDecision::Attach;
4400   }
4401 
4402   return AttachDecision::NoAction;
4403 }
4404 
tryAttachProxy(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)4405 AttachDecision SetPropIRGenerator::tryAttachProxy(HandleObject obj,
4406                                                   ObjOperandId objId,
4407                                                   HandleId id,
4408                                                   ValOperandId rhsId) {
4409   // Don't attach a proxy stub for ops like JSOp::InitElem.
4410   MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
4411 
4412   ProxyStubType type = GetProxyStubType(cx_, obj, id);
4413   if (type == ProxyStubType::None) {
4414     return AttachDecision::NoAction;
4415   }
4416   auto proxy = obj.as<ProxyObject>();
4417 
4418   if (mode_ == ICState::Mode::Megamorphic) {
4419     return tryAttachGenericProxy(proxy, objId, id, rhsId,
4420                                  /* handleDOMProxies = */ true);
4421   }
4422 
4423   switch (type) {
4424     case ProxyStubType::None:
4425       break;
4426     case ProxyStubType::DOMExpando:
4427       TRY_ATTACH(tryAttachDOMProxyExpando(proxy, objId, id, rhsId));
4428       [[fallthrough]];  // Fall through to the generic shadowed case.
4429     case ProxyStubType::DOMShadowed:
4430       return tryAttachDOMProxyShadowed(proxy, objId, id, rhsId);
4431     case ProxyStubType::DOMUnshadowed:
4432       TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy, objId, id, rhsId));
4433       return tryAttachGenericProxy(proxy, objId, id, rhsId,
4434                                    /* handleDOMProxies = */ true);
4435     case ProxyStubType::Generic:
4436       return tryAttachGenericProxy(proxy, objId, id, rhsId,
4437                                    /* handleDOMProxies = */ false);
4438   }
4439 
4440   MOZ_CRASH("Unexpected ProxyStubType");
4441 }
4442 
tryAttachProxyElement(HandleObject obj,ObjOperandId objId,ValOperandId rhsId)4443 AttachDecision SetPropIRGenerator::tryAttachProxyElement(HandleObject obj,
4444                                                          ObjOperandId objId,
4445                                                          ValOperandId rhsId) {
4446   // Don't attach a proxy stub for ops like JSOp::InitElem.
4447   MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
4448 
4449   if (!obj->is<ProxyObject>()) {
4450     return AttachDecision::NoAction;
4451   }
4452 
4453   writer.guardIsProxy(objId);
4454 
4455   // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
4456   // proxies here as we don't have specialized DOM stubs for this.
4457   MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
4458   writer.proxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
4459   writer.returnFromIC();
4460 
4461   trackAttached("ProxyElement");
4462   return AttachDecision::Attach;
4463 }
4464 
tryAttachMegamorphicSetElement(HandleObject obj,ObjOperandId objId,ValOperandId rhsId)4465 AttachDecision SetPropIRGenerator::tryAttachMegamorphicSetElement(
4466     HandleObject obj, ObjOperandId objId, ValOperandId rhsId) {
4467   MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
4468 
4469   if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetElem) {
4470     return AttachDecision::NoAction;
4471   }
4472 
4473   // The generic proxy stubs are faster.
4474   if (obj->is<ProxyObject>()) {
4475     return AttachDecision::NoAction;
4476   }
4477 
4478   writer.megamorphicSetElement(objId, setElemKeyValueId(), rhsId,
4479                                IsStrictSetPC(pc_));
4480   writer.returnFromIC();
4481 
4482   trackAttached("MegamorphicSetElement");
4483   return AttachDecision::Attach;
4484 }
4485 
tryAttachWindowProxy(HandleObject obj,ObjOperandId objId,HandleId id,ValOperandId rhsId)4486 AttachDecision SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj,
4487                                                         ObjOperandId objId,
4488                                                         HandleId id,
4489                                                         ValOperandId rhsId) {
4490   // Attach a stub when the receiver is a WindowProxy and we can do the set
4491   // on the Window (the global object).
4492 
4493   if (!IsWindowProxyForScriptGlobal(script_, obj)) {
4494     return AttachDecision::NoAction;
4495   }
4496 
4497   // If we're megamorphic prefer a generic proxy stub that handles a lot more
4498   // cases.
4499   if (mode_ == ICState::Mode::Megamorphic) {
4500     return AttachDecision::NoAction;
4501   }
4502 
4503   // Now try to do the set on the Window (the current global).
4504   GlobalObject* windowObj = cx_->global();
4505 
4506   Maybe<PropertyInfo> prop;
4507   if (!CanAttachNativeSetSlot(JSOp(*pc_), windowObj, id, &prop)) {
4508     return AttachDecision::NoAction;
4509   }
4510 
4511   maybeEmitIdGuard(id);
4512 
4513   ObjOperandId windowObjId =
4514       GuardAndLoadWindowProxyWindow(writer, objId, windowObj);
4515   writer.guardShape(windowObjId, windowObj->shape());
4516 
4517   EmitStoreSlotAndReturn(writer, windowObjId, windowObj, *prop, rhsId);
4518 
4519   trackAttached("WindowProxySlot");
4520   return AttachDecision::Attach;
4521 }
4522 
canAttachAddSlotStub(HandleObject obj,HandleId id)4523 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj, HandleId id) {
4524   // Special-case JSFunction resolve hook to allow redefining the 'prototype'
4525   // property without triggering lazy expansion of property and object
4526   // allocation.
4527   if (obj->is<JSFunction>() && id.isAtom(cx_->names().prototype)) {
4528     MOZ_ASSERT(ClassMayResolveId(cx_->names(), obj->getClass(), id, obj));
4529 
4530     // We're only interested in functions that have a builtin .prototype
4531     // property (needsPrototypeProperty). The stub will guard on this because
4532     // the builtin .prototype property is non-configurable/non-enumerable and it
4533     // would be wrong to add a property with those attributes to a function that
4534     // doesn't have a builtin .prototype.
4535     //
4536     // Inlining needsPrototypeProperty in JIT code is complicated so we use
4537     // isNonBuiltinConstructor as a stronger condition that's easier to check
4538     // from JIT code.
4539     JSFunction* fun = &obj->as<JSFunction>();
4540     if (!fun->isNonBuiltinConstructor()) {
4541       return false;
4542     }
4543     MOZ_ASSERT(fun->needsPrototypeProperty());
4544 
4545     // If property exists this isn't an "add".
4546     if (fun->lookupPure(id)) {
4547       return false;
4548     }
4549   } else {
4550     // Normal Case: If property exists this isn't an "add"
4551     PropertyResult prop;
4552     if (!LookupOwnPropertyPure(cx_, obj, id, &prop)) {
4553       return false;
4554     }
4555     if (prop.isFound()) {
4556       return false;
4557     }
4558   }
4559 
4560   // For now we don't optimize Watchtower-monitored objects.
4561   if (Watchtower::watchesPropertyAdd(obj.as<NativeObject>())) {
4562     return false;
4563   }
4564 
4565   // Object must be extensible, or we must be initializing a private
4566   // elem.
4567   bool canAddNewProperty = obj->nonProxyIsExtensible() || id.isPrivateName();
4568   if (!canAddNewProperty) {
4569     return false;
4570   }
4571 
4572   // Walk up the object prototype chain and ensure that all prototypes are
4573   // native, and that all prototypes have no setter defined on the property.
4574   for (JSObject* proto = obj->staticPrototype(); proto;
4575        proto = proto->staticPrototype()) {
4576     if (!proto->is<NativeObject>()) {
4577       return false;
4578     }
4579 
4580     // If prototype defines this property in a non-plain way, don't optimize.
4581     Maybe<PropertyInfo> protoProp = proto->as<NativeObject>().lookup(cx_, id);
4582     if (protoProp.isSome() && !protoProp->isDataProperty()) {
4583       return false;
4584     }
4585 
4586     // Otherwise, if there's no such property, watch out for a resolve hook
4587     // that would need to be invoked and thus prevent inlining of property
4588     // addition. Allow the JSFunction resolve hook as it only defines plain
4589     // data properties and we don't need to invoke it for objects on the
4590     // proto chain.
4591     if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
4592         !proto->is<JSFunction>()) {
4593       return false;
4594     }
4595   }
4596 
4597   return true;
4598 }
4599 
tryAttachAddSlotStub(HandleShape oldShape)4600 AttachDecision SetPropIRGenerator::tryAttachAddSlotStub(HandleShape oldShape) {
4601   ValOperandId objValId(writer.setInputOperandId(0));
4602   ValOperandId rhsValId;
4603   if (cacheKind_ == CacheKind::SetProp) {
4604     rhsValId = ValOperandId(writer.setInputOperandId(1));
4605   } else {
4606     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
4607     MOZ_ASSERT(setElemKeyValueId().id() == 1);
4608     writer.setInputOperandId(1);
4609     rhsValId = ValOperandId(writer.setInputOperandId(2));
4610   }
4611 
4612   RootedId id(cx_);
4613   bool nameOrSymbol;
4614   if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
4615     cx_->clearPendingException();
4616     return AttachDecision::NoAction;
4617   }
4618 
4619   if (!lhsVal_.isObject() || !nameOrSymbol) {
4620     return AttachDecision::NoAction;
4621   }
4622 
4623   JSObject* obj = &lhsVal_.toObject();
4624 
4625   PropertyResult prop;
4626   if (!LookupOwnPropertyPure(cx_, obj, id, &prop)) {
4627     return AttachDecision::NoAction;
4628   }
4629   if (prop.isNotFound()) {
4630     return AttachDecision::NoAction;
4631   }
4632 
4633   if (!obj->is<NativeObject>()) {
4634     return AttachDecision::NoAction;
4635   }
4636   auto* nobj = &obj->as<NativeObject>();
4637 
4638   PropertyInfo propInfo = prop.propertyInfo();
4639   NativeObject* holder = nobj;
4640 
4641   // The property must be the last added property of the object.
4642   Shape* newShape = holder->shape();
4643   MOZ_RELEASE_ASSERT(newShape->lastProperty() == propInfo);
4644 
4645 #ifdef DEBUG
4646   // Verify exactly one property was added by comparing the property map
4647   // lengths.
4648   if (oldShape->propMapLength() == PropMap::Capacity) {
4649     MOZ_ASSERT(newShape->propMapLength() == 1);
4650   } else {
4651     MOZ_ASSERT(newShape->propMapLength() == oldShape->propMapLength() + 1);
4652   }
4653 #endif
4654 
4655   // Basic shape checks.
4656   if (newShape->isDictionary() || !propInfo.isDataProperty() ||
4657       !propInfo.writable()) {
4658     return AttachDecision::NoAction;
4659   }
4660 
4661   ObjOperandId objId = writer.guardToObject(objValId);
4662   maybeEmitIdGuard(id);
4663 
4664   // Shape guard the object.
4665   writer.guardShape(objId, oldShape);
4666 
4667   // If this is the special function.prototype case, we need to guard the
4668   // function is a non-builtin constructor. See canAttachAddSlotStub.
4669   if (nobj->is<JSFunction>() && id.isAtom(cx_->names().prototype)) {
4670     MOZ_ASSERT(nobj->as<JSFunction>().isNonBuiltinConstructor());
4671     writer.guardFunctionIsNonBuiltinCtor(objId);
4672   }
4673 
4674   ShapeGuardProtoChain(writer, nobj, objId);
4675 
4676   // If the JSClass has an addProperty hook, we need to call a VM function to
4677   // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
4678   // anything for non-index properties.
4679   DebugOnly<uint32_t> index;
4680   MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
4681   bool mustCallAddPropertyHook =
4682       obj->getClass()->getAddProperty() && !obj->is<ArrayObject>();
4683 
4684   if (mustCallAddPropertyHook) {
4685     writer.addSlotAndCallAddPropHook(objId, rhsValId, newShape);
4686     trackAttached("AddSlotWithAddPropertyHook");
4687   } else if (holder->isFixedSlot(propInfo.slot())) {
4688     size_t offset = NativeObject::getFixedSlotOffset(propInfo.slot());
4689     writer.addAndStoreFixedSlot(objId, offset, rhsValId, newShape);
4690     trackAttached("AddSlot");
4691   } else {
4692     size_t offset = holder->dynamicSlotIndex(propInfo.slot()) * sizeof(Value);
4693     uint32_t numOldSlots = NativeObject::calculateDynamicSlots(oldShape);
4694     uint32_t numNewSlots = holder->numDynamicSlots();
4695     if (numOldSlots == numNewSlots) {
4696       writer.addAndStoreDynamicSlot(objId, offset, rhsValId, newShape);
4697       trackAttached("AddSlot");
4698     } else {
4699       MOZ_ASSERT(numNewSlots > numOldSlots);
4700       writer.allocateAndStoreDynamicSlot(objId, offset, rhsValId, newShape,
4701                                          numNewSlots);
4702       trackAttached("AllocateSlot");
4703     }
4704   }
4705   writer.returnFromIC();
4706 
4707   return AttachDecision::Attach;
4708 }
4709 
InstanceOfIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue lhs,HandleObject rhs)4710 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script,
4711                                              jsbytecode* pc, ICState state,
4712                                              HandleValue lhs, HandleObject rhs)
4713     : IRGenerator(cx, script, pc, CacheKind::InstanceOf, state),
4714       lhsVal_(lhs),
4715       rhsObj_(rhs) {}
4716 
tryAttachStub()4717 AttachDecision InstanceOfIRGenerator::tryAttachStub() {
4718   MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf);
4719   AutoAssertNoPendingException aanpe(cx_);
4720 
4721   // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
4722   // to handle.
4723   if (!rhsObj_->is<JSFunction>()) {
4724     trackAttached(IRGenerator::NotAttached);
4725     return AttachDecision::NoAction;
4726   }
4727 
4728   HandleFunction fun = rhsObj_.as<JSFunction>();
4729 
4730   if (fun->isBoundFunction()) {
4731     trackAttached(IRGenerator::NotAttached);
4732     return AttachDecision::NoAction;
4733   }
4734 
4735   // Look up the @@hasInstance property, and check that Function.__proto__ is
4736   // the property holder, and that no object further down the prototype chain
4737   // (including this function) has shadowed it; together with the fact that
4738   // Function.__proto__[@@hasInstance] is immutable, this ensures that the
4739   // hasInstance hook will not change without the need to guard on the actual
4740   // property value.
4741   PropertyResult hasInstanceProp;
4742   NativeObject* hasInstanceHolder = nullptr;
4743   jsid hasInstanceID = PropertyKey::Symbol(cx_->wellKnownSymbols().hasInstance);
4744   if (!LookupPropertyPure(cx_, fun, hasInstanceID, &hasInstanceHolder,
4745                           &hasInstanceProp) ||
4746       !hasInstanceProp.isNativeProperty()) {
4747     trackAttached(IRGenerator::NotAttached);
4748     return AttachDecision::NoAction;
4749   }
4750 
4751   JSObject& funProto = cx_->global()->getPrototype(JSProto_Function);
4752   if (hasInstanceHolder != &funProto) {
4753     trackAttached(IRGenerator::NotAttached);
4754     return AttachDecision::NoAction;
4755   }
4756 
4757   // If the above succeeded, then these should be true about @@hasInstance,
4758   // because the property on Function.__proto__ is an immutable data property:
4759   MOZ_ASSERT(hasInstanceProp.propertyInfo().isDataProperty());
4760   MOZ_ASSERT(!hasInstanceProp.propertyInfo().configurable());
4761   MOZ_ASSERT(!hasInstanceProp.propertyInfo().writable());
4762 
4763   MOZ_ASSERT(IsCacheableProtoChain(fun, hasInstanceHolder));
4764 
4765   // Ensure that the function's prototype slot is the same.
4766   Maybe<PropertyInfo> prop = fun->lookupPure(cx_->names().prototype);
4767   if (prop.isNothing() || !prop->isDataProperty()) {
4768     trackAttached(IRGenerator::NotAttached);
4769     return AttachDecision::NoAction;
4770   }
4771 
4772   uint32_t slot = prop->slot();
4773   MOZ_ASSERT(slot >= fun->numFixedSlots(), "Stub code relies on this");
4774   if (!fun->getSlot(slot).isObject()) {
4775     trackAttached(IRGenerator::NotAttached);
4776     return AttachDecision::NoAction;
4777   }
4778 
4779   JSObject* prototypeObject = &fun->getSlot(slot).toObject();
4780 
4781   // Abstract Objects
4782   ValOperandId lhs(writer.setInputOperandId(0));
4783   ValOperandId rhs(writer.setInputOperandId(1));
4784 
4785   ObjOperandId rhsId = writer.guardToObject(rhs);
4786   writer.guardShape(rhsId, fun->shape());
4787 
4788   // Ensure that the shapes up the prototype chain for the RHS remain the same
4789   // so that @@hasInstance is not shadowed by some intermediate prototype
4790   // object.
4791   if (hasInstanceHolder != fun) {
4792     GeneratePrototypeGuards(writer, fun, hasInstanceHolder, rhsId);
4793     ObjOperandId holderId = writer.loadObject(hasInstanceHolder);
4794     TestMatchingHolder(writer, hasInstanceHolder, holderId);
4795   }
4796 
4797   // Load prototypeObject into the cache -- consumed twice in the IC
4798   ObjOperandId protoId = writer.loadObject(prototypeObject);
4799   // Ensure that rhs[slot] == prototypeObject.
4800   writer.guardDynamicSlotIsSpecificObject(rhsId, protoId,
4801                                           slot - fun->numFixedSlots());
4802 
4803   // Needn't guard LHS is object, because the actual stub can handle that
4804   // and correctly return false.
4805   writer.loadInstanceOfObjectResult(lhs, protoId);
4806   writer.returnFromIC();
4807   trackAttached("InstanceOf");
4808   return AttachDecision::Attach;
4809 }
4810 
trackAttached(const char * name)4811 void InstanceOfIRGenerator::trackAttached(const char* name) {
4812 #ifdef JS_CACHEIR_SPEW
4813   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4814     sp.valueProperty("lhs", lhsVal_);
4815     sp.valueProperty("rhs", ObjectValue(*rhsObj_));
4816   }
4817 #else
4818   // Silence Clang -Wunused-private-field warning.
4819   (void)lhsVal_;
4820 #endif
4821 }
4822 
TypeOfIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue value)4823 TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script,
4824                                      jsbytecode* pc, ICState state,
4825                                      HandleValue value)
4826     : IRGenerator(cx, script, pc, CacheKind::TypeOf, state), val_(value) {}
4827 
trackAttached(const char * name)4828 void TypeOfIRGenerator::trackAttached(const char* name) {
4829 #ifdef JS_CACHEIR_SPEW
4830   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4831     sp.valueProperty("val", val_);
4832   }
4833 #endif
4834 }
4835 
tryAttachStub()4836 AttachDecision TypeOfIRGenerator::tryAttachStub() {
4837   MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
4838 
4839   AutoAssertNoPendingException aanpe(cx_);
4840 
4841   ValOperandId valId(writer.setInputOperandId(0));
4842 
4843   TRY_ATTACH(tryAttachPrimitive(valId));
4844   TRY_ATTACH(tryAttachObject(valId));
4845 
4846   MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
4847   return AttachDecision::NoAction;
4848 }
4849 
tryAttachPrimitive(ValOperandId valId)4850 AttachDecision TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId) {
4851   if (!val_.isPrimitive()) {
4852     return AttachDecision::NoAction;
4853   }
4854 
4855   // Note: we don't use GuardIsNumber for int32 values because it's less
4856   // efficient in Warp (unboxing to double instead of int32).
4857   if (val_.isDouble()) {
4858     writer.guardIsNumber(valId);
4859   } else {
4860     writer.guardNonDoubleType(valId, val_.type());
4861   }
4862 
4863   writer.loadConstantStringResult(
4864       TypeName(js::TypeOfValue(val_), cx_->names()));
4865   writer.returnFromIC();
4866   writer.setTypeData(TypeData(JSValueType(val_.type())));
4867   trackAttached("Primitive");
4868   return AttachDecision::Attach;
4869 }
4870 
tryAttachObject(ValOperandId valId)4871 AttachDecision TypeOfIRGenerator::tryAttachObject(ValOperandId valId) {
4872   if (!val_.isObject()) {
4873     return AttachDecision::NoAction;
4874   }
4875 
4876   ObjOperandId objId = writer.guardToObject(valId);
4877   writer.loadTypeOfObjectResult(objId);
4878   writer.returnFromIC();
4879   writer.setTypeData(TypeData(JSValueType(val_.type())));
4880   trackAttached("Object");
4881   return AttachDecision::Attach;
4882 }
4883 
GetIteratorIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue value)4884 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx,
4885                                                HandleScript script,
4886                                                jsbytecode* pc, ICState state,
4887                                                HandleValue value)
4888     : IRGenerator(cx, script, pc, CacheKind::GetIterator, state), val_(value) {}
4889 
tryAttachStub()4890 AttachDecision GetIteratorIRGenerator::tryAttachStub() {
4891   MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
4892 
4893   AutoAssertNoPendingException aanpe(cx_);
4894 
4895   ValOperandId valId(writer.setInputOperandId(0));
4896 
4897   if (mode_ == ICState::Mode::Megamorphic) {
4898     TRY_ATTACH(tryAttachMegamorphic(valId));
4899     return AttachDecision::NoAction;
4900   }
4901 
4902   TRY_ATTACH(tryAttachNativeIterator(valId));
4903   TRY_ATTACH(tryAttachNullOrUndefined(valId));
4904 
4905   trackAttached(IRGenerator::NotAttached);
4906   return AttachDecision::NoAction;
4907 }
4908 
tryAttachNativeIterator(ValOperandId valId)4909 AttachDecision GetIteratorIRGenerator::tryAttachNativeIterator(
4910     ValOperandId valId) {
4911   MOZ_ASSERT(JSOp(*pc_) == JSOp::Iter);
4912 
4913   if (!val_.isObject()) {
4914     return AttachDecision::NoAction;
4915   }
4916 
4917   RootedObject obj(cx_, &val_.toObject());
4918   ObjOperandId objId = writer.guardToObject(valId);
4919 
4920   PropertyIteratorObject* iterobj = LookupInIteratorCache(cx_, obj);
4921   if (!iterobj) {
4922     return AttachDecision::NoAction;
4923   }
4924   auto* nobj = &obj->as<NativeObject>();
4925 
4926   // Guard on the receiver's shape.
4927   TestMatchingNativeReceiver(writer, nobj, objId);
4928 
4929   // Ensure the receiver has no dense elements.
4930   writer.guardNoDenseElements(objId);
4931 
4932   // Do the same for the objects on the proto chain.
4933   GeneratePrototypeHoleGuards(writer, nobj, objId,
4934                               /* alwaysGuardFirstProto = */ false);
4935 
4936   ObjOperandId iterId = writer.guardAndGetIterator(
4937       objId, iterobj, &ObjectRealm::get(obj).enumerators);
4938   writer.loadObjectResult(iterId);
4939   writer.returnFromIC();
4940 
4941   trackAttached("NativeIterator");
4942   return AttachDecision::Attach;
4943 }
4944 
tryAttachMegamorphic(ValOperandId valId)4945 AttachDecision GetIteratorIRGenerator::tryAttachMegamorphic(
4946     ValOperandId valId) {
4947   MOZ_ASSERT(JSOp(*pc_) == JSOp::Iter);
4948 
4949   writer.valueToIteratorResult(valId);
4950   writer.returnFromIC();
4951 
4952   trackAttached("Megamorphic");
4953   return AttachDecision::Attach;
4954 }
4955 
tryAttachNullOrUndefined(ValOperandId valId)4956 AttachDecision GetIteratorIRGenerator::tryAttachNullOrUndefined(
4957     ValOperandId valId) {
4958   MOZ_ASSERT(JSOp(*pc_) == JSOp::Iter);
4959 
4960   // For null/undefined we can simply return the empty iterator singleton. This
4961   // works because this iterator is unlinked and immutable.
4962 
4963   if (!val_.isNullOrUndefined()) {
4964     return AttachDecision::NoAction;
4965   }
4966 
4967   PropertyIteratorObject* emptyIter = cx_->global()->maybeEmptyIterator();
4968   if (!emptyIter) {
4969     return AttachDecision::NoAction;
4970   }
4971 
4972   writer.guardIsNullOrUndefined(valId);
4973 
4974   ObjOperandId iterId = writer.loadObject(emptyIter);
4975   writer.loadObjectResult(iterId);
4976   writer.returnFromIC();
4977 
4978   trackAttached("NullOrUndefined");
4979   return AttachDecision::Attach;
4980 }
4981 
trackAttached(const char * name)4982 void GetIteratorIRGenerator::trackAttached(const char* name) {
4983 #ifdef JS_CACHEIR_SPEW
4984   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4985     sp.valueProperty("val", val_);
4986   }
4987 #endif
4988 }
4989 
OptimizeSpreadCallIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue value)4990 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
4991     JSContext* cx, HandleScript script, jsbytecode* pc, ICState state,
4992     HandleValue value)
4993     : IRGenerator(cx, script, pc, CacheKind::OptimizeSpreadCall, state),
4994       val_(value) {}
4995 
tryAttachStub()4996 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachStub() {
4997   MOZ_ASSERT(cacheKind_ == CacheKind::OptimizeSpreadCall);
4998 
4999   AutoAssertNoPendingException aanpe(cx_);
5000 
5001   TRY_ATTACH(tryAttachArray());
5002   TRY_ATTACH(tryAttachArguments());
5003   TRY_ATTACH(tryAttachNotOptimizable());
5004 
5005   trackAttached(IRGenerator::NotAttached);
5006   return AttachDecision::NoAction;
5007 }
5008 
IsArrayPrototypeOptimizable(JSContext * cx,ArrayObject * arr,NativeObject ** arrProto,uint32_t * slot,JSFunction ** iterFun)5009 static bool IsArrayPrototypeOptimizable(JSContext* cx, ArrayObject* arr,
5010                                         NativeObject** arrProto, uint32_t* slot,
5011                                         JSFunction** iterFun) {
5012   // Prototype must be Array.prototype.
5013   auto* proto = cx->global()->maybeGetArrayPrototype();
5014   if (!proto || arr->staticPrototype() != proto) {
5015     return false;
5016   }
5017   *arrProto = proto;
5018 
5019   // The object must not have an own @@iterator property.
5020   PropertyKey iteratorKey =
5021       PropertyKey::Symbol(cx->wellKnownSymbols().iterator);
5022   if (arr->lookupPure(iteratorKey)) {
5023     return false;
5024   }
5025 
5026   // Ensure that Array.prototype's @@iterator slot is unchanged.
5027   Maybe<PropertyInfo> prop = proto->lookupPure(iteratorKey);
5028   if (prop.isNothing() || !prop->isDataProperty()) {
5029     return false;
5030   }
5031 
5032   *slot = prop->slot();
5033   MOZ_ASSERT(proto->numFixedSlots() == 0, "Stub code relies on this");
5034 
5035   const Value& iterVal = proto->getSlot(*slot);
5036   if (!iterVal.isObject() || !iterVal.toObject().is<JSFunction>()) {
5037     return false;
5038   }
5039 
5040   *iterFun = &iterVal.toObject().as<JSFunction>();
5041   return IsSelfHostedFunctionWithName(*iterFun, cx->names().ArrayValues);
5042 }
5043 
IsArrayIteratorPrototypeOptimizable(JSContext * cx,NativeObject ** arrIterProto,uint32_t * slot,JSFunction ** nextFun)5044 static bool IsArrayIteratorPrototypeOptimizable(JSContext* cx,
5045                                                 NativeObject** arrIterProto,
5046                                                 uint32_t* slot,
5047                                                 JSFunction** nextFun) {
5048   auto* proto = cx->global()->maybeGetArrayIteratorPrototype();
5049   if (!proto) {
5050     return false;
5051   }
5052   *arrIterProto = proto;
5053 
5054   // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
5055   Maybe<PropertyInfo> prop = proto->lookupPure(cx->names().next);
5056   if (prop.isNothing() || !prop->isDataProperty()) {
5057     return false;
5058   }
5059 
5060   *slot = prop->slot();
5061   MOZ_ASSERT(proto->numFixedSlots() == 0, "Stub code relies on this");
5062 
5063   const Value& nextVal = proto->getSlot(*slot);
5064   if (!nextVal.isObject() || !nextVal.toObject().is<JSFunction>()) {
5065     return false;
5066   }
5067 
5068   *nextFun = &nextVal.toObject().as<JSFunction>();
5069   return IsSelfHostedFunctionWithName(*nextFun, cx->names().ArrayIteratorNext);
5070 }
5071 
tryAttachArray()5072 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArray() {
5073   if (!isFirstStub_) {
5074     return AttachDecision::NoAction;
5075   }
5076 
5077   // The value must be a packed array.
5078   if (!val_.isObject()) {
5079     return AttachDecision::NoAction;
5080   }
5081   JSObject* obj = &val_.toObject();
5082   if (!IsPackedArray(obj)) {
5083     return AttachDecision::NoAction;
5084   }
5085 
5086   // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
5087   // be modified.
5088   NativeObject* arrProto;
5089   uint32_t arrProtoIterSlot;
5090   JSFunction* iterFun;
5091   if (!IsArrayPrototypeOptimizable(cx_, &obj->as<ArrayObject>(), &arrProto,
5092                                    &arrProtoIterSlot, &iterFun)) {
5093     return AttachDecision::NoAction;
5094   }
5095 
5096   // %ArrayIteratorPrototype%.next must not be modified.
5097   NativeObject* arrayIteratorProto;
5098   uint32_t iterNextSlot;
5099   JSFunction* nextFun;
5100   if (!IsArrayIteratorPrototypeOptimizable(cx_, &arrayIteratorProto,
5101                                            &iterNextSlot, &nextFun)) {
5102     return AttachDecision::NoAction;
5103   }
5104 
5105   ValOperandId valId(writer.setInputOperandId(0));
5106   ObjOperandId objId = writer.guardToObject(valId);
5107 
5108   // Guard the object is a packed array with Array.prototype as proto.
5109   MOZ_ASSERT(obj->is<ArrayObject>());
5110   writer.guardShape(objId, obj->shape());
5111   writer.guardArrayIsPacked(objId);
5112 
5113   // Guard on Array.prototype[@@iterator].
5114   ObjOperandId arrProtoId = writer.loadObject(arrProto);
5115   ObjOperandId iterId = writer.loadObject(iterFun);
5116   writer.guardShape(arrProtoId, arrProto->shape());
5117   writer.guardDynamicSlotIsSpecificObject(arrProtoId, iterId, arrProtoIterSlot);
5118 
5119   // Guard on %ArrayIteratorPrototype%.next.
5120   ObjOperandId iterProtoId = writer.loadObject(arrayIteratorProto);
5121   ObjOperandId nextId = writer.loadObject(nextFun);
5122   writer.guardShape(iterProtoId, arrayIteratorProto->shape());
5123   writer.guardDynamicSlotIsSpecificObject(iterProtoId, nextId, iterNextSlot);
5124 
5125   writer.loadObjectResult(objId);
5126   writer.returnFromIC();
5127 
5128   trackAttached("Array");
5129   return AttachDecision::Attach;
5130 }
5131 
tryAttachArguments()5132 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachArguments() {
5133   // The value must be an arguments object.
5134   if (!val_.isObject()) {
5135     return AttachDecision::NoAction;
5136   }
5137   RootedObject obj(cx_, &val_.toObject());
5138   if (!obj->is<ArgumentsObject>()) {
5139     return AttachDecision::NoAction;
5140   }
5141   auto args = obj.as<ArgumentsObject>();
5142 
5143   // Ensure neither elements, nor the length, nor the iterator has been
5144   // overridden. Also ensure no args are forwarded to allow reading them
5145   // directly from the frame.
5146   if (args->hasOverriddenElement() || args->hasOverriddenLength() ||
5147       args->hasOverriddenIterator() || args->anyArgIsForwarded()) {
5148     return AttachDecision::NoAction;
5149   }
5150 
5151   RootedShape shape(cx_, GlobalObject::getArrayShapeWithDefaultProto(cx_));
5152   if (!shape) {
5153     cx_->recoverFromOutOfMemory();
5154     return AttachDecision::NoAction;
5155   }
5156 
5157   NativeObject* arrayIteratorProto;
5158   uint32_t slot;
5159   JSFunction* nextFun;
5160   if (!IsArrayIteratorPrototypeOptimizable(cx_, &arrayIteratorProto, &slot,
5161                                            &nextFun)) {
5162     return AttachDecision::NoAction;
5163   }
5164 
5165   ValOperandId valId(writer.setInputOperandId(0));
5166   ObjOperandId objId = writer.guardToObject(valId);
5167 
5168   if (args->is<MappedArgumentsObject>()) {
5169     writer.guardClass(objId, GuardClassKind::MappedArguments);
5170   } else {
5171     MOZ_ASSERT(args->is<UnmappedArgumentsObject>());
5172     writer.guardClass(objId, GuardClassKind::UnmappedArguments);
5173   }
5174   uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
5175                   ArgumentsObject::LENGTH_OVERRIDDEN_BIT |
5176                   ArgumentsObject::ITERATOR_OVERRIDDEN_BIT |
5177                   ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
5178   writer.guardArgumentsObjectFlags(objId, flags);
5179 
5180   ObjOperandId protoId = writer.loadObject(arrayIteratorProto);
5181   ObjOperandId nextId = writer.loadObject(nextFun);
5182 
5183   writer.guardShape(protoId, arrayIteratorProto->shape());
5184 
5185   // Ensure that proto[slot] == nextFun.
5186   writer.guardDynamicSlotIsSpecificObject(protoId, nextId, slot);
5187 
5188   writer.arrayFromArgumentsObjectResult(objId, shape);
5189   writer.returnFromIC();
5190 
5191   trackAttached("Arguments");
5192   return AttachDecision::Attach;
5193 }
5194 
tryAttachNotOptimizable()5195 AttachDecision OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
5196   ValOperandId valId(writer.setInputOperandId(0));
5197 
5198   writer.loadUndefinedResult();
5199   writer.returnFromIC();
5200 
5201   trackAttached("NotOptimizable");
5202   return AttachDecision::Attach;
5203 }
5204 
trackAttached(const char * name)5205 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name) {
5206 #ifdef JS_CACHEIR_SPEW
5207   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
5208     sp.valueProperty("val", val_);
5209   }
5210 #endif
5211 }
5212 
CallIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,JSOp op,ICState state,uint32_t argc,HandleValue callee,HandleValue thisval,HandleValue newTarget,HandleValueArray args)5213 CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script,
5214                                  jsbytecode* pc, JSOp op, ICState state,
5215                                  uint32_t argc, HandleValue callee,
5216                                  HandleValue thisval, HandleValue newTarget,
5217                                  HandleValueArray args)
5218     : IRGenerator(cx, script, pc, CacheKind::Call, state),
5219       op_(op),
5220       argc_(argc),
5221       callee_(callee),
5222       thisval_(thisval),
5223       newTarget_(newTarget),
5224       args_(args) {}
5225 
emitNativeCalleeGuard(JSFunction * callee)5226 void CallIRGenerator::emitNativeCalleeGuard(JSFunction* callee) {
5227   // Note: we rely on GuardSpecificFunction to also guard against the same
5228   // native from a different realm.
5229   MOZ_ASSERT(callee->isNativeWithoutJitEntry());
5230 
5231   bool isConstructing = IsConstructPC(pc_);
5232   CallFlags flags(isConstructing, IsSpreadPC(pc_));
5233 
5234   ValOperandId calleeValId =
5235       writer.loadArgumentFixedSlot(ArgumentKind::Callee, argc_, flags);
5236   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
5237   writer.guardSpecificFunction(calleeObjId, callee);
5238 
5239   // If we're constructing we also need to guard newTarget == callee.
5240   if (isConstructing) {
5241     MOZ_ASSERT(&newTarget_.toObject() == callee);
5242     ValOperandId newTargetValId =
5243         writer.loadArgumentFixedSlot(ArgumentKind::NewTarget, argc_, flags);
5244     ObjOperandId newTargetObjId = writer.guardToObject(newTargetValId);
5245     writer.guardSpecificFunction(newTargetObjId, callee);
5246   }
5247 }
5248 
emitCalleeGuard(ObjOperandId calleeId,JSFunction * callee)5249 void CallIRGenerator::emitCalleeGuard(ObjOperandId calleeId,
5250                                       JSFunction* callee) {
5251   // Guarding on the callee JSFunction* is most efficient, but doesn't work well
5252   // for lambda clones (multiple functions with the same BaseScript). We guard
5253   // on the function's BaseScript if the callee is scripted and this isn't the
5254   // first IC stub.
5255   if (isFirstStub_ || !callee->hasBaseScript() ||
5256       callee->isSelfHostedBuiltin()) {
5257     writer.guardSpecificFunction(calleeId, callee);
5258   } else {
5259     writer.guardClass(calleeId, GuardClassKind::JSFunction);
5260     writer.guardFunctionScript(calleeId, callee->baseScript());
5261   }
5262 }
5263 
tryAttachArrayPush(HandleFunction callee)5264 AttachDecision CallIRGenerator::tryAttachArrayPush(HandleFunction callee) {
5265   // Only optimize on obj.push(val);
5266   if (argc_ != 1 || !thisval_.isObject()) {
5267     return AttachDecision::NoAction;
5268   }
5269 
5270   // Where |obj| is a native array.
5271   JSObject* thisobj = &thisval_.toObject();
5272   if (!thisobj->is<ArrayObject>()) {
5273     return AttachDecision::NoAction;
5274   }
5275 
5276   auto* thisarray = &thisobj->as<ArrayObject>();
5277 
5278   // Check for other indexed properties or class hooks.
5279   if (!CanAttachAddElement(thisarray, /* isInit = */ false)) {
5280     return AttachDecision::NoAction;
5281   }
5282 
5283   // Can't add new elements to arrays with non-writable length.
5284   if (!thisarray->lengthIsWritable()) {
5285     return AttachDecision::NoAction;
5286   }
5287 
5288   // Check that array is extensible.
5289   if (!thisarray->isExtensible()) {
5290     return AttachDecision::NoAction;
5291   }
5292 
5293   // Check that the array is completely initialized (no holes).
5294   if (thisarray->getDenseInitializedLength() != thisarray->length()) {
5295     return AttachDecision::NoAction;
5296   }
5297 
5298   MOZ_ASSERT(!thisarray->denseElementsAreFrozen(),
5299              "Extensible arrays should not have frozen elements");
5300 
5301   // After this point, we can generate code fine.
5302 
5303   // Initialize the input operand.
5304   Int32OperandId argcId(writer.setInputOperandId(0));
5305 
5306   // Guard callee is the 'push' native function.
5307   emitNativeCalleeGuard(callee);
5308 
5309   // Guard this is an array object.
5310   ValOperandId thisValId =
5311       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5312   ObjOperandId thisObjId = writer.guardToObject(thisValId);
5313 
5314   // Guard that the shape matches.
5315   TestMatchingNativeReceiver(writer, thisarray, thisObjId);
5316 
5317   // Guard proto chain shapes.
5318   ShapeGuardProtoChain(writer, thisarray, thisObjId);
5319 
5320   // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
5321   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5322   writer.arrayPush(thisObjId, argId);
5323 
5324   writer.returnFromIC();
5325 
5326   trackAttached("ArrayPush");
5327   return AttachDecision::Attach;
5328 }
5329 
tryAttachArrayPopShift(HandleFunction callee,InlinableNative native)5330 AttachDecision CallIRGenerator::tryAttachArrayPopShift(HandleFunction callee,
5331                                                        InlinableNative native) {
5332   // Expecting no arguments.
5333   if (argc_ != 0) {
5334     return AttachDecision::NoAction;
5335   }
5336 
5337   // Only optimize if |this| is a packed array.
5338   if (!thisval_.isObject() || !IsPackedArray(&thisval_.toObject())) {
5339     return AttachDecision::NoAction;
5340   }
5341 
5342   // Other conditions:
5343   //
5344   // * The array length needs to be writable because we're changing it.
5345   // * The array must be extensible. Non-extensible arrays require preserving
5346   //   the |initializedLength == capacity| invariant on ObjectElements.
5347   //   See NativeObject::shrinkCapacityToInitializedLength.
5348   //   This also ensures the elements aren't sealed/frozen.
5349   // * There must not be a for-in iterator for the elements because the IC stub
5350   //   does not suppress deleted properties.
5351   ArrayObject* arr = &thisval_.toObject().as<ArrayObject>();
5352   if (!arr->lengthIsWritable() || !arr->isExtensible() ||
5353       arr->denseElementsHaveMaybeInIterationFlag()) {
5354     return AttachDecision::NoAction;
5355   }
5356 
5357   // Initialize the input operand.
5358   Int32OperandId argcId(writer.setInputOperandId(0));
5359 
5360   // Guard callee is the 'pop' or 'shift' native function.
5361   emitNativeCalleeGuard(callee);
5362 
5363   ValOperandId thisValId =
5364       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5365   ObjOperandId objId = writer.guardToObject(thisValId);
5366   writer.guardClass(objId, GuardClassKind::Array);
5367 
5368   if (native == InlinableNative::ArrayPop) {
5369     writer.packedArrayPopResult(objId);
5370   } else {
5371     MOZ_ASSERT(native == InlinableNative::ArrayShift);
5372     writer.packedArrayShiftResult(objId);
5373   }
5374 
5375   writer.returnFromIC();
5376 
5377   trackAttached("ArrayPopShift");
5378   return AttachDecision::Attach;
5379 }
5380 
tryAttachArrayJoin(HandleFunction callee)5381 AttachDecision CallIRGenerator::tryAttachArrayJoin(HandleFunction callee) {
5382   // Only handle argc <= 1.
5383   if (argc_ > 1) {
5384     return AttachDecision::NoAction;
5385   }
5386 
5387   // Only optimize if |this| is an array.
5388   if (!thisval_.isObject() || !thisval_.toObject().is<ArrayObject>()) {
5389     return AttachDecision::NoAction;
5390   }
5391 
5392   // The separator argument must be a string, if present.
5393   if (argc_ > 0 && !args_[0].isString()) {
5394     return AttachDecision::NoAction;
5395   }
5396 
5397   // IC stub code can handle non-packed array.
5398 
5399   // Initialize the input operand.
5400   Int32OperandId argcId(writer.setInputOperandId(0));
5401 
5402   // Guard callee is the 'join' native function.
5403   emitNativeCalleeGuard(callee);
5404 
5405   // Guard this is an array object.
5406   ValOperandId thisValId =
5407       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5408   ObjOperandId thisObjId = writer.guardToObject(thisValId);
5409   writer.guardClass(thisObjId, GuardClassKind::Array);
5410 
5411   StringOperandId sepId;
5412   if (argc_ == 1) {
5413     // If argcount is 1, guard that the argument is a string.
5414     ValOperandId argValId =
5415         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5416     sepId = writer.guardToString(argValId);
5417   } else {
5418     sepId = writer.loadConstantString(cx_->names().comma);
5419   }
5420 
5421   // Do the join.
5422   writer.arrayJoinResult(thisObjId, sepId);
5423 
5424   writer.returnFromIC();
5425 
5426   trackAttached("ArrayJoin");
5427   return AttachDecision::Attach;
5428 }
5429 
tryAttachArraySlice(HandleFunction callee)5430 AttachDecision CallIRGenerator::tryAttachArraySlice(HandleFunction callee) {
5431   // Only handle argc <= 2.
5432   if (argc_ > 2) {
5433     return AttachDecision::NoAction;
5434   }
5435 
5436   // Only optimize if |this| is a packed array.
5437   if (!thisval_.isObject() || !IsPackedArray(&thisval_.toObject())) {
5438     return AttachDecision::NoAction;
5439   }
5440 
5441   // Arguments for the sliced region must be integers.
5442   if (argc_ > 0 && !args_[0].isInt32()) {
5443     return AttachDecision::NoAction;
5444   }
5445   if (argc_ > 1 && !args_[1].isInt32()) {
5446     return AttachDecision::NoAction;
5447   }
5448 
5449   RootedArrayObject arr(cx_, &thisval_.toObject().as<ArrayObject>());
5450 
5451   JSObject* templateObj = NewDenseFullyAllocatedArray(cx_, 0, TenuredObject);
5452   if (!templateObj) {
5453     cx_->recoverFromOutOfMemory();
5454     return AttachDecision::NoAction;
5455   }
5456 
5457   // Initialize the input operand.
5458   Int32OperandId argcId(writer.setInputOperandId(0));
5459 
5460   // Guard callee is the 'slice' native function.
5461   emitNativeCalleeGuard(callee);
5462 
5463   ValOperandId thisValId =
5464       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5465   ObjOperandId objId = writer.guardToObject(thisValId);
5466   writer.guardClass(objId, GuardClassKind::Array);
5467 
5468   Int32OperandId int32BeginId;
5469   if (argc_ > 0) {
5470     ValOperandId beginId =
5471         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5472     int32BeginId = writer.guardToInt32(beginId);
5473   } else {
5474     int32BeginId = writer.loadInt32Constant(0);
5475   }
5476 
5477   Int32OperandId int32EndId;
5478   if (argc_ > 1) {
5479     ValOperandId endId =
5480         writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
5481     int32EndId = writer.guardToInt32(endId);
5482   } else {
5483     int32EndId = writer.loadInt32ArrayLength(objId);
5484   }
5485 
5486   writer.packedArraySliceResult(templateObj, objId, int32BeginId, int32EndId);
5487   writer.returnFromIC();
5488 
5489   trackAttached("ArraySlice");
5490   return AttachDecision::Attach;
5491 }
5492 
tryAttachArrayIsArray(HandleFunction callee)5493 AttachDecision CallIRGenerator::tryAttachArrayIsArray(HandleFunction callee) {
5494   // Need a single argument.
5495   if (argc_ != 1) {
5496     return AttachDecision::NoAction;
5497   }
5498 
5499   // Initialize the input operand.
5500   Int32OperandId argcId(writer.setInputOperandId(0));
5501 
5502   // Guard callee is the 'isArray' native function.
5503   emitNativeCalleeGuard(callee);
5504 
5505   // Check if the argument is an Array and return result.
5506   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5507   writer.isArrayResult(argId);
5508   writer.returnFromIC();
5509 
5510   trackAttached("ArrayIsArray");
5511   return AttachDecision::Attach;
5512 }
5513 
tryAttachDataViewGet(HandleFunction callee,Scalar::Type type)5514 AttachDecision CallIRGenerator::tryAttachDataViewGet(HandleFunction callee,
5515                                                      Scalar::Type type) {
5516   // Ensure |this| is a DataViewObject.
5517   if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
5518     return AttachDecision::NoAction;
5519   }
5520 
5521   // Expected arguments: offset (number), optional littleEndian (boolean).
5522   if (argc_ < 1 || argc_ > 2) {
5523     return AttachDecision::NoAction;
5524   }
5525   int64_t offsetInt64;
5526   if (!ValueIsInt64Index(args_[0], &offsetInt64)) {
5527     return AttachDecision::NoAction;
5528   }
5529   if (argc_ > 1 && !args_[1].isBoolean()) {
5530     return AttachDecision::NoAction;
5531   }
5532 
5533   DataViewObject* dv = &thisval_.toObject().as<DataViewObject>();
5534 
5535   // Bounds check the offset.
5536   if (offsetInt64 < 0 ||
5537       !dv->offsetIsInBounds(Scalar::byteSize(type), offsetInt64)) {
5538     return AttachDecision::NoAction;
5539   }
5540 
5541   // For getUint32 we let the stub return an Int32 if we have not seen a
5542   // double, to allow better codegen in Warp while avoiding bailout loops.
5543   bool forceDoubleForUint32 = false;
5544   if (type == Scalar::Uint32) {
5545     bool isLittleEndian = argc_ > 1 && args_[1].toBoolean();
5546     uint32_t res = dv->read<uint32_t>(offsetInt64, isLittleEndian);
5547     forceDoubleForUint32 = res >= INT32_MAX;
5548   }
5549 
5550   // Initialize the input operand.
5551   Int32OperandId argcId(writer.setInputOperandId(0));
5552 
5553   // Guard callee is this DataView native function.
5554   emitNativeCalleeGuard(callee);
5555 
5556   // Guard |this| is a DataViewObject.
5557   ValOperandId thisValId =
5558       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5559   ObjOperandId objId = writer.guardToObject(thisValId);
5560   writer.guardClass(objId, GuardClassKind::DataView);
5561 
5562   // Convert offset to intPtr.
5563   ValOperandId offsetId =
5564       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5565   IntPtrOperandId intPtrOffsetId =
5566       guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false);
5567 
5568   BooleanOperandId boolLittleEndianId;
5569   if (argc_ > 1) {
5570     ValOperandId littleEndianId =
5571         writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
5572     boolLittleEndianId = writer.guardToBoolean(littleEndianId);
5573   } else {
5574     boolLittleEndianId = writer.loadBooleanConstant(false);
5575   }
5576 
5577   writer.loadDataViewValueResult(objId, intPtrOffsetId, boolLittleEndianId,
5578                                  type, forceDoubleForUint32);
5579   writer.returnFromIC();
5580 
5581   trackAttached("DataViewGet");
5582   return AttachDecision::Attach;
5583 }
5584 
tryAttachDataViewSet(HandleFunction callee,Scalar::Type type)5585 AttachDecision CallIRGenerator::tryAttachDataViewSet(HandleFunction callee,
5586                                                      Scalar::Type type) {
5587   // Ensure |this| is a DataViewObject.
5588   if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) {
5589     return AttachDecision::NoAction;
5590   }
5591 
5592   // Expected arguments: offset (number), value, optional littleEndian (boolean)
5593   if (argc_ < 2 || argc_ > 3) {
5594     return AttachDecision::NoAction;
5595   }
5596   int64_t offsetInt64;
5597   if (!ValueIsInt64Index(args_[0], &offsetInt64)) {
5598     return AttachDecision::NoAction;
5599   }
5600   if (!ValueIsNumeric(type, args_[1])) {
5601     return AttachDecision::NoAction;
5602   }
5603   if (argc_ > 2 && !args_[2].isBoolean()) {
5604     return AttachDecision::NoAction;
5605   }
5606 
5607   DataViewObject* dv = &thisval_.toObject().as<DataViewObject>();
5608 
5609   // Bounds check the offset.
5610   if (offsetInt64 < 0 ||
5611       !dv->offsetIsInBounds(Scalar::byteSize(type), offsetInt64)) {
5612     return AttachDecision::NoAction;
5613   }
5614 
5615   // Initialize the input operand.
5616   Int32OperandId argcId(writer.setInputOperandId(0));
5617 
5618   // Guard callee is this DataView native function.
5619   emitNativeCalleeGuard(callee);
5620 
5621   // Guard |this| is a DataViewObject.
5622   ValOperandId thisValId =
5623       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
5624   ObjOperandId objId = writer.guardToObject(thisValId);
5625   writer.guardClass(objId, GuardClassKind::DataView);
5626 
5627   // Convert offset to intPtr.
5628   ValOperandId offsetId =
5629       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5630   IntPtrOperandId intPtrOffsetId =
5631       guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false);
5632 
5633   // Convert value to number or BigInt.
5634   ValOperandId valueId =
5635       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
5636   OperandId numericValueId = emitNumericGuard(valueId, type);
5637 
5638   BooleanOperandId boolLittleEndianId;
5639   if (argc_ > 2) {
5640     ValOperandId littleEndianId =
5641         writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
5642     boolLittleEndianId = writer.guardToBoolean(littleEndianId);
5643   } else {
5644     boolLittleEndianId = writer.loadBooleanConstant(false);
5645   }
5646 
5647   writer.storeDataViewValueResult(objId, intPtrOffsetId, numericValueId,
5648                                   boolLittleEndianId, type);
5649   writer.returnFromIC();
5650 
5651   trackAttached("DataViewSet");
5652   return AttachDecision::Attach;
5653 }
5654 
tryAttachUnsafeGetReservedSlot(HandleFunction callee,InlinableNative native)5655 AttachDecision CallIRGenerator::tryAttachUnsafeGetReservedSlot(
5656     HandleFunction callee, InlinableNative native) {
5657   // Self-hosted code calls this with (object, int32) arguments.
5658   MOZ_ASSERT(argc_ == 2);
5659   MOZ_ASSERT(args_[0].isObject());
5660   MOZ_ASSERT(args_[1].isInt32());
5661   MOZ_ASSERT(args_[1].toInt32() >= 0);
5662 
5663   uint32_t slot = uint32_t(args_[1].toInt32());
5664   if (slot >= NativeObject::MAX_FIXED_SLOTS) {
5665     return AttachDecision::NoAction;
5666   }
5667   size_t offset = NativeObject::getFixedSlotOffset(slot);
5668 
5669   // Initialize the input operand.
5670   Int32OperandId argcId(writer.setInputOperandId(0));
5671 
5672   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5673 
5674   // Guard that the first argument is an object.
5675   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5676   ObjOperandId objId = writer.guardToObject(arg0Id);
5677 
5678   // BytecodeEmitter::checkSelfHostedUnsafeGetReservedSlot ensures that the
5679   // slot argument is constant. (At least for direct calls)
5680 
5681   switch (native) {
5682     case InlinableNative::IntrinsicUnsafeGetReservedSlot:
5683       writer.loadFixedSlotResult(objId, offset);
5684       break;
5685     case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
5686       writer.loadFixedSlotTypedResult(objId, offset, ValueType::Object);
5687       break;
5688     case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
5689       writer.loadFixedSlotTypedResult(objId, offset, ValueType::Int32);
5690       break;
5691     case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
5692       writer.loadFixedSlotTypedResult(objId, offset, ValueType::String);
5693       break;
5694     case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
5695       writer.loadFixedSlotTypedResult(objId, offset, ValueType::Boolean);
5696       break;
5697     default:
5698       MOZ_CRASH("unexpected native");
5699   }
5700 
5701   writer.returnFromIC();
5702 
5703   trackAttached("UnsafeGetReservedSlot");
5704   return AttachDecision::Attach;
5705 }
5706 
tryAttachUnsafeSetReservedSlot(HandleFunction callee)5707 AttachDecision CallIRGenerator::tryAttachUnsafeSetReservedSlot(
5708     HandleFunction callee) {
5709   // Self-hosted code calls this with (object, int32, value) arguments.
5710   MOZ_ASSERT(argc_ == 3);
5711   MOZ_ASSERT(args_[0].isObject());
5712   MOZ_ASSERT(args_[1].isInt32());
5713   MOZ_ASSERT(args_[1].toInt32() >= 0);
5714 
5715   uint32_t slot = uint32_t(args_[1].toInt32());
5716   if (slot >= NativeObject::MAX_FIXED_SLOTS) {
5717     return AttachDecision::NoAction;
5718   }
5719   size_t offset = NativeObject::getFixedSlotOffset(slot);
5720 
5721   // Initialize the input operand.
5722   Int32OperandId argcId(writer.setInputOperandId(0));
5723 
5724   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5725 
5726   // Guard that the first argument is an object.
5727   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5728   ObjOperandId objId = writer.guardToObject(arg0Id);
5729 
5730   // BytecodeEmitter::checkSelfHostedUnsafeSetReservedSlot ensures that the
5731   // slot argument is constant. (At least for direct calls)
5732 
5733   // Get the value to set.
5734   ValOperandId valId = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
5735 
5736   // Set the fixed slot and return undefined.
5737   writer.storeFixedSlotUndefinedResult(objId, offset, valId);
5738 
5739   // This stub always returns undefined.
5740   writer.returnFromIC();
5741 
5742   trackAttached("UnsafeSetReservedSlot");
5743   return AttachDecision::Attach;
5744 }
5745 
tryAttachIsSuspendedGenerator(HandleFunction callee)5746 AttachDecision CallIRGenerator::tryAttachIsSuspendedGenerator(
5747     HandleFunction callee) {
5748   // The IsSuspendedGenerator intrinsic is only called in
5749   // self-hosted code, so it's safe to assume we have a single
5750   // argument and the callee is our intrinsic.
5751 
5752   MOZ_ASSERT(argc_ == 1);
5753 
5754   Int32OperandId argcId(writer.setInputOperandId(0));
5755 
5756   // Stack layout here is (bottom to top):
5757   //  2: Callee
5758   //  1: ThisValue
5759   //  0: Arg <-- Top of stack.
5760   // We only care about the argument.
5761   ValOperandId valId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5762 
5763   // Check whether the argument is a suspended generator.
5764   // We don't need guards, because IsSuspendedGenerator returns
5765   // false for values that are not generator objects.
5766   writer.callIsSuspendedGeneratorResult(valId);
5767   writer.returnFromIC();
5768 
5769   trackAttached("IsSuspendedGenerator");
5770   return AttachDecision::Attach;
5771 }
5772 
tryAttachToObject(HandleFunction callee,InlinableNative native)5773 AttachDecision CallIRGenerator::tryAttachToObject(HandleFunction callee,
5774                                                   InlinableNative native) {
5775   // Self-hosted code calls this with a single argument.
5776   MOZ_ASSERT_IF(native == InlinableNative::IntrinsicToObject, argc_ == 1);
5777 
5778   // Need a single object argument.
5779   // TODO(Warp): Support all or more conversions to object.
5780   // Note: ToObject and Object differ in their behavior for undefined/null.
5781   if (argc_ != 1 || !args_[0].isObject()) {
5782     return AttachDecision::NoAction;
5783   }
5784 
5785   // Initialize the input operand.
5786   Int32OperandId argcId(writer.setInputOperandId(0));
5787 
5788   // Guard callee is the 'Object' function.
5789   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5790   if (native == InlinableNative::Object) {
5791     emitNativeCalleeGuard(callee);
5792   }
5793 
5794   // Guard that the argument is an object.
5795   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5796   ObjOperandId objId = writer.guardToObject(argId);
5797 
5798   // Return the object.
5799   writer.loadObjectResult(objId);
5800   writer.returnFromIC();
5801 
5802   if (native == InlinableNative::IntrinsicToObject) {
5803     trackAttached("ToObject");
5804   } else {
5805     MOZ_ASSERT(native == InlinableNative::Object);
5806     trackAttached("Object");
5807   }
5808   return AttachDecision::Attach;
5809 }
5810 
tryAttachToInteger(HandleFunction callee)5811 AttachDecision CallIRGenerator::tryAttachToInteger(HandleFunction callee) {
5812   // Self-hosted code calls this with a single argument.
5813   MOZ_ASSERT(argc_ == 1);
5814 
5815   // Need a single int32 argument.
5816   // TODO(Warp): Support all or more conversions to integer.
5817   // Make sure to update this code correctly if we ever start
5818   // returning non-int32 integers.
5819   if (!args_[0].isInt32()) {
5820     return AttachDecision::NoAction;
5821   }
5822 
5823   // Initialize the input operand.
5824   Int32OperandId argcId(writer.setInputOperandId(0));
5825 
5826   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5827 
5828   // Guard that the argument is an int32.
5829   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5830   Int32OperandId int32Id = writer.guardToInt32(argId);
5831 
5832   // Return the int32.
5833   writer.loadInt32Result(int32Id);
5834   writer.returnFromIC();
5835 
5836   trackAttached("ToInteger");
5837   return AttachDecision::Attach;
5838 }
5839 
tryAttachToLength(HandleFunction callee)5840 AttachDecision CallIRGenerator::tryAttachToLength(HandleFunction callee) {
5841   // Self-hosted code calls this with a single argument.
5842   MOZ_ASSERT(argc_ == 1);
5843 
5844   // Need a single int32 argument.
5845   if (!args_[0].isInt32()) {
5846     return AttachDecision::NoAction;
5847   }
5848 
5849   // Initialize the input operand.
5850   Int32OperandId argcId(writer.setInputOperandId(0));
5851 
5852   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5853 
5854   // ToLength(int32) is equivalent to max(int32, 0).
5855   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5856   Int32OperandId int32ArgId = writer.guardToInt32(argId);
5857   Int32OperandId zeroId = writer.loadInt32Constant(0);
5858   bool isMax = true;
5859   Int32OperandId maxId = writer.int32MinMax(isMax, int32ArgId, zeroId);
5860   writer.loadInt32Result(maxId);
5861   writer.returnFromIC();
5862 
5863   trackAttached("ToLength");
5864   return AttachDecision::Attach;
5865 }
5866 
tryAttachIsObject(HandleFunction callee)5867 AttachDecision CallIRGenerator::tryAttachIsObject(HandleFunction callee) {
5868   // Self-hosted code calls this with a single argument.
5869   MOZ_ASSERT(argc_ == 1);
5870 
5871   // Initialize the input operand.
5872   Int32OperandId argcId(writer.setInputOperandId(0));
5873 
5874   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5875 
5876   // Type check the argument and return result.
5877   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5878   writer.isObjectResult(argId);
5879   writer.returnFromIC();
5880 
5881   trackAttached("IsObject");
5882   return AttachDecision::Attach;
5883 }
5884 
tryAttachIsPackedArray(HandleFunction callee)5885 AttachDecision CallIRGenerator::tryAttachIsPackedArray(HandleFunction callee) {
5886   // Self-hosted code calls this with a single object argument.
5887   MOZ_ASSERT(argc_ == 1);
5888   MOZ_ASSERT(args_[0].isObject());
5889 
5890   // Initialize the input operand.
5891   Int32OperandId argcId(writer.setInputOperandId(0));
5892 
5893   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5894 
5895   // Check if the argument is packed and return result.
5896   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5897   ObjOperandId objArgId = writer.guardToObject(argId);
5898   writer.isPackedArrayResult(objArgId);
5899   writer.returnFromIC();
5900 
5901   trackAttached("IsPackedArray");
5902   return AttachDecision::Attach;
5903 }
5904 
tryAttachIsCallable(HandleFunction callee)5905 AttachDecision CallIRGenerator::tryAttachIsCallable(HandleFunction callee) {
5906   // Self-hosted code calls this with a single argument.
5907   MOZ_ASSERT(argc_ == 1);
5908 
5909   // Initialize the input operand.
5910   Int32OperandId argcId(writer.setInputOperandId(0));
5911 
5912   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5913 
5914   // Check if the argument is callable and return result.
5915   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5916   writer.isCallableResult(argId);
5917   writer.returnFromIC();
5918 
5919   trackAttached("IsCallable");
5920   return AttachDecision::Attach;
5921 }
5922 
tryAttachIsConstructor(HandleFunction callee)5923 AttachDecision CallIRGenerator::tryAttachIsConstructor(HandleFunction callee) {
5924   // Self-hosted code calls this with a single argument.
5925   MOZ_ASSERT(argc_ == 1);
5926 
5927   // Need a single object argument.
5928   if (!args_[0].isObject()) {
5929     return AttachDecision::NoAction;
5930   }
5931 
5932   // Initialize the input operand.
5933   Int32OperandId argcId(writer.setInputOperandId(0));
5934 
5935   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5936 
5937   // Guard that the argument is an object.
5938   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5939   ObjOperandId objId = writer.guardToObject(argId);
5940 
5941   // Check if the argument is a constructor and return result.
5942   writer.isConstructorResult(objId);
5943   writer.returnFromIC();
5944 
5945   trackAttached("IsConstructor");
5946   return AttachDecision::Attach;
5947 }
5948 
tryAttachIsCrossRealmArrayConstructor(HandleFunction callee)5949 AttachDecision CallIRGenerator::tryAttachIsCrossRealmArrayConstructor(
5950     HandleFunction callee) {
5951   // Self-hosted code calls this with an object argument.
5952   MOZ_ASSERT(argc_ == 1);
5953   MOZ_ASSERT(args_[0].isObject());
5954 
5955   if (args_[0].toObject().is<ProxyObject>()) {
5956     return AttachDecision::NoAction;
5957   }
5958 
5959   // Initialize the input operand.
5960   Int32OperandId argcId(writer.setInputOperandId(0));
5961 
5962   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5963 
5964   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5965   ObjOperandId objId = writer.guardToObject(argId);
5966   writer.guardIsNotProxy(objId);
5967   writer.isCrossRealmArrayConstructorResult(objId);
5968   writer.returnFromIC();
5969 
5970   trackAttached("IsCrossRealmArrayConstructor");
5971   return AttachDecision::Attach;
5972 }
5973 
tryAttachGuardToClass(HandleFunction callee,InlinableNative native)5974 AttachDecision CallIRGenerator::tryAttachGuardToClass(HandleFunction callee,
5975                                                       InlinableNative native) {
5976   // Self-hosted code calls this with an object argument.
5977   MOZ_ASSERT(argc_ == 1);
5978   MOZ_ASSERT(args_[0].isObject());
5979 
5980   // Class must match.
5981   const JSClass* clasp = InlinableNativeGuardToClass(native);
5982   if (args_[0].toObject().getClass() != clasp) {
5983     return AttachDecision::NoAction;
5984   }
5985 
5986   // Initialize the input operand.
5987   Int32OperandId argcId(writer.setInputOperandId(0));
5988 
5989   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
5990 
5991   // Guard that the argument is an object.
5992   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
5993   ObjOperandId objId = writer.guardToObject(argId);
5994 
5995   // Guard that the object has the correct class.
5996   writer.guardAnyClass(objId, clasp);
5997 
5998   // Return the object.
5999   writer.loadObjectResult(objId);
6000   writer.returnFromIC();
6001 
6002   trackAttached("GuardToClass");
6003   return AttachDecision::Attach;
6004 }
6005 
tryAttachHasClass(HandleFunction callee,const JSClass * clasp,bool isPossiblyWrapped)6006 AttachDecision CallIRGenerator::tryAttachHasClass(HandleFunction callee,
6007                                                   const JSClass* clasp,
6008                                                   bool isPossiblyWrapped) {
6009   // Self-hosted code calls this with an object argument.
6010   MOZ_ASSERT(argc_ == 1);
6011   MOZ_ASSERT(args_[0].isObject());
6012 
6013   // Only optimize when the object isn't a proxy.
6014   if (isPossiblyWrapped && args_[0].toObject().is<ProxyObject>()) {
6015     return AttachDecision::NoAction;
6016   }
6017 
6018   // Initialize the input operand.
6019   Int32OperandId argcId(writer.setInputOperandId(0));
6020 
6021   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6022 
6023   // Perform the Class check.
6024   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6025   ObjOperandId objId = writer.guardToObject(argId);
6026 
6027   if (isPossiblyWrapped) {
6028     writer.guardIsNotProxy(objId);
6029   }
6030 
6031   writer.hasClassResult(objId, clasp);
6032   writer.returnFromIC();
6033 
6034   trackAttached("HasClass");
6035   return AttachDecision::Attach;
6036 }
6037 
tryAttachRegExpMatcherSearcherTester(HandleFunction callee,InlinableNative native)6038 AttachDecision CallIRGenerator::tryAttachRegExpMatcherSearcherTester(
6039     HandleFunction callee, InlinableNative native) {
6040   // Self-hosted code calls this with (object, string, number) arguments.
6041   MOZ_ASSERT(argc_ == 3);
6042   MOZ_ASSERT(args_[0].isObject());
6043   MOZ_ASSERT(args_[1].isString());
6044   MOZ_ASSERT(args_[2].isNumber());
6045 
6046   // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
6047   if (!args_[2].isInt32()) {
6048     return AttachDecision::NoAction;
6049   }
6050 
6051   // Initialize the input operand.
6052   Int32OperandId argcId(writer.setInputOperandId(0));
6053 
6054   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6055 
6056   // Guard argument types.
6057   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6058   ObjOperandId reId = writer.guardToObject(arg0Id);
6059 
6060   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6061   StringOperandId inputId = writer.guardToString(arg1Id);
6062 
6063   ValOperandId arg2Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
6064   Int32OperandId lastIndexId = writer.guardToInt32(arg2Id);
6065 
6066   switch (native) {
6067     case InlinableNative::RegExpMatcher:
6068       writer.callRegExpMatcherResult(reId, inputId, lastIndexId);
6069       writer.returnFromIC();
6070       trackAttached("RegExpMatcher");
6071       break;
6072 
6073     case InlinableNative::RegExpSearcher:
6074       writer.callRegExpSearcherResult(reId, inputId, lastIndexId);
6075       writer.returnFromIC();
6076       trackAttached("RegExpSearcher");
6077       break;
6078 
6079     case InlinableNative::RegExpTester:
6080       writer.callRegExpTesterResult(reId, inputId, lastIndexId);
6081       writer.returnFromIC();
6082       trackAttached("RegExpTester");
6083       break;
6084 
6085     default:
6086       MOZ_CRASH("Unexpected native");
6087   }
6088 
6089   return AttachDecision::Attach;
6090 }
6091 
tryAttachRegExpPrototypeOptimizable(HandleFunction callee)6092 AttachDecision CallIRGenerator::tryAttachRegExpPrototypeOptimizable(
6093     HandleFunction callee) {
6094   // Self-hosted code calls this with a single object argument.
6095   MOZ_ASSERT(argc_ == 1);
6096   MOZ_ASSERT(args_[0].isObject());
6097 
6098   // Initialize the input operand.
6099   Int32OperandId argcId(writer.setInputOperandId(0));
6100 
6101   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6102 
6103   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6104   ObjOperandId protoId = writer.guardToObject(arg0Id);
6105 
6106   writer.regExpPrototypeOptimizableResult(protoId);
6107   writer.returnFromIC();
6108 
6109   trackAttached("RegExpPrototypeOptimizable");
6110   return AttachDecision::Attach;
6111 }
6112 
tryAttachRegExpInstanceOptimizable(HandleFunction callee)6113 AttachDecision CallIRGenerator::tryAttachRegExpInstanceOptimizable(
6114     HandleFunction callee) {
6115   // Self-hosted code calls this with two object arguments.
6116   MOZ_ASSERT(argc_ == 2);
6117   MOZ_ASSERT(args_[0].isObject());
6118   MOZ_ASSERT(args_[1].isObject());
6119 
6120   // Initialize the input operand.
6121   Int32OperandId argcId(writer.setInputOperandId(0));
6122 
6123   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6124 
6125   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6126   ObjOperandId regexpId = writer.guardToObject(arg0Id);
6127 
6128   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6129   ObjOperandId protoId = writer.guardToObject(arg1Id);
6130 
6131   writer.regExpInstanceOptimizableResult(regexpId, protoId);
6132   writer.returnFromIC();
6133 
6134   trackAttached("RegExpInstanceOptimizable");
6135   return AttachDecision::Attach;
6136 }
6137 
tryAttachGetFirstDollarIndex(HandleFunction callee)6138 AttachDecision CallIRGenerator::tryAttachGetFirstDollarIndex(
6139     HandleFunction callee) {
6140   // Self-hosted code calls this with a single string argument.
6141   MOZ_ASSERT(argc_ == 1);
6142   MOZ_ASSERT(args_[0].isString());
6143 
6144   // Initialize the input operand.
6145   Int32OperandId argcId(writer.setInputOperandId(0));
6146 
6147   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6148 
6149   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6150   StringOperandId strId = writer.guardToString(arg0Id);
6151 
6152   writer.getFirstDollarIndexResult(strId);
6153   writer.returnFromIC();
6154 
6155   trackAttached("GetFirstDollarIndex");
6156   return AttachDecision::Attach;
6157 }
6158 
tryAttachSubstringKernel(HandleFunction callee)6159 AttachDecision CallIRGenerator::tryAttachSubstringKernel(
6160     HandleFunction callee) {
6161   // Self-hosted code calls this with (string, int32, int32) arguments.
6162   MOZ_ASSERT(argc_ == 3);
6163   MOZ_ASSERT(args_[0].isString());
6164   MOZ_ASSERT(args_[1].isInt32());
6165   MOZ_ASSERT(args_[2].isInt32());
6166 
6167   // Initialize the input operand.
6168   Int32OperandId argcId(writer.setInputOperandId(0));
6169 
6170   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6171 
6172   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6173   StringOperandId strId = writer.guardToString(arg0Id);
6174 
6175   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6176   Int32OperandId beginId = writer.guardToInt32(arg1Id);
6177 
6178   ValOperandId arg2Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
6179   Int32OperandId lengthId = writer.guardToInt32(arg2Id);
6180 
6181   writer.callSubstringKernelResult(strId, beginId, lengthId);
6182   writer.returnFromIC();
6183 
6184   trackAttached("SubstringKernel");
6185   return AttachDecision::Attach;
6186 }
6187 
tryAttachObjectHasPrototype(HandleFunction callee)6188 AttachDecision CallIRGenerator::tryAttachObjectHasPrototype(
6189     HandleFunction callee) {
6190   // Self-hosted code calls this with (object, object) arguments.
6191   MOZ_ASSERT(argc_ == 2);
6192   MOZ_ASSERT(args_[0].isObject());
6193   MOZ_ASSERT(args_[1].isObject());
6194 
6195   auto* obj = &args_[0].toObject().as<NativeObject>();
6196   auto* proto = &args_[1].toObject().as<NativeObject>();
6197 
6198   // Only attach when obj.__proto__ is proto.
6199   if (obj->staticPrototype() != proto) {
6200     return AttachDecision::NoAction;
6201   }
6202 
6203   // Initialize the input operand.
6204   Int32OperandId argcId(writer.setInputOperandId(0));
6205 
6206   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6207 
6208   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6209   ObjOperandId objId = writer.guardToObject(arg0Id);
6210 
6211   writer.guardProto(objId, proto);
6212   writer.loadBooleanResult(true);
6213   writer.returnFromIC();
6214 
6215   trackAttached("ObjectHasPrototype");
6216   return AttachDecision::Attach;
6217 }
6218 
tryAttachString(HandleFunction callee)6219 AttachDecision CallIRGenerator::tryAttachString(HandleFunction callee) {
6220   // Need a single argument that is or can be converted to a string.
6221   if (argc_ != 1 || !(args_[0].isString() || args_[0].isNumber())) {
6222     return AttachDecision::NoAction;
6223   }
6224 
6225   // Initialize the input operand.
6226   Int32OperandId argcId(writer.setInputOperandId(0));
6227 
6228   // Guard callee is the 'String' function.
6229   emitNativeCalleeGuard(callee);
6230 
6231   // Guard that the argument is a string or can be converted to one.
6232   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6233   StringOperandId strId = emitToStringGuard(argId, args_[0]);
6234 
6235   // Return the string.
6236   writer.loadStringResult(strId);
6237   writer.returnFromIC();
6238 
6239   trackAttached("String");
6240   return AttachDecision::Attach;
6241 }
6242 
tryAttachStringConstructor(HandleFunction callee)6243 AttachDecision CallIRGenerator::tryAttachStringConstructor(
6244     HandleFunction callee) {
6245   // Need a single argument that is or can be converted to a string.
6246   if (argc_ != 1 || !(args_[0].isString() || args_[0].isNumber())) {
6247     return AttachDecision::NoAction;
6248   }
6249 
6250   RootedString emptyString(cx_, cx_->runtime()->emptyString);
6251   JSObject* templateObj = StringObject::create(
6252       cx_, emptyString, /* proto = */ nullptr, TenuredObject);
6253   if (!templateObj) {
6254     cx_->recoverFromOutOfMemory();
6255     return AttachDecision::NoAction;
6256   }
6257 
6258   // Initialize the input operand.
6259   Int32OperandId argcId(writer.setInputOperandId(0));
6260 
6261   // Guard callee is the 'String' function.
6262   emitNativeCalleeGuard(callee);
6263 
6264   CallFlags flags(IsConstructPC(pc_), IsSpreadPC(pc_));
6265 
6266   // Guard on number and convert to string.
6267   ValOperandId argId =
6268       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_, flags);
6269   StringOperandId strId = emitToStringGuard(argId, args_[0]);
6270 
6271   writer.newStringObjectResult(templateObj, strId);
6272   writer.returnFromIC();
6273 
6274   trackAttached("StringConstructor");
6275   return AttachDecision::Attach;
6276 }
6277 
tryAttachStringToStringValueOf(HandleFunction callee)6278 AttachDecision CallIRGenerator::tryAttachStringToStringValueOf(
6279     HandleFunction callee) {
6280   // Expecting no arguments.
6281   if (argc_ != 0) {
6282     return AttachDecision::NoAction;
6283   }
6284 
6285   // Ensure |this| is a primitive string value.
6286   if (!thisval_.isString()) {
6287     return AttachDecision::NoAction;
6288   }
6289 
6290   // Initialize the input operand.
6291   Int32OperandId argcId(writer.setInputOperandId(0));
6292 
6293   // Guard callee is the 'toString' OR 'valueOf' native function.
6294   emitNativeCalleeGuard(callee);
6295 
6296   // Guard |this| is a string.
6297   ValOperandId thisValId =
6298       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
6299   StringOperandId strId = writer.guardToString(thisValId);
6300 
6301   // Return the string
6302   writer.loadStringResult(strId);
6303   writer.returnFromIC();
6304 
6305   trackAttached("StringToStringValueOf");
6306   return AttachDecision::Attach;
6307 }
6308 
tryAttachStringReplaceString(HandleFunction callee)6309 AttachDecision CallIRGenerator::tryAttachStringReplaceString(
6310     HandleFunction callee) {
6311   // Self-hosted code calls this with (string, string, string) arguments.
6312   MOZ_ASSERT(argc_ == 3);
6313   MOZ_ASSERT(args_[0].isString());
6314   MOZ_ASSERT(args_[1].isString());
6315   MOZ_ASSERT(args_[2].isString());
6316 
6317   // Initialize the input operand.
6318   Int32OperandId argcId(writer.setInputOperandId(0));
6319 
6320   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6321 
6322   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6323   StringOperandId strId = writer.guardToString(arg0Id);
6324 
6325   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6326   StringOperandId patternId = writer.guardToString(arg1Id);
6327 
6328   ValOperandId arg2Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
6329   StringOperandId replacementId = writer.guardToString(arg2Id);
6330 
6331   writer.stringReplaceStringResult(strId, patternId, replacementId);
6332   writer.returnFromIC();
6333 
6334   trackAttached("StringReplaceString");
6335   return AttachDecision::Attach;
6336 }
6337 
tryAttachStringSplitString(HandleFunction callee)6338 AttachDecision CallIRGenerator::tryAttachStringSplitString(
6339     HandleFunction callee) {
6340   // Self-hosted code calls this with (string, string) arguments.
6341   MOZ_ASSERT(argc_ == 2);
6342   MOZ_ASSERT(args_[0].isString());
6343   MOZ_ASSERT(args_[1].isString());
6344 
6345   // Initialize the input operand.
6346   Int32OperandId argcId(writer.setInputOperandId(0));
6347 
6348   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6349 
6350   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6351   StringOperandId strId = writer.guardToString(arg0Id);
6352 
6353   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6354   StringOperandId separatorId = writer.guardToString(arg1Id);
6355 
6356   writer.stringSplitStringResult(strId, separatorId);
6357   writer.returnFromIC();
6358 
6359   trackAttached("StringSplitString");
6360   return AttachDecision::Attach;
6361 }
6362 
tryAttachStringChar(HandleFunction callee,StringChar kind)6363 AttachDecision CallIRGenerator::tryAttachStringChar(HandleFunction callee,
6364                                                     StringChar kind) {
6365   // Need one argument.
6366   if (argc_ != 1) {
6367     return AttachDecision::NoAction;
6368   }
6369 
6370   if (!CanAttachStringChar(thisval_, args_[0])) {
6371     return AttachDecision::NoAction;
6372   }
6373 
6374   // Initialize the input operand.
6375   Int32OperandId argcId(writer.setInputOperandId(0));
6376 
6377   // Guard callee is the 'charCodeAt' or 'charAt' native function.
6378   emitNativeCalleeGuard(callee);
6379 
6380   // Guard this is a string.
6381   ValOperandId thisValId =
6382       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
6383   StringOperandId strId = writer.guardToString(thisValId);
6384 
6385   // Guard int32 index.
6386   ValOperandId indexId =
6387       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6388   Int32OperandId int32IndexId = writer.guardToInt32Index(indexId);
6389 
6390   // Load string char or code.
6391   if (kind == StringChar::CodeAt) {
6392     writer.loadStringCharCodeResult(strId, int32IndexId);
6393   } else {
6394     writer.loadStringCharResult(strId, int32IndexId);
6395   }
6396 
6397   writer.returnFromIC();
6398 
6399   if (kind == StringChar::CodeAt) {
6400     trackAttached("StringCharCodeAt");
6401   } else {
6402     trackAttached("StringCharAt");
6403   }
6404 
6405   return AttachDecision::Attach;
6406 }
6407 
tryAttachStringCharCodeAt(HandleFunction callee)6408 AttachDecision CallIRGenerator::tryAttachStringCharCodeAt(
6409     HandleFunction callee) {
6410   return tryAttachStringChar(callee, StringChar::CodeAt);
6411 }
6412 
tryAttachStringCharAt(HandleFunction callee)6413 AttachDecision CallIRGenerator::tryAttachStringCharAt(HandleFunction callee) {
6414   return tryAttachStringChar(callee, StringChar::At);
6415 }
6416 
tryAttachStringFromCharCode(HandleFunction callee)6417 AttachDecision CallIRGenerator::tryAttachStringFromCharCode(
6418     HandleFunction callee) {
6419   // Need one int32 argument.
6420   if (argc_ != 1 || !args_[0].isInt32()) {
6421     return AttachDecision::NoAction;
6422   }
6423 
6424   // Initialize the input operand.
6425   Int32OperandId argcId(writer.setInputOperandId(0));
6426 
6427   // Guard callee is the 'fromCharCode' native function.
6428   emitNativeCalleeGuard(callee);
6429 
6430   // Guard int32 argument.
6431   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6432   Int32OperandId codeId = writer.guardToInt32(argId);
6433 
6434   // Return string created from code.
6435   writer.stringFromCharCodeResult(codeId);
6436   writer.returnFromIC();
6437 
6438   trackAttached("StringFromCharCode");
6439   return AttachDecision::Attach;
6440 }
6441 
tryAttachStringFromCodePoint(HandleFunction callee)6442 AttachDecision CallIRGenerator::tryAttachStringFromCodePoint(
6443     HandleFunction callee) {
6444   // Need one int32 argument.
6445   if (argc_ != 1 || !args_[0].isInt32()) {
6446     return AttachDecision::NoAction;
6447   }
6448 
6449   // String.fromCodePoint throws for invalid code points.
6450   int32_t codePoint = args_[0].toInt32();
6451   if (codePoint < 0 || codePoint > int32_t(unicode::NonBMPMax)) {
6452     return AttachDecision::NoAction;
6453   }
6454 
6455   // Initialize the input operand.
6456   Int32OperandId argcId(writer.setInputOperandId(0));
6457 
6458   // Guard callee is the 'fromCodePoint' native function.
6459   emitNativeCalleeGuard(callee);
6460 
6461   // Guard int32 argument.
6462   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6463   Int32OperandId codeId = writer.guardToInt32(argId);
6464 
6465   // Return string created from code point.
6466   writer.stringFromCodePointResult(codeId);
6467   writer.returnFromIC();
6468 
6469   trackAttached("StringFromCodePoint");
6470   return AttachDecision::Attach;
6471 }
6472 
tryAttachStringToLowerCase(HandleFunction callee)6473 AttachDecision CallIRGenerator::tryAttachStringToLowerCase(
6474     HandleFunction callee) {
6475   // Expecting no arguments.
6476   if (argc_ != 0) {
6477     return AttachDecision::NoAction;
6478   }
6479 
6480   // Ensure |this| is a primitive string value.
6481   if (!thisval_.isString()) {
6482     return AttachDecision::NoAction;
6483   }
6484 
6485   // Initialize the input operand.
6486   Int32OperandId argcId(writer.setInputOperandId(0));
6487 
6488   // Guard callee is the 'toLowerCase' native function.
6489   emitNativeCalleeGuard(callee);
6490 
6491   // Guard this is a string.
6492   ValOperandId thisValId =
6493       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
6494   StringOperandId strId = writer.guardToString(thisValId);
6495 
6496   // Return string converted to lower-case.
6497   writer.stringToLowerCaseResult(strId);
6498   writer.returnFromIC();
6499 
6500   trackAttached("StringToLowerCase");
6501   return AttachDecision::Attach;
6502 }
6503 
tryAttachStringToUpperCase(HandleFunction callee)6504 AttachDecision CallIRGenerator::tryAttachStringToUpperCase(
6505     HandleFunction callee) {
6506   // Expecting no arguments.
6507   if (argc_ != 0) {
6508     return AttachDecision::NoAction;
6509   }
6510 
6511   // Ensure |this| is a primitive string value.
6512   if (!thisval_.isString()) {
6513     return AttachDecision::NoAction;
6514   }
6515 
6516   // Initialize the input operand.
6517   Int32OperandId argcId(writer.setInputOperandId(0));
6518 
6519   // Guard callee is the 'toUpperCase' native function.
6520   emitNativeCalleeGuard(callee);
6521 
6522   // Guard this is a string.
6523   ValOperandId thisValId =
6524       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
6525   StringOperandId strId = writer.guardToString(thisValId);
6526 
6527   // Return string converted to upper-case.
6528   writer.stringToUpperCaseResult(strId);
6529   writer.returnFromIC();
6530 
6531   trackAttached("StringToUpperCase");
6532   return AttachDecision::Attach;
6533 }
6534 
tryAttachMathRandom(HandleFunction callee)6535 AttachDecision CallIRGenerator::tryAttachMathRandom(HandleFunction callee) {
6536   // Expecting no arguments.
6537   if (argc_ != 0) {
6538     return AttachDecision::NoAction;
6539   }
6540 
6541   MOZ_ASSERT(cx_->realm() == callee->realm(),
6542              "Shouldn't inline cross-realm Math.random because per-realm RNG");
6543 
6544   // Initialize the input operand.
6545   Int32OperandId argcId(writer.setInputOperandId(0));
6546 
6547   // Guard callee is the 'random' native function.
6548   emitNativeCalleeGuard(callee);
6549 
6550   mozilla::non_crypto::XorShift128PlusRNG* rng =
6551       &cx_->realm()->getOrCreateRandomNumberGenerator();
6552   writer.mathRandomResult(rng);
6553 
6554   writer.returnFromIC();
6555 
6556   trackAttached("MathRandom");
6557   return AttachDecision::Attach;
6558 }
6559 
tryAttachMathAbs(HandleFunction callee)6560 AttachDecision CallIRGenerator::tryAttachMathAbs(HandleFunction callee) {
6561   // Need one argument.
6562   if (argc_ != 1) {
6563     return AttachDecision::NoAction;
6564   }
6565 
6566   if (!args_[0].isNumber()) {
6567     return AttachDecision::NoAction;
6568   }
6569 
6570   // Initialize the input operand.
6571   Int32OperandId argcId(writer.setInputOperandId(0));
6572 
6573   // Guard callee is the 'abs' native function.
6574   emitNativeCalleeGuard(callee);
6575 
6576   ValOperandId argumentId =
6577       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6578 
6579   // abs(INT_MIN) is a double.
6580   if (args_[0].isInt32() && args_[0].toInt32() != INT_MIN) {
6581     Int32OperandId int32Id = writer.guardToInt32(argumentId);
6582     writer.mathAbsInt32Result(int32Id);
6583   } else {
6584     NumberOperandId numberId = writer.guardIsNumber(argumentId);
6585     writer.mathAbsNumberResult(numberId);
6586   }
6587 
6588   writer.returnFromIC();
6589 
6590   trackAttached("MathAbs");
6591   return AttachDecision::Attach;
6592 }
6593 
tryAttachMathClz32(HandleFunction callee)6594 AttachDecision CallIRGenerator::tryAttachMathClz32(HandleFunction callee) {
6595   // Need one (number) argument.
6596   if (argc_ != 1 || !args_[0].isNumber()) {
6597     return AttachDecision::NoAction;
6598   }
6599 
6600   // Initialize the input operand.
6601   Int32OperandId argcId(writer.setInputOperandId(0));
6602 
6603   // Guard callee is the 'clz32' native function.
6604   emitNativeCalleeGuard(callee);
6605 
6606   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6607 
6608   Int32OperandId int32Id;
6609   if (args_[0].isInt32()) {
6610     int32Id = writer.guardToInt32(argId);
6611   } else {
6612     MOZ_ASSERT(args_[0].isDouble());
6613     NumberOperandId numId = writer.guardIsNumber(argId);
6614     int32Id = writer.truncateDoubleToUInt32(numId);
6615   }
6616   writer.mathClz32Result(int32Id);
6617   writer.returnFromIC();
6618 
6619   trackAttached("MathClz32");
6620   return AttachDecision::Attach;
6621 }
6622 
tryAttachMathSign(HandleFunction callee)6623 AttachDecision CallIRGenerator::tryAttachMathSign(HandleFunction callee) {
6624   // Need one (number) argument.
6625   if (argc_ != 1 || !args_[0].isNumber()) {
6626     return AttachDecision::NoAction;
6627   }
6628 
6629   // Initialize the input operand.
6630   Int32OperandId argcId(writer.setInputOperandId(0));
6631 
6632   // Guard callee is the 'sign' native function.
6633   emitNativeCalleeGuard(callee);
6634 
6635   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6636 
6637   if (args_[0].isInt32()) {
6638     Int32OperandId int32Id = writer.guardToInt32(argId);
6639     writer.mathSignInt32Result(int32Id);
6640   } else {
6641     // Math.sign returns a double only if the input is -0 or NaN so try to
6642     // optimize the common Number => Int32 case.
6643     double d = math_sign_impl(args_[0].toDouble());
6644     int32_t unused;
6645     bool resultIsInt32 = mozilla::NumberIsInt32(d, &unused);
6646 
6647     NumberOperandId numId = writer.guardIsNumber(argId);
6648     if (resultIsInt32) {
6649       writer.mathSignNumberToInt32Result(numId);
6650     } else {
6651       writer.mathSignNumberResult(numId);
6652     }
6653   }
6654 
6655   writer.returnFromIC();
6656 
6657   trackAttached("MathSign");
6658   return AttachDecision::Attach;
6659 }
6660 
tryAttachMathImul(HandleFunction callee)6661 AttachDecision CallIRGenerator::tryAttachMathImul(HandleFunction callee) {
6662   // Need two (number) arguments.
6663   if (argc_ != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
6664     return AttachDecision::NoAction;
6665   }
6666 
6667   // Initialize the input operand.
6668   Int32OperandId argcId(writer.setInputOperandId(0));
6669 
6670   // Guard callee is the 'imul' native function.
6671   emitNativeCalleeGuard(callee);
6672 
6673   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6674   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6675 
6676   Int32OperandId int32Arg0Id, int32Arg1Id;
6677   if (args_[0].isInt32() && args_[1].isInt32()) {
6678     int32Arg0Id = writer.guardToInt32(arg0Id);
6679     int32Arg1Id = writer.guardToInt32(arg1Id);
6680   } else {
6681     // Treat both arguments as numbers if at least one of them is non-int32.
6682     NumberOperandId numArg0Id = writer.guardIsNumber(arg0Id);
6683     NumberOperandId numArg1Id = writer.guardIsNumber(arg1Id);
6684     int32Arg0Id = writer.truncateDoubleToUInt32(numArg0Id);
6685     int32Arg1Id = writer.truncateDoubleToUInt32(numArg1Id);
6686   }
6687   writer.mathImulResult(int32Arg0Id, int32Arg1Id);
6688   writer.returnFromIC();
6689 
6690   trackAttached("MathImul");
6691   return AttachDecision::Attach;
6692 }
6693 
tryAttachMathFloor(HandleFunction callee)6694 AttachDecision CallIRGenerator::tryAttachMathFloor(HandleFunction callee) {
6695   // Need one (number) argument.
6696   if (argc_ != 1 || !args_[0].isNumber()) {
6697     return AttachDecision::NoAction;
6698   }
6699 
6700   // Check if the result fits in int32.
6701   double res = math_floor_impl(args_[0].toNumber());
6702   int32_t unused;
6703   bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
6704 
6705   // Initialize the input operand.
6706   Int32OperandId argcId(writer.setInputOperandId(0));
6707 
6708   // Guard callee is the 'floor' native function.
6709   emitNativeCalleeGuard(callee);
6710 
6711   ValOperandId argumentId =
6712       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6713 
6714   if (args_[0].isInt32()) {
6715     MOZ_ASSERT(resultIsInt32);
6716 
6717     // Use an indirect truncation to inform the optimizer it needs to preserve
6718     // a bailout when the input can't be represented as an int32, even if the
6719     // final result is fully truncated.
6720     Int32OperandId intId = writer.guardToInt32(argumentId);
6721     writer.indirectTruncateInt32Result(intId);
6722   } else {
6723     NumberOperandId numberId = writer.guardIsNumber(argumentId);
6724 
6725     if (resultIsInt32) {
6726       writer.mathFloorToInt32Result(numberId);
6727     } else {
6728       writer.mathFloorNumberResult(numberId);
6729     }
6730   }
6731 
6732   writer.returnFromIC();
6733 
6734   trackAttached("MathFloor");
6735   return AttachDecision::Attach;
6736 }
6737 
tryAttachMathCeil(HandleFunction callee)6738 AttachDecision CallIRGenerator::tryAttachMathCeil(HandleFunction callee) {
6739   // Need one (number) argument.
6740   if (argc_ != 1 || !args_[0].isNumber()) {
6741     return AttachDecision::NoAction;
6742   }
6743 
6744   // Check if the result fits in int32.
6745   double res = math_ceil_impl(args_[0].toNumber());
6746   int32_t unused;
6747   bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
6748 
6749   // Initialize the input operand.
6750   Int32OperandId argcId(writer.setInputOperandId(0));
6751 
6752   // Guard callee is the 'ceil' native function.
6753   emitNativeCalleeGuard(callee);
6754 
6755   ValOperandId argumentId =
6756       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6757 
6758   if (args_[0].isInt32()) {
6759     MOZ_ASSERT(resultIsInt32);
6760 
6761     // Use an indirect truncation to inform the optimizer it needs to preserve
6762     // a bailout when the input can't be represented as an int32, even if the
6763     // final result is fully truncated.
6764     Int32OperandId intId = writer.guardToInt32(argumentId);
6765     writer.indirectTruncateInt32Result(intId);
6766   } else {
6767     NumberOperandId numberId = writer.guardIsNumber(argumentId);
6768 
6769     if (resultIsInt32) {
6770       writer.mathCeilToInt32Result(numberId);
6771     } else {
6772       writer.mathCeilNumberResult(numberId);
6773     }
6774   }
6775 
6776   writer.returnFromIC();
6777 
6778   trackAttached("MathCeil");
6779   return AttachDecision::Attach;
6780 }
6781 
tryAttachMathTrunc(HandleFunction callee)6782 AttachDecision CallIRGenerator::tryAttachMathTrunc(HandleFunction callee) {
6783   // Need one (number) argument.
6784   if (argc_ != 1 || !args_[0].isNumber()) {
6785     return AttachDecision::NoAction;
6786   }
6787 
6788   // Check if the result fits in int32.
6789   double res = math_trunc_impl(args_[0].toNumber());
6790   int32_t unused;
6791   bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
6792 
6793   // Initialize the input operand.
6794   Int32OperandId argcId(writer.setInputOperandId(0));
6795 
6796   // Guard callee is the 'trunc' native function.
6797   emitNativeCalleeGuard(callee);
6798 
6799   ValOperandId argumentId =
6800       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6801 
6802   if (args_[0].isInt32()) {
6803     MOZ_ASSERT(resultIsInt32);
6804 
6805     // We don't need an indirect truncation barrier here, because Math.trunc
6806     // always truncates, but never rounds its input away from zero.
6807     Int32OperandId intId = writer.guardToInt32(argumentId);
6808     writer.loadInt32Result(intId);
6809   } else {
6810     NumberOperandId numberId = writer.guardIsNumber(argumentId);
6811 
6812     if (resultIsInt32) {
6813       writer.mathTruncToInt32Result(numberId);
6814     } else {
6815       writer.mathTruncNumberResult(numberId);
6816     }
6817   }
6818 
6819   writer.returnFromIC();
6820 
6821   trackAttached("MathTrunc");
6822   return AttachDecision::Attach;
6823 }
6824 
tryAttachMathRound(HandleFunction callee)6825 AttachDecision CallIRGenerator::tryAttachMathRound(HandleFunction callee) {
6826   // Need one (number) argument.
6827   if (argc_ != 1 || !args_[0].isNumber()) {
6828     return AttachDecision::NoAction;
6829   }
6830 
6831   // Check if the result fits in int32.
6832   double res = math_round_impl(args_[0].toNumber());
6833   int32_t unused;
6834   bool resultIsInt32 = mozilla::NumberIsInt32(res, &unused);
6835 
6836   // Initialize the input operand.
6837   Int32OperandId argcId(writer.setInputOperandId(0));
6838 
6839   // Guard callee is the 'round' native function.
6840   emitNativeCalleeGuard(callee);
6841 
6842   ValOperandId argumentId =
6843       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6844 
6845   if (args_[0].isInt32()) {
6846     MOZ_ASSERT(resultIsInt32);
6847 
6848     // Use an indirect truncation to inform the optimizer it needs to preserve
6849     // a bailout when the input can't be represented as an int32, even if the
6850     // final result is fully truncated.
6851     Int32OperandId intId = writer.guardToInt32(argumentId);
6852     writer.indirectTruncateInt32Result(intId);
6853   } else {
6854     NumberOperandId numberId = writer.guardIsNumber(argumentId);
6855 
6856     if (resultIsInt32) {
6857       writer.mathRoundToInt32Result(numberId);
6858     } else {
6859       writer.mathFunctionNumberResult(numberId, UnaryMathFunction::Round);
6860     }
6861   }
6862 
6863   writer.returnFromIC();
6864 
6865   trackAttached("MathRound");
6866   return AttachDecision::Attach;
6867 }
6868 
tryAttachMathSqrt(HandleFunction callee)6869 AttachDecision CallIRGenerator::tryAttachMathSqrt(HandleFunction callee) {
6870   // Need one (number) argument.
6871   if (argc_ != 1 || !args_[0].isNumber()) {
6872     return AttachDecision::NoAction;
6873   }
6874 
6875   // Initialize the input operand.
6876   Int32OperandId argcId(writer.setInputOperandId(0));
6877 
6878   // Guard callee is the 'sqrt' native function.
6879   emitNativeCalleeGuard(callee);
6880 
6881   ValOperandId argumentId =
6882       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6883   NumberOperandId numberId = writer.guardIsNumber(argumentId);
6884   writer.mathSqrtNumberResult(numberId);
6885   writer.returnFromIC();
6886 
6887   trackAttached("MathSqrt");
6888   return AttachDecision::Attach;
6889 }
6890 
tryAttachMathFRound(HandleFunction callee)6891 AttachDecision CallIRGenerator::tryAttachMathFRound(HandleFunction callee) {
6892   // Need one (number) argument.
6893   if (argc_ != 1 || !args_[0].isNumber()) {
6894     return AttachDecision::NoAction;
6895   }
6896 
6897   // Initialize the input operand.
6898   Int32OperandId argcId(writer.setInputOperandId(0));
6899 
6900   // Guard callee is the 'fround' native function.
6901   emitNativeCalleeGuard(callee);
6902 
6903   ValOperandId argumentId =
6904       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6905   NumberOperandId numberId = writer.guardIsNumber(argumentId);
6906   writer.mathFRoundNumberResult(numberId);
6907   writer.returnFromIC();
6908 
6909   trackAttached("MathFRound");
6910   return AttachDecision::Attach;
6911 }
6912 
CanAttachInt32Pow(const Value & baseVal,const Value & powerVal)6913 static bool CanAttachInt32Pow(const Value& baseVal, const Value& powerVal) {
6914   auto valToInt32 = [](const Value& v) {
6915     if (v.isInt32()) {
6916       return v.toInt32();
6917     }
6918     if (v.isBoolean()) {
6919       return int32_t(v.toBoolean());
6920     }
6921     MOZ_ASSERT(v.isNull());
6922     return 0;
6923   };
6924   int32_t base = valToInt32(baseVal);
6925   int32_t power = valToInt32(powerVal);
6926 
6927   // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
6928   // gets large enough. It's hard to determine when exactly y is "large enough",
6929   // so we don't use Int32PowResult when x != 1 and y < 0.
6930   // Note: it's important for this condition to match the code generated by
6931   // MacroAssembler::pow32 to prevent failure loops.
6932   if (power < 0) {
6933     return base == 1;
6934   }
6935 
6936   double res = powi(base, power);
6937   int32_t unused;
6938   return mozilla::NumberIsInt32(res, &unused);
6939 }
6940 
tryAttachMathPow(HandleFunction callee)6941 AttachDecision CallIRGenerator::tryAttachMathPow(HandleFunction callee) {
6942   // Need two number arguments.
6943   if (argc_ != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
6944     return AttachDecision::NoAction;
6945   }
6946 
6947   // Initialize the input operand.
6948   Int32OperandId argcId(writer.setInputOperandId(0));
6949 
6950   // Guard callee is the 'pow' function.
6951   emitNativeCalleeGuard(callee);
6952 
6953   ValOperandId baseId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6954   ValOperandId exponentId =
6955       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6956 
6957   if (args_[0].isInt32() && args_[1].isInt32() &&
6958       CanAttachInt32Pow(args_[0], args_[1])) {
6959     Int32OperandId baseInt32Id = writer.guardToInt32(baseId);
6960     Int32OperandId exponentInt32Id = writer.guardToInt32(exponentId);
6961     writer.int32PowResult(baseInt32Id, exponentInt32Id);
6962   } else {
6963     NumberOperandId baseNumberId = writer.guardIsNumber(baseId);
6964     NumberOperandId exponentNumberId = writer.guardIsNumber(exponentId);
6965     writer.doublePowResult(baseNumberId, exponentNumberId);
6966   }
6967 
6968   writer.returnFromIC();
6969 
6970   trackAttached("MathPow");
6971   return AttachDecision::Attach;
6972 }
6973 
tryAttachMathHypot(HandleFunction callee)6974 AttachDecision CallIRGenerator::tryAttachMathHypot(HandleFunction callee) {
6975   // Only optimize if there are 2-4 arguments.
6976   if (argc_ < 2 || argc_ > 4) {
6977     return AttachDecision::NoAction;
6978   }
6979 
6980   for (size_t i = 0; i < argc_; i++) {
6981     if (!args_[i].isNumber()) {
6982       return AttachDecision::NoAction;
6983     }
6984   }
6985 
6986   // Initialize the input operand.
6987   Int32OperandId argcId(writer.setInputOperandId(0));
6988 
6989   // Guard callee is the 'hypot' native function.
6990   emitNativeCalleeGuard(callee);
6991 
6992   ValOperandId firstId =
6993       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
6994   ValOperandId secondId =
6995       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
6996 
6997   NumberOperandId firstNumId = writer.guardIsNumber(firstId);
6998   NumberOperandId secondNumId = writer.guardIsNumber(secondId);
6999 
7000   ValOperandId thirdId;
7001   ValOperandId fourthId;
7002   NumberOperandId thirdNumId;
7003   NumberOperandId fourthNumId;
7004 
7005   switch (argc_) {
7006     case 2:
7007       writer.mathHypot2NumberResult(firstNumId, secondNumId);
7008       break;
7009     case 3:
7010       thirdId = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
7011       thirdNumId = writer.guardIsNumber(thirdId);
7012       writer.mathHypot3NumberResult(firstNumId, secondNumId, thirdNumId);
7013       break;
7014     case 4:
7015       thirdId = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
7016       fourthId = writer.loadArgumentFixedSlot(ArgumentKind::Arg3, argc_);
7017       thirdNumId = writer.guardIsNumber(thirdId);
7018       fourthNumId = writer.guardIsNumber(fourthId);
7019       writer.mathHypot4NumberResult(firstNumId, secondNumId, thirdNumId,
7020                                     fourthNumId);
7021       break;
7022     default:
7023       MOZ_CRASH("Unexpected number of arguments to hypot function.");
7024   }
7025 
7026   writer.returnFromIC();
7027 
7028   trackAttached("MathHypot");
7029   return AttachDecision::Attach;
7030 }
7031 
tryAttachMathATan2(HandleFunction callee)7032 AttachDecision CallIRGenerator::tryAttachMathATan2(HandleFunction callee) {
7033   // Requires two numbers as arguments.
7034   if (argc_ != 2 || !args_[0].isNumber() || !args_[1].isNumber()) {
7035     return AttachDecision::NoAction;
7036   }
7037 
7038   // Initialize the input operand.
7039   Int32OperandId argcId(writer.setInputOperandId(0));
7040 
7041   // Guard callee is the 'atan2' native function.
7042   emitNativeCalleeGuard(callee);
7043 
7044   ValOperandId yId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7045   ValOperandId xId = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7046 
7047   NumberOperandId yNumberId = writer.guardIsNumber(yId);
7048   NumberOperandId xNumberId = writer.guardIsNumber(xId);
7049 
7050   writer.mathAtan2NumberResult(yNumberId, xNumberId);
7051   writer.returnFromIC();
7052 
7053   trackAttached("MathAtan2");
7054   return AttachDecision::Attach;
7055 }
7056 
tryAttachMathMinMax(HandleFunction callee,bool isMax)7057 AttachDecision CallIRGenerator::tryAttachMathMinMax(HandleFunction callee,
7058                                                     bool isMax) {
7059   // For now only optimize if there are 1-4 arguments.
7060   if (argc_ < 1 || argc_ > 4) {
7061     return AttachDecision::NoAction;
7062   }
7063 
7064   // Ensure all arguments are numbers.
7065   bool allInt32 = true;
7066   for (size_t i = 0; i < argc_; i++) {
7067     if (!args_[i].isNumber()) {
7068       return AttachDecision::NoAction;
7069     }
7070     if (!args_[i].isInt32()) {
7071       allInt32 = false;
7072     }
7073   }
7074 
7075   // Initialize the input operand.
7076   Int32OperandId argcId(writer.setInputOperandId(0));
7077 
7078   // Guard callee is this Math function.
7079   emitNativeCalleeGuard(callee);
7080 
7081   if (allInt32) {
7082     ValOperandId valId =
7083         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7084     Int32OperandId resId = writer.guardToInt32(valId);
7085     for (size_t i = 1; i < argc_; i++) {
7086       ValOperandId argId =
7087           writer.loadArgumentFixedSlot(ArgumentKindForArgIndex(i), argc_);
7088       Int32OperandId argInt32Id = writer.guardToInt32(argId);
7089       resId = writer.int32MinMax(isMax, resId, argInt32Id);
7090     }
7091     writer.loadInt32Result(resId);
7092   } else {
7093     ValOperandId valId =
7094         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7095     NumberOperandId resId = writer.guardIsNumber(valId);
7096     for (size_t i = 1; i < argc_; i++) {
7097       ValOperandId argId =
7098           writer.loadArgumentFixedSlot(ArgumentKindForArgIndex(i), argc_);
7099       NumberOperandId argNumId = writer.guardIsNumber(argId);
7100       resId = writer.numberMinMax(isMax, resId, argNumId);
7101     }
7102     writer.loadDoubleResult(resId);
7103   }
7104 
7105   writer.returnFromIC();
7106 
7107   trackAttached(isMax ? "MathMax" : "MathMin");
7108   return AttachDecision::Attach;
7109 }
7110 
tryAttachSpreadMathMinMax(HandleFunction callee,bool isMax)7111 AttachDecision CallIRGenerator::tryAttachSpreadMathMinMax(HandleFunction callee,
7112                                                           bool isMax) {
7113   MOZ_ASSERT(op_ == JSOp::SpreadCall);
7114 
7115   // The result will be an int32 if there is at least one argument,
7116   // and all the arguments are int32.
7117   bool int32Result = args_.length() > 0;
7118   for (size_t i = 0; i < args_.length(); i++) {
7119     if (!args_[i].isNumber()) {
7120       return AttachDecision::NoAction;
7121     }
7122     if (!args_[i].isInt32()) {
7123       int32Result = false;
7124     }
7125   }
7126 
7127   // Initialize the input operand.
7128   Int32OperandId argcId(writer.setInputOperandId(0));
7129 
7130   // Guard callee is this Math function.
7131   emitNativeCalleeGuard(callee);
7132 
7133   // Load the argument array
7134   ObjOperandId argsId = writer.loadSpreadArgs();
7135 
7136   if (int32Result) {
7137     writer.int32MinMaxArrayResult(argsId, isMax);
7138   } else {
7139     writer.numberMinMaxArrayResult(argsId, isMax);
7140   }
7141 
7142   writer.returnFromIC();
7143 
7144   trackAttached(isMax ? "MathMax" : "MathMin");
7145   return AttachDecision::Attach;
7146 }
7147 
tryAttachMathFunction(HandleFunction callee,UnaryMathFunction fun)7148 AttachDecision CallIRGenerator::tryAttachMathFunction(HandleFunction callee,
7149                                                       UnaryMathFunction fun) {
7150   // Need one argument.
7151   if (argc_ != 1) {
7152     return AttachDecision::NoAction;
7153   }
7154 
7155   if (!args_[0].isNumber()) {
7156     return AttachDecision::NoAction;
7157   }
7158 
7159   // Initialize the input operand.
7160   Int32OperandId argcId(writer.setInputOperandId(0));
7161 
7162   // Guard callee is this Math function.
7163   emitNativeCalleeGuard(callee);
7164 
7165   ValOperandId argumentId =
7166       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7167   NumberOperandId numberId = writer.guardIsNumber(argumentId);
7168   writer.mathFunctionNumberResult(numberId, fun);
7169   writer.returnFromIC();
7170 
7171   trackAttached("MathFunction");
7172   return AttachDecision::Attach;
7173 }
7174 
emitToStringGuard(ValOperandId id,const Value & v)7175 StringOperandId IRGenerator::emitToStringGuard(ValOperandId id,
7176                                                const Value& v) {
7177   if (v.isString()) {
7178     return writer.guardToString(id);
7179   }
7180   if (v.isInt32()) {
7181     Int32OperandId intId = writer.guardToInt32(id);
7182     return writer.callInt32ToString(intId);
7183   }
7184   // At this point we are creating an IC that will handle
7185   // both Int32 and Double cases.
7186   MOZ_ASSERT(v.isNumber());
7187   NumberOperandId numId = writer.guardIsNumber(id);
7188   return writer.callNumberToString(numId);
7189 }
7190 
tryAttachNumberToString(HandleFunction callee)7191 AttachDecision CallIRGenerator::tryAttachNumberToString(HandleFunction callee) {
7192   // Expecting no arguments, which means base 10.
7193   if (argc_ != 0) {
7194     return AttachDecision::NoAction;
7195   }
7196 
7197   // Ensure |this| is a primitive number value.
7198   if (!thisval_.isNumber()) {
7199     return AttachDecision::NoAction;
7200   }
7201 
7202   // Initialize the input operand.
7203   Int32OperandId argcId(writer.setInputOperandId(0));
7204 
7205   // Guard callee is the 'toString' native function.
7206   emitNativeCalleeGuard(callee);
7207 
7208   // Initialize the |this| operand.
7209   ValOperandId thisValId =
7210       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
7211 
7212   // Guard on number and convert to string.
7213   StringOperandId strId = emitToStringGuard(thisValId, thisval_);
7214 
7215   // Return the string.
7216   writer.loadStringResult(strId);
7217   writer.returnFromIC();
7218 
7219   trackAttached("NumberToString");
7220   return AttachDecision::Attach;
7221 }
7222 
tryAttachReflectGetPrototypeOf(HandleFunction callee)7223 AttachDecision CallIRGenerator::tryAttachReflectGetPrototypeOf(
7224     HandleFunction callee) {
7225   // Need one argument.
7226   if (argc_ != 1) {
7227     return AttachDecision::NoAction;
7228   }
7229 
7230   if (!args_[0].isObject()) {
7231     return AttachDecision::NoAction;
7232   }
7233 
7234   // Initialize the input operand.
7235   Int32OperandId argcId(writer.setInputOperandId(0));
7236 
7237   // Guard callee is the 'getPrototypeOf' native function.
7238   emitNativeCalleeGuard(callee);
7239 
7240   ValOperandId argumentId =
7241       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7242   ObjOperandId objId = writer.guardToObject(argumentId);
7243 
7244   writer.reflectGetPrototypeOfResult(objId);
7245   writer.returnFromIC();
7246 
7247   trackAttached("ReflectGetPrototypeOf");
7248   return AttachDecision::Attach;
7249 }
7250 
AtomicsMeetsPreconditions(TypedArrayObject * typedArray,const Value & index)7251 static bool AtomicsMeetsPreconditions(TypedArrayObject* typedArray,
7252                                       const Value& index) {
7253   switch (typedArray->type()) {
7254     case Scalar::Int8:
7255     case Scalar::Uint8:
7256     case Scalar::Int16:
7257     case Scalar::Uint16:
7258     case Scalar::Int32:
7259     case Scalar::Uint32:
7260     case Scalar::BigInt64:
7261     case Scalar::BigUint64:
7262       break;
7263 
7264     case Scalar::Float32:
7265     case Scalar::Float64:
7266     case Scalar::Uint8Clamped:
7267       // Exclude floating types and Uint8Clamped.
7268       return false;
7269 
7270     case Scalar::MaxTypedArrayViewType:
7271     case Scalar::Int64:
7272     case Scalar::Simd128:
7273       MOZ_CRASH("Unsupported TypedArray type");
7274   }
7275 
7276   // Bounds check the index argument.
7277   int64_t indexInt64;
7278   if (!ValueIsInt64Index(index, &indexInt64)) {
7279     return false;
7280   }
7281   if (indexInt64 < 0 || uint64_t(indexInt64) >= typedArray->length()) {
7282     return false;
7283   }
7284 
7285   return true;
7286 }
7287 
tryAttachAtomicsCompareExchange(HandleFunction callee)7288 AttachDecision CallIRGenerator::tryAttachAtomicsCompareExchange(
7289     HandleFunction callee) {
7290   if (!JitSupportsAtomics()) {
7291     return AttachDecision::NoAction;
7292   }
7293 
7294   // Need four arguments.
7295   if (argc_ != 4) {
7296     return AttachDecision::NoAction;
7297   }
7298 
7299   // Arguments: typedArray, index (number), expected, replacement.
7300   if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
7301     return AttachDecision::NoAction;
7302   }
7303   if (!args_[1].isNumber()) {
7304     return AttachDecision::NoAction;
7305   }
7306 
7307   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7308   if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
7309     return AttachDecision::NoAction;
7310   }
7311 
7312   Scalar::Type elementType = typedArray->type();
7313   if (!ValueIsNumeric(elementType, args_[2])) {
7314     return AttachDecision::NoAction;
7315   }
7316   if (!ValueIsNumeric(elementType, args_[3])) {
7317     return AttachDecision::NoAction;
7318   }
7319 
7320   // Initialize the input operand.
7321   Int32OperandId argcId(writer.setInputOperandId(0));
7322 
7323   // Guard callee is the `compareExchange` native function.
7324   emitNativeCalleeGuard(callee);
7325 
7326   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7327   ObjOperandId objId = writer.guardToObject(arg0Id);
7328   writer.guardShapeForClass(objId, typedArray->shape());
7329 
7330   // Convert index to intPtr.
7331   ValOperandId indexId =
7332       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7333   IntPtrOperandId intPtrIndexId =
7334       guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
7335 
7336   // Convert expected value to int32/BigInt.
7337   ValOperandId expectedId =
7338       writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
7339   OperandId numericExpectedId = emitNumericGuard(expectedId, elementType);
7340 
7341   // Convert replacement value to int32/BigInt.
7342   ValOperandId replacementId =
7343       writer.loadArgumentFixedSlot(ArgumentKind::Arg3, argc_);
7344   OperandId numericReplacementId = emitNumericGuard(replacementId, elementType);
7345 
7346   writer.atomicsCompareExchangeResult(objId, intPtrIndexId, numericExpectedId,
7347                                       numericReplacementId, typedArray->type());
7348   writer.returnFromIC();
7349 
7350   trackAttached("AtomicsCompareExchange");
7351   return AttachDecision::Attach;
7352 }
7353 
canAttachAtomicsReadWriteModify()7354 bool CallIRGenerator::canAttachAtomicsReadWriteModify() {
7355   if (!JitSupportsAtomics()) {
7356     return false;
7357   }
7358 
7359   // Need three arguments.
7360   if (argc_ != 3) {
7361     return false;
7362   }
7363 
7364   // Arguments: typedArray, index (number), value.
7365   if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
7366     return false;
7367   }
7368   if (!args_[1].isNumber()) {
7369     return false;
7370   }
7371 
7372   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7373   if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
7374     return false;
7375   }
7376   if (!ValueIsNumeric(typedArray->type(), args_[2])) {
7377     return false;
7378   }
7379   return true;
7380 }
7381 
7382 CallIRGenerator::AtomicsReadWriteModifyOperands
emitAtomicsReadWriteModifyOperands(HandleFunction callee)7383 CallIRGenerator::emitAtomicsReadWriteModifyOperands(HandleFunction callee) {
7384   MOZ_ASSERT(canAttachAtomicsReadWriteModify());
7385 
7386   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7387 
7388   // Initialize the input operand.
7389   Int32OperandId argcId(writer.setInputOperandId(0));
7390 
7391   // Guard callee is this Atomics function.
7392   emitNativeCalleeGuard(callee);
7393 
7394   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7395   ObjOperandId objId = writer.guardToObject(arg0Id);
7396   writer.guardShapeForClass(objId, typedArray->shape());
7397 
7398   // Convert index to intPtr.
7399   ValOperandId indexId =
7400       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7401   IntPtrOperandId intPtrIndexId =
7402       guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
7403 
7404   // Convert value to int32/BigInt.
7405   ValOperandId valueId =
7406       writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
7407   OperandId numericValueId = emitNumericGuard(valueId, typedArray->type());
7408 
7409   return {objId, intPtrIndexId, numericValueId};
7410 }
7411 
tryAttachAtomicsExchange(HandleFunction callee)7412 AttachDecision CallIRGenerator::tryAttachAtomicsExchange(
7413     HandleFunction callee) {
7414   if (!canAttachAtomicsReadWriteModify()) {
7415     return AttachDecision::NoAction;
7416   }
7417 
7418   auto [objId, intPtrIndexId, numericValueId] =
7419       emitAtomicsReadWriteModifyOperands(callee);
7420 
7421   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7422 
7423   writer.atomicsExchangeResult(objId, intPtrIndexId, numericValueId,
7424                                typedArray->type());
7425   writer.returnFromIC();
7426 
7427   trackAttached("AtomicsExchange");
7428   return AttachDecision::Attach;
7429 }
7430 
tryAttachAtomicsAdd(HandleFunction callee)7431 AttachDecision CallIRGenerator::tryAttachAtomicsAdd(HandleFunction callee) {
7432   if (!canAttachAtomicsReadWriteModify()) {
7433     return AttachDecision::NoAction;
7434   }
7435 
7436   auto [objId, intPtrIndexId, numericValueId] =
7437       emitAtomicsReadWriteModifyOperands(callee);
7438 
7439   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7440   bool forEffect = (op_ == JSOp::CallIgnoresRv);
7441 
7442   writer.atomicsAddResult(objId, intPtrIndexId, numericValueId,
7443                           typedArray->type(), forEffect);
7444   writer.returnFromIC();
7445 
7446   trackAttached("AtomicsAdd");
7447   return AttachDecision::Attach;
7448 }
7449 
tryAttachAtomicsSub(HandleFunction callee)7450 AttachDecision CallIRGenerator::tryAttachAtomicsSub(HandleFunction callee) {
7451   if (!canAttachAtomicsReadWriteModify()) {
7452     return AttachDecision::NoAction;
7453   }
7454 
7455   auto [objId, intPtrIndexId, numericValueId] =
7456       emitAtomicsReadWriteModifyOperands(callee);
7457 
7458   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7459   bool forEffect = (op_ == JSOp::CallIgnoresRv);
7460 
7461   writer.atomicsSubResult(objId, intPtrIndexId, numericValueId,
7462                           typedArray->type(), forEffect);
7463   writer.returnFromIC();
7464 
7465   trackAttached("AtomicsSub");
7466   return AttachDecision::Attach;
7467 }
7468 
tryAttachAtomicsAnd(HandleFunction callee)7469 AttachDecision CallIRGenerator::tryAttachAtomicsAnd(HandleFunction callee) {
7470   if (!canAttachAtomicsReadWriteModify()) {
7471     return AttachDecision::NoAction;
7472   }
7473 
7474   auto [objId, intPtrIndexId, numericValueId] =
7475       emitAtomicsReadWriteModifyOperands(callee);
7476 
7477   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7478   bool forEffect = (op_ == JSOp::CallIgnoresRv);
7479 
7480   writer.atomicsAndResult(objId, intPtrIndexId, numericValueId,
7481                           typedArray->type(), forEffect);
7482   writer.returnFromIC();
7483 
7484   trackAttached("AtomicsAnd");
7485   return AttachDecision::Attach;
7486 }
7487 
tryAttachAtomicsOr(HandleFunction callee)7488 AttachDecision CallIRGenerator::tryAttachAtomicsOr(HandleFunction callee) {
7489   if (!canAttachAtomicsReadWriteModify()) {
7490     return AttachDecision::NoAction;
7491   }
7492 
7493   auto [objId, intPtrIndexId, numericValueId] =
7494       emitAtomicsReadWriteModifyOperands(callee);
7495 
7496   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7497   bool forEffect = (op_ == JSOp::CallIgnoresRv);
7498 
7499   writer.atomicsOrResult(objId, intPtrIndexId, numericValueId,
7500                          typedArray->type(), forEffect);
7501   writer.returnFromIC();
7502 
7503   trackAttached("AtomicsOr");
7504   return AttachDecision::Attach;
7505 }
7506 
tryAttachAtomicsXor(HandleFunction callee)7507 AttachDecision CallIRGenerator::tryAttachAtomicsXor(HandleFunction callee) {
7508   if (!canAttachAtomicsReadWriteModify()) {
7509     return AttachDecision::NoAction;
7510   }
7511 
7512   auto [objId, intPtrIndexId, numericValueId] =
7513       emitAtomicsReadWriteModifyOperands(callee);
7514 
7515   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7516   bool forEffect = (op_ == JSOp::CallIgnoresRv);
7517 
7518   writer.atomicsXorResult(objId, intPtrIndexId, numericValueId,
7519                           typedArray->type(), forEffect);
7520   writer.returnFromIC();
7521 
7522   trackAttached("AtomicsXor");
7523   return AttachDecision::Attach;
7524 }
7525 
tryAttachAtomicsLoad(HandleFunction callee)7526 AttachDecision CallIRGenerator::tryAttachAtomicsLoad(HandleFunction callee) {
7527   if (!JitSupportsAtomics()) {
7528     return AttachDecision::NoAction;
7529   }
7530 
7531   // Need two arguments.
7532   if (argc_ != 2) {
7533     return AttachDecision::NoAction;
7534   }
7535 
7536   // Arguments: typedArray, index (number).
7537   if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
7538     return AttachDecision::NoAction;
7539   }
7540   if (!args_[1].isNumber()) {
7541     return AttachDecision::NoAction;
7542   }
7543 
7544   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7545   if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
7546     return AttachDecision::NoAction;
7547   }
7548 
7549   // Initialize the input operand.
7550   Int32OperandId argcId(writer.setInputOperandId(0));
7551 
7552   // Guard callee is the `load` native function.
7553   emitNativeCalleeGuard(callee);
7554 
7555   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7556   ObjOperandId objId = writer.guardToObject(arg0Id);
7557   writer.guardShapeForClass(objId, typedArray->shape());
7558 
7559   // Convert index to intPtr.
7560   ValOperandId indexId =
7561       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7562   IntPtrOperandId intPtrIndexId =
7563       guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
7564 
7565   writer.atomicsLoadResult(objId, intPtrIndexId, typedArray->type());
7566   writer.returnFromIC();
7567 
7568   trackAttached("AtomicsLoad");
7569   return AttachDecision::Attach;
7570 }
7571 
tryAttachAtomicsStore(HandleFunction callee)7572 AttachDecision CallIRGenerator::tryAttachAtomicsStore(HandleFunction callee) {
7573   if (!JitSupportsAtomics()) {
7574     return AttachDecision::NoAction;
7575   }
7576 
7577   // Need three arguments.
7578   if (argc_ != 3) {
7579     return AttachDecision::NoAction;
7580   }
7581 
7582   // Atomics.store() is annoying because it returns the result of converting the
7583   // value by ToInteger(), not the input value, nor the result of converting the
7584   // value by ToInt32(). It is especially annoying because almost nobody uses
7585   // the result value.
7586   //
7587   // As an expedient compromise, therefore, we inline only if the result is
7588   // obviously unused or if the argument is already Int32 and thus requires no
7589   // conversion.
7590 
7591   // Arguments: typedArray, index (number), value.
7592   if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
7593     return AttachDecision::NoAction;
7594   }
7595   if (!args_[1].isNumber()) {
7596     return AttachDecision::NoAction;
7597   }
7598 
7599   auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
7600   if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
7601     return AttachDecision::NoAction;
7602   }
7603 
7604   Scalar::Type elementType = typedArray->type();
7605   if (!ValueIsNumeric(elementType, args_[2])) {
7606     return AttachDecision::NoAction;
7607   }
7608 
7609   bool guardIsInt32 =
7610       !Scalar::isBigIntType(elementType) && op_ != JSOp::CallIgnoresRv;
7611 
7612   if (guardIsInt32 && !args_[2].isInt32()) {
7613     return AttachDecision::NoAction;
7614   }
7615 
7616   // Initialize the input operand.
7617   Int32OperandId argcId(writer.setInputOperandId(0));
7618 
7619   // Guard callee is the `store` native function.
7620   emitNativeCalleeGuard(callee);
7621 
7622   ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7623   ObjOperandId objId = writer.guardToObject(arg0Id);
7624   writer.guardShapeForClass(objId, typedArray->shape());
7625 
7626   // Convert index to intPtr.
7627   ValOperandId indexId =
7628       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7629   IntPtrOperandId intPtrIndexId =
7630       guardToIntPtrIndex(args_[1], indexId, /* supportOOB = */ false);
7631 
7632   // Ensure value is int32 or BigInt.
7633   ValOperandId valueId =
7634       writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
7635   OperandId numericValueId;
7636   if (guardIsInt32) {
7637     numericValueId = writer.guardToInt32(valueId);
7638   } else {
7639     numericValueId = emitNumericGuard(valueId, elementType);
7640   }
7641 
7642   writer.atomicsStoreResult(objId, intPtrIndexId, numericValueId,
7643                             typedArray->type());
7644   writer.returnFromIC();
7645 
7646   trackAttached("AtomicsStore");
7647   return AttachDecision::Attach;
7648 }
7649 
tryAttachAtomicsIsLockFree(HandleFunction callee)7650 AttachDecision CallIRGenerator::tryAttachAtomicsIsLockFree(
7651     HandleFunction callee) {
7652   // Need one argument.
7653   if (argc_ != 1) {
7654     return AttachDecision::NoAction;
7655   }
7656 
7657   if (!args_[0].isInt32()) {
7658     return AttachDecision::NoAction;
7659   }
7660 
7661   // Initialize the input operand.
7662   Int32OperandId argcId(writer.setInputOperandId(0));
7663 
7664   // Guard callee is the `isLockFree` native function.
7665   emitNativeCalleeGuard(callee);
7666 
7667   // Ensure value is int32.
7668   ValOperandId valueId =
7669       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7670   Int32OperandId int32ValueId = writer.guardToInt32(valueId);
7671 
7672   writer.atomicsIsLockFreeResult(int32ValueId);
7673   writer.returnFromIC();
7674 
7675   trackAttached("AtomicsIsLockFree");
7676   return AttachDecision::Attach;
7677 }
7678 
tryAttachBoolean(HandleFunction callee)7679 AttachDecision CallIRGenerator::tryAttachBoolean(HandleFunction callee) {
7680   // Need zero or one argument.
7681   if (argc_ > 1) {
7682     return AttachDecision::NoAction;
7683   }
7684 
7685   // Initialize the input operand.
7686   Int32OperandId argcId(writer.setInputOperandId(0));
7687 
7688   // Guard callee is the 'Boolean' native function.
7689   emitNativeCalleeGuard(callee);
7690 
7691   if (argc_ == 0) {
7692     writer.loadBooleanResult(false);
7693   } else {
7694     ValOperandId valId =
7695         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7696 
7697     writer.loadValueTruthyResult(valId);
7698   }
7699 
7700   writer.returnFromIC();
7701 
7702   trackAttached("Boolean");
7703   return AttachDecision::Attach;
7704 }
7705 
tryAttachBailout(HandleFunction callee)7706 AttachDecision CallIRGenerator::tryAttachBailout(HandleFunction callee) {
7707   // Expecting no arguments.
7708   if (argc_ != 0) {
7709     return AttachDecision::NoAction;
7710   }
7711 
7712   // Initialize the input operand.
7713   Int32OperandId argcId(writer.setInputOperandId(0));
7714 
7715   // Guard callee is the 'bailout' native function.
7716   emitNativeCalleeGuard(callee);
7717 
7718   writer.bailout();
7719   writer.loadUndefinedResult();
7720   writer.returnFromIC();
7721 
7722   trackAttached("Bailout");
7723   return AttachDecision::Attach;
7724 }
7725 
tryAttachAssertFloat32(HandleFunction callee)7726 AttachDecision CallIRGenerator::tryAttachAssertFloat32(HandleFunction callee) {
7727   // Expecting two arguments.
7728   if (argc_ != 2) {
7729     return AttachDecision::NoAction;
7730   }
7731 
7732   // Initialize the input operand.
7733   Int32OperandId argcId(writer.setInputOperandId(0));
7734 
7735   // Guard callee is the 'assertFloat32' native function.
7736   emitNativeCalleeGuard(callee);
7737 
7738   // TODO: Warp doesn't yet optimize Float32 (bug 1655773).
7739 
7740   // NOP when not in IonMonkey.
7741   writer.loadUndefinedResult();
7742   writer.returnFromIC();
7743 
7744   trackAttached("AssertFloat32");
7745   return AttachDecision::Attach;
7746 }
7747 
tryAttachAssertRecoveredOnBailout(HandleFunction callee)7748 AttachDecision CallIRGenerator::tryAttachAssertRecoveredOnBailout(
7749     HandleFunction callee) {
7750   // Expecting two arguments.
7751   if (argc_ != 2) {
7752     return AttachDecision::NoAction;
7753   }
7754 
7755   // (Fuzzing unsafe) testing function which must be called with a constant
7756   // boolean as its second argument.
7757   bool mustBeRecovered = args_[1].toBoolean();
7758 
7759   // Initialize the input operand.
7760   Int32OperandId argcId(writer.setInputOperandId(0));
7761 
7762   // Guard callee is the 'assertRecoveredOnBailout' native function.
7763   emitNativeCalleeGuard(callee);
7764 
7765   ValOperandId valId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7766 
7767   writer.assertRecoveredOnBailoutResult(valId, mustBeRecovered);
7768   writer.returnFromIC();
7769 
7770   trackAttached("AssertRecoveredOnBailout");
7771   return AttachDecision::Attach;
7772 }
7773 
tryAttachObjectIs(HandleFunction callee)7774 AttachDecision CallIRGenerator::tryAttachObjectIs(HandleFunction callee) {
7775   // Need two arguments.
7776   if (argc_ != 2) {
7777     return AttachDecision::NoAction;
7778   }
7779 
7780   // Initialize the input operand.
7781   Int32OperandId argcId(writer.setInputOperandId(0));
7782 
7783   // Guard callee is the `is` native function.
7784   emitNativeCalleeGuard(callee);
7785 
7786   ValOperandId lhsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7787   ValOperandId rhsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7788 
7789   HandleValue lhs = args_[0];
7790   HandleValue rhs = args_[1];
7791 
7792   if (!isFirstStub_) {
7793     writer.sameValueResult(lhsId, rhsId);
7794   } else if (lhs.isNumber() && rhs.isNumber() &&
7795              !(lhs.isInt32() && rhs.isInt32())) {
7796     NumberOperandId lhsNumId = writer.guardIsNumber(lhsId);
7797     NumberOperandId rhsNumId = writer.guardIsNumber(rhsId);
7798     writer.compareDoubleSameValueResult(lhsNumId, rhsNumId);
7799   } else if (!SameType(lhs, rhs)) {
7800     // Compare tags for strictly different types.
7801     ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
7802     ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
7803     writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
7804     writer.loadBooleanResult(false);
7805   } else {
7806     MOZ_ASSERT(lhs.type() == rhs.type());
7807     MOZ_ASSERT(lhs.type() != JS::ValueType::Double);
7808 
7809     switch (lhs.type()) {
7810       case JS::ValueType::Int32: {
7811         Int32OperandId lhsIntId = writer.guardToInt32(lhsId);
7812         Int32OperandId rhsIntId = writer.guardToInt32(rhsId);
7813         writer.compareInt32Result(JSOp::StrictEq, lhsIntId, rhsIntId);
7814         break;
7815       }
7816       case JS::ValueType::Boolean: {
7817         Int32OperandId lhsIntId = writer.guardBooleanToInt32(lhsId);
7818         Int32OperandId rhsIntId = writer.guardBooleanToInt32(rhsId);
7819         writer.compareInt32Result(JSOp::StrictEq, lhsIntId, rhsIntId);
7820         break;
7821       }
7822       case JS::ValueType::Undefined: {
7823         writer.guardIsUndefined(lhsId);
7824         writer.guardIsUndefined(rhsId);
7825         writer.loadBooleanResult(true);
7826         break;
7827       }
7828       case JS::ValueType::Null: {
7829         writer.guardIsNull(lhsId);
7830         writer.guardIsNull(rhsId);
7831         writer.loadBooleanResult(true);
7832         break;
7833       }
7834       case JS::ValueType::String: {
7835         StringOperandId lhsStrId = writer.guardToString(lhsId);
7836         StringOperandId rhsStrId = writer.guardToString(rhsId);
7837         writer.compareStringResult(JSOp::StrictEq, lhsStrId, rhsStrId);
7838         break;
7839       }
7840       case JS::ValueType::Symbol: {
7841         SymbolOperandId lhsSymId = writer.guardToSymbol(lhsId);
7842         SymbolOperandId rhsSymId = writer.guardToSymbol(rhsId);
7843         writer.compareSymbolResult(JSOp::StrictEq, lhsSymId, rhsSymId);
7844         break;
7845       }
7846       case JS::ValueType::BigInt: {
7847         BigIntOperandId lhsBigIntId = writer.guardToBigInt(lhsId);
7848         BigIntOperandId rhsBigIntId = writer.guardToBigInt(rhsId);
7849         writer.compareBigIntResult(JSOp::StrictEq, lhsBigIntId, rhsBigIntId);
7850         break;
7851       }
7852       case JS::ValueType::Object: {
7853         ObjOperandId lhsObjId = writer.guardToObject(lhsId);
7854         ObjOperandId rhsObjId = writer.guardToObject(rhsId);
7855         writer.compareObjectResult(JSOp::StrictEq, lhsObjId, rhsObjId);
7856         break;
7857       }
7858 
7859 #ifdef ENABLE_RECORD_TUPLE
7860       case ValueType::ExtendedPrimitive:
7861 #endif
7862       case JS::ValueType::Double:
7863       case JS::ValueType::Magic:
7864       case JS::ValueType::PrivateGCThing:
7865         MOZ_CRASH("Unexpected type");
7866     }
7867   }
7868 
7869   writer.returnFromIC();
7870 
7871   trackAttached("ObjectIs");
7872   return AttachDecision::Attach;
7873 }
7874 
tryAttachObjectIsPrototypeOf(HandleFunction callee)7875 AttachDecision CallIRGenerator::tryAttachObjectIsPrototypeOf(
7876     HandleFunction callee) {
7877   // Ensure |this| is an object.
7878   if (!thisval_.isObject()) {
7879     return AttachDecision::NoAction;
7880   }
7881 
7882   // Need a single argument.
7883   if (argc_ != 1) {
7884     return AttachDecision::NoAction;
7885   }
7886 
7887   // Initialize the input operand.
7888   Int32OperandId argcId(writer.setInputOperandId(0));
7889 
7890   // Guard callee is the `isPrototypeOf` native function.
7891   emitNativeCalleeGuard(callee);
7892 
7893   // Guard that |this| is an object.
7894   ValOperandId thisValId =
7895       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
7896   ObjOperandId thisObjId = writer.guardToObject(thisValId);
7897 
7898   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7899 
7900   writer.loadInstanceOfObjectResult(argId, thisObjId);
7901   writer.returnFromIC();
7902 
7903   trackAttached("ObjectIsPrototypeOf");
7904   return AttachDecision::Attach;
7905 }
7906 
tryAttachObjectToString(HandleFunction callee)7907 AttachDecision CallIRGenerator::tryAttachObjectToString(HandleFunction callee) {
7908   // Expecting no arguments.
7909   if (argc_ != 0) {
7910     return AttachDecision::NoAction;
7911   }
7912 
7913   // Ensure |this| is an object.
7914   if (!thisval_.isObject()) {
7915     return AttachDecision::NoAction;
7916   }
7917 
7918   // Don't attach if the object has @@toStringTag or is a proxy.
7919   if (!ObjectClassToString(cx_, &thisval_.toObject())) {
7920     return AttachDecision::NoAction;
7921   }
7922 
7923   // Initialize the input operand.
7924   Int32OperandId argcId(writer.setInputOperandId(0));
7925 
7926   // Guard callee is the 'toString' native function.
7927   emitNativeCalleeGuard(callee);
7928 
7929   // Guard that |this| is an object.
7930   ValOperandId thisValId =
7931       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
7932   ObjOperandId thisObjId = writer.guardToObject(thisValId);
7933 
7934   writer.objectToStringResult(thisObjId);
7935   writer.returnFromIC();
7936 
7937   trackAttached("ObjectToString");
7938   return AttachDecision::Attach;
7939 }
7940 
tryAttachBigIntAsIntN(HandleFunction callee)7941 AttachDecision CallIRGenerator::tryAttachBigIntAsIntN(HandleFunction callee) {
7942   // Need two arguments (Int32, BigInt).
7943   if (argc_ != 2 || !args_[0].isInt32() || !args_[1].isBigInt()) {
7944     return AttachDecision::NoAction;
7945   }
7946 
7947   // Negative bits throws an error.
7948   if (args_[0].toInt32() < 0) {
7949     return AttachDecision::NoAction;
7950   }
7951 
7952   // Initialize the input operand.
7953   Int32OperandId argcId(writer.setInputOperandId(0));
7954 
7955   // Guard callee is the 'BigInt.asIntN' native function.
7956   emitNativeCalleeGuard(callee);
7957 
7958   // Convert bits to int32.
7959   ValOperandId bitsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7960   Int32OperandId int32BitsId = writer.guardToInt32Index(bitsId);
7961 
7962   // Number of bits mustn't be negative.
7963   writer.guardInt32IsNonNegative(int32BitsId);
7964 
7965   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
7966   BigIntOperandId bigIntId = writer.guardToBigInt(arg1Id);
7967 
7968   writer.bigIntAsIntNResult(int32BitsId, bigIntId);
7969   writer.returnFromIC();
7970 
7971   trackAttached("BigIntAsIntN");
7972   return AttachDecision::Attach;
7973 }
7974 
tryAttachBigIntAsUintN(HandleFunction callee)7975 AttachDecision CallIRGenerator::tryAttachBigIntAsUintN(HandleFunction callee) {
7976   // Need two arguments (Int32, BigInt).
7977   if (argc_ != 2 || !args_[0].isInt32() || !args_[1].isBigInt()) {
7978     return AttachDecision::NoAction;
7979   }
7980 
7981   // Negative bits throws an error.
7982   if (args_[0].toInt32() < 0) {
7983     return AttachDecision::NoAction;
7984   }
7985 
7986   // Initialize the input operand.
7987   Int32OperandId argcId(writer.setInputOperandId(0));
7988 
7989   // Guard callee is the 'BigInt.asUintN' native function.
7990   emitNativeCalleeGuard(callee);
7991 
7992   // Convert bits to int32.
7993   ValOperandId bitsId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
7994   Int32OperandId int32BitsId = writer.guardToInt32Index(bitsId);
7995 
7996   // Number of bits mustn't be negative.
7997   writer.guardInt32IsNonNegative(int32BitsId);
7998 
7999   ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
8000   BigIntOperandId bigIntId = writer.guardToBigInt(arg1Id);
8001 
8002   writer.bigIntAsUintNResult(int32BitsId, bigIntId);
8003   writer.returnFromIC();
8004 
8005   trackAttached("BigIntAsUintN");
8006   return AttachDecision::Attach;
8007 }
8008 
tryAttachSetHas(HandleFunction callee)8009 AttachDecision CallIRGenerator::tryAttachSetHas(HandleFunction callee) {
8010   // Ensure |this| is a SetObject.
8011   if (!thisval_.isObject() || !thisval_.toObject().is<SetObject>()) {
8012     return AttachDecision::NoAction;
8013   }
8014 
8015   // Need a single argument.
8016   if (argc_ != 1) {
8017     return AttachDecision::NoAction;
8018   }
8019 
8020   // Initialize the input operand.
8021   Int32OperandId argcId(writer.setInputOperandId(0));
8022 
8023   // Guard callee is the 'has' native function.
8024   emitNativeCalleeGuard(callee);
8025 
8026   // Guard |this| is a SetObject.
8027   ValOperandId thisValId =
8028       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
8029   ObjOperandId objId = writer.guardToObject(thisValId);
8030   writer.guardClass(objId, GuardClassKind::Set);
8031 
8032   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8033 
8034 #ifndef JS_CODEGEN_X86
8035   // Assume the hash key will likely always have the same type when attaching
8036   // the first stub. If the call is polymorphic on the hash key, attach a stub
8037   // which handles any value.
8038   if (isFirstStub_) {
8039     switch (args_[0].type()) {
8040       case ValueType::Double:
8041       case ValueType::Int32:
8042       case ValueType::Boolean:
8043       case ValueType::Undefined:
8044       case ValueType::Null: {
8045         writer.guardToNonGCThing(argId);
8046         writer.setHasNonGCThingResult(objId, argId);
8047         break;
8048       }
8049       case ValueType::String: {
8050         StringOperandId strId = writer.guardToString(argId);
8051         writer.setHasStringResult(objId, strId);
8052         break;
8053       }
8054       case ValueType::Symbol: {
8055         SymbolOperandId symId = writer.guardToSymbol(argId);
8056         writer.setHasSymbolResult(objId, symId);
8057         break;
8058       }
8059       case ValueType::BigInt: {
8060         BigIntOperandId bigIntId = writer.guardToBigInt(argId);
8061         writer.setHasBigIntResult(objId, bigIntId);
8062         break;
8063       }
8064       case ValueType::Object: {
8065         // Currently only supported on 64-bit platforms.
8066 #  ifdef JS_PUNBOX64
8067         ObjOperandId valId = writer.guardToObject(argId);
8068         writer.setHasObjectResult(objId, valId);
8069 #  else
8070         writer.setHasResult(objId, argId);
8071 #  endif
8072         break;
8073       }
8074 
8075 #  ifdef ENABLE_RECORD_TUPLE
8076       case ValueType::ExtendedPrimitive:
8077 #  endif
8078       case ValueType::Magic:
8079       case ValueType::PrivateGCThing:
8080         MOZ_CRASH("Unexpected type");
8081     }
8082   } else {
8083     writer.setHasResult(objId, argId);
8084   }
8085 #else
8086   // The optimized versions require too many registers on x86.
8087   writer.setHasResult(objId, argId);
8088 #endif
8089 
8090   writer.returnFromIC();
8091 
8092   trackAttached("SetHas");
8093   return AttachDecision::Attach;
8094 }
8095 
tryAttachMapHas(HandleFunction callee)8096 AttachDecision CallIRGenerator::tryAttachMapHas(HandleFunction callee) {
8097   // Ensure |this| is a MapObject.
8098   if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
8099     return AttachDecision::NoAction;
8100   }
8101 
8102   // Need a single argument.
8103   if (argc_ != 1) {
8104     return AttachDecision::NoAction;
8105   }
8106 
8107   // Initialize the input operand.
8108   Int32OperandId argcId(writer.setInputOperandId(0));
8109 
8110   // Guard callee is the 'has' native function.
8111   emitNativeCalleeGuard(callee);
8112 
8113   // Guard |this| is a MapObject.
8114   ValOperandId thisValId =
8115       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
8116   ObjOperandId objId = writer.guardToObject(thisValId);
8117   writer.guardClass(objId, GuardClassKind::Map);
8118 
8119   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8120 
8121 #ifndef JS_CODEGEN_X86
8122   // Assume the hash key will likely always have the same type when attaching
8123   // the first stub. If the call is polymorphic on the hash key, attach a stub
8124   // which handles any value.
8125   if (isFirstStub_) {
8126     switch (args_[0].type()) {
8127       case ValueType::Double:
8128       case ValueType::Int32:
8129       case ValueType::Boolean:
8130       case ValueType::Undefined:
8131       case ValueType::Null: {
8132         writer.guardToNonGCThing(argId);
8133         writer.mapHasNonGCThingResult(objId, argId);
8134         break;
8135       }
8136       case ValueType::String: {
8137         StringOperandId strId = writer.guardToString(argId);
8138         writer.mapHasStringResult(objId, strId);
8139         break;
8140       }
8141       case ValueType::Symbol: {
8142         SymbolOperandId symId = writer.guardToSymbol(argId);
8143         writer.mapHasSymbolResult(objId, symId);
8144         break;
8145       }
8146       case ValueType::BigInt: {
8147         BigIntOperandId bigIntId = writer.guardToBigInt(argId);
8148         writer.mapHasBigIntResult(objId, bigIntId);
8149         break;
8150       }
8151       case ValueType::Object: {
8152         // Currently only supported on 64-bit platforms.
8153 #  ifdef JS_PUNBOX64
8154         ObjOperandId valId = writer.guardToObject(argId);
8155         writer.mapHasObjectResult(objId, valId);
8156 #  else
8157         writer.mapHasResult(objId, argId);
8158 #  endif
8159         break;
8160       }
8161 
8162 #  ifdef ENABLE_RECORD_TUPLE
8163       case ValueType::ExtendedPrimitive:
8164 #  endif
8165       case ValueType::Magic:
8166       case ValueType::PrivateGCThing:
8167         MOZ_CRASH("Unexpected type");
8168     }
8169   } else {
8170     writer.mapHasResult(objId, argId);
8171   }
8172 #else
8173   // The optimized versions require too many registers on x86.
8174   writer.mapHasResult(objId, argId);
8175 #endif
8176 
8177   writer.returnFromIC();
8178 
8179   trackAttached("MapHas");
8180   return AttachDecision::Attach;
8181 }
8182 
tryAttachMapGet(HandleFunction callee)8183 AttachDecision CallIRGenerator::tryAttachMapGet(HandleFunction callee) {
8184   // Ensure |this| is a MapObject.
8185   if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) {
8186     return AttachDecision::NoAction;
8187   }
8188 
8189   // Need a single argument.
8190   if (argc_ != 1) {
8191     return AttachDecision::NoAction;
8192   }
8193 
8194   // Initialize the input operand.
8195   Int32OperandId argcId(writer.setInputOperandId(0));
8196 
8197   // Guard callee is the 'get' native function.
8198   emitNativeCalleeGuard(callee);
8199 
8200   // Guard |this| is a MapObject.
8201   ValOperandId thisValId =
8202       writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
8203   ObjOperandId objId = writer.guardToObject(thisValId);
8204   writer.guardClass(objId, GuardClassKind::Map);
8205 
8206   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8207 
8208 #ifndef JS_CODEGEN_X86
8209   // Assume the hash key will likely always have the same type when attaching
8210   // the first stub. If the call is polymorphic on the hash key, attach a stub
8211   // which handles any value.
8212   if (isFirstStub_) {
8213     switch (args_[0].type()) {
8214       case ValueType::Double:
8215       case ValueType::Int32:
8216       case ValueType::Boolean:
8217       case ValueType::Undefined:
8218       case ValueType::Null: {
8219         writer.guardToNonGCThing(argId);
8220         writer.mapGetNonGCThingResult(objId, argId);
8221         break;
8222       }
8223       case ValueType::String: {
8224         StringOperandId strId = writer.guardToString(argId);
8225         writer.mapGetStringResult(objId, strId);
8226         break;
8227       }
8228       case ValueType::Symbol: {
8229         SymbolOperandId symId = writer.guardToSymbol(argId);
8230         writer.mapGetSymbolResult(objId, symId);
8231         break;
8232       }
8233       case ValueType::BigInt: {
8234         BigIntOperandId bigIntId = writer.guardToBigInt(argId);
8235         writer.mapGetBigIntResult(objId, bigIntId);
8236         break;
8237       }
8238       case ValueType::Object: {
8239         // Currently only supported on 64-bit platforms.
8240 #  ifdef JS_PUNBOX64
8241         ObjOperandId valId = writer.guardToObject(argId);
8242         writer.mapGetObjectResult(objId, valId);
8243 #  else
8244         writer.mapGetResult(objId, argId);
8245 #  endif
8246         break;
8247       }
8248 
8249 #  ifdef ENABLE_RECORD_TUPLE
8250       case ValueType::ExtendedPrimitive:
8251 #  endif
8252       case ValueType::Magic:
8253       case ValueType::PrivateGCThing:
8254         MOZ_CRASH("Unexpected type");
8255     }
8256   } else {
8257     writer.mapGetResult(objId, argId);
8258   }
8259 #else
8260   // The optimized versions require too many registers on x86.
8261   writer.mapGetResult(objId, argId);
8262 #endif
8263 
8264   writer.returnFromIC();
8265 
8266   trackAttached("MapGet");
8267   return AttachDecision::Attach;
8268 }
8269 
tryAttachFunCall(HandleFunction callee)8270 AttachDecision CallIRGenerator::tryAttachFunCall(HandleFunction callee) {
8271   if (!callee->isNativeWithoutJitEntry() || callee->native() != fun_call) {
8272     return AttachDecision::NoAction;
8273   }
8274 
8275   if (!thisval_.isObject() || !thisval_.toObject().is<JSFunction>()) {
8276     return AttachDecision::NoAction;
8277   }
8278   auto* target = &thisval_.toObject().as<JSFunction>();
8279 
8280   bool isScripted = target->hasJitEntry();
8281   MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
8282 
8283   if (target->isClassConstructor()) {
8284     return AttachDecision::NoAction;
8285   }
8286   Int32OperandId argcId(writer.setInputOperandId(0));
8287 
8288   // Guard that callee is the |fun_call| native function.
8289   ValOperandId calleeValId =
8290       writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId);
8291   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
8292   writer.guardSpecificFunction(calleeObjId, callee);
8293 
8294   // Guard that |this| is an object.
8295   ValOperandId thisValId =
8296       writer.loadArgumentDynamicSlot(ArgumentKind::This, argcId);
8297   ObjOperandId thisObjId = writer.guardToObject(thisValId);
8298 
8299   CallFlags targetFlags(CallFlags::FunCall);
8300   if (mode_ == ICState::Mode::Specialized) {
8301     // Ensure that |this| is the expected target function.
8302     emitCalleeGuard(thisObjId, target);
8303 
8304     if (cx_->realm() == target->realm()) {
8305       targetFlags.setIsSameRealm();
8306     }
8307 
8308     if (isScripted) {
8309       writer.callScriptedFunction(thisObjId, argcId, targetFlags);
8310     } else {
8311       writer.callNativeFunction(thisObjId, argcId, op_, target, targetFlags);
8312     }
8313   } else {
8314     // Guard that |this| is a function.
8315     writer.guardClass(thisObjId, GuardClassKind::JSFunction);
8316 
8317     // Guard that function is not a class constructor.
8318     writer.guardNotClassConstructor(thisObjId);
8319 
8320     if (isScripted) {
8321       writer.guardFunctionHasJitEntry(thisObjId, /*isConstructing =*/false);
8322       writer.callScriptedFunction(thisObjId, argcId, targetFlags);
8323     } else {
8324       writer.guardFunctionHasNoJitEntry(thisObjId);
8325       writer.callAnyNativeFunction(thisObjId, argcId, targetFlags);
8326     }
8327   }
8328 
8329   writer.returnFromIC();
8330 
8331   if (isScripted) {
8332     trackAttached("Scripted fun_call");
8333   } else {
8334     trackAttached("Native fun_call");
8335   }
8336 
8337   return AttachDecision::Attach;
8338 }
8339 
tryAttachIsTypedArray(HandleFunction callee,bool isPossiblyWrapped)8340 AttachDecision CallIRGenerator::tryAttachIsTypedArray(HandleFunction callee,
8341                                                       bool isPossiblyWrapped) {
8342   // Self-hosted code calls this with a single object argument.
8343   MOZ_ASSERT(argc_ == 1);
8344   MOZ_ASSERT(args_[0].isObject());
8345 
8346   // Initialize the input operand.
8347   Int32OperandId argcId(writer.setInputOperandId(0));
8348 
8349   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8350 
8351   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8352   ObjOperandId objArgId = writer.guardToObject(argId);
8353   writer.isTypedArrayResult(objArgId, isPossiblyWrapped);
8354   writer.returnFromIC();
8355 
8356   trackAttached(isPossiblyWrapped ? "IsPossiblyWrappedTypedArray"
8357                                   : "IsTypedArray");
8358   return AttachDecision::Attach;
8359 }
8360 
tryAttachIsTypedArrayConstructor(HandleFunction callee)8361 AttachDecision CallIRGenerator::tryAttachIsTypedArrayConstructor(
8362     HandleFunction callee) {
8363   // Self-hosted code calls this with a single object argument.
8364   MOZ_ASSERT(argc_ == 1);
8365   MOZ_ASSERT(args_[0].isObject());
8366 
8367   // Initialize the input operand.
8368   Int32OperandId argcId(writer.setInputOperandId(0));
8369 
8370   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8371 
8372   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8373   ObjOperandId objArgId = writer.guardToObject(argId);
8374   writer.isTypedArrayConstructorResult(objArgId);
8375   writer.returnFromIC();
8376 
8377   trackAttached("IsTypedArrayConstructor");
8378   return AttachDecision::Attach;
8379 }
8380 
tryAttachTypedArrayByteOffset(HandleFunction callee)8381 AttachDecision CallIRGenerator::tryAttachTypedArrayByteOffset(
8382     HandleFunction callee) {
8383   // Self-hosted code calls this with a single TypedArrayObject argument.
8384   MOZ_ASSERT(argc_ == 1);
8385   MOZ_ASSERT(args_[0].isObject());
8386   MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>());
8387 
8388   auto* tarr = &args_[0].toObject().as<TypedArrayObject>();
8389 
8390   // Initialize the input operand.
8391   Int32OperandId argcId(writer.setInputOperandId(0));
8392 
8393   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8394 
8395   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8396   ObjOperandId objArgId = writer.guardToObject(argId);
8397   if (tarr->byteOffset() <= INT32_MAX) {
8398     writer.arrayBufferViewByteOffsetInt32Result(objArgId);
8399   } else {
8400     writer.arrayBufferViewByteOffsetDoubleResult(objArgId);
8401   }
8402   writer.returnFromIC();
8403 
8404   trackAttached("TypedArrayByteOffset");
8405   return AttachDecision::Attach;
8406 }
8407 
tryAttachTypedArrayElementSize(HandleFunction callee)8408 AttachDecision CallIRGenerator::tryAttachTypedArrayElementSize(
8409     HandleFunction callee) {
8410   // Self-hosted code calls this with a single TypedArrayObject argument.
8411   MOZ_ASSERT(argc_ == 1);
8412   MOZ_ASSERT(args_[0].isObject());
8413   MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>());
8414 
8415   // Initialize the input operand.
8416   Int32OperandId argcId(writer.setInputOperandId(0));
8417 
8418   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8419 
8420   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8421   ObjOperandId objArgId = writer.guardToObject(argId);
8422   writer.typedArrayElementSizeResult(objArgId);
8423   writer.returnFromIC();
8424 
8425   trackAttached("TypedArrayElementSize");
8426   return AttachDecision::Attach;
8427 }
8428 
tryAttachTypedArrayLength(HandleFunction callee,bool isPossiblyWrapped)8429 AttachDecision CallIRGenerator::tryAttachTypedArrayLength(
8430     HandleFunction callee, bool isPossiblyWrapped) {
8431   // Self-hosted code calls this with a single, possibly wrapped,
8432   // TypedArrayObject argument.
8433   MOZ_ASSERT(argc_ == 1);
8434   MOZ_ASSERT(args_[0].isObject());
8435 
8436   // Only optimize when the object isn't a wrapper.
8437   if (isPossiblyWrapped && IsWrapper(&args_[0].toObject())) {
8438     return AttachDecision::NoAction;
8439   }
8440 
8441   MOZ_ASSERT(args_[0].toObject().is<TypedArrayObject>());
8442 
8443   auto* tarr = &args_[0].toObject().as<TypedArrayObject>();
8444 
8445   // Initialize the input operand.
8446   Int32OperandId argcId(writer.setInputOperandId(0));
8447 
8448   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8449 
8450   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8451   ObjOperandId objArgId = writer.guardToObject(argId);
8452 
8453   if (isPossiblyWrapped) {
8454     writer.guardIsNotProxy(objArgId);
8455   }
8456 
8457   if (tarr->length() <= INT32_MAX) {
8458     writer.loadArrayBufferViewLengthInt32Result(objArgId);
8459   } else {
8460     writer.loadArrayBufferViewLengthDoubleResult(objArgId);
8461   }
8462   writer.returnFromIC();
8463 
8464   trackAttached("TypedArrayLength");
8465   return AttachDecision::Attach;
8466 }
8467 
tryAttachArrayBufferByteLength(HandleFunction callee,bool isPossiblyWrapped)8468 AttachDecision CallIRGenerator::tryAttachArrayBufferByteLength(
8469     HandleFunction callee, bool isPossiblyWrapped) {
8470   // Self-hosted code calls this with a single, possibly wrapped,
8471   // ArrayBufferObject argument.
8472   MOZ_ASSERT(argc_ == 1);
8473   MOZ_ASSERT(args_[0].isObject());
8474 
8475   // Only optimize when the object isn't a wrapper.
8476   if (isPossiblyWrapped && IsWrapper(&args_[0].toObject())) {
8477     return AttachDecision::NoAction;
8478   }
8479 
8480   MOZ_ASSERT(args_[0].toObject().is<ArrayBufferObject>());
8481 
8482   auto* buffer = &args_[0].toObject().as<ArrayBufferObject>();
8483 
8484   // Initialize the input operand.
8485   Int32OperandId argcId(writer.setInputOperandId(0));
8486 
8487   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8488 
8489   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8490   ObjOperandId objArgId = writer.guardToObject(argId);
8491 
8492   if (isPossiblyWrapped) {
8493     writer.guardIsNotProxy(objArgId);
8494   }
8495 
8496   if (buffer->byteLength() <= INT32_MAX) {
8497     writer.loadArrayBufferByteLengthInt32Result(objArgId);
8498   } else {
8499     writer.loadArrayBufferByteLengthDoubleResult(objArgId);
8500   }
8501   writer.returnFromIC();
8502 
8503   trackAttached("ArrayBufferByteLength");
8504   return AttachDecision::Attach;
8505 }
8506 
tryAttachIsConstructing(HandleFunction callee)8507 AttachDecision CallIRGenerator::tryAttachIsConstructing(HandleFunction callee) {
8508   // Self-hosted code calls this with no arguments in function scripts.
8509   MOZ_ASSERT(argc_ == 0);
8510   MOZ_ASSERT(script_->isFunction());
8511 
8512   // Initialize the input operand.
8513   Int32OperandId argcId(writer.setInputOperandId(0));
8514 
8515   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8516 
8517   writer.frameIsConstructingResult();
8518   writer.returnFromIC();
8519 
8520   trackAttached("IsConstructing");
8521   return AttachDecision::Attach;
8522 }
8523 
tryAttachGetNextMapSetEntryForIterator(HandleFunction callee,bool isMap)8524 AttachDecision CallIRGenerator::tryAttachGetNextMapSetEntryForIterator(
8525     HandleFunction callee, bool isMap) {
8526   // Self-hosted code calls this with two objects.
8527   MOZ_ASSERT(argc_ == 2);
8528   if (isMap) {
8529     MOZ_ASSERT(args_[0].toObject().is<MapIteratorObject>());
8530   } else {
8531     MOZ_ASSERT(args_[0].toObject().is<SetIteratorObject>());
8532   }
8533   MOZ_ASSERT(args_[1].toObject().is<ArrayObject>());
8534 
8535   // Initialize the input operand.
8536   Int32OperandId argcId(writer.setInputOperandId(0));
8537 
8538   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8539 
8540   ValOperandId iterId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8541   ObjOperandId objIterId = writer.guardToObject(iterId);
8542 
8543   ValOperandId resultArrId =
8544       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
8545   ObjOperandId objResultArrId = writer.guardToObject(resultArrId);
8546 
8547   writer.getNextMapSetEntryForIteratorResult(objIterId, objResultArrId, isMap);
8548   writer.returnFromIC();
8549 
8550   trackAttached("GetNextMapSetEntryForIterator");
8551   return AttachDecision::Attach;
8552 }
8553 
tryAttachFinishBoundFunctionInit(HandleFunction callee)8554 AttachDecision CallIRGenerator::tryAttachFinishBoundFunctionInit(
8555     HandleFunction callee) {
8556   // Self-hosted code calls this with (boundFunction, targetObj, argCount)
8557   // arguments.
8558   MOZ_ASSERT(argc_ == 3);
8559   MOZ_ASSERT(args_[0].toObject().is<JSFunction>());
8560   MOZ_ASSERT(args_[1].isObject());
8561   MOZ_ASSERT(args_[2].isInt32());
8562 
8563   // Initialize the input operand.
8564   Int32OperandId argcId(writer.setInputOperandId(0));
8565 
8566   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8567 
8568   ValOperandId boundId =
8569       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8570   ObjOperandId objBoundId = writer.guardToObject(boundId);
8571 
8572   ValOperandId targetId =
8573       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
8574   ObjOperandId objTargetId = writer.guardToObject(targetId);
8575 
8576   ValOperandId argCountId =
8577       writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
8578   Int32OperandId int32ArgCountId = writer.guardToInt32(argCountId);
8579 
8580   writer.finishBoundFunctionInitResult(objBoundId, objTargetId,
8581                                        int32ArgCountId);
8582   writer.returnFromIC();
8583 
8584   trackAttached("FinishBoundFunctionInit");
8585   return AttachDecision::Attach;
8586 }
8587 
tryAttachNewArrayIterator(HandleFunction callee)8588 AttachDecision CallIRGenerator::tryAttachNewArrayIterator(
8589     HandleFunction callee) {
8590   // Self-hosted code calls this without any arguments
8591   MOZ_ASSERT(argc_ == 0);
8592 
8593   JSObject* templateObj = NewArrayIteratorTemplate(cx_);
8594   if (!templateObj) {
8595     cx_->recoverFromOutOfMemory();
8596     return AttachDecision::NoAction;
8597   }
8598 
8599   // Initialize the input operand.
8600   Int32OperandId argcId(writer.setInputOperandId(0));
8601 
8602   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8603 
8604   writer.newArrayIteratorResult(templateObj);
8605   writer.returnFromIC();
8606 
8607   trackAttached("NewArrayIterator");
8608   return AttachDecision::Attach;
8609 }
8610 
tryAttachNewStringIterator(HandleFunction callee)8611 AttachDecision CallIRGenerator::tryAttachNewStringIterator(
8612     HandleFunction callee) {
8613   // Self-hosted code calls this without any arguments
8614   MOZ_ASSERT(argc_ == 0);
8615 
8616   JSObject* templateObj = NewStringIteratorTemplate(cx_);
8617   if (!templateObj) {
8618     cx_->recoverFromOutOfMemory();
8619     return AttachDecision::NoAction;
8620   }
8621 
8622   // Initialize the input operand.
8623   Int32OperandId argcId(writer.setInputOperandId(0));
8624 
8625   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8626 
8627   writer.newStringIteratorResult(templateObj);
8628   writer.returnFromIC();
8629 
8630   trackAttached("NewStringIterator");
8631   return AttachDecision::Attach;
8632 }
8633 
tryAttachNewRegExpStringIterator(HandleFunction callee)8634 AttachDecision CallIRGenerator::tryAttachNewRegExpStringIterator(
8635     HandleFunction callee) {
8636   // Self-hosted code calls this without any arguments
8637   MOZ_ASSERT(argc_ == 0);
8638 
8639   JSObject* templateObj = NewRegExpStringIteratorTemplate(cx_);
8640   if (!templateObj) {
8641     cx_->recoverFromOutOfMemory();
8642     return AttachDecision::NoAction;
8643   }
8644 
8645   // Initialize the input operand.
8646   Int32OperandId argcId(writer.setInputOperandId(0));
8647 
8648   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8649 
8650   writer.newRegExpStringIteratorResult(templateObj);
8651   writer.returnFromIC();
8652 
8653   trackAttached("NewRegExpStringIterator");
8654   return AttachDecision::Attach;
8655 }
8656 
tryAttachArrayIteratorPrototypeOptimizable(HandleFunction callee)8657 AttachDecision CallIRGenerator::tryAttachArrayIteratorPrototypeOptimizable(
8658     HandleFunction callee) {
8659   // Self-hosted code calls this without any arguments
8660   MOZ_ASSERT(argc_ == 0);
8661 
8662   if (!isFirstStub_) {
8663     // Attach only once to prevent slowdowns for polymorphic calls.
8664     return AttachDecision::NoAction;
8665   }
8666 
8667   NativeObject* arrayIteratorProto;
8668   uint32_t slot;
8669   JSFunction* nextFun;
8670   if (!IsArrayIteratorPrototypeOptimizable(cx_, &arrayIteratorProto, &slot,
8671                                            &nextFun)) {
8672     return AttachDecision::NoAction;
8673   }
8674 
8675   // Initialize the input operand.
8676   Int32OperandId argcId(writer.setInputOperandId(0));
8677 
8678   // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
8679 
8680   ObjOperandId protoId = writer.loadObject(arrayIteratorProto);
8681   ObjOperandId nextId = writer.loadObject(nextFun);
8682 
8683   writer.guardShape(protoId, arrayIteratorProto->shape());
8684 
8685   // Ensure that proto[slot] == nextFun.
8686   writer.guardDynamicSlotIsSpecificObject(protoId, nextId, slot);
8687   writer.loadBooleanResult(true);
8688   writer.returnFromIC();
8689 
8690   trackAttached("ArrayIteratorPrototypeOptimizable");
8691   return AttachDecision::Attach;
8692 }
8693 
tryAttachObjectCreate(HandleFunction callee)8694 AttachDecision CallIRGenerator::tryAttachObjectCreate(HandleFunction callee) {
8695   // Need a single object-or-null argument.
8696   if (argc_ != 1 || !args_[0].isObjectOrNull()) {
8697     return AttachDecision::NoAction;
8698   }
8699 
8700   if (!isFirstStub_) {
8701     // Attach only once to prevent slowdowns for polymorphic calls.
8702     return AttachDecision::NoAction;
8703   }
8704 
8705   RootedObject proto(cx_, args_[0].toObjectOrNull());
8706   JSObject* templateObj = ObjectCreateImpl(cx_, proto, TenuredObject);
8707   if (!templateObj) {
8708     cx_->recoverFromOutOfMemory();
8709     return AttachDecision::NoAction;
8710   }
8711 
8712   // Initialize the input operand.
8713   Int32OperandId argcId(writer.setInputOperandId(0));
8714 
8715   // Guard callee is the 'create' native function.
8716   emitNativeCalleeGuard(callee);
8717 
8718   // Guard on the proto argument.
8719   ValOperandId argId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
8720   if (proto) {
8721     ObjOperandId protoId = writer.guardToObject(argId);
8722     writer.guardSpecificObject(protoId, proto);
8723   } else {
8724     writer.guardIsNull(argId);
8725   }
8726 
8727   writer.objectCreateResult(templateObj);
8728   writer.returnFromIC();
8729 
8730   trackAttached("ObjectCreate");
8731   return AttachDecision::Attach;
8732 }
8733 
tryAttachArrayConstructor(HandleFunction callee)8734 AttachDecision CallIRGenerator::tryAttachArrayConstructor(
8735     HandleFunction callee) {
8736   // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
8737   // for now. Note that self-hosted code calls this without |new| via std_Array.
8738   if (argc_ > 1) {
8739     return AttachDecision::NoAction;
8740   }
8741   if (argc_ == 1 && !args_[0].isInt32()) {
8742     return AttachDecision::NoAction;
8743   }
8744 
8745   int32_t length = (argc_ == 1) ? args_[0].toInt32() : 0;
8746   if (length < 0 || uint32_t(length) > ArrayObject::EagerAllocationMaxLength) {
8747     return AttachDecision::NoAction;
8748   }
8749 
8750   // We allow inlining this function across realms so make sure the template
8751   // object is allocated in that realm. See CanInlineNativeCrossRealm.
8752   JSObject* templateObj;
8753   {
8754     AutoRealm ar(cx_, callee);
8755     templateObj = NewDenseFullyAllocatedArray(cx_, length, TenuredObject);
8756     if (!templateObj) {
8757       cx_->recoverFromOutOfMemory();
8758       return AttachDecision::NoAction;
8759     }
8760   }
8761 
8762   // Initialize the input operand.
8763   Int32OperandId argcId(writer.setInputOperandId(0));
8764 
8765   // Guard callee and newTarget (if constructing) are this Array constructor
8766   // function.
8767   emitNativeCalleeGuard(callee);
8768 
8769   CallFlags flags(IsConstructPC(pc_), IsSpreadPC(pc_));
8770 
8771   Int32OperandId lengthId;
8772   if (argc_ == 1) {
8773     ValOperandId arg0Id =
8774         writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_, flags);
8775     lengthId = writer.guardToInt32(arg0Id);
8776   } else {
8777     MOZ_ASSERT(argc_ == 0);
8778     lengthId = writer.loadInt32Constant(0);
8779   }
8780 
8781   writer.newArrayFromLengthResult(templateObj, lengthId);
8782   writer.returnFromIC();
8783 
8784   trackAttached("ArrayConstructor");
8785   return AttachDecision::Attach;
8786 }
8787 
tryAttachTypedArrayConstructor(HandleFunction callee)8788 AttachDecision CallIRGenerator::tryAttachTypedArrayConstructor(
8789     HandleFunction callee) {
8790   MOZ_ASSERT(IsConstructPC(pc_));
8791 
8792   if (argc_ == 0 || argc_ > 3) {
8793     return AttachDecision::NoAction;
8794   }
8795 
8796   if (!isFirstStub_) {
8797     // Attach only once to prevent slowdowns for polymorphic calls.
8798     return AttachDecision::NoAction;
8799   }
8800 
8801   // The first argument must be int32 or a non-proxy object.
8802   if (!args_[0].isInt32() && !args_[0].isObject()) {
8803     return AttachDecision::NoAction;
8804   }
8805   if (args_[0].isObject() && args_[0].toObject().is<ProxyObject>()) {
8806     return AttachDecision::NoAction;
8807   }
8808 
8809 #ifdef JS_CODEGEN_X86
8810   // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
8811   // we can easily support on 32-bit x86 for now.
8812   if (args_[0].isObject() &&
8813       args_[0].toObject().is<ArrayBufferObjectMaybeShared>()) {
8814     return AttachDecision::NoAction;
8815   }
8816 #endif
8817 
8818   RootedObject templateObj(cx_);
8819   if (!TypedArrayObject::GetTemplateObjectForNative(cx_, callee->native(),
8820                                                     args_, &templateObj)) {
8821     cx_->recoverFromOutOfMemory();
8822     return AttachDecision::NoAction;
8823   }
8824 
8825   if (!templateObj) {
8826     // This can happen for large length values.
8827     MOZ_ASSERT(args_[0].isInt32());
8828     return AttachDecision::NoAction;
8829   }
8830 
8831   // Initialize the input operand.
8832   Int32OperandId argcId(writer.setInputOperandId(0));
8833 
8834   // Guard callee and newTarget are this TypedArray constructor function.
8835   emitNativeCalleeGuard(callee);
8836 
8837   CallFlags flags(IsConstructPC(pc_), IsSpreadPC(pc_));
8838   ValOperandId arg0Id =
8839       writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_, flags);
8840 
8841   if (args_[0].isInt32()) {
8842     // From length.
8843     Int32OperandId lengthId = writer.guardToInt32(arg0Id);
8844     writer.newTypedArrayFromLengthResult(templateObj, lengthId);
8845   } else {
8846     JSObject* obj = &args_[0].toObject();
8847     ObjOperandId objId = writer.guardToObject(arg0Id);
8848 
8849     if (obj->is<ArrayBufferObjectMaybeShared>()) {
8850       // From ArrayBuffer.
8851       if (obj->is<ArrayBufferObject>()) {
8852         writer.guardClass(objId, GuardClassKind::ArrayBuffer);
8853       } else {
8854         MOZ_ASSERT(obj->is<SharedArrayBufferObject>());
8855         writer.guardClass(objId, GuardClassKind::SharedArrayBuffer);
8856       }
8857       ValOperandId byteOffsetId;
8858       if (argc_ > 1) {
8859         byteOffsetId =
8860             writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_, flags);
8861       } else {
8862         byteOffsetId = writer.loadUndefined();
8863       }
8864       ValOperandId lengthId;
8865       if (argc_ > 2) {
8866         lengthId =
8867             writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_, flags);
8868       } else {
8869         lengthId = writer.loadUndefined();
8870       }
8871       writer.newTypedArrayFromArrayBufferResult(templateObj, objId,
8872                                                 byteOffsetId, lengthId);
8873     } else {
8874       // From Array-like.
8875       writer.guardIsNotArrayBufferMaybeShared(objId);
8876       writer.guardIsNotProxy(objId);
8877       writer.newTypedArrayFromArrayResult(templateObj, objId);
8878     }
8879   }
8880 
8881   writer.returnFromIC();
8882 
8883   trackAttached("TypedArrayConstructor");
8884   return AttachDecision::Attach;
8885 }
8886 
tryAttachFunApply(HandleFunction calleeFunc)8887 AttachDecision CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc) {
8888   if (!calleeFunc->isNativeWithoutJitEntry() ||
8889       calleeFunc->native() != fun_apply) {
8890     return AttachDecision::NoAction;
8891   }
8892 
8893   if (argc_ != 2) {
8894     return AttachDecision::NoAction;
8895   }
8896 
8897   if (!thisval_.isObject() || !thisval_.toObject().is<JSFunction>()) {
8898     return AttachDecision::NoAction;
8899   }
8900   auto* target = &thisval_.toObject().as<JSFunction>();
8901 
8902   bool isScripted = target->hasJitEntry();
8903   MOZ_ASSERT_IF(!isScripted, target->isNativeWithoutJitEntry());
8904 
8905   if (target->isClassConstructor()) {
8906     return AttachDecision::NoAction;
8907   }
8908 
8909   CallFlags::ArgFormat format = CallFlags::Standard;
8910   if (args_[1].isObject() && args_[1].toObject().is<ArgumentsObject>()) {
8911     auto* argsObj = &args_[1].toObject().as<ArgumentsObject>();
8912     if (argsObj->hasOverriddenElement() || argsObj->anyArgIsForwarded() ||
8913         argsObj->hasOverriddenLength() ||
8914         argsObj->initialLength() > JIT_ARGS_LENGTH_MAX) {
8915       return AttachDecision::NoAction;
8916     }
8917     format = CallFlags::FunApplyArgsObj;
8918   } else if (args_[1].isObject() && args_[1].toObject().is<ArrayObject>() &&
8919              args_[1].toObject().as<ArrayObject>().length() <=
8920                  JIT_ARGS_LENGTH_MAX) {
8921     format = CallFlags::FunApplyArray;
8922   } else {
8923     return AttachDecision::NoAction;
8924   }
8925 
8926   Int32OperandId argcId(writer.setInputOperandId(0));
8927 
8928   // Guard that callee is the |fun_apply| native function.
8929   ValOperandId calleeValId =
8930       writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId);
8931   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
8932   writer.guardSpecificFunction(calleeObjId, calleeFunc);
8933 
8934   // Guard that |this| is an object.
8935   ValOperandId thisValId =
8936       writer.loadArgumentDynamicSlot(ArgumentKind::This, argcId);
8937   ObjOperandId thisObjId = writer.guardToObject(thisValId);
8938 
8939   ValOperandId argValId =
8940       writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
8941 
8942   if (format == CallFlags::FunApplyArgsObj) {
8943     ObjOperandId argObjId = writer.guardToObject(argValId);
8944     if (args_[1].toObject().is<MappedArgumentsObject>()) {
8945       writer.guardClass(argObjId, GuardClassKind::MappedArguments);
8946     } else {
8947       MOZ_ASSERT(args_[1].toObject().is<UnmappedArgumentsObject>());
8948       writer.guardClass(argObjId, GuardClassKind::UnmappedArguments);
8949     }
8950     uint8_t flags = ArgumentsObject::ELEMENT_OVERRIDDEN_BIT |
8951                     ArgumentsObject::FORWARDED_ARGUMENTS_BIT;
8952     writer.guardArgumentsObjectFlags(argObjId, flags);
8953   } else {
8954     MOZ_ASSERT(format == CallFlags::FunApplyArray);
8955     ObjOperandId argObjId = writer.guardToObject(argValId);
8956     writer.guardClass(argObjId, GuardClassKind::Array);
8957     writer.guardArrayIsPacked(argObjId);
8958   }
8959 
8960   CallFlags targetFlags(format);
8961   if (mode_ == ICState::Mode::Specialized) {
8962     // Ensure that |this| is the expected target function.
8963     emitCalleeGuard(thisObjId, target);
8964 
8965     if (cx_->realm() == target->realm()) {
8966       targetFlags.setIsSameRealm();
8967     }
8968 
8969     if (isScripted) {
8970       writer.callScriptedFunction(thisObjId, argcId, targetFlags);
8971     } else {
8972       writer.callNativeFunction(thisObjId, argcId, op_, target, targetFlags);
8973     }
8974   } else {
8975     // Guard that |this| is a function.
8976     writer.guardClass(thisObjId, GuardClassKind::JSFunction);
8977 
8978     // Guard that function is not a class constructor.
8979     writer.guardNotClassConstructor(thisObjId);
8980 
8981     if (isScripted) {
8982       // Guard that function is scripted.
8983       writer.guardFunctionHasJitEntry(thisObjId, /*constructing =*/false);
8984       writer.callScriptedFunction(thisObjId, argcId, targetFlags);
8985     } else {
8986       // Guard that function is native.
8987       writer.guardFunctionHasNoJitEntry(thisObjId);
8988       writer.callAnyNativeFunction(thisObjId, argcId, targetFlags);
8989     }
8990   }
8991 
8992   writer.returnFromIC();
8993 
8994   if (isScripted) {
8995     trackAttached("Scripted fun_apply");
8996   } else {
8997     trackAttached("Native fun_apply");
8998   }
8999 
9000   return AttachDecision::Attach;
9001 }
9002 
tryAttachWasmCall(HandleFunction calleeFunc)9003 AttachDecision CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc) {
9004   // Try to optimize calls into Wasm code by emitting the CallWasmFunction
9005   // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
9006   // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
9007   // into Wasm code.
9008   //
9009   // Note: some code refers to these optimized Wasm calls as "inlined" calls.
9010 
9011   MOZ_ASSERT(calleeFunc->isWasmWithJitEntry());
9012 
9013   if (!JitOptions.enableWasmIonFastCalls) {
9014     return AttachDecision::NoAction;
9015   }
9016   if (!isFirstStub_) {
9017     return AttachDecision::NoAction;
9018   }
9019   if (JSOp(*pc_) != JSOp::Call && JSOp(*pc_) != JSOp::CallIgnoresRv) {
9020     return AttachDecision::NoAction;
9021   }
9022   if (cx_->realm() != calleeFunc->realm()) {
9023     return AttachDecision::NoAction;
9024   }
9025 
9026   wasm::Instance& inst = wasm::ExportedFunctionToInstance(calleeFunc);
9027   uint32_t funcIndex = inst.code().getFuncIndex(calleeFunc);
9028 
9029   auto bestTier = inst.code().bestTier();
9030   const wasm::FuncExport& funcExport =
9031       inst.metadata(bestTier).lookupFuncExport(funcIndex);
9032 
9033   MOZ_ASSERT(!IsInsideNursery(inst.object()));
9034   MOZ_ASSERT(funcExport.canHaveJitEntry(),
9035              "Function should allow a Wasm JitEntry");
9036 
9037   const wasm::FuncType& sig = funcExport.funcType();
9038 
9039   // If there are too many arguments, don't optimize (we won't be able to store
9040   // the arguments in the LIR node).
9041   static_assert(wasm::MaxArgsForJitInlineCall <= ArgumentKindArgIndexLimit);
9042   if (sig.args().length() > wasm::MaxArgsForJitInlineCall ||
9043       argc_ > ArgumentKindArgIndexLimit) {
9044     return AttachDecision::NoAction;
9045   }
9046 
9047   // If there are too many results, don't optimize as Warp currently doesn't
9048   // have code to handle this.
9049   if (sig.results().length() > wasm::MaxResultsForJitInlineCall) {
9050     return AttachDecision::NoAction;
9051   }
9052 
9053   // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
9054   // because it is more difficult (because it requires multiple LIR arguments
9055   // per I64).
9056   //
9057   // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
9058   // spilled to the stack because it causes problems with register allocation.
9059 #ifdef JS_64BIT
9060   constexpr bool optimizeWithI64 = true;
9061 #else
9062   constexpr bool optimizeWithI64 = false;
9063 #endif
9064   ABIArgGenerator abi;
9065   for (const auto& valType : sig.args()) {
9066     MIRType mirType = ToMIRType(valType);
9067     ABIArg abiArg = abi.next(mirType);
9068     if (mirType != MIRType::Int64) {
9069       continue;
9070     }
9071     if (!optimizeWithI64 || abiArg.kind() == ABIArg::Stack) {
9072       return AttachDecision::NoAction;
9073     }
9074   }
9075 
9076   // Check that all arguments can be converted to the Wasm type in Warp code
9077   // without bailing out.
9078   for (size_t i = 0; i < sig.args().length(); i++) {
9079     Value argVal = i < argc_ ? args_[i] : UndefinedValue();
9080     switch (sig.args()[i].kind()) {
9081       case wasm::ValType::I32:
9082       case wasm::ValType::F32:
9083       case wasm::ValType::F64:
9084         if (!argVal.isNumber() && !argVal.isBoolean() &&
9085             !argVal.isUndefined()) {
9086           return AttachDecision::NoAction;
9087         }
9088         break;
9089       case wasm::ValType::I64:
9090         if (!argVal.isBigInt() && !argVal.isBoolean() && !argVal.isString()) {
9091           return AttachDecision::NoAction;
9092         }
9093         break;
9094       case wasm::ValType::Rtt:
9095       case wasm::ValType::V128:
9096         MOZ_CRASH("Function should not have a Wasm JitEntry");
9097       case wasm::ValType::Ref:
9098         // All values can be boxed as AnyRef.
9099         MOZ_ASSERT(sig.args()[i].refTypeKind() == wasm::RefType::Extern,
9100                    "Unexpected type for Wasm JitEntry");
9101         break;
9102     }
9103   }
9104 
9105   CallFlags flags(/* isConstructing = */ false, /* isSpread = */ false,
9106                   /* isSameRealm = */ true);
9107 
9108   // Load argc.
9109   Int32OperandId argcId(writer.setInputOperandId(0));
9110 
9111   // Load the callee and ensure it is an object
9112   ValOperandId calleeValId =
9113       writer.loadArgumentFixedSlot(ArgumentKind::Callee, argc_, flags);
9114   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
9115 
9116   // Ensure the callee is this Wasm function.
9117   emitCalleeGuard(calleeObjId, calleeFunc);
9118 
9119   // Guard the argument types.
9120   uint32_t guardedArgs = std::min<uint32_t>(sig.args().length(), argc_);
9121   for (uint32_t i = 0; i < guardedArgs; i++) {
9122     ArgumentKind argKind = ArgumentKindForArgIndex(i);
9123     ValOperandId argId = writer.loadArgumentFixedSlot(argKind, argc_, flags);
9124     writer.guardWasmArg(argId, sig.args()[i].kind());
9125   }
9126 
9127   writer.callWasmFunction(calleeObjId, argcId, flags, &funcExport,
9128                           inst.object());
9129   writer.returnFromIC();
9130 
9131   trackAttached("WasmCall");
9132 
9133   return AttachDecision::Attach;
9134 }
9135 
tryAttachInlinableNative(HandleFunction callee)9136 AttachDecision CallIRGenerator::tryAttachInlinableNative(
9137     HandleFunction callee) {
9138   MOZ_ASSERT(mode_ == ICState::Mode::Specialized);
9139   MOZ_ASSERT(callee->isNativeWithoutJitEntry());
9140 
9141   // Special case functions are only optimized for normal calls.
9142   if (op_ != JSOp::Call && op_ != JSOp::New && op_ != JSOp::CallIgnoresRv &&
9143       op_ != JSOp::SpreadCall) {
9144     return AttachDecision::NoAction;
9145   }
9146 
9147   if (!callee->hasJitInfo() ||
9148       callee->jitInfo()->type() != JSJitInfo::InlinableNative) {
9149     return AttachDecision::NoAction;
9150   }
9151 
9152   InlinableNative native = callee->jitInfo()->inlinableNative;
9153 
9154   // Not all natives can be inlined cross-realm.
9155   if (cx_->realm() != callee->realm() && !CanInlineNativeCrossRealm(native)) {
9156     return AttachDecision::NoAction;
9157   }
9158 
9159   // Check for special-cased native constructors.
9160   if (op_ == JSOp::New) {
9161     // newTarget must match the callee. CacheIR for this is emitted in
9162     // emitNativeCalleeGuard.
9163     if (callee_ != newTarget_) {
9164       return AttachDecision::NoAction;
9165     }
9166     switch (native) {
9167       case InlinableNative::Array:
9168         return tryAttachArrayConstructor(callee);
9169       case InlinableNative::TypedArrayConstructor:
9170         return tryAttachTypedArrayConstructor(callee);
9171       case InlinableNative::String:
9172         return tryAttachStringConstructor(callee);
9173       default:
9174         break;
9175     }
9176     return AttachDecision::NoAction;
9177   }
9178 
9179   // Check for special-cased native spread calls.
9180   if (op_ == JSOp::SpreadCall) {
9181     switch (native) {
9182       case InlinableNative::MathMin:
9183         return tryAttachSpreadMathMinMax(callee, /*isMax = */ false);
9184       case InlinableNative::MathMax:
9185         return tryAttachSpreadMathMinMax(callee, /*isMax = */ true);
9186       default:
9187         break;
9188     }
9189     return AttachDecision::NoAction;
9190   }
9191 
9192   // Check for special-cased native functions.
9193   switch (native) {
9194     // Array natives.
9195     case InlinableNative::Array:
9196       return tryAttachArrayConstructor(callee);
9197     case InlinableNative::ArrayPush:
9198       return tryAttachArrayPush(callee);
9199     case InlinableNative::ArrayPop:
9200     case InlinableNative::ArrayShift:
9201       return tryAttachArrayPopShift(callee, native);
9202     case InlinableNative::ArrayJoin:
9203       return tryAttachArrayJoin(callee);
9204     case InlinableNative::ArraySlice:
9205       return tryAttachArraySlice(callee);
9206     case InlinableNative::ArrayIsArray:
9207       return tryAttachArrayIsArray(callee);
9208 
9209     // DataView natives.
9210     case InlinableNative::DataViewGetInt8:
9211       return tryAttachDataViewGet(callee, Scalar::Int8);
9212     case InlinableNative::DataViewGetUint8:
9213       return tryAttachDataViewGet(callee, Scalar::Uint8);
9214     case InlinableNative::DataViewGetInt16:
9215       return tryAttachDataViewGet(callee, Scalar::Int16);
9216     case InlinableNative::DataViewGetUint16:
9217       return tryAttachDataViewGet(callee, Scalar::Uint16);
9218     case InlinableNative::DataViewGetInt32:
9219       return tryAttachDataViewGet(callee, Scalar::Int32);
9220     case InlinableNative::DataViewGetUint32:
9221       return tryAttachDataViewGet(callee, Scalar::Uint32);
9222     case InlinableNative::DataViewGetFloat32:
9223       return tryAttachDataViewGet(callee, Scalar::Float32);
9224     case InlinableNative::DataViewGetFloat64:
9225       return tryAttachDataViewGet(callee, Scalar::Float64);
9226     case InlinableNative::DataViewGetBigInt64:
9227       return tryAttachDataViewGet(callee, Scalar::BigInt64);
9228     case InlinableNative::DataViewGetBigUint64:
9229       return tryAttachDataViewGet(callee, Scalar::BigUint64);
9230     case InlinableNative::DataViewSetInt8:
9231       return tryAttachDataViewSet(callee, Scalar::Int8);
9232     case InlinableNative::DataViewSetUint8:
9233       return tryAttachDataViewSet(callee, Scalar::Uint8);
9234     case InlinableNative::DataViewSetInt16:
9235       return tryAttachDataViewSet(callee, Scalar::Int16);
9236     case InlinableNative::DataViewSetUint16:
9237       return tryAttachDataViewSet(callee, Scalar::Uint16);
9238     case InlinableNative::DataViewSetInt32:
9239       return tryAttachDataViewSet(callee, Scalar::Int32);
9240     case InlinableNative::DataViewSetUint32:
9241       return tryAttachDataViewSet(callee, Scalar::Uint32);
9242     case InlinableNative::DataViewSetFloat32:
9243       return tryAttachDataViewSet(callee, Scalar::Float32);
9244     case InlinableNative::DataViewSetFloat64:
9245       return tryAttachDataViewSet(callee, Scalar::Float64);
9246     case InlinableNative::DataViewSetBigInt64:
9247       return tryAttachDataViewSet(callee, Scalar::BigInt64);
9248     case InlinableNative::DataViewSetBigUint64:
9249       return tryAttachDataViewSet(callee, Scalar::BigUint64);
9250 
9251     // Intl natives.
9252     case InlinableNative::IntlGuardToCollator:
9253     case InlinableNative::IntlGuardToDateTimeFormat:
9254     case InlinableNative::IntlGuardToDisplayNames:
9255     case InlinableNative::IntlGuardToListFormat:
9256     case InlinableNative::IntlGuardToNumberFormat:
9257     case InlinableNative::IntlGuardToPluralRules:
9258     case InlinableNative::IntlGuardToRelativeTimeFormat:
9259       return tryAttachGuardToClass(callee, native);
9260 
9261     // Slot intrinsics.
9262     case InlinableNative::IntrinsicUnsafeGetReservedSlot:
9263     case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
9264     case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
9265     case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
9266     case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
9267       return tryAttachUnsafeGetReservedSlot(callee, native);
9268     case InlinableNative::IntrinsicUnsafeSetReservedSlot:
9269       return tryAttachUnsafeSetReservedSlot(callee);
9270 
9271     // Intrinsics.
9272     case InlinableNative::IntrinsicIsSuspendedGenerator:
9273       return tryAttachIsSuspendedGenerator(callee);
9274     case InlinableNative::IntrinsicToObject:
9275       return tryAttachToObject(callee, native);
9276     case InlinableNative::IntrinsicToInteger:
9277       return tryAttachToInteger(callee);
9278     case InlinableNative::IntrinsicToLength:
9279       return tryAttachToLength(callee);
9280     case InlinableNative::IntrinsicIsObject:
9281       return tryAttachIsObject(callee);
9282     case InlinableNative::IntrinsicIsPackedArray:
9283       return tryAttachIsPackedArray(callee);
9284     case InlinableNative::IntrinsicIsCallable:
9285       return tryAttachIsCallable(callee);
9286     case InlinableNative::IntrinsicIsConstructor:
9287       return tryAttachIsConstructor(callee);
9288     case InlinableNative::IntrinsicIsCrossRealmArrayConstructor:
9289       return tryAttachIsCrossRealmArrayConstructor(callee);
9290     case InlinableNative::IntrinsicGuardToArrayIterator:
9291     case InlinableNative::IntrinsicGuardToMapIterator:
9292     case InlinableNative::IntrinsicGuardToSetIterator:
9293     case InlinableNative::IntrinsicGuardToStringIterator:
9294     case InlinableNative::IntrinsicGuardToRegExpStringIterator:
9295     case InlinableNative::IntrinsicGuardToWrapForValidIterator:
9296     case InlinableNative::IntrinsicGuardToIteratorHelper:
9297     case InlinableNative::IntrinsicGuardToAsyncIteratorHelper:
9298       return tryAttachGuardToClass(callee, native);
9299     case InlinableNative::IntrinsicSubstringKernel:
9300       return tryAttachSubstringKernel(callee);
9301     case InlinableNative::IntrinsicIsConstructing:
9302       return tryAttachIsConstructing(callee);
9303     case InlinableNative::IntrinsicFinishBoundFunctionInit:
9304       return tryAttachFinishBoundFunctionInit(callee);
9305     case InlinableNative::IntrinsicNewArrayIterator:
9306       return tryAttachNewArrayIterator(callee);
9307     case InlinableNative::IntrinsicNewStringIterator:
9308       return tryAttachNewStringIterator(callee);
9309     case InlinableNative::IntrinsicNewRegExpStringIterator:
9310       return tryAttachNewRegExpStringIterator(callee);
9311     case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable:
9312       return tryAttachArrayIteratorPrototypeOptimizable(callee);
9313     case InlinableNative::IntrinsicObjectHasPrototype:
9314       return tryAttachObjectHasPrototype(callee);
9315 
9316     // RegExp natives.
9317     case InlinableNative::IsRegExpObject:
9318       return tryAttachHasClass(callee, &RegExpObject::class_,
9319                                /* isPossiblyWrapped = */ false);
9320     case InlinableNative::IsPossiblyWrappedRegExpObject:
9321       return tryAttachHasClass(callee, &RegExpObject::class_,
9322                                /* isPossiblyWrapped = */ true);
9323     case InlinableNative::RegExpMatcher:
9324     case InlinableNative::RegExpSearcher:
9325     case InlinableNative::RegExpTester:
9326       return tryAttachRegExpMatcherSearcherTester(callee, native);
9327     case InlinableNative::RegExpPrototypeOptimizable:
9328       return tryAttachRegExpPrototypeOptimizable(callee);
9329     case InlinableNative::RegExpInstanceOptimizable:
9330       return tryAttachRegExpInstanceOptimizable(callee);
9331     case InlinableNative::GetFirstDollarIndex:
9332       return tryAttachGetFirstDollarIndex(callee);
9333 
9334     // String natives.
9335     case InlinableNative::String:
9336       return tryAttachString(callee);
9337     case InlinableNative::StringToString:
9338     case InlinableNative::StringValueOf:
9339       return tryAttachStringToStringValueOf(callee);
9340     case InlinableNative::StringCharCodeAt:
9341       return tryAttachStringCharCodeAt(callee);
9342     case InlinableNative::StringCharAt:
9343       return tryAttachStringCharAt(callee);
9344     case InlinableNative::StringFromCharCode:
9345       return tryAttachStringFromCharCode(callee);
9346     case InlinableNative::StringFromCodePoint:
9347       return tryAttachStringFromCodePoint(callee);
9348     case InlinableNative::StringToLowerCase:
9349       return tryAttachStringToLowerCase(callee);
9350     case InlinableNative::StringToUpperCase:
9351       return tryAttachStringToUpperCase(callee);
9352     case InlinableNative::IntrinsicStringReplaceString:
9353       return tryAttachStringReplaceString(callee);
9354     case InlinableNative::IntrinsicStringSplitString:
9355       return tryAttachStringSplitString(callee);
9356 
9357     // Math natives.
9358     case InlinableNative::MathRandom:
9359       return tryAttachMathRandom(callee);
9360     case InlinableNative::MathAbs:
9361       return tryAttachMathAbs(callee);
9362     case InlinableNative::MathClz32:
9363       return tryAttachMathClz32(callee);
9364     case InlinableNative::MathSign:
9365       return tryAttachMathSign(callee);
9366     case InlinableNative::MathImul:
9367       return tryAttachMathImul(callee);
9368     case InlinableNative::MathFloor:
9369       return tryAttachMathFloor(callee);
9370     case InlinableNative::MathCeil:
9371       return tryAttachMathCeil(callee);
9372     case InlinableNative::MathTrunc:
9373       return tryAttachMathTrunc(callee);
9374     case InlinableNative::MathRound:
9375       return tryAttachMathRound(callee);
9376     case InlinableNative::MathSqrt:
9377       return tryAttachMathSqrt(callee);
9378     case InlinableNative::MathFRound:
9379       return tryAttachMathFRound(callee);
9380     case InlinableNative::MathHypot:
9381       return tryAttachMathHypot(callee);
9382     case InlinableNative::MathATan2:
9383       return tryAttachMathATan2(callee);
9384     case InlinableNative::MathSin:
9385       return tryAttachMathFunction(callee, UnaryMathFunction::Sin);
9386     case InlinableNative::MathTan:
9387       return tryAttachMathFunction(callee, UnaryMathFunction::Tan);
9388     case InlinableNative::MathCos:
9389       return tryAttachMathFunction(callee, UnaryMathFunction::Cos);
9390     case InlinableNative::MathExp:
9391       return tryAttachMathFunction(callee, UnaryMathFunction::Exp);
9392     case InlinableNative::MathLog:
9393       return tryAttachMathFunction(callee, UnaryMathFunction::Log);
9394     case InlinableNative::MathASin:
9395       return tryAttachMathFunction(callee, UnaryMathFunction::ASin);
9396     case InlinableNative::MathATan:
9397       return tryAttachMathFunction(callee, UnaryMathFunction::ATan);
9398     case InlinableNative::MathACos:
9399       return tryAttachMathFunction(callee, UnaryMathFunction::ACos);
9400     case InlinableNative::MathLog10:
9401       return tryAttachMathFunction(callee, UnaryMathFunction::Log10);
9402     case InlinableNative::MathLog2:
9403       return tryAttachMathFunction(callee, UnaryMathFunction::Log2);
9404     case InlinableNative::MathLog1P:
9405       return tryAttachMathFunction(callee, UnaryMathFunction::Log1P);
9406     case InlinableNative::MathExpM1:
9407       return tryAttachMathFunction(callee, UnaryMathFunction::ExpM1);
9408     case InlinableNative::MathCosH:
9409       return tryAttachMathFunction(callee, UnaryMathFunction::CosH);
9410     case InlinableNative::MathSinH:
9411       return tryAttachMathFunction(callee, UnaryMathFunction::SinH);
9412     case InlinableNative::MathTanH:
9413       return tryAttachMathFunction(callee, UnaryMathFunction::TanH);
9414     case InlinableNative::MathACosH:
9415       return tryAttachMathFunction(callee, UnaryMathFunction::ACosH);
9416     case InlinableNative::MathASinH:
9417       return tryAttachMathFunction(callee, UnaryMathFunction::ASinH);
9418     case InlinableNative::MathATanH:
9419       return tryAttachMathFunction(callee, UnaryMathFunction::ATanH);
9420     case InlinableNative::MathCbrt:
9421       return tryAttachMathFunction(callee, UnaryMathFunction::Cbrt);
9422     case InlinableNative::MathPow:
9423       return tryAttachMathPow(callee);
9424     case InlinableNative::MathMin:
9425       return tryAttachMathMinMax(callee, /* isMax = */ false);
9426     case InlinableNative::MathMax:
9427       return tryAttachMathMinMax(callee, /* isMax = */ true);
9428 
9429     // Map intrinsics.
9430     case InlinableNative::IntrinsicGuardToMapObject:
9431       return tryAttachGuardToClass(callee, native);
9432     case InlinableNative::IntrinsicGetNextMapEntryForIterator:
9433       return tryAttachGetNextMapSetEntryForIterator(callee, /* isMap = */ true);
9434 
9435     // Number natives.
9436     case InlinableNative::NumberToString:
9437       return tryAttachNumberToString(callee);
9438 
9439     // Object natives.
9440     case InlinableNative::Object:
9441       return tryAttachToObject(callee, native);
9442     case InlinableNative::ObjectCreate:
9443       return tryAttachObjectCreate(callee);
9444     case InlinableNative::ObjectIs:
9445       return tryAttachObjectIs(callee);
9446     case InlinableNative::ObjectIsPrototypeOf:
9447       return tryAttachObjectIsPrototypeOf(callee);
9448     case InlinableNative::ObjectToString:
9449       return tryAttachObjectToString(callee);
9450 
9451     // Set intrinsics.
9452     case InlinableNative::IntrinsicGuardToSetObject:
9453       return tryAttachGuardToClass(callee, native);
9454     case InlinableNative::IntrinsicGetNextSetEntryForIterator:
9455       return tryAttachGetNextMapSetEntryForIterator(callee,
9456                                                     /* isMap = */ false);
9457 
9458     // ArrayBuffer intrinsics.
9459     case InlinableNative::IntrinsicGuardToArrayBuffer:
9460       return tryAttachGuardToClass(callee, native);
9461     case InlinableNative::IntrinsicArrayBufferByteLength:
9462       return tryAttachArrayBufferByteLength(callee,
9463                                             /* isPossiblyWrapped = */ false);
9464     case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
9465       return tryAttachArrayBufferByteLength(callee,
9466                                             /* isPossiblyWrapped = */ true);
9467 
9468     // SharedArrayBuffer intrinsics.
9469     case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
9470       return tryAttachGuardToClass(callee, native);
9471 
9472     // TypedArray intrinsics.
9473     case InlinableNative::TypedArrayConstructor:
9474       return AttachDecision::NoAction;  // Not callable.
9475     case InlinableNative::IntrinsicIsTypedArray:
9476       return tryAttachIsTypedArray(callee, /* isPossiblyWrapped = */ false);
9477     case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
9478       return tryAttachIsTypedArray(callee, /* isPossiblyWrapped = */ true);
9479     case InlinableNative::IntrinsicIsTypedArrayConstructor:
9480       return tryAttachIsTypedArrayConstructor(callee);
9481     case InlinableNative::IntrinsicTypedArrayByteOffset:
9482       return tryAttachTypedArrayByteOffset(callee);
9483     case InlinableNative::IntrinsicTypedArrayElementSize:
9484       return tryAttachTypedArrayElementSize(callee);
9485     case InlinableNative::IntrinsicTypedArrayLength:
9486       return tryAttachTypedArrayLength(callee, /* isPossiblyWrapped = */ false);
9487     case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
9488       return tryAttachTypedArrayLength(callee, /* isPossiblyWrapped = */ true);
9489 
9490     // Reflect natives.
9491     case InlinableNative::ReflectGetPrototypeOf:
9492       return tryAttachReflectGetPrototypeOf(callee);
9493 
9494     // Atomics intrinsics:
9495     case InlinableNative::AtomicsCompareExchange:
9496       return tryAttachAtomicsCompareExchange(callee);
9497     case InlinableNative::AtomicsExchange:
9498       return tryAttachAtomicsExchange(callee);
9499     case InlinableNative::AtomicsAdd:
9500       return tryAttachAtomicsAdd(callee);
9501     case InlinableNative::AtomicsSub:
9502       return tryAttachAtomicsSub(callee);
9503     case InlinableNative::AtomicsAnd:
9504       return tryAttachAtomicsAnd(callee);
9505     case InlinableNative::AtomicsOr:
9506       return tryAttachAtomicsOr(callee);
9507     case InlinableNative::AtomicsXor:
9508       return tryAttachAtomicsXor(callee);
9509     case InlinableNative::AtomicsLoad:
9510       return tryAttachAtomicsLoad(callee);
9511     case InlinableNative::AtomicsStore:
9512       return tryAttachAtomicsStore(callee);
9513     case InlinableNative::AtomicsIsLockFree:
9514       return tryAttachAtomicsIsLockFree(callee);
9515 
9516     // BigInt natives.
9517     case InlinableNative::BigIntAsIntN:
9518       return tryAttachBigIntAsIntN(callee);
9519     case InlinableNative::BigIntAsUintN:
9520       return tryAttachBigIntAsUintN(callee);
9521 
9522     // Boolean natives.
9523     case InlinableNative::Boolean:
9524       return tryAttachBoolean(callee);
9525 
9526     // Set natives.
9527     case InlinableNative::SetHas:
9528       return tryAttachSetHas(callee);
9529 
9530     // Map natives.
9531     case InlinableNative::MapHas:
9532       return tryAttachMapHas(callee);
9533     case InlinableNative::MapGet:
9534       return tryAttachMapGet(callee);
9535 
9536     // Testing functions.
9537     case InlinableNative::TestBailout:
9538       return tryAttachBailout(callee);
9539     case InlinableNative::TestAssertFloat32:
9540       return tryAttachAssertFloat32(callee);
9541     case InlinableNative::TestAssertRecoveredOnBailout:
9542       return tryAttachAssertRecoveredOnBailout(callee);
9543 
9544     case InlinableNative::Limit:
9545       break;
9546   }
9547 
9548   MOZ_CRASH("Shouldn't get here");
9549 }
9550 
9551 // Remember the shape of the this object for any script being called as a
9552 // constructor, for later use during Ion compilation.
getThisShapeForScripted(HandleFunction calleeFunc,MutableHandleShape result)9553 ScriptedThisResult CallIRGenerator::getThisShapeForScripted(
9554     HandleFunction calleeFunc, MutableHandleShape result) {
9555   // Some constructors allocate their own |this| object.
9556   if (calleeFunc->constructorNeedsUninitializedThis()) {
9557     return ScriptedThisResult::UninitializedThis;
9558   }
9559 
9560   // Only attach a stub if the newTarget is a function with a
9561   // nonconfigurable prototype.
9562   RootedObject newTarget(cx_, &newTarget_.toObject());
9563   if (!newTarget->is<JSFunction>() ||
9564       !newTarget->as<JSFunction>().hasNonConfigurablePrototypeDataProperty()) {
9565     return ScriptedThisResult::NoAction;
9566   }
9567 
9568   AutoRealm ar(cx_, calleeFunc);
9569   Shape* thisShape = ThisShapeForFunction(cx_, calleeFunc, newTarget);
9570   if (!thisShape) {
9571     cx_->clearPendingException();
9572     return ScriptedThisResult::NoAction;
9573   }
9574 
9575   MOZ_ASSERT(thisShape->realm() == calleeFunc->realm());
9576   result.set(thisShape);
9577   return ScriptedThisResult::PlainObjectShape;
9578 }
9579 
tryAttachCallScripted(HandleFunction calleeFunc)9580 AttachDecision CallIRGenerator::tryAttachCallScripted(
9581     HandleFunction calleeFunc) {
9582   MOZ_ASSERT(calleeFunc->hasJitEntry());
9583 
9584   if (calleeFunc->isWasmWithJitEntry()) {
9585     TRY_ATTACH(tryAttachWasmCall(calleeFunc));
9586   }
9587 
9588   bool isSpecialized = mode_ == ICState::Mode::Specialized;
9589 
9590   bool isConstructing = IsConstructPC(pc_);
9591   bool isSpread = IsSpreadPC(pc_);
9592   bool isSameRealm = isSpecialized && cx_->realm() == calleeFunc->realm();
9593   CallFlags flags(isConstructing, isSpread, isSameRealm);
9594 
9595   // If callee is not an interpreted constructor, we have to throw.
9596   if (isConstructing && !calleeFunc->isConstructor()) {
9597     return AttachDecision::NoAction;
9598   }
9599 
9600   // Likewise, if the callee is a class constructor, we have to throw.
9601   if (!isConstructing && calleeFunc->isClassConstructor()) {
9602     return AttachDecision::NoAction;
9603   }
9604 
9605   if (isConstructing && !calleeFunc->hasJitScript()) {
9606     // If we're constructing, require the callee to have a JitScript. This isn't
9607     // required for correctness but avoids allocating a template object below
9608     // for constructors that aren't hot. See bug 1419758.
9609     return AttachDecision::TemporarilyUnoptimizable;
9610   }
9611 
9612   // Verify that spread calls have a reasonable number of arguments.
9613   if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
9614     return AttachDecision::NoAction;
9615   }
9616 
9617   RootedShape thisShape(cx_);
9618   if (isConstructing && isSpecialized) {
9619     switch (getThisShapeForScripted(calleeFunc, &thisShape)) {
9620       case ScriptedThisResult::PlainObjectShape:
9621         break;
9622       case ScriptedThisResult::UninitializedThis:
9623         flags.setNeedsUninitializedThis();
9624         break;
9625       case ScriptedThisResult::NoAction:
9626         return AttachDecision::NoAction;
9627     }
9628   }
9629 
9630   // Load argc.
9631   Int32OperandId argcId(writer.setInputOperandId(0));
9632 
9633   // Load the callee and ensure it is an object
9634   ValOperandId calleeValId =
9635       writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
9636   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
9637 
9638   if (isSpecialized) {
9639     MOZ_ASSERT_IF(isConstructing, thisShape || flags.needsUninitializedThis());
9640 
9641     // Ensure callee matches this stub's callee
9642     emitCalleeGuard(calleeObjId, calleeFunc);
9643     if (thisShape) {
9644       // Emit guards to ensure the newTarget's .prototype property is what we
9645       // expect. Note that getThisForScripted checked newTarget is a function
9646       // with a non-configurable .prototype data property.
9647       JSFunction* newTarget = &newTarget_.toObject().as<JSFunction>();
9648       Maybe<PropertyInfo> prop = newTarget->lookupPure(cx_->names().prototype);
9649       MOZ_ASSERT(prop.isSome());
9650       uint32_t slot = prop->slot();
9651       MOZ_ASSERT(slot >= newTarget->numFixedSlots(),
9652                  "Stub code relies on this");
9653 
9654       ValOperandId newTargetValId = writer.loadArgumentDynamicSlot(
9655           ArgumentKind::NewTarget, argcId, flags);
9656       ObjOperandId newTargetObjId = writer.guardToObject(newTargetValId);
9657       writer.guardShape(newTargetObjId, newTarget->shape());
9658 
9659       const Value& value = newTarget->getSlot(slot);
9660       if (value.isObject()) {
9661         JSObject* prototypeObject = &value.toObject();
9662 
9663         ObjOperandId protoId = writer.loadObject(prototypeObject);
9664         writer.guardDynamicSlotIsSpecificObject(
9665             newTargetObjId, protoId, slot - newTarget->numFixedSlots());
9666       } else {
9667         writer.guardDynamicSlotIsNotObject(newTargetObjId,
9668                                            slot - newTarget->numFixedSlots());
9669       }
9670 
9671       // Call metaScriptedThisShape before emitting the call, so that Warp can
9672       // use the shape to create the |this| object before transpiling the call.
9673       writer.metaScriptedThisShape(thisShape);
9674     }
9675   } else {
9676     // Guard that object is a scripted function
9677     writer.guardClass(calleeObjId, GuardClassKind::JSFunction);
9678     writer.guardFunctionHasJitEntry(calleeObjId, isConstructing);
9679 
9680     if (isConstructing) {
9681       // If callee is not a constructor, we have to throw.
9682       writer.guardFunctionIsConstructor(calleeObjId);
9683     } else {
9684       // If callee is a class constructor, we have to throw.
9685       writer.guardNotClassConstructor(calleeObjId);
9686     }
9687   }
9688 
9689   writer.callScriptedFunction(calleeObjId, argcId, flags);
9690   writer.returnFromIC();
9691 
9692   if (isSpecialized) {
9693     trackAttached("Call scripted func");
9694   } else {
9695     trackAttached("Call any scripted func");
9696   }
9697 
9698   return AttachDecision::Attach;
9699 }
9700 
tryAttachCallNative(HandleFunction calleeFunc)9701 AttachDecision CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc) {
9702   MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
9703 
9704   bool isSpecialized = mode_ == ICState::Mode::Specialized;
9705 
9706   bool isSpread = IsSpreadPC(pc_);
9707   bool isSameRealm = isSpecialized && cx_->realm() == calleeFunc->realm();
9708   bool isConstructing = IsConstructPC(pc_);
9709   CallFlags flags(isConstructing, isSpread, isSameRealm);
9710 
9711   if (isConstructing && !calleeFunc->isConstructor()) {
9712     return AttachDecision::NoAction;
9713   }
9714 
9715   // Verify that spread calls have a reasonable number of arguments.
9716   if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
9717     return AttachDecision::NoAction;
9718   }
9719 
9720   // Check for specific native-function optimizations.
9721   if (isSpecialized) {
9722     TRY_ATTACH(tryAttachInlinableNative(calleeFunc));
9723   }
9724 
9725   // Load argc.
9726   Int32OperandId argcId(writer.setInputOperandId(0));
9727 
9728   // Load the callee and ensure it is an object
9729   ValOperandId calleeValId =
9730       writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
9731   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
9732 
9733   // DOM calls need an additional guard so only try optimizing the first stub.
9734   // Can only optimize normal (non-spread) calls.
9735   if (isFirstStub_ && !isSpread && thisval_.isObject() &&
9736       CanAttachDOMCall(cx_, JSJitInfo::Method, &thisval_.toObject(), calleeFunc,
9737                        mode_)) {
9738     MOZ_ASSERT(!isConstructing, "DOM functions are not constructors");
9739 
9740     // Guard that |this| is an object.
9741     ValOperandId thisValId =
9742         writer.loadArgumentDynamicSlot(ArgumentKind::This, argcId, flags);
9743     ObjOperandId thisObjId = writer.guardToObject(thisValId);
9744 
9745     // Guard on the |this| class to make sure it's the right instance.
9746     writer.guardAnyClass(thisObjId, thisval_.toObject().getClass());
9747 
9748     // Ensure callee matches this stub's callee
9749     writer.guardSpecificFunction(calleeObjId, calleeFunc);
9750     writer.callDOMFunction(calleeObjId, argcId, thisObjId, calleeFunc, flags);
9751 
9752     trackAttached("CallDOM");
9753   } else if (isSpecialized) {
9754     // Ensure callee matches this stub's callee
9755     writer.guardSpecificFunction(calleeObjId, calleeFunc);
9756     writer.callNativeFunction(calleeObjId, argcId, op_, calleeFunc, flags);
9757 
9758     trackAttached("CallNative");
9759   } else {
9760     // Guard that object is a native function
9761     writer.guardClass(calleeObjId, GuardClassKind::JSFunction);
9762     writer.guardFunctionHasNoJitEntry(calleeObjId);
9763 
9764     if (isConstructing) {
9765       // If callee is not a constructor, we have to throw.
9766       writer.guardFunctionIsConstructor(calleeObjId);
9767     } else {
9768       // If callee is a class constructor, we have to throw.
9769       writer.guardNotClassConstructor(calleeObjId);
9770     }
9771     writer.callAnyNativeFunction(calleeObjId, argcId, flags);
9772 
9773     trackAttached("CallAnyNative");
9774   }
9775 
9776   writer.returnFromIC();
9777 
9778   return AttachDecision::Attach;
9779 }
9780 
tryAttachCallHook(HandleObject calleeObj)9781 AttachDecision CallIRGenerator::tryAttachCallHook(HandleObject calleeObj) {
9782   if (op_ == JSOp::FunCall || op_ == JSOp::FunApply) {
9783     return AttachDecision::NoAction;
9784   }
9785 
9786   if (mode_ != ICState::Mode::Specialized) {
9787     // We do not have megamorphic call hook stubs.
9788     // TODO: Should we attach specialized call hook stubs in
9789     // megamorphic mode to avoid going generic?
9790     return AttachDecision::NoAction;
9791   }
9792 
9793   bool isSpread = IsSpreadPC(pc_);
9794   bool isConstructing = IsConstructPC(pc_);
9795   CallFlags flags(isConstructing, isSpread);
9796   JSNative hook =
9797       isConstructing ? calleeObj->constructHook() : calleeObj->callHook();
9798   if (!hook) {
9799     return AttachDecision::NoAction;
9800   }
9801 
9802   // Verify that spread calls have a reasonable number of arguments.
9803   if (isSpread && args_.length() > JIT_ARGS_LENGTH_MAX) {
9804     return AttachDecision::NoAction;
9805   }
9806 
9807   // Load argc.
9808   Int32OperandId argcId(writer.setInputOperandId(0));
9809 
9810   // Load the callee and ensure it is an object
9811   ValOperandId calleeValId =
9812       writer.loadArgumentDynamicSlot(ArgumentKind::Callee, argcId, flags);
9813   ObjOperandId calleeObjId = writer.guardToObject(calleeValId);
9814 
9815   // Ensure the callee's class matches the one in this stub.
9816   writer.guardAnyClass(calleeObjId, calleeObj->getClass());
9817 
9818   writer.callClassHook(calleeObjId, argcId, hook, flags);
9819   writer.returnFromIC();
9820 
9821   trackAttached("Call native func");
9822 
9823   return AttachDecision::Attach;
9824 }
9825 
tryAttachStub()9826 AttachDecision CallIRGenerator::tryAttachStub() {
9827   AutoAssertNoPendingException aanpe(cx_);
9828 
9829   // Some opcodes are not yet supported.
9830   switch (op_) {
9831     case JSOp::Call:
9832     case JSOp::CallIgnoresRv:
9833     case JSOp::CallIter:
9834     case JSOp::SpreadCall:
9835     case JSOp::New:
9836     case JSOp::SpreadNew:
9837     case JSOp::SuperCall:
9838     case JSOp::SpreadSuperCall:
9839     case JSOp::FunCall:
9840     case JSOp::FunApply:
9841       break;
9842     default:
9843       return AttachDecision::NoAction;
9844   }
9845 
9846   MOZ_ASSERT(mode_ != ICState::Mode::Generic);
9847 
9848   // Ensure callee is a function.
9849   if (!callee_.isObject()) {
9850     return AttachDecision::NoAction;
9851   }
9852 
9853   RootedObject calleeObj(cx_, &callee_.toObject());
9854   if (!calleeObj->is<JSFunction>()) {
9855     return tryAttachCallHook(calleeObj);
9856   }
9857 
9858   HandleFunction calleeFunc = calleeObj.as<JSFunction>();
9859 
9860   if (op_ == JSOp::FunCall) {
9861     return tryAttachFunCall(calleeFunc);
9862   }
9863   if (op_ == JSOp::FunApply) {
9864     return tryAttachFunApply(calleeFunc);
9865   }
9866 
9867   // Check for scripted optimizations.
9868   if (calleeFunc->hasJitEntry()) {
9869     return tryAttachCallScripted(calleeFunc);
9870   }
9871 
9872   // Check for native-function optimizations.
9873   MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
9874 
9875   return tryAttachCallNative(calleeFunc);
9876 }
9877 
trackAttached(const char * name)9878 void CallIRGenerator::trackAttached(const char* name) {
9879 #ifdef JS_CACHEIR_SPEW
9880   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
9881     sp.valueProperty("callee", callee_);
9882     sp.valueProperty("thisval", thisval_);
9883     sp.valueProperty("argc", Int32Value(argc_));
9884 
9885     // Try to log the first two arguments.
9886     if (args_.length() >= 1) {
9887       sp.valueProperty("arg0", args_[0]);
9888     }
9889     if (args_.length() >= 2) {
9890       sp.valueProperty("arg1", args_[1]);
9891     }
9892   }
9893 #endif
9894 }
9895 
9896 // Class which holds a shape pointer for use when caches might reference data in
9897 // other zones.
9898 static const JSClass shapeContainerClass = {"ShapeContainer",
9899                                             JSCLASS_HAS_RESERVED_SLOTS(1)};
9900 
9901 static const size_t SHAPE_CONTAINER_SLOT = 0;
9902 
NewWrapperWithObjectShape(JSContext * cx,HandleNativeObject obj)9903 static JSObject* NewWrapperWithObjectShape(JSContext* cx,
9904                                            HandleNativeObject obj) {
9905   MOZ_ASSERT(cx->compartment() != obj->compartment());
9906 
9907   RootedObject wrapper(cx);
9908   {
9909     AutoRealm ar(cx, obj);
9910     wrapper = NewBuiltinClassInstance(cx, &shapeContainerClass);
9911     if (!wrapper) {
9912       return nullptr;
9913     }
9914     wrapper->as<NativeObject>().setReservedSlot(
9915         SHAPE_CONTAINER_SLOT, PrivateGCThingValue(obj->shape()));
9916   }
9917   if (!JS_WrapObject(cx, &wrapper)) {
9918     return nullptr;
9919   }
9920   MOZ_ASSERT(IsWrapper(wrapper));
9921   return wrapper;
9922 }
9923 
LoadShapeWrapperContents(MacroAssembler & masm,Register obj,Register dst,Label * failure)9924 void jit::LoadShapeWrapperContents(MacroAssembler& masm, Register obj,
9925                                    Register dst, Label* failure) {
9926   masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), dst);
9927   Address privateAddr(dst,
9928                       js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
9929   masm.fallibleUnboxObject(privateAddr, dst, failure);
9930   masm.unboxNonDouble(
9931       Address(dst, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT)), dst,
9932       JSVAL_TYPE_PRIVATE_GCTHING);
9933 }
9934 
CompareIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,JSOp op,HandleValue lhsVal,HandleValue rhsVal)9935 CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script,
9936                                        jsbytecode* pc, ICState state, JSOp op,
9937                                        HandleValue lhsVal, HandleValue rhsVal)
9938     : IRGenerator(cx, script, pc, CacheKind::Compare, state),
9939       op_(op),
9940       lhsVal_(lhsVal),
9941       rhsVal_(rhsVal) {}
9942 
tryAttachString(ValOperandId lhsId,ValOperandId rhsId)9943 AttachDecision CompareIRGenerator::tryAttachString(ValOperandId lhsId,
9944                                                    ValOperandId rhsId) {
9945   if (!lhsVal_.isString() || !rhsVal_.isString()) {
9946     return AttachDecision::NoAction;
9947   }
9948 
9949   StringOperandId lhsStrId = writer.guardToString(lhsId);
9950   StringOperandId rhsStrId = writer.guardToString(rhsId);
9951   writer.compareStringResult(op_, lhsStrId, rhsStrId);
9952   writer.returnFromIC();
9953 
9954   trackAttached("String");
9955   return AttachDecision::Attach;
9956 }
9957 
tryAttachObject(ValOperandId lhsId,ValOperandId rhsId)9958 AttachDecision CompareIRGenerator::tryAttachObject(ValOperandId lhsId,
9959                                                    ValOperandId rhsId) {
9960   MOZ_ASSERT(IsEqualityOp(op_));
9961 
9962   if (!lhsVal_.isObject() || !rhsVal_.isObject()) {
9963     return AttachDecision::NoAction;
9964   }
9965 
9966   ObjOperandId lhsObjId = writer.guardToObject(lhsId);
9967   ObjOperandId rhsObjId = writer.guardToObject(rhsId);
9968   writer.compareObjectResult(op_, lhsObjId, rhsObjId);
9969   writer.returnFromIC();
9970 
9971   trackAttached("Object");
9972   return AttachDecision::Attach;
9973 }
9974 
tryAttachSymbol(ValOperandId lhsId,ValOperandId rhsId)9975 AttachDecision CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId,
9976                                                    ValOperandId rhsId) {
9977   MOZ_ASSERT(IsEqualityOp(op_));
9978 
9979   if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol()) {
9980     return AttachDecision::NoAction;
9981   }
9982 
9983   SymbolOperandId lhsSymId = writer.guardToSymbol(lhsId);
9984   SymbolOperandId rhsSymId = writer.guardToSymbol(rhsId);
9985   writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
9986   writer.returnFromIC();
9987 
9988   trackAttached("Symbol");
9989   return AttachDecision::Attach;
9990 }
9991 
tryAttachStrictDifferentTypes(ValOperandId lhsId,ValOperandId rhsId)9992 AttachDecision CompareIRGenerator::tryAttachStrictDifferentTypes(
9993     ValOperandId lhsId, ValOperandId rhsId) {
9994   MOZ_ASSERT(IsEqualityOp(op_));
9995 
9996   if (op_ != JSOp::StrictEq && op_ != JSOp::StrictNe) {
9997     return AttachDecision::NoAction;
9998   }
9999 
10000   // Probably can't hit some of these.
10001   if (SameType(lhsVal_, rhsVal_) ||
10002       (lhsVal_.isNumber() && rhsVal_.isNumber())) {
10003     return AttachDecision::NoAction;
10004   }
10005 
10006   // Compare tags
10007   ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
10008   ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
10009   writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
10010 
10011   // Now that we've passed the guard, we know differing types, so return the
10012   // bool result.
10013   writer.loadBooleanResult(op_ == JSOp::StrictNe ? true : false);
10014   writer.returnFromIC();
10015 
10016   trackAttached("StrictDifferentTypes");
10017   return AttachDecision::Attach;
10018 }
10019 
tryAttachInt32(ValOperandId lhsId,ValOperandId rhsId)10020 AttachDecision CompareIRGenerator::tryAttachInt32(ValOperandId lhsId,
10021                                                   ValOperandId rhsId) {
10022   if ((!lhsVal_.isInt32() && !lhsVal_.isBoolean()) ||
10023       (!rhsVal_.isInt32() && !rhsVal_.isBoolean())) {
10024     return AttachDecision::NoAction;
10025   }
10026 
10027   Int32OperandId lhsIntId = lhsVal_.isBoolean()
10028                                 ? writer.guardBooleanToInt32(lhsId)
10029                                 : writer.guardToInt32(lhsId);
10030   Int32OperandId rhsIntId = rhsVal_.isBoolean()
10031                                 ? writer.guardBooleanToInt32(rhsId)
10032                                 : writer.guardToInt32(rhsId);
10033 
10034   // Strictly different types should have been handed by
10035   // tryAttachStrictDifferentTypes
10036   MOZ_ASSERT_IF(op_ == JSOp::StrictEq || op_ == JSOp::StrictNe,
10037                 lhsVal_.isInt32() == rhsVal_.isInt32());
10038 
10039   writer.compareInt32Result(op_, lhsIntId, rhsIntId);
10040   writer.returnFromIC();
10041 
10042   trackAttached(lhsVal_.isBoolean() ? "Boolean" : "Int32");
10043   return AttachDecision::Attach;
10044 }
10045 
tryAttachNumber(ValOperandId lhsId,ValOperandId rhsId)10046 AttachDecision CompareIRGenerator::tryAttachNumber(ValOperandId lhsId,
10047                                                    ValOperandId rhsId) {
10048   if (!lhsVal_.isNumber() || !rhsVal_.isNumber()) {
10049     return AttachDecision::NoAction;
10050   }
10051 
10052   NumberOperandId lhs = writer.guardIsNumber(lhsId);
10053   NumberOperandId rhs = writer.guardIsNumber(rhsId);
10054   writer.compareDoubleResult(op_, lhs, rhs);
10055   writer.returnFromIC();
10056 
10057   trackAttached("Number");
10058   return AttachDecision::Attach;
10059 }
10060 
tryAttachBigInt(ValOperandId lhsId,ValOperandId rhsId)10061 AttachDecision CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId,
10062                                                    ValOperandId rhsId) {
10063   if (!lhsVal_.isBigInt() || !rhsVal_.isBigInt()) {
10064     return AttachDecision::NoAction;
10065   }
10066 
10067   BigIntOperandId lhs = writer.guardToBigInt(lhsId);
10068   BigIntOperandId rhs = writer.guardToBigInt(rhsId);
10069 
10070   writer.compareBigIntResult(op_, lhs, rhs);
10071   writer.returnFromIC();
10072 
10073   trackAttached("BigInt");
10074   return AttachDecision::Attach;
10075 }
10076 
tryAttachNumberUndefined(ValOperandId lhsId,ValOperandId rhsId)10077 AttachDecision CompareIRGenerator::tryAttachNumberUndefined(
10078     ValOperandId lhsId, ValOperandId rhsId) {
10079   if (!(lhsVal_.isUndefined() && rhsVal_.isNumber()) &&
10080       !(rhsVal_.isUndefined() && lhsVal_.isNumber())) {
10081     return AttachDecision::NoAction;
10082   }
10083 
10084   // Should have been handled by tryAttachAnyNullUndefined.
10085   MOZ_ASSERT(!IsEqualityOp(op_));
10086 
10087   if (lhsVal_.isNumber()) {
10088     writer.guardIsNumber(lhsId);
10089   } else {
10090     writer.guardIsUndefined(lhsId);
10091   }
10092 
10093   if (rhsVal_.isNumber()) {
10094     writer.guardIsNumber(rhsId);
10095   } else {
10096     writer.guardIsUndefined(rhsId);
10097   }
10098 
10099   // Relational comparing a number with undefined will always be false.
10100   writer.loadBooleanResult(false);
10101   writer.returnFromIC();
10102 
10103   trackAttached("NumberUndefined");
10104   return AttachDecision::Attach;
10105 }
10106 
tryAttachAnyNullUndefined(ValOperandId lhsId,ValOperandId rhsId)10107 AttachDecision CompareIRGenerator::tryAttachAnyNullUndefined(
10108     ValOperandId lhsId, ValOperandId rhsId) {
10109   MOZ_ASSERT(IsEqualityOp(op_));
10110 
10111   // Either RHS or LHS needs to be null/undefined.
10112   if (!lhsVal_.isNullOrUndefined() && !rhsVal_.isNullOrUndefined()) {
10113     return AttachDecision::NoAction;
10114   }
10115 
10116   // We assume that the side with null/undefined is usually constant, in
10117   // code like `if (x === undefined) { x = {}; }`.
10118   // That is why we don't attach when both sides are undefined/null,
10119   // because we would basically need to decide by chance which side is
10120   // the likely constant.
10121   // The actual generated code however handles null/undefined of course.
10122   if (lhsVal_.isNullOrUndefined() && rhsVal_.isNullOrUndefined()) {
10123     return AttachDecision::NoAction;
10124   }
10125 
10126   if (rhsVal_.isNullOrUndefined()) {
10127     if (rhsVal_.isNull()) {
10128       writer.guardIsNull(rhsId);
10129       writer.compareNullUndefinedResult(op_, /* isUndefined */ false, lhsId);
10130       trackAttached("AnyNull");
10131     } else {
10132       writer.guardIsUndefined(rhsId);
10133       writer.compareNullUndefinedResult(op_, /* isUndefined */ true, lhsId);
10134       trackAttached("AnyUndefined");
10135     }
10136   } else {
10137     if (lhsVal_.isNull()) {
10138       writer.guardIsNull(lhsId);
10139       writer.compareNullUndefinedResult(op_, /* isUndefined */ false, rhsId);
10140       trackAttached("NullAny");
10141     } else {
10142       writer.guardIsUndefined(lhsId);
10143       writer.compareNullUndefinedResult(op_, /* isUndefined */ true, rhsId);
10144       trackAttached("UndefinedAny");
10145     }
10146   }
10147 
10148   writer.returnFromIC();
10149   return AttachDecision::Attach;
10150 }
10151 
10152 // Handle {null/undefined} x {null,undefined} equality comparisons
tryAttachNullUndefined(ValOperandId lhsId,ValOperandId rhsId)10153 AttachDecision CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId,
10154                                                           ValOperandId rhsId) {
10155   if (!lhsVal_.isNullOrUndefined() || !rhsVal_.isNullOrUndefined()) {
10156     return AttachDecision::NoAction;
10157   }
10158 
10159   if (op_ == JSOp::Eq || op_ == JSOp::Ne) {
10160     writer.guardIsNullOrUndefined(lhsId);
10161     writer.guardIsNullOrUndefined(rhsId);
10162     // Sloppy equality means we actually only care about the op:
10163     writer.loadBooleanResult(op_ == JSOp::Eq);
10164     trackAttached("SloppyNullUndefined");
10165   } else {
10166     // Strict equality only hits this branch, and only in the
10167     // undef {!,=}==  undef and null {!,=}== null cases.
10168     // The other cases should have hit tryAttachStrictDifferentTypes.
10169     MOZ_ASSERT(lhsVal_.isNull() == rhsVal_.isNull());
10170     lhsVal_.isNull() ? writer.guardIsNull(lhsId)
10171                      : writer.guardIsUndefined(lhsId);
10172     rhsVal_.isNull() ? writer.guardIsNull(rhsId)
10173                      : writer.guardIsUndefined(rhsId);
10174     writer.loadBooleanResult(op_ == JSOp::StrictEq);
10175     trackAttached("StrictNullUndefinedEquality");
10176   }
10177 
10178   writer.returnFromIC();
10179   return AttachDecision::Attach;
10180 }
10181 
tryAttachStringNumber(ValOperandId lhsId,ValOperandId rhsId)10182 AttachDecision CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId,
10183                                                          ValOperandId rhsId) {
10184   // Ensure String x Number
10185   if (!(lhsVal_.isString() && rhsVal_.isNumber()) &&
10186       !(rhsVal_.isString() && lhsVal_.isNumber())) {
10187     return AttachDecision::NoAction;
10188   }
10189 
10190   // Case should have been handled by tryAttachStrictDifferentTypes
10191   MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
10192 
10193   auto createGuards = [&](const Value& v, ValOperandId vId) {
10194     if (v.isString()) {
10195       StringOperandId strId = writer.guardToString(vId);
10196       return writer.guardStringToNumber(strId);
10197     }
10198     MOZ_ASSERT(v.isNumber());
10199     NumberOperandId numId = writer.guardIsNumber(vId);
10200     return numId;
10201   };
10202 
10203   NumberOperandId lhsGuardedId = createGuards(lhsVal_, lhsId);
10204   NumberOperandId rhsGuardedId = createGuards(rhsVal_, rhsId);
10205   writer.compareDoubleResult(op_, lhsGuardedId, rhsGuardedId);
10206   writer.returnFromIC();
10207 
10208   trackAttached("StringNumber");
10209   return AttachDecision::Attach;
10210 }
10211 
tryAttachPrimitiveSymbol(ValOperandId lhsId,ValOperandId rhsId)10212 AttachDecision CompareIRGenerator::tryAttachPrimitiveSymbol(
10213     ValOperandId lhsId, ValOperandId rhsId) {
10214   MOZ_ASSERT(IsEqualityOp(op_));
10215 
10216   // The set of primitive cases we want to handle here (excluding null,
10217   // undefined, and symbol)
10218   auto isPrimitive = [](const Value& x) {
10219     return x.isString() || x.isBoolean() || x.isNumber() || x.isBigInt();
10220   };
10221 
10222   // Ensure Symbol x {String, Bool, Number, BigInt}.
10223   if (!(lhsVal_.isSymbol() && isPrimitive(rhsVal_)) &&
10224       !(rhsVal_.isSymbol() && isPrimitive(lhsVal_))) {
10225     return AttachDecision::NoAction;
10226   }
10227 
10228   auto guardPrimitive = [&](const Value& v, ValOperandId id) {
10229     MOZ_ASSERT(isPrimitive(v));
10230     if (v.isNumber()) {
10231       writer.guardIsNumber(id);
10232       return;
10233     }
10234     switch (v.extractNonDoubleType()) {
10235       case JSVAL_TYPE_STRING:
10236         writer.guardToString(id);
10237         return;
10238       case JSVAL_TYPE_BOOLEAN:
10239         writer.guardToBoolean(id);
10240         return;
10241       case JSVAL_TYPE_BIGINT:
10242         writer.guardToBigInt(id);
10243         return;
10244       default:
10245         MOZ_CRASH("unexpected type");
10246         return;
10247     }
10248   };
10249 
10250   if (lhsVal_.isSymbol()) {
10251     writer.guardToSymbol(lhsId);
10252     guardPrimitive(rhsVal_, rhsId);
10253   } else {
10254     guardPrimitive(lhsVal_, lhsId);
10255     writer.guardToSymbol(rhsId);
10256   }
10257 
10258   // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
10259   // always be false for other compare ops.
10260   writer.loadBooleanResult(op_ == JSOp::Ne || op_ == JSOp::StrictNe);
10261   writer.returnFromIC();
10262 
10263   trackAttached("PrimitiveSymbol");
10264   return AttachDecision::Attach;
10265 }
10266 
tryAttachBoolStringOrNumber(ValOperandId lhsId,ValOperandId rhsId)10267 AttachDecision CompareIRGenerator::tryAttachBoolStringOrNumber(
10268     ValOperandId lhsId, ValOperandId rhsId) {
10269   // Ensure Boolean x {String, Number}.
10270   if (!(lhsVal_.isBoolean() && (rhsVal_.isString() || rhsVal_.isNumber())) &&
10271       !(rhsVal_.isBoolean() && (lhsVal_.isString() || lhsVal_.isNumber()))) {
10272     return AttachDecision::NoAction;
10273   }
10274 
10275   // Case should have been handled by tryAttachStrictDifferentTypes
10276   MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
10277 
10278   // Case should have been handled by tryAttachInt32
10279   MOZ_ASSERT(!lhsVal_.isInt32() && !rhsVal_.isInt32());
10280 
10281   auto createGuards = [&](const Value& v, ValOperandId vId) {
10282     if (v.isBoolean()) {
10283       BooleanOperandId boolId = writer.guardToBoolean(vId);
10284       return writer.booleanToNumber(boolId);
10285     }
10286     if (v.isString()) {
10287       StringOperandId strId = writer.guardToString(vId);
10288       return writer.guardStringToNumber(strId);
10289     }
10290     MOZ_ASSERT(v.isNumber());
10291     return writer.guardIsNumber(vId);
10292   };
10293 
10294   NumberOperandId lhsGuardedId = createGuards(lhsVal_, lhsId);
10295   NumberOperandId rhsGuardedId = createGuards(rhsVal_, rhsId);
10296   writer.compareDoubleResult(op_, lhsGuardedId, rhsGuardedId);
10297   writer.returnFromIC();
10298 
10299   trackAttached("BoolStringOrNumber");
10300   return AttachDecision::Attach;
10301 }
10302 
tryAttachBigIntInt32(ValOperandId lhsId,ValOperandId rhsId)10303 AttachDecision CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId,
10304                                                         ValOperandId rhsId) {
10305   // Ensure BigInt x {Int32, Boolean}.
10306   if (!(lhsVal_.isBigInt() && (rhsVal_.isInt32() || rhsVal_.isBoolean())) &&
10307       !(rhsVal_.isBigInt() && (lhsVal_.isInt32() || lhsVal_.isBoolean()))) {
10308     return AttachDecision::NoAction;
10309   }
10310 
10311   // Case should have been handled by tryAttachStrictDifferentTypes
10312   MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
10313 
10314   auto createGuards = [&](const Value& v, ValOperandId vId) {
10315     if (v.isBoolean()) {
10316       return writer.guardBooleanToInt32(vId);
10317     }
10318     MOZ_ASSERT(v.isInt32());
10319     return writer.guardToInt32(vId);
10320   };
10321 
10322   if (lhsVal_.isBigInt()) {
10323     BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
10324     Int32OperandId intId = createGuards(rhsVal_, rhsId);
10325 
10326     writer.compareBigIntInt32Result(op_, bigIntId, intId);
10327   } else {
10328     Int32OperandId intId = createGuards(lhsVal_, lhsId);
10329     BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
10330 
10331     writer.compareBigIntInt32Result(ReverseCompareOp(op_), bigIntId, intId);
10332   }
10333   writer.returnFromIC();
10334 
10335   trackAttached("BigIntInt32");
10336   return AttachDecision::Attach;
10337 }
10338 
tryAttachBigIntNumber(ValOperandId lhsId,ValOperandId rhsId)10339 AttachDecision CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId,
10340                                                          ValOperandId rhsId) {
10341   // Ensure BigInt x Number.
10342   if (!(lhsVal_.isBigInt() && rhsVal_.isNumber()) &&
10343       !(rhsVal_.isBigInt() && lhsVal_.isNumber())) {
10344     return AttachDecision::NoAction;
10345   }
10346 
10347   // Case should have been handled by tryAttachStrictDifferentTypes
10348   MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
10349 
10350   if (lhsVal_.isBigInt()) {
10351     BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
10352     NumberOperandId numId = writer.guardIsNumber(rhsId);
10353 
10354     writer.compareBigIntNumberResult(op_, bigIntId, numId);
10355   } else {
10356     NumberOperandId numId = writer.guardIsNumber(lhsId);
10357     BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
10358 
10359     writer.compareBigIntNumberResult(ReverseCompareOp(op_), bigIntId, numId);
10360   }
10361   writer.returnFromIC();
10362 
10363   trackAttached("BigIntNumber");
10364   return AttachDecision::Attach;
10365 }
10366 
tryAttachBigIntString(ValOperandId lhsId,ValOperandId rhsId)10367 AttachDecision CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId,
10368                                                          ValOperandId rhsId) {
10369   // Ensure BigInt x String.
10370   if (!(lhsVal_.isBigInt() && rhsVal_.isString()) &&
10371       !(rhsVal_.isBigInt() && lhsVal_.isString())) {
10372     return AttachDecision::NoAction;
10373   }
10374 
10375   // Case should have been handled by tryAttachStrictDifferentTypes
10376   MOZ_ASSERT(op_ != JSOp::StrictEq && op_ != JSOp::StrictNe);
10377 
10378   if (lhsVal_.isBigInt()) {
10379     BigIntOperandId bigIntId = writer.guardToBigInt(lhsId);
10380     StringOperandId strId = writer.guardToString(rhsId);
10381 
10382     writer.compareBigIntStringResult(op_, bigIntId, strId);
10383   } else {
10384     StringOperandId strId = writer.guardToString(lhsId);
10385     BigIntOperandId bigIntId = writer.guardToBigInt(rhsId);
10386 
10387     writer.compareBigIntStringResult(ReverseCompareOp(op_), bigIntId, strId);
10388   }
10389   writer.returnFromIC();
10390 
10391   trackAttached("BigIntString");
10392   return AttachDecision::Attach;
10393 }
10394 
tryAttachStub()10395 AttachDecision CompareIRGenerator::tryAttachStub() {
10396   MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
10397   MOZ_ASSERT(IsEqualityOp(op_) || IsRelationalOp(op_));
10398 
10399   AutoAssertNoPendingException aanpe(cx_);
10400 
10401   constexpr uint8_t lhsIndex = 0;
10402   constexpr uint8_t rhsIndex = 1;
10403 
10404   static_assert(lhsIndex == 0 && rhsIndex == 1,
10405                 "Indexes relied upon by baseline inspector");
10406 
10407   ValOperandId lhsId(writer.setInputOperandId(lhsIndex));
10408   ValOperandId rhsId(writer.setInputOperandId(rhsIndex));
10409 
10410   // For sloppy equality ops, there are cases this IC does not handle:
10411   // - {Object} x {String, Symbol, Bool, Number, BigInt}.
10412   //
10413   // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
10414   // already present.)
10415 
10416   if (IsEqualityOp(op_)) {
10417     TRY_ATTACH(tryAttachObject(lhsId, rhsId));
10418     TRY_ATTACH(tryAttachSymbol(lhsId, rhsId));
10419 
10420     // Handles any (non null or undefined) comparison with null/undefined.
10421     TRY_ATTACH(tryAttachAnyNullUndefined(lhsId, rhsId));
10422 
10423     // This covers -strict- equality/inequality using a type tag check, so
10424     // catches all different type pairs outside of Numbers, which cannot be
10425     // checked on tags alone.
10426     TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId, rhsId));
10427 
10428     TRY_ATTACH(tryAttachNullUndefined(lhsId, rhsId));
10429 
10430     TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId, rhsId));
10431   }
10432 
10433   // This should preceed the Int32/Number cases to allow
10434   // them to not concern themselves with handling undefined
10435   // or null.
10436   TRY_ATTACH(tryAttachNumberUndefined(lhsId, rhsId));
10437 
10438   // We want these to be last, to allow us to bypass the
10439   // strictly-different-types cases in the below attachment code
10440   TRY_ATTACH(tryAttachInt32(lhsId, rhsId));
10441   TRY_ATTACH(tryAttachNumber(lhsId, rhsId));
10442   TRY_ATTACH(tryAttachBigInt(lhsId, rhsId));
10443   TRY_ATTACH(tryAttachString(lhsId, rhsId));
10444 
10445   TRY_ATTACH(tryAttachStringNumber(lhsId, rhsId));
10446   TRY_ATTACH(tryAttachBoolStringOrNumber(lhsId, rhsId));
10447 
10448   TRY_ATTACH(tryAttachBigIntInt32(lhsId, rhsId));
10449   TRY_ATTACH(tryAttachBigIntNumber(lhsId, rhsId));
10450   TRY_ATTACH(tryAttachBigIntString(lhsId, rhsId));
10451 
10452   trackAttached(IRGenerator::NotAttached);
10453   return AttachDecision::NoAction;
10454 }
10455 
trackAttached(const char * name)10456 void CompareIRGenerator::trackAttached(const char* name) {
10457 #ifdef JS_CACHEIR_SPEW
10458   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
10459     sp.valueProperty("lhs", lhsVal_);
10460     sp.valueProperty("rhs", rhsVal_);
10461     sp.opcodeProperty("op", op_);
10462   }
10463 #endif
10464 }
10465 
ToBoolIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue val)10466 ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script,
10467                                      jsbytecode* pc, ICState state,
10468                                      HandleValue val)
10469     : IRGenerator(cx, script, pc, CacheKind::ToBool, state), val_(val) {}
10470 
trackAttached(const char * name)10471 void ToBoolIRGenerator::trackAttached(const char* name) {
10472 #ifdef JS_CACHEIR_SPEW
10473   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
10474     sp.valueProperty("val", val_);
10475   }
10476 #endif
10477 }
10478 
tryAttachStub()10479 AttachDecision ToBoolIRGenerator::tryAttachStub() {
10480   AutoAssertNoPendingException aanpe(cx_);
10481   writer.setTypeData(TypeData(JSValueType(val_.type())));
10482 
10483   TRY_ATTACH(tryAttachBool());
10484   TRY_ATTACH(tryAttachInt32());
10485   TRY_ATTACH(tryAttachNumber());
10486   TRY_ATTACH(tryAttachString());
10487   TRY_ATTACH(tryAttachNullOrUndefined());
10488   TRY_ATTACH(tryAttachObject());
10489   TRY_ATTACH(tryAttachSymbol());
10490   TRY_ATTACH(tryAttachBigInt());
10491 
10492   trackAttached(IRGenerator::NotAttached);
10493   return AttachDecision::NoAction;
10494 }
10495 
tryAttachBool()10496 AttachDecision ToBoolIRGenerator::tryAttachBool() {
10497   if (!val_.isBoolean()) {
10498     return AttachDecision::NoAction;
10499   }
10500 
10501   ValOperandId valId(writer.setInputOperandId(0));
10502   writer.guardNonDoubleType(valId, ValueType::Boolean);
10503   writer.loadOperandResult(valId);
10504   writer.returnFromIC();
10505   trackAttached("ToBoolBool");
10506   return AttachDecision::Attach;
10507 }
10508 
tryAttachInt32()10509 AttachDecision ToBoolIRGenerator::tryAttachInt32() {
10510   if (!val_.isInt32()) {
10511     return AttachDecision::NoAction;
10512   }
10513 
10514   ValOperandId valId(writer.setInputOperandId(0));
10515   writer.guardNonDoubleType(valId, ValueType::Int32);
10516   writer.loadInt32TruthyResult(valId);
10517   writer.returnFromIC();
10518   trackAttached("ToBoolInt32");
10519   return AttachDecision::Attach;
10520 }
10521 
tryAttachNumber()10522 AttachDecision ToBoolIRGenerator::tryAttachNumber() {
10523   if (!val_.isNumber()) {
10524     return AttachDecision::NoAction;
10525   }
10526 
10527   ValOperandId valId(writer.setInputOperandId(0));
10528   NumberOperandId numId = writer.guardIsNumber(valId);
10529   writer.loadDoubleTruthyResult(numId);
10530   writer.returnFromIC();
10531   trackAttached("ToBoolNumber");
10532   return AttachDecision::Attach;
10533 }
10534 
tryAttachSymbol()10535 AttachDecision ToBoolIRGenerator::tryAttachSymbol() {
10536   if (!val_.isSymbol()) {
10537     return AttachDecision::NoAction;
10538   }
10539 
10540   ValOperandId valId(writer.setInputOperandId(0));
10541   writer.guardNonDoubleType(valId, ValueType::Symbol);
10542   writer.loadBooleanResult(true);
10543   writer.returnFromIC();
10544   trackAttached("ToBoolSymbol");
10545   return AttachDecision::Attach;
10546 }
10547 
tryAttachString()10548 AttachDecision ToBoolIRGenerator::tryAttachString() {
10549   if (!val_.isString()) {
10550     return AttachDecision::NoAction;
10551   }
10552 
10553   ValOperandId valId(writer.setInputOperandId(0));
10554   StringOperandId strId = writer.guardToString(valId);
10555   writer.loadStringTruthyResult(strId);
10556   writer.returnFromIC();
10557   trackAttached("ToBoolString");
10558   return AttachDecision::Attach;
10559 }
10560 
tryAttachNullOrUndefined()10561 AttachDecision ToBoolIRGenerator::tryAttachNullOrUndefined() {
10562   if (!val_.isNullOrUndefined()) {
10563     return AttachDecision::NoAction;
10564   }
10565 
10566   ValOperandId valId(writer.setInputOperandId(0));
10567   writer.guardIsNullOrUndefined(valId);
10568   writer.loadBooleanResult(false);
10569   writer.returnFromIC();
10570   trackAttached("ToBoolNullOrUndefined");
10571   return AttachDecision::Attach;
10572 }
10573 
tryAttachObject()10574 AttachDecision ToBoolIRGenerator::tryAttachObject() {
10575   if (!val_.isObject()) {
10576     return AttachDecision::NoAction;
10577   }
10578 
10579   ValOperandId valId(writer.setInputOperandId(0));
10580   ObjOperandId objId = writer.guardToObject(valId);
10581   writer.loadObjectTruthyResult(objId);
10582   writer.returnFromIC();
10583   trackAttached("ToBoolObject");
10584   return AttachDecision::Attach;
10585 }
10586 
tryAttachBigInt()10587 AttachDecision ToBoolIRGenerator::tryAttachBigInt() {
10588   if (!val_.isBigInt()) {
10589     return AttachDecision::NoAction;
10590   }
10591 
10592   ValOperandId valId(writer.setInputOperandId(0));
10593   BigIntOperandId bigIntId = writer.guardToBigInt(valId);
10594   writer.loadBigIntTruthyResult(bigIntId);
10595   writer.returnFromIC();
10596   trackAttached("ToBoolBigInt");
10597   return AttachDecision::Attach;
10598 }
10599 
GetIntrinsicIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue val)10600 GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext* cx,
10601                                                  HandleScript script,
10602                                                  jsbytecode* pc, ICState state,
10603                                                  HandleValue val)
10604     : IRGenerator(cx, script, pc, CacheKind::GetIntrinsic, state), val_(val) {}
10605 
trackAttached(const char * name)10606 void GetIntrinsicIRGenerator::trackAttached(const char* name) {
10607 #ifdef JS_CACHEIR_SPEW
10608   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
10609     sp.valueProperty("val", val_);
10610   }
10611 #endif
10612 }
10613 
tryAttachStub()10614 AttachDecision GetIntrinsicIRGenerator::tryAttachStub() {
10615   AutoAssertNoPendingException aanpe(cx_);
10616   writer.loadValueResult(val_);
10617   writer.returnFromIC();
10618   trackAttached("GetIntrinsic");
10619   return AttachDecision::Attach;
10620 }
10621 
UnaryArithIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,JSOp op,HandleValue val,HandleValue res)10622 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext* cx, HandleScript script,
10623                                              jsbytecode* pc, ICState state,
10624                                              JSOp op, HandleValue val,
10625                                              HandleValue res)
10626     : IRGenerator(cx, script, pc, CacheKind::UnaryArith, state),
10627       op_(op),
10628       val_(val),
10629       res_(res) {}
10630 
trackAttached(const char * name)10631 void UnaryArithIRGenerator::trackAttached(const char* name) {
10632 #ifdef JS_CACHEIR_SPEW
10633   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
10634     sp.valueProperty("val", val_);
10635     sp.valueProperty("res", res_);
10636   }
10637 #endif
10638 }
10639 
tryAttachStub()10640 AttachDecision UnaryArithIRGenerator::tryAttachStub() {
10641   AutoAssertNoPendingException aanpe(cx_);
10642   TRY_ATTACH(tryAttachInt32());
10643   TRY_ATTACH(tryAttachNumber());
10644   TRY_ATTACH(tryAttachBitwise());
10645   TRY_ATTACH(tryAttachBigInt());
10646   TRY_ATTACH(tryAttachStringInt32());
10647   TRY_ATTACH(tryAttachStringNumber());
10648 
10649   trackAttached(IRGenerator::NotAttached);
10650   return AttachDecision::NoAction;
10651 }
10652 
CanConvertToInt32ForToNumber(const Value & v)10653 static bool CanConvertToInt32ForToNumber(const Value& v) {
10654   return v.isInt32() || v.isBoolean() || v.isNull();
10655 }
10656 
EmitGuardToInt32ForToNumber(CacheIRWriter & writer,ValOperandId id,const Value & v)10657 static Int32OperandId EmitGuardToInt32ForToNumber(CacheIRWriter& writer,
10658                                                   ValOperandId id,
10659                                                   const Value& v) {
10660   if (v.isInt32()) {
10661     return writer.guardToInt32(id);
10662   }
10663   if (v.isNull()) {
10664     writer.guardIsNull(id);
10665     return writer.loadInt32Constant(0);
10666   }
10667   MOZ_ASSERT(v.isBoolean());
10668   return writer.guardBooleanToInt32(id);
10669 }
10670 
tryAttachInt32()10671 AttachDecision UnaryArithIRGenerator::tryAttachInt32() {
10672   if (op_ == JSOp::BitNot) {
10673     return AttachDecision::NoAction;
10674   }
10675   if (!CanConvertToInt32ForToNumber(val_) || !res_.isInt32()) {
10676     return AttachDecision::NoAction;
10677   }
10678 
10679   ValOperandId valId(writer.setInputOperandId(0));
10680 
10681   Int32OperandId intId = EmitGuardToInt32ForToNumber(writer, valId, val_);
10682   switch (op_) {
10683     case JSOp::Pos:
10684       writer.loadInt32Result(intId);
10685       trackAttached("UnaryArith.Int32Pos");
10686       break;
10687     case JSOp::Neg:
10688       writer.int32NegationResult(intId);
10689       trackAttached("UnaryArith.Int32Neg");
10690       break;
10691     case JSOp::Inc:
10692       writer.int32IncResult(intId);
10693       trackAttached("UnaryArith.Int32Inc");
10694       break;
10695     case JSOp::Dec:
10696       writer.int32DecResult(intId);
10697       trackAttached("UnaryArith.Int32Dec");
10698       break;
10699     case JSOp::ToNumeric:
10700       writer.loadInt32Result(intId);
10701       trackAttached("UnaryArith.Int32ToNumeric");
10702       break;
10703     default:
10704       MOZ_CRASH("unexpected OP");
10705   }
10706 
10707   writer.returnFromIC();
10708   return AttachDecision::Attach;
10709 }
10710 
CanConvertToDoubleForToNumber(const Value & v)10711 static bool CanConvertToDoubleForToNumber(const Value& v) {
10712   return v.isNumber() || v.isBoolean() || v.isNullOrUndefined();
10713 }
10714 
EmitGuardToDoubleForToNumber(CacheIRWriter & writer,ValOperandId id,const Value & v)10715 static NumberOperandId EmitGuardToDoubleForToNumber(CacheIRWriter& writer,
10716                                                     ValOperandId id,
10717                                                     const Value& v) {
10718   if (v.isNumber()) {
10719     return writer.guardIsNumber(id);
10720   }
10721   if (v.isBoolean()) {
10722     BooleanOperandId boolId = writer.guardToBoolean(id);
10723     return writer.booleanToNumber(boolId);
10724   }
10725   if (v.isNull()) {
10726     writer.guardIsNull(id);
10727     return writer.loadDoubleConstant(0.0);
10728   }
10729   MOZ_ASSERT(v.isUndefined());
10730   writer.guardIsUndefined(id);
10731   return writer.loadDoubleConstant(JS::GenericNaN());
10732 }
10733 
tryAttachNumber()10734 AttachDecision UnaryArithIRGenerator::tryAttachNumber() {
10735   if (op_ == JSOp::BitNot) {
10736     return AttachDecision::NoAction;
10737   }
10738   if (!CanConvertToDoubleForToNumber(val_)) {
10739     return AttachDecision::NoAction;
10740   }
10741   MOZ_ASSERT(res_.isNumber());
10742 
10743   ValOperandId valId(writer.setInputOperandId(0));
10744   NumberOperandId numId = EmitGuardToDoubleForToNumber(writer, valId, val_);
10745 
10746   switch (op_) {
10747     case JSOp::Pos:
10748       writer.loadDoubleResult(numId);
10749       trackAttached("UnaryArith.DoublePos");
10750       break;
10751     case JSOp::Neg:
10752       writer.doubleNegationResult(numId);
10753       trackAttached("UnaryArith.DoubleNeg");
10754       break;
10755     case JSOp::Inc:
10756       writer.doubleIncResult(numId);
10757       trackAttached("UnaryArith.DoubleInc");
10758       break;
10759     case JSOp::Dec:
10760       writer.doubleDecResult(numId);
10761       trackAttached("UnaryArith.DoubleDec");
10762       break;
10763     case JSOp::ToNumeric:
10764       writer.loadDoubleResult(numId);
10765       trackAttached("UnaryArith.DoubleToNumeric");
10766       break;
10767     default:
10768       MOZ_CRASH("Unexpected OP");
10769   }
10770 
10771   writer.returnFromIC();
10772   return AttachDecision::Attach;
10773 }
10774 
CanTruncateToInt32(const Value & val)10775 static bool CanTruncateToInt32(const Value& val) {
10776   return val.isNumber() || val.isBoolean() || val.isNullOrUndefined() ||
10777          val.isString();
10778 }
10779 
10780 // Convert type into int32 for the bitwise/shift operands.
EmitTruncateToInt32Guard(CacheIRWriter & writer,ValOperandId id,const Value & val)10781 static Int32OperandId EmitTruncateToInt32Guard(CacheIRWriter& writer,
10782                                                ValOperandId id,
10783                                                const Value& val) {
10784   MOZ_ASSERT(CanTruncateToInt32(val));
10785   if (val.isInt32()) {
10786     return writer.guardToInt32(id);
10787   }
10788   if (val.isBoolean()) {
10789     return writer.guardBooleanToInt32(id);
10790   }
10791   if (val.isNullOrUndefined()) {
10792     writer.guardIsNullOrUndefined(id);
10793     return writer.loadInt32Constant(0);
10794   }
10795   NumberOperandId numId;
10796   if (val.isString()) {
10797     StringOperandId strId = writer.guardToString(id);
10798     numId = writer.guardStringToNumber(strId);
10799   } else {
10800     MOZ_ASSERT(val.isDouble());
10801     numId = writer.guardIsNumber(id);
10802   }
10803   return writer.truncateDoubleToUInt32(numId);
10804 }
10805 
tryAttachBitwise()10806 AttachDecision UnaryArithIRGenerator::tryAttachBitwise() {
10807   // Only bitwise operators.
10808   if (op_ != JSOp::BitNot) {
10809     return AttachDecision::NoAction;
10810   }
10811 
10812   // Check guard conditions
10813   if (!CanTruncateToInt32(val_)) {
10814     return AttachDecision::NoAction;
10815   }
10816 
10817   // Bitwise operators always produce Int32 values.
10818   MOZ_ASSERT(res_.isInt32());
10819 
10820   ValOperandId valId(writer.setInputOperandId(0));
10821   Int32OperandId intId = EmitTruncateToInt32Guard(writer, valId, val_);
10822   writer.int32NotResult(intId);
10823   trackAttached("BinaryArith.Bitwise.BitNot");
10824 
10825   writer.returnFromIC();
10826   return AttachDecision::Attach;
10827 }
10828 
tryAttachBigInt()10829 AttachDecision UnaryArithIRGenerator::tryAttachBigInt() {
10830   if (!val_.isBigInt()) {
10831     return AttachDecision::NoAction;
10832   }
10833   MOZ_ASSERT(res_.isBigInt());
10834 
10835   MOZ_ASSERT(op_ != JSOp::Pos,
10836              "Applying the unary + operator on BigInt values throws an error");
10837 
10838   ValOperandId valId(writer.setInputOperandId(0));
10839   BigIntOperandId bigIntId = writer.guardToBigInt(valId);
10840   switch (op_) {
10841     case JSOp::BitNot:
10842       writer.bigIntNotResult(bigIntId);
10843       trackAttached("UnaryArith.BigIntNot");
10844       break;
10845     case JSOp::Neg:
10846       writer.bigIntNegationResult(bigIntId);
10847       trackAttached("UnaryArith.BigIntNeg");
10848       break;
10849     case JSOp::Inc:
10850       writer.bigIntIncResult(bigIntId);
10851       trackAttached("UnaryArith.BigIntInc");
10852       break;
10853     case JSOp::Dec:
10854       writer.bigIntDecResult(bigIntId);
10855       trackAttached("UnaryArith.BigIntDec");
10856       break;
10857     case JSOp::ToNumeric:
10858       writer.loadBigIntResult(bigIntId);
10859       trackAttached("UnaryArith.BigIntToNumeric");
10860       break;
10861     default:
10862       MOZ_CRASH("Unexpected OP");
10863   }
10864 
10865   writer.returnFromIC();
10866   return AttachDecision::Attach;
10867 }
10868 
tryAttachStringInt32()10869 AttachDecision UnaryArithIRGenerator::tryAttachStringInt32() {
10870   if (!val_.isString()) {
10871     return AttachDecision::NoAction;
10872   }
10873   MOZ_ASSERT(res_.isNumber());
10874 
10875   // Case should have been handled by tryAttachBitwise.
10876   MOZ_ASSERT(op_ != JSOp::BitNot);
10877 
10878   if (!res_.isInt32()) {
10879     return AttachDecision::NoAction;
10880   }
10881 
10882   ValOperandId valId(writer.setInputOperandId(0));
10883   StringOperandId stringId = writer.guardToString(valId);
10884   Int32OperandId intId = writer.guardStringToInt32(stringId);
10885 
10886   switch (op_) {
10887     case JSOp::Pos:
10888       writer.loadInt32Result(intId);
10889       trackAttached("UnaryArith.StringInt32Pos");
10890       break;
10891     case JSOp::Neg:
10892       writer.int32NegationResult(intId);
10893       trackAttached("UnaryArith.StringInt32Neg");
10894       break;
10895     case JSOp::Inc:
10896       writer.int32IncResult(intId);
10897       trackAttached("UnaryArith.StringInt32Inc");
10898       break;
10899     case JSOp::Dec:
10900       writer.int32DecResult(intId);
10901       trackAttached("UnaryArith.StringInt32Dec");
10902       break;
10903     case JSOp::ToNumeric:
10904       writer.loadInt32Result(intId);
10905       trackAttached("UnaryArith.StringInt32ToNumeric");
10906       break;
10907     default:
10908       MOZ_CRASH("Unexpected OP");
10909   }
10910 
10911   writer.returnFromIC();
10912   return AttachDecision::Attach;
10913 }
10914 
tryAttachStringNumber()10915 AttachDecision UnaryArithIRGenerator::tryAttachStringNumber() {
10916   if (!val_.isString()) {
10917     return AttachDecision::NoAction;
10918   }
10919   MOZ_ASSERT(res_.isNumber());
10920 
10921   // Case should have been handled by tryAttachBitwise.
10922   MOZ_ASSERT(op_ != JSOp::BitNot);
10923 
10924   ValOperandId valId(writer.setInputOperandId(0));
10925   StringOperandId stringId = writer.guardToString(valId);
10926   NumberOperandId numId = writer.guardStringToNumber(stringId);
10927 
10928   Int32OperandId truncatedId;
10929   switch (op_) {
10930     case JSOp::Pos:
10931       writer.loadDoubleResult(numId);
10932       trackAttached("UnaryArith.StringNumberPos");
10933       break;
10934     case JSOp::Neg:
10935       writer.doubleNegationResult(numId);
10936       trackAttached("UnaryArith.StringNumberNeg");
10937       break;
10938     case JSOp::Inc:
10939       writer.doubleIncResult(numId);
10940       trackAttached("UnaryArith.StringNumberInc");
10941       break;
10942     case JSOp::Dec:
10943       writer.doubleDecResult(numId);
10944       trackAttached("UnaryArith.StringNumberDec");
10945       break;
10946     case JSOp::ToNumeric:
10947       writer.loadDoubleResult(numId);
10948       trackAttached("UnaryArith.StringNumberToNumeric");
10949       break;
10950     default:
10951       MOZ_CRASH("Unexpected OP");
10952   }
10953 
10954   writer.returnFromIC();
10955   return AttachDecision::Attach;
10956 }
10957 
ToPropertyKeyIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,HandleValue val)10958 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext* cx,
10959                                                    HandleScript script,
10960                                                    jsbytecode* pc,
10961                                                    ICState state,
10962                                                    HandleValue val)
10963     : IRGenerator(cx, script, pc, CacheKind::ToPropertyKey, state), val_(val) {}
10964 
trackAttached(const char * name)10965 void ToPropertyKeyIRGenerator::trackAttached(const char* name) {
10966 #ifdef JS_CACHEIR_SPEW
10967   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
10968     sp.valueProperty("val", val_);
10969   }
10970 #endif
10971 }
10972 
tryAttachStub()10973 AttachDecision ToPropertyKeyIRGenerator::tryAttachStub() {
10974   AutoAssertNoPendingException aanpe(cx_);
10975   TRY_ATTACH(tryAttachInt32());
10976   TRY_ATTACH(tryAttachNumber());
10977   TRY_ATTACH(tryAttachString());
10978   TRY_ATTACH(tryAttachSymbol());
10979 
10980   trackAttached(IRGenerator::NotAttached);
10981   return AttachDecision::NoAction;
10982 }
10983 
tryAttachInt32()10984 AttachDecision ToPropertyKeyIRGenerator::tryAttachInt32() {
10985   if (!val_.isInt32()) {
10986     return AttachDecision::NoAction;
10987   }
10988 
10989   ValOperandId valId(writer.setInputOperandId(0));
10990 
10991   Int32OperandId intId = writer.guardToInt32(valId);
10992   writer.loadInt32Result(intId);
10993   writer.returnFromIC();
10994 
10995   trackAttached("ToPropertyKey.Int32");
10996   return AttachDecision::Attach;
10997 }
10998 
tryAttachNumber()10999 AttachDecision ToPropertyKeyIRGenerator::tryAttachNumber() {
11000   if (!val_.isNumber()) {
11001     return AttachDecision::NoAction;
11002   }
11003 
11004   // We allow negative zero here because ToPropertyKey(-0.0) is 0.
11005   int32_t unused;
11006   if (!mozilla::NumberEqualsInt32(val_.toNumber(), &unused)) {
11007     return AttachDecision::NoAction;
11008   }
11009 
11010   ValOperandId valId(writer.setInputOperandId(0));
11011 
11012   Int32OperandId intId = writer.guardToInt32Index(valId);
11013   writer.loadInt32Result(intId);
11014   writer.returnFromIC();
11015 
11016   trackAttached("ToPropertyKey.Number");
11017   return AttachDecision::Attach;
11018 }
11019 
tryAttachString()11020 AttachDecision ToPropertyKeyIRGenerator::tryAttachString() {
11021   if (!val_.isString()) {
11022     return AttachDecision::NoAction;
11023   }
11024 
11025   ValOperandId valId(writer.setInputOperandId(0));
11026 
11027   StringOperandId strId = writer.guardToString(valId);
11028   writer.loadStringResult(strId);
11029   writer.returnFromIC();
11030 
11031   trackAttached("ToPropertyKey.String");
11032   return AttachDecision::Attach;
11033 }
11034 
tryAttachSymbol()11035 AttachDecision ToPropertyKeyIRGenerator::tryAttachSymbol() {
11036   if (!val_.isSymbol()) {
11037     return AttachDecision::NoAction;
11038   }
11039 
11040   ValOperandId valId(writer.setInputOperandId(0));
11041 
11042   SymbolOperandId strId = writer.guardToSymbol(valId);
11043   writer.loadSymbolResult(strId);
11044   writer.returnFromIC();
11045 
11046   trackAttached("ToPropertyKey.Symbol");
11047   return AttachDecision::Attach;
11048 }
11049 
BinaryArithIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,JSOp op,HandleValue lhs,HandleValue rhs,HandleValue res)11050 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext* cx,
11051                                                HandleScript script,
11052                                                jsbytecode* pc, ICState state,
11053                                                JSOp op, HandleValue lhs,
11054                                                HandleValue rhs, HandleValue res)
11055     : IRGenerator(cx, script, pc, CacheKind::BinaryArith, state),
11056       op_(op),
11057       lhs_(lhs),
11058       rhs_(rhs),
11059       res_(res) {}
11060 
trackAttached(const char * name)11061 void BinaryArithIRGenerator::trackAttached(const char* name) {
11062 #ifdef JS_CACHEIR_SPEW
11063   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
11064     sp.opcodeProperty("op", op_);
11065     sp.valueProperty("rhs", rhs_);
11066     sp.valueProperty("lhs", lhs_);
11067   }
11068 #endif
11069 }
11070 
tryAttachStub()11071 AttachDecision BinaryArithIRGenerator::tryAttachStub() {
11072   AutoAssertNoPendingException aanpe(cx_);
11073   // Arithmetic operations with Int32 operands
11074   TRY_ATTACH(tryAttachInt32());
11075 
11076   // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
11077   // operands.
11078   TRY_ATTACH(tryAttachBitwise());
11079 
11080   // Arithmetic operations with Double operands. This needs to come after
11081   // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
11082   // more specialized Int32 IC if it is possible.
11083   TRY_ATTACH(tryAttachDouble());
11084 
11085   // String x String
11086   TRY_ATTACH(tryAttachStringConcat());
11087 
11088   // String x Object
11089   TRY_ATTACH(tryAttachStringObjectConcat());
11090 
11091   TRY_ATTACH(tryAttachStringNumberConcat());
11092 
11093   // String + Boolean
11094   TRY_ATTACH(tryAttachStringBooleanConcat());
11095 
11096   // Arithmetic operations or bitwise operations with BigInt operands
11097   TRY_ATTACH(tryAttachBigInt());
11098 
11099   // Arithmetic operations (without addition) with String x Int32.
11100   TRY_ATTACH(tryAttachStringInt32Arith());
11101 
11102   trackAttached(IRGenerator::NotAttached);
11103   return AttachDecision::NoAction;
11104 }
11105 
tryAttachBitwise()11106 AttachDecision BinaryArithIRGenerator::tryAttachBitwise() {
11107   // Only bit-wise and shifts.
11108   if (op_ != JSOp::BitOr && op_ != JSOp::BitXor && op_ != JSOp::BitAnd &&
11109       op_ != JSOp::Lsh && op_ != JSOp::Rsh && op_ != JSOp::Ursh) {
11110     return AttachDecision::NoAction;
11111   }
11112 
11113   // Check guard conditions
11114   if (!CanTruncateToInt32(lhs_) || !CanTruncateToInt32(rhs_)) {
11115     return AttachDecision::NoAction;
11116   }
11117 
11118   // All ops, with the exception of Ursh, produce Int32 values.
11119   MOZ_ASSERT_IF(op_ != JSOp::Ursh, res_.isInt32());
11120 
11121   ValOperandId lhsId(writer.setInputOperandId(0));
11122   ValOperandId rhsId(writer.setInputOperandId(1));
11123 
11124   Int32OperandId lhsIntId = EmitTruncateToInt32Guard(writer, lhsId, lhs_);
11125   Int32OperandId rhsIntId = EmitTruncateToInt32Guard(writer, rhsId, rhs_);
11126 
11127   switch (op_) {
11128     case JSOp::BitOr:
11129       writer.int32BitOrResult(lhsIntId, rhsIntId);
11130       trackAttached("BinaryArith.Bitwise.BitOr");
11131       break;
11132     case JSOp::BitXor:
11133       writer.int32BitXorResult(lhsIntId, rhsIntId);
11134       trackAttached("BinaryArith.Bitwise.BitXor");
11135       break;
11136     case JSOp::BitAnd:
11137       writer.int32BitAndResult(lhsIntId, rhsIntId);
11138       trackAttached("BinaryArith.Bitwise.BitAnd");
11139       break;
11140     case JSOp::Lsh:
11141       writer.int32LeftShiftResult(lhsIntId, rhsIntId);
11142       trackAttached("BinaryArith.Bitwise.LeftShift");
11143       break;
11144     case JSOp::Rsh:
11145       writer.int32RightShiftResult(lhsIntId, rhsIntId);
11146       trackAttached("BinaryArith.Bitwise.RightShift");
11147       break;
11148     case JSOp::Ursh:
11149       writer.int32URightShiftResult(lhsIntId, rhsIntId, res_.isDouble());
11150       trackAttached("BinaryArith.Bitwise.UnsignedRightShift");
11151       break;
11152     default:
11153       MOZ_CRASH("Unhandled op in tryAttachBitwise");
11154   }
11155 
11156   writer.returnFromIC();
11157   return AttachDecision::Attach;
11158 }
11159 
tryAttachDouble()11160 AttachDecision BinaryArithIRGenerator::tryAttachDouble() {
11161   // Check valid opcodes
11162   if (op_ != JSOp::Add && op_ != JSOp::Sub && op_ != JSOp::Mul &&
11163       op_ != JSOp::Div && op_ != JSOp::Mod && op_ != JSOp::Pow) {
11164     return AttachDecision::NoAction;
11165   }
11166 
11167   // Check guard conditions.
11168   if (!CanConvertToDoubleForToNumber(lhs_) ||
11169       !CanConvertToDoubleForToNumber(rhs_)) {
11170     return AttachDecision::NoAction;
11171   }
11172 
11173   ValOperandId lhsId(writer.setInputOperandId(0));
11174   ValOperandId rhsId(writer.setInputOperandId(1));
11175 
11176   NumberOperandId lhs = EmitGuardToDoubleForToNumber(writer, lhsId, lhs_);
11177   NumberOperandId rhs = EmitGuardToDoubleForToNumber(writer, rhsId, rhs_);
11178 
11179   switch (op_) {
11180     case JSOp::Add:
11181       writer.doubleAddResult(lhs, rhs);
11182       trackAttached("BinaryArith.Double.Add");
11183       break;
11184     case JSOp::Sub:
11185       writer.doubleSubResult(lhs, rhs);
11186       trackAttached("BinaryArith.Double.Sub");
11187       break;
11188     case JSOp::Mul:
11189       writer.doubleMulResult(lhs, rhs);
11190       trackAttached("BinaryArith.Double.Mul");
11191       break;
11192     case JSOp::Div:
11193       writer.doubleDivResult(lhs, rhs);
11194       trackAttached("BinaryArith.Double.Div");
11195       break;
11196     case JSOp::Mod:
11197       writer.doubleModResult(lhs, rhs);
11198       trackAttached("BinaryArith.Double.Mod");
11199       break;
11200     case JSOp::Pow:
11201       writer.doublePowResult(lhs, rhs);
11202       trackAttached("BinaryArith.Double.Pow");
11203       break;
11204     default:
11205       MOZ_CRASH("Unhandled Op");
11206   }
11207   writer.returnFromIC();
11208   return AttachDecision::Attach;
11209 }
11210 
tryAttachInt32()11211 AttachDecision BinaryArithIRGenerator::tryAttachInt32() {
11212   // Check guard conditions.
11213   if (!CanConvertToInt32ForToNumber(lhs_) ||
11214       !CanConvertToInt32ForToNumber(rhs_)) {
11215     return AttachDecision::NoAction;
11216   }
11217 
11218   // These ICs will failure() if result can't be encoded in an Int32:
11219   // If sample result is not Int32, we should avoid IC.
11220   if (!res_.isInt32()) {
11221     return AttachDecision::NoAction;
11222   }
11223 
11224   if (op_ != JSOp::Add && op_ != JSOp::Sub && op_ != JSOp::Mul &&
11225       op_ != JSOp::Div && op_ != JSOp::Mod && op_ != JSOp::Pow) {
11226     return AttachDecision::NoAction;
11227   }
11228 
11229   if (op_ == JSOp::Pow && !CanAttachInt32Pow(lhs_, rhs_)) {
11230     return AttachDecision::NoAction;
11231   }
11232 
11233   ValOperandId lhsId(writer.setInputOperandId(0));
11234   ValOperandId rhsId(writer.setInputOperandId(1));
11235 
11236   Int32OperandId lhsIntId = EmitGuardToInt32ForToNumber(writer, lhsId, lhs_);
11237   Int32OperandId rhsIntId = EmitGuardToInt32ForToNumber(writer, rhsId, rhs_);
11238 
11239   switch (op_) {
11240     case JSOp::Add:
11241       writer.int32AddResult(lhsIntId, rhsIntId);
11242       trackAttached("BinaryArith.Int32.Add");
11243       break;
11244     case JSOp::Sub:
11245       writer.int32SubResult(lhsIntId, rhsIntId);
11246       trackAttached("BinaryArith.Int32.Sub");
11247       break;
11248     case JSOp::Mul:
11249       writer.int32MulResult(lhsIntId, rhsIntId);
11250       trackAttached("BinaryArith.Int32.Mul");
11251       break;
11252     case JSOp::Div:
11253       writer.int32DivResult(lhsIntId, rhsIntId);
11254       trackAttached("BinaryArith.Int32.Div");
11255       break;
11256     case JSOp::Mod:
11257       writer.int32ModResult(lhsIntId, rhsIntId);
11258       trackAttached("BinaryArith.Int32.Mod");
11259       break;
11260     case JSOp::Pow:
11261       writer.int32PowResult(lhsIntId, rhsIntId);
11262       trackAttached("BinaryArith.Int32.Pow");
11263       break;
11264     default:
11265       MOZ_CRASH("Unhandled op in tryAttachInt32");
11266   }
11267 
11268   writer.returnFromIC();
11269   return AttachDecision::Attach;
11270 }
11271 
tryAttachStringNumberConcat()11272 AttachDecision BinaryArithIRGenerator::tryAttachStringNumberConcat() {
11273   // Only Addition
11274   if (op_ != JSOp::Add) {
11275     return AttachDecision::NoAction;
11276   }
11277 
11278   if (!(lhs_.isString() && rhs_.isNumber()) &&
11279       !(lhs_.isNumber() && rhs_.isString())) {
11280     return AttachDecision::NoAction;
11281   }
11282 
11283   ValOperandId lhsId(writer.setInputOperandId(0));
11284   ValOperandId rhsId(writer.setInputOperandId(1));
11285 
11286   StringOperandId lhsStrId = emitToStringGuard(lhsId, lhs_);
11287   StringOperandId rhsStrId = emitToStringGuard(rhsId, rhs_);
11288 
11289   writer.callStringConcatResult(lhsStrId, rhsStrId);
11290 
11291   writer.returnFromIC();
11292   trackAttached("BinaryArith.StringNumberConcat");
11293   return AttachDecision::Attach;
11294 }
11295 
tryAttachStringBooleanConcat()11296 AttachDecision BinaryArithIRGenerator::tryAttachStringBooleanConcat() {
11297   // Only Addition
11298   if (op_ != JSOp::Add) {
11299     return AttachDecision::NoAction;
11300   }
11301 
11302   if ((!lhs_.isString() || !rhs_.isBoolean()) &&
11303       (!lhs_.isBoolean() || !rhs_.isString())) {
11304     return AttachDecision::NoAction;
11305   }
11306 
11307   ValOperandId lhsId(writer.setInputOperandId(0));
11308   ValOperandId rhsId(writer.setInputOperandId(1));
11309 
11310   auto guardToString = [&](ValOperandId id, const Value& v) {
11311     if (v.isString()) {
11312       return writer.guardToString(id);
11313     }
11314     MOZ_ASSERT(v.isBoolean());
11315     BooleanOperandId boolId = writer.guardToBoolean(id);
11316     return writer.booleanToString(boolId);
11317   };
11318 
11319   StringOperandId lhsStrId = guardToString(lhsId, lhs_);
11320   StringOperandId rhsStrId = guardToString(rhsId, rhs_);
11321 
11322   writer.callStringConcatResult(lhsStrId, rhsStrId);
11323 
11324   writer.returnFromIC();
11325   trackAttached("BinaryArith.StringBooleanConcat");
11326   return AttachDecision::Attach;
11327 }
11328 
tryAttachStringConcat()11329 AttachDecision BinaryArithIRGenerator::tryAttachStringConcat() {
11330   // Only Addition
11331   if (op_ != JSOp::Add) {
11332     return AttachDecision::NoAction;
11333   }
11334 
11335   // Check guards
11336   if (!lhs_.isString() || !rhs_.isString()) {
11337     return AttachDecision::NoAction;
11338   }
11339 
11340   ValOperandId lhsId(writer.setInputOperandId(0));
11341   ValOperandId rhsId(writer.setInputOperandId(1));
11342 
11343   StringOperandId lhsStrId = writer.guardToString(lhsId);
11344   StringOperandId rhsStrId = writer.guardToString(rhsId);
11345 
11346   writer.callStringConcatResult(lhsStrId, rhsStrId);
11347 
11348   writer.returnFromIC();
11349   trackAttached("BinaryArith.StringConcat");
11350   return AttachDecision::Attach;
11351 }
11352 
tryAttachStringObjectConcat()11353 AttachDecision BinaryArithIRGenerator::tryAttachStringObjectConcat() {
11354   // Only Addition
11355   if (op_ != JSOp::Add) {
11356     return AttachDecision::NoAction;
11357   }
11358 
11359   // Check Guards
11360   if (!(lhs_.isObject() && rhs_.isString()) &&
11361       !(lhs_.isString() && rhs_.isObject()))
11362     return AttachDecision::NoAction;
11363 
11364   ValOperandId lhsId(writer.setInputOperandId(0));
11365   ValOperandId rhsId(writer.setInputOperandId(1));
11366 
11367   // This guard is actually overly tight, as the runtime
11368   // helper can handle lhs or rhs being a string, so long
11369   // as the other is an object.
11370   if (lhs_.isString()) {
11371     writer.guardToString(lhsId);
11372     writer.guardToObject(rhsId);
11373   } else {
11374     writer.guardToObject(lhsId);
11375     writer.guardToString(rhsId);
11376   }
11377 
11378   writer.callStringObjectConcatResult(lhsId, rhsId);
11379 
11380   writer.returnFromIC();
11381   trackAttached("BinaryArith.StringObjectConcat");
11382   return AttachDecision::Attach;
11383 }
11384 
tryAttachBigInt()11385 AttachDecision BinaryArithIRGenerator::tryAttachBigInt() {
11386   // Check Guards
11387   if (!lhs_.isBigInt() || !rhs_.isBigInt()) {
11388     return AttachDecision::NoAction;
11389   }
11390 
11391   switch (op_) {
11392     case JSOp::Add:
11393     case JSOp::Sub:
11394     case JSOp::Mul:
11395     case JSOp::Div:
11396     case JSOp::Mod:
11397     case JSOp::Pow:
11398       // Arithmetic operations.
11399       break;
11400 
11401     case JSOp::BitOr:
11402     case JSOp::BitXor:
11403     case JSOp::BitAnd:
11404     case JSOp::Lsh:
11405     case JSOp::Rsh:
11406       // Bitwise operations.
11407       break;
11408 
11409     default:
11410       return AttachDecision::NoAction;
11411   }
11412 
11413   ValOperandId lhsId(writer.setInputOperandId(0));
11414   ValOperandId rhsId(writer.setInputOperandId(1));
11415 
11416   BigIntOperandId lhsBigIntId = writer.guardToBigInt(lhsId);
11417   BigIntOperandId rhsBigIntId = writer.guardToBigInt(rhsId);
11418 
11419   switch (op_) {
11420     case JSOp::Add:
11421       writer.bigIntAddResult(lhsBigIntId, rhsBigIntId);
11422       trackAttached("BinaryArith.BigInt.Add");
11423       break;
11424     case JSOp::Sub:
11425       writer.bigIntSubResult(lhsBigIntId, rhsBigIntId);
11426       trackAttached("BinaryArith.BigInt.Sub");
11427       break;
11428     case JSOp::Mul:
11429       writer.bigIntMulResult(lhsBigIntId, rhsBigIntId);
11430       trackAttached("BinaryArith.BigInt.Mul");
11431       break;
11432     case JSOp::Div:
11433       writer.bigIntDivResult(lhsBigIntId, rhsBigIntId);
11434       trackAttached("BinaryArith.BigInt.Div");
11435       break;
11436     case JSOp::Mod:
11437       writer.bigIntModResult(lhsBigIntId, rhsBigIntId);
11438       trackAttached("BinaryArith.BigInt.Mod");
11439       break;
11440     case JSOp::Pow:
11441       writer.bigIntPowResult(lhsBigIntId, rhsBigIntId);
11442       trackAttached("BinaryArith.BigInt.Pow");
11443       break;
11444     case JSOp::BitOr:
11445       writer.bigIntBitOrResult(lhsBigIntId, rhsBigIntId);
11446       trackAttached("BinaryArith.BigInt.BitOr");
11447       break;
11448     case JSOp::BitXor:
11449       writer.bigIntBitXorResult(lhsBigIntId, rhsBigIntId);
11450       trackAttached("BinaryArith.BigInt.BitXor");
11451       break;
11452     case JSOp::BitAnd:
11453       writer.bigIntBitAndResult(lhsBigIntId, rhsBigIntId);
11454       trackAttached("BinaryArith.BigInt.BitAnd");
11455       break;
11456     case JSOp::Lsh:
11457       writer.bigIntLeftShiftResult(lhsBigIntId, rhsBigIntId);
11458       trackAttached("BinaryArith.BigInt.LeftShift");
11459       break;
11460     case JSOp::Rsh:
11461       writer.bigIntRightShiftResult(lhsBigIntId, rhsBigIntId);
11462       trackAttached("BinaryArith.BigInt.RightShift");
11463       break;
11464     default:
11465       MOZ_CRASH("Unhandled op in tryAttachBigInt");
11466   }
11467 
11468   writer.returnFromIC();
11469   return AttachDecision::Attach;
11470 }
11471 
tryAttachStringInt32Arith()11472 AttachDecision BinaryArithIRGenerator::tryAttachStringInt32Arith() {
11473   // Check for either int32 x string or string x int32.
11474   if (!(lhs_.isInt32() && rhs_.isString()) &&
11475       !(lhs_.isString() && rhs_.isInt32())) {
11476     return AttachDecision::NoAction;
11477   }
11478 
11479   // The created ICs will fail if the result can't be encoded as as int32.
11480   // Thus skip this IC, if the sample result is not an int32.
11481   if (!res_.isInt32()) {
11482     return AttachDecision::NoAction;
11483   }
11484 
11485   // Must _not_ support Add, because it would be string concatenation instead.
11486   // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
11487   // reject that as well.
11488   if (op_ != JSOp::Sub && op_ != JSOp::Mul && op_ != JSOp::Div &&
11489       op_ != JSOp::Mod) {
11490     return AttachDecision::NoAction;
11491   }
11492 
11493   ValOperandId lhsId(writer.setInputOperandId(0));
11494   ValOperandId rhsId(writer.setInputOperandId(1));
11495 
11496   auto guardToInt32 = [&](ValOperandId id, const Value& v) {
11497     if (v.isInt32()) {
11498       return writer.guardToInt32(id);
11499     }
11500 
11501     MOZ_ASSERT(v.isString());
11502     StringOperandId strId = writer.guardToString(id);
11503     return writer.guardStringToInt32(strId);
11504   };
11505 
11506   Int32OperandId lhsIntId = guardToInt32(lhsId, lhs_);
11507   Int32OperandId rhsIntId = guardToInt32(rhsId, rhs_);
11508 
11509   switch (op_) {
11510     case JSOp::Sub:
11511       writer.int32SubResult(lhsIntId, rhsIntId);
11512       trackAttached("BinaryArith.StringInt32.Sub");
11513       break;
11514     case JSOp::Mul:
11515       writer.int32MulResult(lhsIntId, rhsIntId);
11516       trackAttached("BinaryArith.StringInt32.Mul");
11517       break;
11518     case JSOp::Div:
11519       writer.int32DivResult(lhsIntId, rhsIntId);
11520       trackAttached("BinaryArith.StringInt32.Div");
11521       break;
11522     case JSOp::Mod:
11523       writer.int32ModResult(lhsIntId, rhsIntId);
11524       trackAttached("BinaryArith.StringInt32.Mod");
11525       break;
11526     default:
11527       MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
11528   }
11529 
11530   writer.returnFromIC();
11531   return AttachDecision::Attach;
11532 }
11533 
NewArrayIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,JSOp op,HandleObject templateObj,BaselineFrame * frame)11534 NewArrayIRGenerator::NewArrayIRGenerator(JSContext* cx, HandleScript script,
11535                                          jsbytecode* pc, ICState state, JSOp op,
11536                                          HandleObject templateObj,
11537                                          BaselineFrame* frame)
11538     : IRGenerator(cx, script, pc, CacheKind::NewArray, state),
11539 #ifdef JS_CACHEIR_SPEW
11540       op_(op),
11541 #endif
11542       templateObject_(templateObj),
11543       frame_(frame) {
11544   MOZ_ASSERT(templateObject_);
11545 }
11546 
trackAttached(const char * name)11547 void NewArrayIRGenerator::trackAttached(const char* name) {
11548 #ifdef JS_CACHEIR_SPEW
11549   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
11550     sp.opcodeProperty("op", op_);
11551   }
11552 #endif
11553 }
11554 
11555 // Allocation sites are usually created during baseline compilation, but we also
11556 // need to created them when an IC stub is added to a baseline compiled script
11557 // and when trial inlining.
MaybeCreateAllocSite(jsbytecode * pc,BaselineFrame * frame)11558 static gc::AllocSite* MaybeCreateAllocSite(jsbytecode* pc,
11559                                            BaselineFrame* frame) {
11560   MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc)));
11561 
11562   JSScript* outerScript = frame->outerScript();
11563   bool inInterpreter = frame->runningInInterpreter();
11564   bool isInlined = frame->icScript()->isInlined();
11565 
11566   if (inInterpreter && !isInlined) {
11567     return outerScript->zone()->unknownAllocSite();
11568   }
11569 
11570   return outerScript->createAllocSite();
11571 }
11572 
tryAttachArrayObject()11573 AttachDecision NewArrayIRGenerator::tryAttachArrayObject() {
11574   ArrayObject* arrayObj = &templateObject_->as<ArrayObject>();
11575 
11576   MOZ_ASSERT(arrayObj->numUsedFixedSlots() == 0);
11577   MOZ_ASSERT(arrayObj->numDynamicSlots() == 0);
11578   MOZ_ASSERT(!arrayObj->isSharedMemory());
11579 
11580   // The macro assembler only supports creating arrays with fixed elements.
11581   if (arrayObj->hasDynamicElements()) {
11582     return AttachDecision::NoAction;
11583   }
11584 
11585   // Stub doesn't support metadata builder
11586   if (cx_->realm()->hasAllocationMetadataBuilder()) {
11587     return AttachDecision::NoAction;
11588   }
11589 
11590   writer.guardNoAllocationMetadataBuilder(
11591       cx_->realm()->addressOfMetadataBuilder());
11592 
11593   gc::AllocSite* site = MaybeCreateAllocSite(pc_, frame_);
11594   if (!site) {
11595     return AttachDecision::NoAction;
11596   }
11597 
11598   Shape* shape = arrayObj->shape();
11599   uint32_t length = arrayObj->length();
11600 
11601   writer.newArrayObjectResult(length, shape, site);
11602 
11603   writer.returnFromIC();
11604 
11605   trackAttached("NewArrayObject");
11606   return AttachDecision::Attach;
11607 }
11608 
tryAttachStub()11609 AttachDecision NewArrayIRGenerator::tryAttachStub() {
11610   AutoAssertNoPendingException aanpe(cx_);
11611 
11612   TRY_ATTACH(tryAttachArrayObject());
11613 
11614   trackAttached(IRGenerator::NotAttached);
11615   return AttachDecision::NoAction;
11616 }
11617 
NewObjectIRGenerator(JSContext * cx,HandleScript script,jsbytecode * pc,ICState state,JSOp op,HandleObject templateObj,BaselineFrame * frame)11618 NewObjectIRGenerator::NewObjectIRGenerator(JSContext* cx, HandleScript script,
11619                                            jsbytecode* pc, ICState state,
11620                                            JSOp op, HandleObject templateObj,
11621                                            BaselineFrame* frame)
11622     : IRGenerator(cx, script, pc, CacheKind::NewObject, state),
11623 #ifdef JS_CACHEIR_SPEW
11624       op_(op),
11625 #endif
11626       templateObject_(templateObj),
11627       frame_(frame) {
11628   MOZ_ASSERT(templateObject_);
11629 }
11630 
trackAttached(const char * name)11631 void NewObjectIRGenerator::trackAttached(const char* name) {
11632 #ifdef JS_CACHEIR_SPEW
11633   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
11634     sp.opcodeProperty("op", op_);
11635   }
11636 #endif
11637 }
11638 
tryAttachPlainObject()11639 AttachDecision NewObjectIRGenerator::tryAttachPlainObject() {
11640   // Don't optimize allocations with too many dynamic slots. We use an unrolled
11641   // loop when initializing slots and this avoids generating too much code.
11642   static const uint32_t MaxDynamicSlotsToOptimize = 64;
11643 
11644   NativeObject* nativeObj = &templateObject_->as<NativeObject>();
11645   MOZ_ASSERT(nativeObj->is<PlainObject>());
11646 
11647   // Stub doesn't support metadata builder
11648   if (cx_->realm()->hasAllocationMetadataBuilder()) {
11649     return AttachDecision::NoAction;
11650   }
11651 
11652   if (nativeObj->numDynamicSlots() > MaxDynamicSlotsToOptimize) {
11653     return AttachDecision::NoAction;
11654   }
11655 
11656   MOZ_ASSERT(!nativeObj->hasDynamicElements());
11657   MOZ_ASSERT(!nativeObj->isSharedMemory());
11658 
11659   gc::AllocSite* site = MaybeCreateAllocSite(pc_, frame_);
11660   if (!site) {
11661     return AttachDecision::NoAction;
11662   }
11663 
11664   uint32_t numFixedSlots = nativeObj->numUsedFixedSlots();
11665   uint32_t numDynamicSlots = nativeObj->numDynamicSlots();
11666   gc::AllocKind allocKind = nativeObj->allocKindForTenure();
11667   Shape* shape = nativeObj->shape();
11668 
11669   writer.guardNoAllocationMetadataBuilder(
11670       cx_->realm()->addressOfMetadataBuilder());
11671   writer.newPlainObjectResult(numFixedSlots, numDynamicSlots, allocKind, shape,
11672                               site);
11673 
11674   writer.returnFromIC();
11675 
11676   trackAttached("NewPlainObject");
11677   return AttachDecision::Attach;
11678 }
11679 
tryAttachStub()11680 AttachDecision NewObjectIRGenerator::tryAttachStub() {
11681   AutoAssertNoPendingException aanpe(cx_);
11682 
11683   TRY_ATTACH(tryAttachPlainObject());
11684 
11685   trackAttached(IRGenerator::NotAttached);
11686   return AttachDecision::NoAction;
11687 }
11688 
11689 #ifdef JS_SIMULATOR
CallAnyNative(JSContext * cx,unsigned argc,Value * vp)11690 bool js::jit::CallAnyNative(JSContext* cx, unsigned argc, Value* vp) {
11691   CallArgs args = CallArgsFromVp(argc, vp);
11692   JSObject* calleeObj = &args.callee();
11693 
11694   MOZ_ASSERT(calleeObj->is<JSFunction>());
11695   auto* calleeFunc = &calleeObj->as<JSFunction>();
11696   MOZ_ASSERT(calleeFunc->isNativeWithoutJitEntry());
11697 
11698   JSNative native = calleeFunc->native();
11699   return native(cx, args.length(), args.base());
11700 }
11701 #endif
11702