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/. */
7 #include "jit/CacheIR.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/FloatingPoint.h"
16 #include "builtin/DataViewObject.h"
17 #include "builtin/MapObject.h"
18 #include "builtin/ModuleObject.h"
19 #include "builtin/Object.h"
20 #include "jit/BaselineIC.h"
21 #include "jit/CacheIRCloner.h"
22 #include "jit/CacheIRCompiler.h"
23 #include "jit/CacheIRGenerator.h"
24 #include "jit/CacheIRSpewer.h"
25 #include "jit/CacheIRWriter.h"
26 #include "jit/InlinableNatives.h"
27 #include "jit/JitContext.h"
28 #include "jit/JitZone.h"
29 #include "js/experimental/JitInfo.h" // JSJitInfo
30 #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
31 #include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy, js::ToWindowIfWindowProxy
32 #include "js/friend/XrayJitInfo.h" // js::jit::GetXrayJitInfo, JS::XrayJitInfo
33 #include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis
34 #include "js/RegExpFlags.h" // JS::RegExpFlags
35 #include "js/ScalarType.h" // js::Scalar::Type
36 #include "js/Utility.h" // JS::AutoEnterOOMUnsafeRegion
37 #include "js/Wrapper.h"
38 #include "proxy/DOMProxy.h" // js::GetDOMProxyHandlerFamily
39 #include "proxy/ScriptedProxyHandler.h"
40 #include "util/DifferentialTesting.h"
41 #include "util/Unicode.h"
42 #include "vm/ArrayBufferObject.h"
43 #include "vm/BoundFunctionObject.h"
44 #include "vm/BytecodeUtil.h"
45 #include "vm/Compartment.h"
46 #include "vm/Iteration.h"
47 #include "vm/PlainObject.h" // js::PlainObject
48 #include "vm/ProxyObject.h"
49 #include "vm/RegExpObject.h"
50 #include "vm/SelfHosting.h"
51 #include "vm/ThrowMsgKind.h" // ThrowCondition
52 #include "vm/Watchtower.h"
53 #include "wasm/WasmInstance.h"
55 #include "jit/BaselineFrame-inl.h"
56 #include "jit/MacroAssembler-inl.h"
57 #include "vm/ArrayBufferObject-inl.h"
58 #include "vm/BytecodeUtil-inl.h"
59 #include "vm/EnvironmentObject-inl.h"
60 #include "vm/JSContext-inl.h"
61 #include "vm/JSFunction-inl.h"
62 #include "vm/JSObject-inl.h"
63 #include "vm/JSScript-inl.h"
64 #include "vm/NativeObject-inl.h"
65 #include "vm/PlainObject-inl.h"
66 #include "vm/StringObject-inl.h"
67 #include "wasm/WasmInstance-inl.h"
70 using namespace js::jit
;
72 using mozilla::DebugOnly
;
75 using JS::DOMProxyShadowsResult
;
76 using JS::ExpandoAndGeneration
;
78 const char* const js::jit::CacheKindNames
[] = {
79 #define DEFINE_KIND(kind) #kind,
80 CACHE_IR_KINDS(DEFINE_KIND
)
84 const char* const js::jit::CacheIROpNames
[] = {
85 #define OPNAME(op, ...) #op,
90 const CacheIROpInfo
js::jit::CacheIROpInfos
[] = {
91 #define OPINFO(op, len, transpile, ...) {len, transpile},
96 const uint32_t js::jit::CacheIROpHealth
[] = {
97 #define OPHEALTH(op, len, transpile, health) health,
98 CACHE_IR_OPS(OPHEALTH
)
102 size_t js::jit::NumInputsForCacheKind(CacheKind kind
) {
104 case CacheKind::NewArray
:
105 case CacheKind::NewObject
:
106 case CacheKind::GetIntrinsic
:
108 case CacheKind::GetProp
:
109 case CacheKind::TypeOf
:
110 case CacheKind::ToPropertyKey
:
111 case CacheKind::GetIterator
:
112 case CacheKind::ToBool
:
113 case CacheKind::UnaryArith
:
114 case CacheKind::GetName
:
115 case CacheKind::BindName
:
116 case CacheKind::Call
:
117 case CacheKind::OptimizeSpreadCall
:
118 case CacheKind::CloseIter
:
119 case CacheKind::OptimizeGetIterator
:
121 case CacheKind::Compare
:
122 case CacheKind::GetElem
:
123 case CacheKind::GetPropSuper
:
124 case CacheKind::SetProp
:
126 case CacheKind::HasOwn
:
127 case CacheKind::CheckPrivateField
:
128 case CacheKind::InstanceOf
:
129 case CacheKind::BinaryArith
:
131 case CacheKind::GetElemSuper
:
132 case CacheKind::SetElem
:
135 MOZ_CRASH("Invalid kind");
139 void CacheIRWriter::assertSameCompartment(JSObject
* obj
) {
140 MOZ_ASSERT(cx_
->compartment() == obj
->compartment());
142 void CacheIRWriter::assertSameZone(Shape
* shape
) {
143 MOZ_ASSERT(cx_
->zone() == shape
->zone());
147 StubField
CacheIRWriter::readStubField(uint32_t offset
,
148 StubField::Type type
) const {
150 size_t currentOffset
= 0;
152 // If we've seen an offset earlier than this before, we know we can start the
153 // search there at least, otherwise, we start the search from the beginning.
154 if (lastOffset_
< offset
) {
155 currentOffset
= lastOffset_
;
159 while (currentOffset
!= offset
) {
160 currentOffset
+= StubField::sizeInBytes(stubFields_
[index
].type());
162 MOZ_ASSERT(index
< stubFields_
.length());
165 MOZ_ASSERT(stubFields_
[index
].type() == type
);
167 lastOffset_
= currentOffset
;
170 return stubFields_
[index
];
173 CacheIRCloner::CacheIRCloner(ICCacheIRStub
* stub
)
174 : stubInfo_(stub
->stubInfo()), stubData_(stub
->stubDataStart()) {}
176 void CacheIRCloner::cloneOp(CacheOp op
, CacheIRReader
& reader
,
177 CacheIRWriter
& writer
) {
179 #define DEFINE_OP(op, ...) \
181 clone##op(reader, writer); \
183 CACHE_IR_OPS(DEFINE_OP
)
186 MOZ_CRASH("Invalid op");
190 uintptr_t CacheIRCloner::readStubWord(uint32_t offset
) {
191 return stubInfo_
->getStubRawWord(stubData_
, offset
);
193 int64_t CacheIRCloner::readStubInt64(uint32_t offset
) {
194 return stubInfo_
->getStubRawInt64(stubData_
, offset
);
197 Shape
* CacheIRCloner::getShapeField(uint32_t stubOffset
) {
198 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
200 Shape
* CacheIRCloner::getWeakShapeField(uint32_t stubOffset
) {
201 // No barrier is required to clone a weak pointer.
202 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
204 GetterSetter
* CacheIRCloner::getWeakGetterSetterField(uint32_t stubOffset
) {
205 // No barrier is required to clone a weak pointer.
206 return reinterpret_cast<GetterSetter
*>(readStubWord(stubOffset
));
208 JSObject
* CacheIRCloner::getObjectField(uint32_t stubOffset
) {
209 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
211 JSObject
* CacheIRCloner::getWeakObjectField(uint32_t stubOffset
) {
212 // No barrier is required to clone a weak pointer.
213 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
215 JSString
* CacheIRCloner::getStringField(uint32_t stubOffset
) {
216 return reinterpret_cast<JSString
*>(readStubWord(stubOffset
));
218 JSAtom
* CacheIRCloner::getAtomField(uint32_t stubOffset
) {
219 return reinterpret_cast<JSAtom
*>(readStubWord(stubOffset
));
221 JS::Symbol
* CacheIRCloner::getSymbolField(uint32_t stubOffset
) {
222 return reinterpret_cast<JS::Symbol
*>(readStubWord(stubOffset
));
224 BaseScript
* CacheIRCloner::getWeakBaseScriptField(uint32_t stubOffset
) {
225 // No barrier is required to clone a weak pointer.
226 return reinterpret_cast<BaseScript
*>(readStubWord(stubOffset
));
228 JitCode
* CacheIRCloner::getJitCodeField(uint32_t stubOffset
) {
229 return reinterpret_cast<JitCode
*>(readStubWord(stubOffset
));
231 uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset
) {
232 return uint32_t(reinterpret_cast<uintptr_t>(readStubWord(stubOffset
)));
234 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset
) {
235 return reinterpret_cast<const void*>(readStubWord(stubOffset
));
237 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset
) {
238 return static_cast<uint64_t>(readStubInt64(stubOffset
));
240 gc::AllocSite
* CacheIRCloner::getAllocSiteField(uint32_t stubOffset
) {
241 return reinterpret_cast<gc::AllocSite
*>(readStubWord(stubOffset
));
244 jsid
CacheIRCloner::getIdField(uint32_t stubOffset
) {
245 return jsid::fromRawBits(readStubWord(stubOffset
));
247 const Value
CacheIRCloner::getValueField(uint32_t stubOffset
) {
248 return Value::fromRawBits(uint64_t(readStubInt64(stubOffset
)));
250 double CacheIRCloner::getDoubleField(uint32_t stubOffset
) {
251 uint64_t bits
= uint64_t(readStubInt64(stubOffset
));
252 return mozilla::BitwiseCast
<double>(bits
);
255 IRGenerator::IRGenerator(JSContext
* cx
, HandleScript script
, jsbytecode
* pc
,
256 CacheKind cacheKind
, ICState state
)
261 cacheKind_(cacheKind
),
263 isFirstStub_(state
.newStubIsFirstStub()) {}
265 GetPropIRGenerator::GetPropIRGenerator(JSContext
* cx
, HandleScript script
,
266 jsbytecode
* pc
, ICState state
,
267 CacheKind cacheKind
, HandleValue val
,
269 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
271 static void EmitLoadSlotResult(CacheIRWriter
& writer
, ObjOperandId holderId
,
272 NativeObject
* holder
, PropertyInfo prop
) {
273 if (holder
->isFixedSlot(prop
.slot())) {
274 writer
.loadFixedSlotResult(holderId
,
275 NativeObject::getFixedSlotOffset(prop
.slot()));
277 size_t dynamicSlotOffset
=
278 holder
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
279 writer
.loadDynamicSlotResult(holderId
, dynamicSlotOffset
);
286 // DOM proxies are proxies that are used to implement various DOM objects like
287 // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
288 // object that stores extra properties added to the object. The following
289 // CacheIR instructions are only used with DOM proxies:
291 // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
292 // returns either an UndefinedValue (no expando), ObjectValue (the expando
293 // object), or PrivateValue(ExpandoAndGeneration*).
295 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
296 // slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
297 // generation, then returns expandoAndGeneration->expando. This Value is
298 // either an UndefinedValue or ObjectValue.
300 // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
301 // expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
302 // returns the expandoAndGeneration->expando Value.
304 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
305 // guards it's either UndefinedValue or an object with the expected shape.
307 enum class ProxyStubType
{
315 static bool IsCacheableDOMProxy(ProxyObject
* obj
) {
316 const BaseProxyHandler
* handler
= obj
->handler();
317 if (handler
->family() != GetDOMProxyHandlerFamily()) {
321 // Some DOM proxies have dynamic prototypes. We can't really cache those very
323 return obj
->hasStaticPrototype();
326 static ProxyStubType
GetProxyStubType(JSContext
* cx
, HandleObject obj
,
328 if (!obj
->is
<ProxyObject
>()) {
329 return ProxyStubType::None
;
331 auto proxy
= obj
.as
<ProxyObject
>();
333 if (!IsCacheableDOMProxy(proxy
)) {
334 return ProxyStubType::Generic
;
337 // Private fields are defined on a separate expando object.
338 if (id
.isPrivateName()) {
339 return ProxyStubType::Generic
;
342 DOMProxyShadowsResult shadows
= GetDOMProxyShadowsCheck()(cx
, proxy
, id
);
343 if (shadows
== DOMProxyShadowsResult::ShadowCheckFailed
) {
344 cx
->clearPendingException();
345 return ProxyStubType::None
;
348 if (DOMProxyIsShadowing(shadows
)) {
349 if (shadows
== DOMProxyShadowsResult::ShadowsViaDirectExpando
||
350 shadows
== DOMProxyShadowsResult::ShadowsViaIndirectExpando
) {
351 return ProxyStubType::DOMExpando
;
353 return ProxyStubType::DOMShadowed
;
356 MOZ_ASSERT(shadows
== DOMProxyShadowsResult::DoesntShadow
||
357 shadows
== DOMProxyShadowsResult::DoesntShadowUnique
);
358 return ProxyStubType::DOMUnshadowed
;
361 static bool ValueToNameOrSymbolId(JSContext
* cx
, HandleValue idVal
,
362 MutableHandleId id
, bool* nameOrSymbol
) {
363 *nameOrSymbol
= false;
365 if (!idVal
.isString() && !idVal
.isSymbol() && !idVal
.isUndefined() &&
370 if (!PrimitiveValueToId
<CanGC
>(cx
, idVal
, id
)) {
374 if (!id
.isAtom() && !id
.isSymbol()) {
375 id
.set(JS::PropertyKey::Void());
379 if (id
.isAtom() && id
.toAtom()->isIndex()) {
380 id
.set(JS::PropertyKey::Void());
384 *nameOrSymbol
= true;
388 AttachDecision
GetPropIRGenerator::tryAttachStub() {
389 AutoAssertNoPendingException
aanpe(cx_
);
391 ValOperandId
valId(writer
.setInputOperandId(0));
392 if (cacheKind_
!= CacheKind::GetProp
) {
393 MOZ_ASSERT_IF(cacheKind_
== CacheKind::GetPropSuper
,
394 getSuperReceiverValueId().id() == 1);
395 MOZ_ASSERT_IF(cacheKind_
!= CacheKind::GetPropSuper
,
396 getElemKeyValueId().id() == 1);
397 writer
.setInputOperandId(1);
399 if (cacheKind_
== CacheKind::GetElemSuper
) {
400 MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
401 writer
.setInputOperandId(2);
406 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
407 cx_
->clearPendingException();
408 return AttachDecision::NoAction
;
411 // |super.prop| getter calls use a |this| value that differs from lookup
413 ValOperandId receiverId
= isSuper() ? getSuperReceiverValueId() : valId
;
415 if (val_
.isObject()) {
416 RootedObject
obj(cx_
, &val_
.toObject());
417 ObjOperandId objId
= writer
.guardToObject(valId
);
419 TRY_ATTACH(tryAttachObjectLength(obj
, objId
, id
));
420 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, id
));
421 TRY_ATTACH(tryAttachDataView(obj
, objId
, id
));
422 TRY_ATTACH(tryAttachArrayBufferMaybeShared(obj
, objId
, id
));
423 TRY_ATTACH(tryAttachRegExp(obj
, objId
, id
));
424 TRY_ATTACH(tryAttachMap(obj
, objId
, id
));
425 TRY_ATTACH(tryAttachSet(obj
, objId
, id
));
426 TRY_ATTACH(tryAttachNative(obj
, objId
, id
, receiverId
));
427 TRY_ATTACH(tryAttachModuleNamespace(obj
, objId
, id
));
428 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
));
429 TRY_ATTACH(tryAttachCrossCompartmentWrapper(obj
, objId
, id
));
431 tryAttachXrayCrossCompartmentWrapper(obj
, objId
, id
, receiverId
));
432 TRY_ATTACH(tryAttachFunction(obj
, objId
, id
));
433 TRY_ATTACH(tryAttachArgumentsObjectIterator(obj
, objId
, id
));
434 TRY_ATTACH(tryAttachArgumentsObjectCallee(obj
, objId
, id
));
435 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, receiverId
));
437 trackAttached(IRGenerator::NotAttached
);
438 return AttachDecision::NoAction
;
441 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
442 cacheKind_
== CacheKind::GetElemSuper
);
444 TRY_ATTACH(tryAttachProxyElement(obj
, objId
));
445 TRY_ATTACH(tryAttachTypedArrayElement(obj
, objId
));
448 Int32OperandId indexId
;
449 if (maybeGuardInt32Index(idVal_
, getElemKeyValueId(), &index
, &indexId
)) {
450 TRY_ATTACH(tryAttachDenseElement(obj
, objId
, index
, indexId
));
451 TRY_ATTACH(tryAttachDenseElementHole(obj
, objId
, index
, indexId
));
452 TRY_ATTACH(tryAttachSparseElement(obj
, objId
, index
, indexId
));
453 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, index
, indexId
));
454 TRY_ATTACH(tryAttachArgumentsObjectArgHole(obj
, objId
, index
, indexId
));
456 tryAttachGenericElement(obj
, objId
, index
, indexId
, receiverId
));
458 trackAttached(IRGenerator::NotAttached
);
459 return AttachDecision::NoAction
;
462 trackAttached(IRGenerator::NotAttached
);
463 return AttachDecision::NoAction
;
467 TRY_ATTACH(tryAttachPrimitive(valId
, id
));
468 TRY_ATTACH(tryAttachStringLength(valId
, id
));
470 trackAttached(IRGenerator::NotAttached
);
471 return AttachDecision::NoAction
;
474 if (idVal_
.isInt32()) {
475 ValOperandId indexId
= getElemKeyValueId();
476 TRY_ATTACH(tryAttachStringChar(valId
, indexId
));
478 trackAttached(IRGenerator::NotAttached
);
479 return AttachDecision::NoAction
;
482 trackAttached(IRGenerator::NotAttached
);
483 return AttachDecision::NoAction
;
487 // Any property lookups performed when trying to attach ICs must be pure, i.e.
488 // must use LookupPropertyPure() or similar functions. Pure lookups are
489 // guaranteed to never modify the prototype chain. This ensures that the holder
490 // object can always be found on the prototype chain.
491 static bool IsCacheableProtoChain(NativeObject
* obj
, NativeObject
* holder
) {
492 while (obj
!= holder
) {
493 JSObject
* proto
= obj
->staticPrototype();
494 if (!proto
|| !proto
->is
<NativeObject
>()) {
497 obj
= &proto
->as
<NativeObject
>();
503 static bool IsCacheableGetPropSlot(NativeObject
* obj
, NativeObject
* holder
,
505 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
507 return prop
.isDataProperty();
510 enum class NativeGetPropKind
{
518 static NativeGetPropKind
IsCacheableGetPropCall(NativeObject
* obj
,
519 NativeObject
* holder
,
521 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
523 if (!prop
.isAccessorProperty()) {
524 return NativeGetPropKind::None
;
527 JSObject
* getterObject
= holder
->getGetter(prop
);
528 if (!getterObject
|| !getterObject
->is
<JSFunction
>()) {
529 return NativeGetPropKind::None
;
532 JSFunction
& getter
= getterObject
->as
<JSFunction
>();
534 if (getter
.isClassConstructor()) {
535 return NativeGetPropKind::None
;
538 // Scripted functions and natives with JIT entry can use the scripted path.
539 if (getter
.hasJitEntry()) {
540 return NativeGetPropKind::ScriptedGetter
;
543 MOZ_ASSERT(getter
.isNativeWithoutJitEntry());
544 return NativeGetPropKind::NativeGetter
;
547 static bool CheckHasNoSuchOwnProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
548 if (!obj
->is
<NativeObject
>()) {
551 // Don't handle objects with resolve hooks.
552 if (ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
)) {
555 if (obj
->as
<NativeObject
>().contains(cx
, id
)) {
561 static bool CheckHasNoSuchProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
562 JSObject
* curObj
= obj
;
564 if (!CheckHasNoSuchOwnProperty(cx
, curObj
, id
)) {
568 curObj
= curObj
->staticPrototype();
574 static bool IsCacheableNoProperty(JSContext
* cx
, NativeObject
* obj
,
575 NativeObject
* holder
, jsid id
,
579 // If we're doing a name lookup, we have to throw a ReferenceError.
580 if (JSOp(*pc
) == JSOp::GetBoundName
) {
584 return CheckHasNoSuchProperty(cx
, obj
, id
);
587 static NativeGetPropKind
CanAttachNativeGetProp(JSContext
* cx
, JSObject
* obj
,
589 NativeObject
** holder
,
590 Maybe
<PropertyInfo
>* propInfo
,
592 MOZ_ASSERT(id
.isString() || id
.isSymbol());
593 MOZ_ASSERT(!*holder
);
595 // The lookup needs to be universally pure, otherwise we risk calling hooks
596 // out of turn. We don't mind doing this even when purity isn't required,
597 // because we only miss out on shape hashification, which is only a temporary
598 // perf cost. The limits were arbitrarily set, anyways.
599 NativeObject
* baseHolder
= nullptr;
601 if (!LookupPropertyPure(cx
, obj
, id
, &baseHolder
, &prop
)) {
602 return NativeGetPropKind::None
;
604 auto* nobj
= &obj
->as
<NativeObject
>();
606 if (prop
.isNativeProperty()) {
607 MOZ_ASSERT(baseHolder
);
608 *holder
= baseHolder
;
609 *propInfo
= mozilla::Some(prop
.propertyInfo());
611 if (IsCacheableGetPropSlot(nobj
, *holder
, propInfo
->ref())) {
612 return NativeGetPropKind::Slot
;
615 return IsCacheableGetPropCall(nobj
, *holder
, propInfo
->ref());
618 if (!prop
.isFound()) {
619 if (IsCacheableNoProperty(cx
, nobj
, *holder
, id
, pc
)) {
620 return NativeGetPropKind::Missing
;
624 return NativeGetPropKind::None
;
627 static void GuardReceiverProto(CacheIRWriter
& writer
, NativeObject
* obj
,
628 ObjOperandId objId
) {
629 // Note: we guard on the actual prototype and not on the shape because this is
630 // used for sparse elements where we expect shape changes.
632 if (JSObject
* proto
= obj
->staticPrototype()) {
633 writer
.guardProto(objId
, proto
);
635 writer
.guardNullProto(objId
);
639 // Guard that a given object has same class and same OwnProperties (excluding
640 // dense elements and dynamic properties).
641 static void TestMatchingNativeReceiver(CacheIRWriter
& writer
, NativeObject
* obj
,
642 ObjOperandId objId
) {
643 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
646 // Similar to |TestMatchingNativeReceiver|, but specialized for ProxyObject.
647 static void TestMatchingProxyReceiver(CacheIRWriter
& writer
, ProxyObject
* obj
,
648 ObjOperandId objId
) {
649 writer
.guardShapeForClass(objId
, obj
->shape());
652 static void GeneratePrototypeGuards(CacheIRWriter
& writer
, JSObject
* obj
,
653 NativeObject
* holder
, ObjOperandId objId
) {
654 // Assuming target property is on |holder|, generate appropriate guards to
655 // ensure |holder| is still on the prototype chain of |obj| and we haven't
656 // introduced any shadowing definitions.
658 // For each item in the proto chain before holder, we must ensure that
659 // [[GetPrototypeOf]] still has the expected result, and that
660 // [[GetOwnProperty]] has no definition of the target property.
663 // [SMDOC] Shape Teleporting Optimization
664 // --------------------------------------
666 // Starting with the assumption (and guideline to developers) that mutating
667 // prototypes is an uncommon and fair-to-penalize operation we move cost
668 // from the access side to the mutation side.
670 // Consider the following proto chain, with B defining a property 'x':
672 // D -> C -> B{x: 3} -> A -> null
674 // When accessing |D.x| we refer to D as the "receiver", and B as the
675 // "holder". To optimize this access we need to ensure that neither D nor C
676 // has since defined a shadowing property 'x'. Since C is a prototype that
677 // we assume is rarely mutated we would like to avoid checking each time if
678 // new properties are added. To do this we require that whenever C starts
679 // shadowing a property on its proto chain, we invalidate (and opt out of) the
680 // teleporting optimization by setting the InvalidatedTeleporting flag on the
681 // object we're shadowing, triggering a shape change of that object. As a
682 // result, checking the shape of D and B is sufficient. Note that we do not
683 // care if the shape or properties of A change since the lookup of 'x' will
686 // The second condition we must verify is that the prototype chain was not
687 // mutated. The same mechanism as above is used. When the prototype link is
688 // changed, we generate a new shape for the object. If the object whose
689 // link we are mutating is itself a prototype, we regenerate shapes down
690 // the chain by setting the InvalidatedTeleporting flag on them. This means
691 // the same two shape checks as above are sufficient.
693 // Once the InvalidatedTeleporting flag is set, it means the shape will no
694 // longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
695 // In this case we can no longer apply the optimization.
698 // - ReshapeForProtoMutation
699 // - ReshapeForShadowedProp
702 MOZ_ASSERT(obj
!= holder
);
704 // Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
705 // unchanged so peel off the receiver.
706 JSObject
* pobj
= obj
->staticPrototype();
707 MOZ_ASSERT(pobj
->isUsedAsPrototype());
709 // If teleporting is supported for this holder, we are done.
710 if (!holder
->hasInvalidatedTeleporting()) {
714 // If already at the holder, no further proto checks are needed.
715 if (pobj
== holder
) {
719 // Synchronize pobj and protoId.
720 MOZ_ASSERT(pobj
== obj
->staticPrototype());
721 ObjOperandId protoId
= writer
.loadProto(objId
);
723 // Shape guard each prototype object between receiver and holder. This guards
724 // against both proto changes and shadowing properties.
725 while (pobj
!= holder
) {
726 writer
.guardShape(protoId
, pobj
->shape());
728 pobj
= pobj
->staticPrototype();
729 protoId
= writer
.loadProto(protoId
);
733 static void GeneratePrototypeHoleGuards(CacheIRWriter
& writer
,
734 NativeObject
* obj
, ObjOperandId objId
,
735 bool alwaysGuardFirstProto
) {
736 if (alwaysGuardFirstProto
) {
737 GuardReceiverProto(writer
, obj
, objId
);
740 JSObject
* pobj
= obj
->staticPrototype();
742 ObjOperandId protoId
= writer
.loadObject(pobj
);
744 // Make sure the shape matches, to ensure the proto is unchanged and to
745 // avoid non-dense elements or anything else that is being checked by
746 // CanAttachDenseElementHole.
747 MOZ_ASSERT(pobj
->is
<NativeObject
>());
748 writer
.guardShape(protoId
, pobj
->shape());
750 // Also make sure there are no dense elements.
751 writer
.guardNoDenseElements(protoId
);
753 pobj
= pobj
->staticPrototype();
757 // Similar to |TestMatchingReceiver|, but for the holder object (when it
758 // differs from the receiver). The holder may also be the expando of the
759 // receiver if it exists.
760 static void TestMatchingHolder(CacheIRWriter
& writer
, NativeObject
* obj
,
761 ObjOperandId objId
) {
762 // The GeneratePrototypeGuards + TestMatchingHolder checks only support
763 // prototype chains composed of NativeObject (excluding the receiver
765 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
768 enum class IsCrossCompartment
{ No
, Yes
};
770 // Emit a shape guard for all objects on the proto chain. This does NOT include
771 // the receiver; callers must ensure the receiver's proto is the first proto by
772 // either emitting a shape guard or a prototype guard for |objId|.
774 // Note: this relies on shape implying proto.
775 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
776 static void ShapeGuardProtoChain(CacheIRWriter
& writer
, NativeObject
* obj
,
777 ObjOperandId objId
) {
779 static const uint32_t MAX_CACHED_LOADS
= 4;
780 ObjOperandId receiverObjId
= objId
;
783 JSObject
* proto
= obj
->staticPrototype();
788 obj
= &proto
->as
<NativeObject
>();
790 // After guarding the shape of an object, we can safely bake that
791 // object's proto into the stub data. Compared to LoadProto, this
792 // takes one load instead of three (object -> shape -> baseshape
793 // -> proto). We cap the depth to avoid bloating the size of the
794 // stub data. To avoid compartment mismatch, we skip this optimization
795 // in the cross-compartment case.
796 if (depth
< MAX_CACHED_LOADS
&&
797 MaybeCrossCompartment
== IsCrossCompartment::No
) {
798 objId
= writer
.loadProtoObject(obj
, receiverObjId
);
800 objId
= writer
.loadProto(objId
);
804 writer
.guardShape(objId
, obj
->shape());
808 // For cross compartment guards we shape-guard the prototype chain to avoid
809 // referencing the holder object.
811 // This peels off the first layer because it's guarded against obj == holder.
813 // Returns the holder's OperandId.
814 static ObjOperandId
ShapeGuardProtoChainForCrossCompartmentHolder(
815 CacheIRWriter
& writer
, NativeObject
* obj
, ObjOperandId objId
,
816 NativeObject
* holder
) {
817 MOZ_ASSERT(obj
!= holder
);
820 MOZ_ASSERT(obj
->staticPrototype());
821 obj
= &obj
->staticPrototype()->as
<NativeObject
>();
823 objId
= writer
.loadProto(objId
);
825 TestMatchingHolder(writer
, obj
, objId
);
828 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
832 // Emit guards for reading a data property on |holder|. Returns the holder's
834 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
835 static ObjOperandId
EmitReadSlotGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
836 NativeObject
* holder
,
837 ObjOperandId objId
) {
839 TestMatchingNativeReceiver(writer
, obj
, objId
);
845 if (MaybeCrossCompartment
== IsCrossCompartment::Yes
) {
846 // Guard proto chain integrity.
847 // We use a variant of guards that avoid baking in any cross-compartment
849 return ShapeGuardProtoChainForCrossCompartmentHolder(writer
, obj
, objId
,
853 // Guard proto chain integrity.
854 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
856 // Guard on the holder's shape.
857 ObjOperandId holderId
= writer
.loadObject(holder
);
858 TestMatchingHolder(writer
, holder
, holderId
);
862 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
863 static void EmitMissingPropGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
864 ObjOperandId objId
) {
865 TestMatchingNativeReceiver(writer
, obj
, objId
);
867 // The property does not exist. Guard on everything in the prototype
868 // chain. This is guaranteed to see only Native objects because of
869 // CanAttachNativeGetProp().
870 ShapeGuardProtoChain
<MaybeCrossCompartment
>(writer
, obj
, objId
);
873 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
874 static void EmitReadSlotResult(CacheIRWriter
& writer
, NativeObject
* obj
,
875 NativeObject
* holder
, PropertyInfo prop
,
876 ObjOperandId objId
) {
879 ObjOperandId holderId
=
880 EmitReadSlotGuard
<MaybeCrossCompartment
>(writer
, obj
, holder
, objId
);
882 MOZ_ASSERT(holderId
.valid());
883 EmitLoadSlotResult(writer
, holderId
, holder
, prop
);
886 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
887 static void EmitMissingPropResult(CacheIRWriter
& writer
, NativeObject
* obj
,
888 ObjOperandId objId
) {
889 EmitMissingPropGuard
<MaybeCrossCompartment
>(writer
, obj
, objId
);
890 writer
.loadUndefinedResult();
893 static void EmitCallGetterResultNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
894 NativeGetPropKind kind
,
896 NativeObject
* holder
,
898 ValOperandId receiverId
) {
899 MOZ_ASSERT(IsCacheableGetPropCall(obj
, holder
, prop
) == kind
);
901 JSFunction
* target
= &holder
->getGetter(prop
)->as
<JSFunction
>();
902 bool sameRealm
= cx
->realm() == target
->realm();
905 case NativeGetPropKind::NativeGetter
: {
906 writer
.callNativeGetterResult(receiverId
, target
, sameRealm
);
907 writer
.returnFromIC();
910 case NativeGetPropKind::ScriptedGetter
: {
911 writer
.callScriptedGetterResult(receiverId
, target
, sameRealm
);
912 writer
.returnFromIC();
916 // CanAttachNativeGetProp guarantees that the getter is either a native or
917 // a scripted function.
918 MOZ_ASSERT_UNREACHABLE("Can't attach getter");
923 // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
925 static void EmitGuardGetterSetterSlot(CacheIRWriter
& writer
,
926 NativeObject
* holder
, PropertyInfo prop
,
927 ObjOperandId holderId
,
928 bool holderIsConstant
= false) {
929 // If the holder is guaranteed to be the same object, and it never had a
930 // slot holding a GetterSetter mutated or deleted, its Shape will change when
931 // that does happen so we don't need to guard on the GetterSetter.
932 if (holderIsConstant
&& !holder
->hadGetterSetterChange()) {
936 size_t slot
= prop
.slot();
937 Value slotVal
= holder
->getSlot(slot
);
938 MOZ_ASSERT(slotVal
.isPrivateGCThing());
940 if (holder
->isFixedSlot(slot
)) {
941 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
942 writer
.guardFixedSlotValue(holderId
, offset
, slotVal
);
944 size_t offset
= holder
->dynamicSlotIndex(slot
) * sizeof(Value
);
945 writer
.guardDynamicSlotValue(holderId
, offset
, slotVal
);
949 static void EmitCallGetterResultGuards(CacheIRWriter
& writer
, NativeObject
* obj
,
950 NativeObject
* holder
, HandleId id
,
951 PropertyInfo prop
, ObjOperandId objId
,
952 ICState::Mode mode
) {
953 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
954 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
955 // require outerizing).
957 MOZ_ASSERT(holder
->containsPure(id
, prop
));
959 if (mode
== ICState::Mode::Specialized
|| IsWindow(obj
)) {
960 TestMatchingNativeReceiver(writer
, obj
, objId
);
963 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
965 // Guard on the holder's shape.
966 ObjOperandId holderId
= writer
.loadObject(holder
);
967 TestMatchingHolder(writer
, holder
, holderId
);
969 EmitGuardGetterSetterSlot(writer
, holder
, prop
, holderId
,
970 /* holderIsConstant = */ true);
972 EmitGuardGetterSetterSlot(writer
, holder
, prop
, objId
);
975 GetterSetter
* gs
= holder
->getGetterSetter(prop
);
976 writer
.guardHasGetterSetter(objId
, id
, gs
);
980 static void EmitCallGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
981 NativeGetPropKind kind
, NativeObject
* obj
,
982 NativeObject
* holder
, HandleId id
,
983 PropertyInfo prop
, ObjOperandId objId
,
984 ValOperandId receiverId
, ICState::Mode mode
) {
985 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
, mode
);
986 EmitCallGetterResultNoGuards(cx
, writer
, kind
, obj
, holder
, prop
, receiverId
);
989 static bool CanAttachDOMCall(JSContext
* cx
, JSJitInfo::OpType type
,
990 JSObject
* obj
, JSFunction
* fun
,
991 ICState::Mode mode
) {
992 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
||
993 type
== JSJitInfo::Method
);
995 if (mode
!= ICState::Mode::Specialized
) {
999 if (!fun
->hasJitInfo()) {
1003 if (cx
->realm() != fun
->realm()) {
1007 const JSJitInfo
* jitInfo
= fun
->jitInfo();
1008 if (jitInfo
->type() != type
) {
1012 MOZ_ASSERT_IF(IsWindow(obj
), !jitInfo
->needsOuterizedThisObject());
1014 const JSClass
* clasp
= obj
->getClass();
1015 if (!clasp
->isDOMClass()) {
1019 if (type
!= JSJitInfo::Method
&& clasp
->isProxyObject()) {
1023 // Ion codegen expects DOM_OBJECT_SLOT to be a fixed slot in LoadDOMPrivate.
1024 // It can be a dynamic slot if we transplanted this reflector object with a
1026 if (obj
->is
<NativeObject
>() && obj
->as
<NativeObject
>().numFixedSlots() == 0) {
1030 // Tell the analysis the |DOMInstanceClassHasProtoAtDepth| hook can't GC.
1031 JS::AutoSuppressGCAnalysis nogc
;
1033 DOMInstanceClassHasProtoAtDepth instanceChecker
=
1034 cx
->runtime()->DOMcallbacks
->instanceClassMatchesProto
;
1035 return instanceChecker(clasp
, jitInfo
->protoID
, jitInfo
->depth
);
1038 static bool CanAttachDOMGetterSetter(JSContext
* cx
, JSJitInfo::OpType type
,
1039 NativeObject
* obj
, NativeObject
* holder
,
1040 PropertyInfo prop
, ICState::Mode mode
) {
1041 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
);
1043 JSObject
* accessor
= type
== JSJitInfo::Getter
? holder
->getGetter(prop
)
1044 : holder
->getSetter(prop
);
1045 JSFunction
* fun
= &accessor
->as
<JSFunction
>();
1047 return CanAttachDOMCall(cx
, type
, obj
, fun
, mode
);
1050 static void EmitCallDOMGetterResultNoGuards(CacheIRWriter
& writer
,
1051 NativeObject
* holder
,
1053 ObjOperandId objId
) {
1054 JSFunction
* getter
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1055 writer
.callDOMGetterResult(objId
, getter
->jitInfo());
1056 writer
.returnFromIC();
1059 static void EmitCallDOMGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
1060 NativeObject
* obj
, NativeObject
* holder
,
1061 HandleId id
, PropertyInfo prop
,
1062 ObjOperandId objId
) {
1063 // Note: this relies on EmitCallGetterResultGuards emitting a shape guard
1064 // for specialized stubs.
1065 // The shape guard ensures the receiver's Class is valid for this DOM getter.
1066 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
,
1067 ICState::Mode::Specialized
);
1068 EmitCallDOMGetterResultNoGuards(writer
, holder
, prop
, objId
);
1071 static ValOperandId
EmitLoadSlot(CacheIRWriter
& writer
, NativeObject
* holder
,
1072 ObjOperandId holderId
, uint32_t slot
) {
1073 if (holder
->isFixedSlot(slot
)) {
1074 return writer
.loadFixedSlot(holderId
,
1075 NativeObject::getFixedSlotOffset(slot
));
1077 size_t dynamicSlotIndex
= holder
->dynamicSlotIndex(slot
);
1078 return writer
.loadDynamicSlot(holderId
, dynamicSlotIndex
);
1081 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId
,
1083 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1085 // We don't support GetBoundName because environment objects have
1086 // lookupProperty hooks and GetBoundName is usually not megamorphic.
1087 MOZ_ASSERT(JSOp(*pc_
) != JSOp::GetBoundName
);
1089 if (cacheKind_
== CacheKind::GetProp
||
1090 cacheKind_
== CacheKind::GetPropSuper
) {
1091 writer
.megamorphicLoadSlotResult(objId
, id
);
1093 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
1094 cacheKind_
== CacheKind::GetElemSuper
);
1095 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1097 writer
.returnFromIC();
1099 trackAttached("GetProp.MegamorphicNativeSlot");
1102 AttachDecision
GetPropIRGenerator::tryAttachNative(HandleObject obj
,
1105 ValOperandId receiverId
) {
1106 Maybe
<PropertyInfo
> prop
;
1107 NativeObject
* holder
= nullptr;
1109 NativeGetPropKind kind
=
1110 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
1112 case NativeGetPropKind::None
:
1113 return AttachDecision::NoAction
;
1114 case NativeGetPropKind::Missing
:
1115 case NativeGetPropKind::Slot
: {
1116 auto* nobj
= &obj
->as
<NativeObject
>();
1118 if (mode_
== ICState::Mode::Megamorphic
&&
1119 JSOp(*pc_
) != JSOp::GetBoundName
) {
1120 attachMegamorphicNativeSlot(objId
, id
);
1121 return AttachDecision::Attach
;
1124 maybeEmitIdGuard(id
);
1125 if (kind
== NativeGetPropKind::Slot
) {
1126 EmitReadSlotResult(writer
, nobj
, holder
, *prop
, objId
);
1127 writer
.returnFromIC();
1128 trackAttached("GetProp.NativeSlot");
1130 EmitMissingPropResult(writer
, nobj
, objId
);
1131 writer
.returnFromIC();
1132 trackAttached("GetProp.Missing");
1134 return AttachDecision::Attach
;
1136 case NativeGetPropKind::ScriptedGetter
:
1137 case NativeGetPropKind::NativeGetter
: {
1138 auto* nobj
= &obj
->as
<NativeObject
>();
1140 maybeEmitIdGuard(id
);
1142 if (!isSuper() && CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, nobj
,
1143 holder
, *prop
, mode_
)) {
1144 EmitCallDOMGetterResult(cx_
, writer
, nobj
, holder
, id
, *prop
, objId
);
1146 trackAttached("GetProp.DOMGetter");
1147 return AttachDecision::Attach
;
1150 EmitCallGetterResult(cx_
, writer
, kind
, nobj
, holder
, id
, *prop
, objId
,
1153 trackAttached("GetProp.NativeGetter");
1154 return AttachDecision::Attach
;
1158 MOZ_CRASH("Bad NativeGetPropKind");
1161 // Returns whether obj is a WindowProxy wrapping the script's global.
1162 static bool IsWindowProxyForScriptGlobal(JSScript
* script
, JSObject
* obj
) {
1163 if (!IsWindowProxy(obj
)) {
1167 MOZ_ASSERT(obj
->getClass() ==
1168 script
->runtimeFromMainThread()->maybeWindowProxyClass());
1170 JSObject
* window
= ToWindowIfWindowProxy(obj
);
1172 // Ion relies on the WindowProxy's group changing (and the group getting
1173 // marked as having unknown properties) on navigation. If we ever stop
1174 // transplanting same-compartment WindowProxies, this assert will fail and we
1175 // need to fix that code.
1176 MOZ_ASSERT(window
== &obj
->nonCCWGlobal());
1178 // This must be a WindowProxy for a global in this compartment. Else it would
1179 // be a cross-compartment wrapper and IsWindowProxy returns false for
1181 MOZ_ASSERT(script
->compartment() == obj
->compartment());
1183 // Only optimize lookups on the WindowProxy for the current global. Other
1184 // WindowProxies in the compartment may require security checks (based on
1185 // mutable document.domain). See bug 1516775.
1186 return window
== &script
->global();
1189 // Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
1190 static ObjOperandId
GuardAndLoadWindowProxyWindow(CacheIRWriter
& writer
,
1192 GlobalObject
* windowObj
) {
1193 writer
.guardClass(objId
, GuardClassKind::WindowProxy
);
1194 ObjOperandId windowObjId
= writer
.loadWrapperTarget(objId
);
1195 writer
.guardSpecificObject(windowObjId
, windowObj
);
1199 // Whether a getter/setter on the global should have the WindowProxy as |this|
1200 // value instead of the Window (the global object). This always returns true for
1201 // scripted functions.
1202 static bool GetterNeedsWindowProxyThis(NativeObject
* holder
,
1203 PropertyInfo prop
) {
1204 JSFunction
* callee
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1205 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1207 static bool SetterNeedsWindowProxyThis(NativeObject
* holder
,
1208 PropertyInfo prop
) {
1209 JSFunction
* callee
= &holder
->getSetter(prop
)->as
<JSFunction
>();
1210 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1213 AttachDecision
GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
1216 // Attach a stub when the receiver is a WindowProxy and we can do the lookup
1217 // on the Window (the global object).
1219 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
1220 return AttachDecision::NoAction
;
1223 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1225 if (mode_
== ICState::Mode::Megamorphic
) {
1226 return AttachDecision::NoAction
;
1229 // Now try to do the lookup on the Window (the current global).
1230 GlobalObject
* windowObj
= cx_
->global();
1231 NativeObject
* holder
= nullptr;
1232 Maybe
<PropertyInfo
> prop
;
1233 NativeGetPropKind kind
=
1234 CanAttachNativeGetProp(cx_
, windowObj
, id
, &holder
, &prop
, pc_
);
1236 case NativeGetPropKind::None
:
1237 return AttachDecision::NoAction
;
1239 case NativeGetPropKind::Slot
: {
1240 maybeEmitIdGuard(id
);
1241 ObjOperandId windowObjId
=
1242 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1243 EmitReadSlotResult(writer
, windowObj
, holder
, *prop
, windowObjId
);
1244 writer
.returnFromIC();
1246 trackAttached("GetProp.WindowProxySlot");
1247 return AttachDecision::Attach
;
1250 case NativeGetPropKind::Missing
: {
1251 maybeEmitIdGuard(id
);
1252 ObjOperandId windowObjId
=
1253 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1254 EmitMissingPropResult(writer
, windowObj
, windowObjId
);
1255 writer
.returnFromIC();
1257 trackAttached("GetProp.WindowProxyMissing");
1258 return AttachDecision::Attach
;
1261 case NativeGetPropKind::NativeGetter
:
1262 case NativeGetPropKind::ScriptedGetter
: {
1263 // If a |super| access, it is not worth the complexity to attach an IC.
1265 return AttachDecision::NoAction
;
1268 bool needsWindowProxy
= GetterNeedsWindowProxyThis(holder
, *prop
);
1270 // Guard the incoming object is a WindowProxy and inline a getter call
1271 // based on the Window object.
1272 maybeEmitIdGuard(id
);
1273 ObjOperandId windowObjId
=
1274 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1276 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, windowObj
, holder
,
1278 MOZ_ASSERT(!needsWindowProxy
);
1279 EmitCallDOMGetterResult(cx_
, writer
, windowObj
, holder
, id
, *prop
,
1281 trackAttached("GetProp.WindowProxyDOMGetter");
1283 ValOperandId receiverId
=
1284 writer
.boxObject(needsWindowProxy
? objId
: windowObjId
);
1285 EmitCallGetterResult(cx_
, writer
, kind
, windowObj
, holder
, id
, *prop
,
1286 windowObjId
, receiverId
, mode_
);
1287 trackAttached("GetProp.WindowProxyGetter");
1290 return AttachDecision::Attach
;
1294 MOZ_CRASH("Unreachable");
1297 AttachDecision
GetPropIRGenerator::tryAttachCrossCompartmentWrapper(
1298 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
1299 // We can only optimize this very wrapper-handler, because others might
1300 // have a security policy.
1301 if (!IsWrapper(obj
) ||
1302 Wrapper::wrapperHandler(obj
) != &CrossCompartmentWrapper::singleton
) {
1303 return AttachDecision::NoAction
;
1306 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1308 if (mode_
== ICState::Mode::Megamorphic
) {
1309 return AttachDecision::NoAction
;
1312 RootedObject
unwrapped(cx_
, Wrapper::wrappedObject(obj
));
1313 MOZ_ASSERT(unwrapped
== UnwrapOneCheckedStatic(obj
));
1314 MOZ_ASSERT(!IsCrossCompartmentWrapper(unwrapped
),
1315 "CCWs must not wrap other CCWs");
1317 // If we allowed different zones we would have to wrap strings.
1318 if (unwrapped
->compartment()->zone() != cx_
->compartment()->zone()) {
1319 return AttachDecision::NoAction
;
1322 // Take the unwrapped object's global, and wrap in a
1323 // this-compartment wrapper. This is what will be stored in the IC
1324 // keep the compartment alive.
1325 RootedObject
wrappedTargetGlobal(cx_
, &unwrapped
->nonCCWGlobal());
1326 if (!cx_
->compartment()->wrap(cx_
, &wrappedTargetGlobal
)) {
1327 cx_
->clearPendingException();
1328 return AttachDecision::NoAction
;
1331 NativeObject
* holder
= nullptr;
1332 Maybe
<PropertyInfo
> prop
;
1334 // Enter realm of target to prevent failing compartment assertions when doing
1337 AutoRealm
ar(cx_
, unwrapped
);
1339 NativeGetPropKind kind
=
1340 CanAttachNativeGetProp(cx_
, unwrapped
, id
, &holder
, &prop
, pc_
);
1341 if (kind
!= NativeGetPropKind::Slot
&& kind
!= NativeGetPropKind::Missing
) {
1342 return AttachDecision::NoAction
;
1345 auto* unwrappedNative
= &unwrapped
->as
<NativeObject
>();
1347 maybeEmitIdGuard(id
);
1348 writer
.guardIsProxy(objId
);
1349 writer
.guardHasProxyHandler(objId
, Wrapper::wrapperHandler(obj
));
1351 // Load the object wrapped by the CCW
1352 ObjOperandId wrapperTargetId
= writer
.loadWrapperTarget(objId
);
1354 // If the compartment of the wrapped object is different we should fail.
1355 writer
.guardCompartment(wrapperTargetId
, wrappedTargetGlobal
,
1356 unwrappedNative
->compartment());
1358 ObjOperandId unwrappedId
= wrapperTargetId
;
1360 EmitReadSlotResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
, holder
,
1361 *prop
, unwrappedId
);
1362 writer
.wrapResult();
1363 writer
.returnFromIC();
1364 trackAttached("GetProp.CCWSlot");
1366 EmitMissingPropResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
,
1368 writer
.returnFromIC();
1369 trackAttached("GetProp.CCWMissing");
1371 return AttachDecision::Attach
;
1374 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
1375 Handle
<NativeObject
*> obj
);
1377 static bool GetXrayExpandoShapeWrapper(JSContext
* cx
, HandleObject xray
,
1378 MutableHandleObject wrapper
) {
1379 Value v
= GetProxyReservedSlot(xray
, GetXrayJitInfo()->xrayHolderSlot
);
1381 NativeObject
* holder
= &v
.toObject().as
<NativeObject
>();
1382 v
= holder
->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot
);
1384 Rooted
<NativeObject
*> expando(
1385 cx
, &UncheckedUnwrap(&v
.toObject())->as
<NativeObject
>());
1386 wrapper
.set(NewWrapperWithObjectShape(cx
, expando
));
1387 return wrapper
!= nullptr;
1390 wrapper
.set(nullptr);
1394 AttachDecision
GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(
1395 HandleObject obj
, ObjOperandId objId
, HandleId id
,
1396 ValOperandId receiverId
) {
1397 if (!obj
->is
<ProxyObject
>()) {
1398 return AttachDecision::NoAction
;
1401 JS::XrayJitInfo
* info
= GetXrayJitInfo();
1402 if (!info
|| !info
->isCrossCompartmentXray(GetProxyHandler(obj
))) {
1403 return AttachDecision::NoAction
;
1406 if (!info
->compartmentHasExclusiveExpandos(obj
)) {
1407 return AttachDecision::NoAction
;
1410 RootedObject
target(cx_
, UncheckedUnwrap(obj
));
1412 RootedObject
expandoShapeWrapper(cx_
);
1413 if (!GetXrayExpandoShapeWrapper(cx_
, obj
, &expandoShapeWrapper
)) {
1414 cx_
->recoverFromOutOfMemory();
1415 return AttachDecision::NoAction
;
1418 // Look for a getter we can call on the xray or its prototype chain.
1419 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx_
);
1420 RootedObject
holder(cx_
, obj
);
1421 RootedObjectVector
prototypes(cx_
);
1422 RootedObjectVector
prototypeExpandoShapeWrappers(cx_
);
1424 if (!GetOwnPropertyDescriptor(cx_
, holder
, id
, &desc
)) {
1425 cx_
->clearPendingException();
1426 return AttachDecision::NoAction
;
1428 if (desc
.isSome()) {
1431 if (!GetPrototype(cx_
, holder
, &holder
)) {
1432 cx_
->clearPendingException();
1433 return AttachDecision::NoAction
;
1435 if (!holder
|| !holder
->is
<ProxyObject
>() ||
1436 !info
->isCrossCompartmentXray(GetProxyHandler(holder
))) {
1437 return AttachDecision::NoAction
;
1439 RootedObject
prototypeExpandoShapeWrapper(cx_
);
1440 if (!GetXrayExpandoShapeWrapper(cx_
, holder
,
1441 &prototypeExpandoShapeWrapper
) ||
1442 !prototypes
.append(holder
) ||
1443 !prototypeExpandoShapeWrappers
.append(prototypeExpandoShapeWrapper
)) {
1444 cx_
->recoverFromOutOfMemory();
1445 return AttachDecision::NoAction
;
1448 if (!desc
->isAccessorDescriptor()) {
1449 return AttachDecision::NoAction
;
1452 RootedObject
getter(cx_
, desc
->getter());
1453 if (!getter
|| !getter
->is
<JSFunction
>() ||
1454 !getter
->as
<JSFunction
>().isNativeWithoutJitEntry()) {
1455 return AttachDecision::NoAction
;
1458 maybeEmitIdGuard(id
);
1459 writer
.guardIsProxy(objId
);
1460 writer
.guardHasProxyHandler(objId
, GetProxyHandler(obj
));
1462 // Load the object wrapped by the CCW
1463 ObjOperandId wrapperTargetId
= writer
.loadWrapperTarget(objId
);
1465 // Test the wrapped object's class. The properties held by xrays or their
1466 // prototypes will be invariant for objects of a given class, except for
1467 // changes due to xray expandos or xray prototype mutations.
1468 writer
.guardAnyClass(wrapperTargetId
, target
->getClass());
1470 // Make sure the expandos on the xray and its prototype chain match up with
1471 // what we expect. The expando shape needs to be consistent, to ensure it
1472 // has not had any shadowing properties added, and the expando cannot have
1473 // any custom prototype (xray prototypes are stable otherwise).
1475 // We can only do this for xrays with exclusive access to their expandos
1476 // (as we checked earlier), which store a pointer to their expando
1477 // directly. Xrays in other compartments may share their expandos with each
1478 // other and a VM call is needed just to find the expando.
1479 if (expandoShapeWrapper
) {
1480 writer
.guardXrayExpandoShapeAndDefaultProto(objId
, expandoShapeWrapper
);
1482 writer
.guardXrayNoExpando(objId
);
1484 for (size_t i
= 0; i
< prototypes
.length(); i
++) {
1485 JSObject
* proto
= prototypes
[i
];
1486 ObjOperandId protoId
= writer
.loadObject(proto
);
1487 if (JSObject
* protoShapeWrapper
= prototypeExpandoShapeWrappers
[i
]) {
1488 writer
.guardXrayExpandoShapeAndDefaultProto(protoId
, protoShapeWrapper
);
1490 writer
.guardXrayNoExpando(protoId
);
1494 bool sameRealm
= cx_
->realm() == getter
->as
<JSFunction
>().realm();
1495 writer
.callNativeGetterResult(receiverId
, &getter
->as
<JSFunction
>(),
1497 writer
.returnFromIC();
1499 trackAttached("GetProp.XrayCCW");
1500 return AttachDecision::Attach
;
1504 AttachDecision
GetPropIRGenerator::tryAttachScriptedProxy(
1505 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1506 if (cacheKind_
!= CacheKind::GetProp
&& cacheKind_
!= CacheKind::GetElem
) {
1507 return AttachDecision::NoAction
;
1509 if (cacheKind_
== CacheKind::GetElem
) {
1510 if (!idVal_
.isString() && !idVal_
.isInt32() && !idVal_
.isSymbol()) {
1511 return AttachDecision::NoAction
;
1515 JSObject
* handlerObj
= ScriptedProxyHandler::handlerObject(obj
);
1517 return AttachDecision::NoAction
;
1520 NativeObject
* trapHolder
= nullptr;
1521 Maybe
<PropertyInfo
> trapProp
;
1522 // We call with pc_ even though that's not the actual corresponding pc. It
1523 // should, however, be fine, because it's just used to check if this is a
1524 // GetBoundName, which it's not.
1525 NativeGetPropKind trapKind
= CanAttachNativeGetProp(
1526 cx_
, handlerObj
, NameToId(cx_
->names().get
), &trapHolder
, &trapProp
, pc_
);
1528 if (trapKind
!= NativeGetPropKind::Missing
&&
1529 trapKind
!= NativeGetPropKind::Slot
) {
1530 return AttachDecision::NoAction
;
1533 if (trapKind
!= NativeGetPropKind::Missing
) {
1534 uint32_t trapSlot
= trapProp
->slot();
1535 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1536 if (!trapVal
.isObject()) {
1537 return AttachDecision::NoAction
;
1540 JSObject
* trapObj
= &trapVal
.toObject();
1541 if (!trapObj
->is
<JSFunction
>()) {
1542 return AttachDecision::NoAction
;
1545 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1546 if (trapFn
->isClassConstructor()) {
1547 return AttachDecision::NoAction
;
1550 if (!trapFn
->hasJitEntry()) {
1551 return AttachDecision::NoAction
;
1554 if (cx_
->realm() != trapFn
->realm()) {
1555 return AttachDecision::NoAction
;
1559 NativeObject
* nHandlerObj
= &handlerObj
->as
<NativeObject
>();
1560 JSObject
* targetObj
= obj
->target();
1561 MOZ_ASSERT(targetObj
, "Guaranteed by the scripted Proxy constructor");
1563 // We just require that the target is a NativeObject to make our lives
1564 // easier. There's too much nonsense we might have to handle otherwise and
1565 // we're not set up to recursively call GetPropIRGenerator::tryAttachStub
1566 // for the target object.
1567 if (!targetObj
->is
<NativeObject
>()) {
1568 return AttachDecision::NoAction
;
1571 writer
.guardIsProxy(objId
);
1572 writer
.guardHasProxyHandler(objId
, &ScriptedProxyHandler::singleton
);
1573 ValOperandId handlerValId
= writer
.loadScriptedProxyHandler(objId
);
1574 ObjOperandId handlerObjId
= writer
.guardToObject(handlerValId
);
1575 ObjOperandId targetObjId
= writer
.loadWrapperTarget(objId
);
1577 writer
.guardIsNativeObject(targetObjId
);
1579 if (trapKind
== NativeGetPropKind::Missing
) {
1580 EmitMissingPropGuard(writer
, nHandlerObj
, handlerObjId
);
1581 if (cacheKind_
== CacheKind::GetProp
) {
1582 writer
.megamorphicLoadSlotResult(targetObjId
, id
);
1584 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1587 uint32_t trapSlot
= trapProp
->slot();
1588 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1589 JSObject
* trapObj
= &trapVal
.toObject();
1590 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1591 ObjOperandId trapHolderId
=
1592 EmitReadSlotGuard(writer
, nHandlerObj
, trapHolder
, handlerObjId
);
1594 ValOperandId fnValId
=
1595 EmitLoadSlot(writer
, trapHolder
, trapHolderId
, trapSlot
);
1596 ObjOperandId fnObjId
= writer
.guardToObject(fnValId
);
1597 writer
.guardSpecificFunction(fnObjId
, trapFn
);
1598 ValOperandId targetValId
= writer
.boxObject(targetObjId
);
1599 if (cacheKind_
== CacheKind::GetProp
) {
1600 writer
.callScriptedProxyGetResult(targetValId
, objId
, handlerObjId
,
1603 ValOperandId idId
= getElemKeyValueId();
1604 ValOperandId stringIdId
= writer
.idToStringOrSymbol(idId
);
1605 writer
.callScriptedProxyGetByValueResult(targetValId
, objId
, handlerObjId
,
1606 stringIdId
, trapFn
);
1609 writer
.returnFromIC();
1611 trackAttached("GetScriptedProxy");
1612 return AttachDecision::Attach
;
1616 AttachDecision
GetPropIRGenerator::tryAttachGenericProxy(
1617 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1618 bool handleDOMProxies
) {
1619 writer
.guardIsProxy(objId
);
1621 if (!handleDOMProxies
) {
1622 // Ensure that the incoming object is not a DOM proxy, so that we can get to
1623 // the specialized stubs
1624 writer
.guardIsNotDOMProxy(objId
);
1627 if (cacheKind_
== CacheKind::GetProp
|| mode_
== ICState::Mode::Specialized
) {
1628 MOZ_ASSERT(!isSuper());
1629 maybeEmitIdGuard(id
);
1630 writer
.proxyGetResult(objId
, id
);
1632 // Attach a stub that handles every id.
1633 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
1634 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1635 MOZ_ASSERT(!isSuper());
1636 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
1639 writer
.returnFromIC();
1641 trackAttached("GetProp.GenericProxy");
1642 return AttachDecision::Attach
;
1645 static bool ValueIsInt64Index(const Value
& val
, int64_t* index
) {
1646 // Try to convert the Value to a TypedArray index or DataView offset.
1648 if (val
.isInt32()) {
1649 *index
= val
.toInt32();
1653 if (val
.isDouble()) {
1654 // Use NumberEqualsInt64 because ToPropertyKey(-0) is 0.
1655 return mozilla::NumberEqualsInt64(val
.toDouble(), index
);
1661 IntPtrOperandId
IRGenerator::guardToIntPtrIndex(const Value
& index
,
1662 ValOperandId indexId
,
1666 MOZ_ASSERT_IF(!supportOOB
, ValueIsInt64Index(index
, &indexInt64
));
1669 if (index
.isInt32()) {
1670 Int32OperandId int32IndexId
= writer
.guardToInt32(indexId
);
1671 return writer
.int32ToIntPtr(int32IndexId
);
1674 MOZ_ASSERT(index
.isNumber());
1675 NumberOperandId numberIndexId
= writer
.guardIsNumber(indexId
);
1676 return writer
.guardNumberToIntPtrIndex(numberIndexId
, supportOOB
);
1679 ObjOperandId
IRGenerator::guardDOMProxyExpandoObjectAndShape(
1680 ProxyObject
* obj
, ObjOperandId objId
, const Value
& expandoVal
,
1681 NativeObject
* expandoObj
) {
1682 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1684 TestMatchingProxyReceiver(writer
, obj
, objId
);
1686 // Shape determines Class, so now it must be a DOM proxy.
1687 ValOperandId expandoValId
;
1688 if (expandoVal
.isObject()) {
1689 expandoValId
= writer
.loadDOMExpandoValue(objId
);
1691 expandoValId
= writer
.loadDOMExpandoValueIgnoreGeneration(objId
);
1694 // Guard the expando is an object and shape guard.
1695 ObjOperandId expandoObjId
= writer
.guardToObject(expandoValId
);
1696 TestMatchingHolder(writer
, expandoObj
, expandoObjId
);
1697 return expandoObjId
;
1700 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyExpando(
1701 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1702 ValOperandId receiverId
) {
1703 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1705 Value expandoVal
= GetProxyPrivate(obj
);
1706 JSObject
* expandoObj
;
1707 if (expandoVal
.isObject()) {
1708 expandoObj
= &expandoVal
.toObject();
1710 MOZ_ASSERT(!expandoVal
.isUndefined(),
1711 "How did a missing expando manage to shadow things?");
1712 auto expandoAndGeneration
=
1713 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1714 MOZ_ASSERT(expandoAndGeneration
);
1715 expandoObj
= &expandoAndGeneration
->expando
.toObject();
1718 // Try to do the lookup on the expando object.
1719 NativeObject
* holder
= nullptr;
1720 Maybe
<PropertyInfo
> prop
;
1721 NativeGetPropKind kind
=
1722 CanAttachNativeGetProp(cx_
, expandoObj
, id
, &holder
, &prop
, pc_
);
1723 if (kind
== NativeGetPropKind::None
) {
1724 return AttachDecision::NoAction
;
1727 return AttachDecision::NoAction
;
1729 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
1731 MOZ_ASSERT(holder
== nativeExpandoObj
);
1733 maybeEmitIdGuard(id
);
1734 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
1735 obj
, objId
, expandoVal
, nativeExpandoObj
);
1737 if (kind
== NativeGetPropKind::Slot
) {
1738 // Load from the expando's slots.
1739 EmitLoadSlotResult(writer
, expandoObjId
, nativeExpandoObj
, *prop
);
1740 writer
.returnFromIC();
1742 // Call the getter. Note that we pass objId, the DOM proxy, as |this|
1743 // and not the expando object.
1744 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1745 kind
== NativeGetPropKind::ScriptedGetter
);
1746 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
1747 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeExpandoObj
,
1748 nativeExpandoObj
, *prop
, receiverId
);
1751 trackAttached("GetProp.DOMProxyExpando");
1752 return AttachDecision::Attach
;
1755 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyShadowed(
1756 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1757 MOZ_ASSERT(!isSuper());
1758 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1760 maybeEmitIdGuard(id
);
1761 TestMatchingProxyReceiver(writer
, obj
, objId
);
1762 writer
.proxyGetResult(objId
, id
);
1763 writer
.returnFromIC();
1765 trackAttached("GetProp.DOMProxyShadowed");
1766 return AttachDecision::Attach
;
1769 // Emit CacheIR to guard the DOM proxy doesn't shadow |id|. There are two types
1772 // (a) DOM proxies marked LegacyOverrideBuiltIns in WebIDL, for example
1773 // HTMLDocument or HTMLFormElement. These proxies look up properties in this
1776 // (1) The expando object.
1777 // (2) The proxy's named-property handler.
1778 // (3) The prototype chain.
1780 // To optimize properties on the prototype chain, we have to guard that (1)
1781 // and (2) don't shadow (3). We handle (1) by either emitting a shape guard
1782 // for the expando object or by guarding the proxy has no expando object. To
1783 // efficiently handle (2), the proxy must have an ExpandoAndGeneration*
1784 // stored as PrivateValue. We guard on its generation field to ensure the
1785 // set of names hasn't changed.
1787 // Missing properties can be optimized in a similar way by emitting shape
1788 // guards for the prototype chain.
1790 // (b) Other DOM proxies. These proxies look up properties in this
1793 // (1) The expando object.
1794 // (2) The prototype chain.
1795 // (3) The proxy's named-property handler.
1797 // To optimize properties on the prototype chain, we only have to guard the
1798 // expando object doesn't shadow it.
1800 // Missing properties can't be optimized in this case because we don't have
1801 // an efficient way to guard against the proxy handler shadowing the
1802 // property (there's no ExpandoAndGeneration*).
1805 // * DOMProxyShadows in DOMJSProxyHandler.cpp
1806 // * https://webidl.spec.whatwg.org/#dfn-named-property-visibility (the Note at
1809 // Callers are expected to have already guarded on the shape of the
1810 // object, which guarantees the object is a DOM proxy.
1811 static void CheckDOMProxyDoesNotShadow(CacheIRWriter
& writer
, ProxyObject
* obj
,
1812 jsid id
, ObjOperandId objId
,
1813 bool* canOptimizeMissing
) {
1814 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1816 Value expandoVal
= GetProxyPrivate(obj
);
1818 ValOperandId expandoId
;
1819 if (!expandoVal
.isObject() && !expandoVal
.isUndefined()) {
1821 auto expandoAndGeneration
=
1822 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1823 uint64_t generation
= expandoAndGeneration
->generation
;
1824 expandoId
= writer
.loadDOMExpandoValueGuardGeneration(
1825 objId
, expandoAndGeneration
, generation
);
1826 expandoVal
= expandoAndGeneration
->expando
;
1827 *canOptimizeMissing
= true;
1830 expandoId
= writer
.loadDOMExpandoValue(objId
);
1831 *canOptimizeMissing
= false;
1834 if (expandoVal
.isUndefined()) {
1835 // Guard there's no expando object.
1836 writer
.guardNonDoubleType(expandoId
, ValueType::Undefined
);
1837 } else if (expandoVal
.isObject()) {
1838 // Guard the proxy either has no expando object or, if it has one, that
1839 // the shape matches the current expando object.
1840 NativeObject
& expandoObj
= expandoVal
.toObject().as
<NativeObject
>();
1841 MOZ_ASSERT(!expandoObj
.containsPure(id
));
1842 writer
.guardDOMExpandoMissingOrGuardShape(expandoId
, expandoObj
.shape());
1844 MOZ_CRASH("Invalid expando value");
1848 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
1849 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1850 ValOperandId receiverId
) {
1851 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1853 JSObject
* protoObj
= obj
->staticPrototype();
1855 return AttachDecision::NoAction
;
1858 NativeObject
* holder
= nullptr;
1859 Maybe
<PropertyInfo
> prop
;
1860 NativeGetPropKind kind
=
1861 CanAttachNativeGetProp(cx_
, protoObj
, id
, &holder
, &prop
, pc_
);
1862 if (kind
== NativeGetPropKind::None
) {
1863 return AttachDecision::NoAction
;
1865 auto* nativeProtoObj
= &protoObj
->as
<NativeObject
>();
1867 maybeEmitIdGuard(id
);
1869 // Guard that our proxy (expando) object hasn't started shadowing this
1871 TestMatchingProxyReceiver(writer
, obj
, objId
);
1872 bool canOptimizeMissing
= false;
1873 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
1876 // Found the property on the prototype chain. Treat it like a native
1878 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
1880 // Guard on the holder of the property.
1881 ObjOperandId holderId
= writer
.loadObject(holder
);
1882 TestMatchingHolder(writer
, holder
, holderId
);
1884 if (kind
== NativeGetPropKind::Slot
) {
1885 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
1886 writer
.returnFromIC();
1888 // EmitCallGetterResultNoGuards expects |obj| to be the object the
1889 // property is on to do some checks. Since we actually looked at
1890 // checkObj, and no extra guards will be generated, we can just
1891 // pass that instead.
1892 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1893 kind
== NativeGetPropKind::ScriptedGetter
);
1894 MOZ_ASSERT(!isSuper());
1895 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
1896 /* holderIsConstant = */ true);
1897 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeProtoObj
, holder
,
1901 // Property was not found on the prototype chain.
1902 MOZ_ASSERT(kind
== NativeGetPropKind::Missing
);
1903 if (canOptimizeMissing
) {
1904 // We already guarded on the proxy's shape, so now shape guard the proto
1906 ObjOperandId protoId
= writer
.loadObject(nativeProtoObj
);
1907 EmitMissingPropResult(writer
, nativeProtoObj
, protoId
);
1909 MOZ_ASSERT(!isSuper());
1910 writer
.proxyGetResult(objId
, id
);
1912 writer
.returnFromIC();
1915 trackAttached("GetProp.DOMProxyUnshadowed");
1916 return AttachDecision::Attach
;
1919 AttachDecision
GetPropIRGenerator::tryAttachProxy(HandleObject obj
,
1922 ValOperandId receiverId
) {
1923 // The proxy stubs don't currently support |super| access.
1925 return AttachDecision::NoAction
;
1928 // Always try to attach scripted proxy get even if we're megamorphic.
1929 // In Speedometer 3 we'll often run into cases where we're megamorphic
1930 // overall, but monomorphic for the proxy case. This is because there
1931 // are functions which lazily turn various differently-shaped objects
1932 // into proxies. So the un-proxified objects are megamorphic, but the
1933 // proxy handlers are actually monomorphic. There is room for a bit
1934 // more sophistication here, but this should do for now.
1935 if (!obj
->is
<ProxyObject
>()) {
1936 return AttachDecision::NoAction
;
1938 auto proxy
= obj
.as
<ProxyObject
>();
1940 if (proxy
->handler()->isScripted()) {
1941 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, id
));
1945 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
1946 if (type
== ProxyStubType::None
) {
1947 return AttachDecision::NoAction
;
1950 if (mode_
== ICState::Mode::Megamorphic
) {
1951 return tryAttachGenericProxy(proxy
, objId
, id
,
1952 /* handleDOMProxies = */ true);
1956 case ProxyStubType::None
:
1958 case ProxyStubType::DOMExpando
:
1959 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, receiverId
));
1960 [[fallthrough
]]; // Fall through to the generic shadowed case.
1961 case ProxyStubType::DOMShadowed
:
1962 return tryAttachDOMProxyShadowed(proxy
, objId
, id
);
1963 case ProxyStubType::DOMUnshadowed
:
1964 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, receiverId
));
1965 return tryAttachGenericProxy(proxy
, objId
, id
,
1966 /* handleDOMProxies = */ true);
1967 case ProxyStubType::Generic
:
1968 return tryAttachGenericProxy(proxy
, objId
, id
,
1969 /* handleDOMProxies = */ false);
1972 MOZ_CRASH("Unexpected ProxyStubType");
1975 // Guards the class of an object. Because shape implies class, and a shape guard
1976 // is faster than a class guard, if this is our first time attaching a stub, we
1977 // instead generate a shape guard.
1978 void IRGenerator::emitOptimisticClassGuard(ObjOperandId objId
, JSObject
* obj
,
1979 GuardClassKind kind
) {
1982 case GuardClassKind::Array
:
1983 MOZ_ASSERT(obj
->is
<ArrayObject
>());
1985 case GuardClassKind::PlainObject
:
1986 MOZ_ASSERT(obj
->is
<PlainObject
>());
1988 case GuardClassKind::FixedLengthArrayBuffer
:
1989 MOZ_ASSERT(obj
->is
<FixedLengthArrayBufferObject
>());
1991 case GuardClassKind::SharedArrayBuffer
:
1992 MOZ_ASSERT(obj
->is
<SharedArrayBufferObject
>());
1994 case GuardClassKind::FixedLengthDataView
:
1995 MOZ_ASSERT(obj
->is
<FixedLengthDataViewObject
>());
1997 case GuardClassKind::Set
:
1998 MOZ_ASSERT(obj
->is
<SetObject
>());
2000 case GuardClassKind::Map
:
2001 MOZ_ASSERT(obj
->is
<MapObject
>());
2004 case GuardClassKind::MappedArguments
:
2005 case GuardClassKind::UnmappedArguments
:
2006 case GuardClassKind::JSFunction
:
2007 case GuardClassKind::BoundFunction
:
2008 case GuardClassKind::WindowProxy
:
2009 // Arguments, functions, and the global object have
2010 // less consistent shapes.
2011 MOZ_CRASH("GuardClassKind not supported");
2016 writer
.guardShapeForClass(objId
, obj
->shape());
2018 writer
.guardClass(objId
, kind
);
2022 static void AssertArgumentsCustomDataProp(ArgumentsObject
* obj
,
2025 // The property must still be a custom data property if it has been resolved.
2026 // If this assertion fails, we're probably missing a call to mark this
2027 // property overridden.
2028 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(key
);
2029 MOZ_ASSERT_IF(prop
, prop
->isCustomDataProperty());
2033 AttachDecision
GetPropIRGenerator::tryAttachObjectLength(HandleObject obj
,
2036 if (!id
.isAtom(cx_
->names().length
)) {
2037 return AttachDecision::NoAction
;
2040 if (obj
->is
<ArrayObject
>()) {
2041 if (obj
->as
<ArrayObject
>().length() > INT32_MAX
) {
2042 return AttachDecision::NoAction
;
2045 maybeEmitIdGuard(id
);
2046 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
2047 writer
.loadInt32ArrayLengthResult(objId
);
2048 writer
.returnFromIC();
2050 trackAttached("GetProp.ArrayLength");
2051 return AttachDecision::Attach
;
2054 if (obj
->is
<ArgumentsObject
>() &&
2055 !obj
->as
<ArgumentsObject
>().hasOverriddenLength()) {
2056 AssertArgumentsCustomDataProp(&obj
->as
<ArgumentsObject
>(), id
);
2057 maybeEmitIdGuard(id
);
2058 if (obj
->is
<MappedArgumentsObject
>()) {
2059 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2061 MOZ_ASSERT(obj
->is
<UnmappedArgumentsObject
>());
2062 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2064 writer
.loadArgumentsObjectLengthResult(objId
);
2065 writer
.returnFromIC();
2067 trackAttached("GetProp.ArgumentsObjectLength");
2068 return AttachDecision::Attach
;
2071 return AttachDecision::NoAction
;
2074 AttachDecision
GetPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
2077 if (!obj
->is
<TypedArrayObject
>()) {
2078 return AttachDecision::NoAction
;
2081 if (mode_
!= ICState::Mode::Specialized
) {
2082 return AttachDecision::NoAction
;
2085 // Receiver should be the object.
2087 return AttachDecision::NoAction
;
2090 bool isLength
= id
.isAtom(cx_
->names().length
);
2091 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2092 if (!isLength
&& !isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2093 return AttachDecision::NoAction
;
2096 NativeObject
* holder
= nullptr;
2097 Maybe
<PropertyInfo
> prop
;
2098 NativeGetPropKind kind
=
2099 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2100 if (kind
!= NativeGetPropKind::NativeGetter
) {
2101 return AttachDecision::NoAction
;
2104 JSFunction
& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2106 if (!TypedArrayObject::isOriginalLengthGetter(fun
.native())) {
2107 return AttachDecision::NoAction
;
2109 } else if (isByteOffset
) {
2110 if (!TypedArrayObject::isOriginalByteOffsetGetter(fun
.native())) {
2111 return AttachDecision::NoAction
;
2114 if (!TypedArrayObject::isOriginalByteLengthGetter(fun
.native())) {
2115 return AttachDecision::NoAction
;
2119 auto* tarr
= &obj
->as
<TypedArrayObject
>();
2121 maybeEmitIdGuard(id
);
2122 // Emit all the normal guards for calling this native, but specialize
2123 // callNativeGetterResult.
2124 EmitCallGetterResultGuards(writer
, tarr
, holder
, id
, *prop
, objId
, mode_
);
2126 if (tarr
->length() <= INT32_MAX
) {
2127 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2129 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2131 trackAttached("GetProp.TypedArrayLength");
2132 } else if (isByteOffset
) {
2133 if (tarr
->byteOffset() <= INT32_MAX
) {
2134 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2136 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2138 trackAttached("GetProp.TypedArrayByteOffset");
2140 if (tarr
->byteLength() <= INT32_MAX
) {
2141 writer
.typedArrayByteLengthInt32Result(objId
);
2143 writer
.typedArrayByteLengthDoubleResult(objId
);
2145 trackAttached("GetProp.TypedArrayByteLength");
2147 writer
.returnFromIC();
2149 return AttachDecision::Attach
;
2152 AttachDecision
GetPropIRGenerator::tryAttachDataView(HandleObject obj
,
2155 // TODO: Support resizable dataviews. (bug 1842999)
2156 if (!obj
->is
<FixedLengthDataViewObject
>()) {
2157 return AttachDecision::NoAction
;
2159 auto* dv
= &obj
->as
<FixedLengthDataViewObject
>();
2161 if (mode_
!= ICState::Mode::Specialized
) {
2162 return AttachDecision::NoAction
;
2165 // Receiver should be the object.
2167 return AttachDecision::NoAction
;
2170 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2171 if (!isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2172 return AttachDecision::NoAction
;
2175 // byteOffset and byteLength both throw when the ArrayBuffer is detached.
2176 if (dv
->hasDetachedBuffer()) {
2177 return AttachDecision::NoAction
;
2180 NativeObject
* holder
= nullptr;
2181 Maybe
<PropertyInfo
> prop
;
2182 NativeGetPropKind kind
=
2183 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2184 if (kind
!= NativeGetPropKind::NativeGetter
) {
2185 return AttachDecision::NoAction
;
2188 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2190 if (!DataViewObject::isOriginalByteOffsetGetter(fun
.native())) {
2191 return AttachDecision::NoAction
;
2194 if (!DataViewObject::isOriginalByteLengthGetter(fun
.native())) {
2195 return AttachDecision::NoAction
;
2199 maybeEmitIdGuard(id
);
2200 // Emit all the normal guards for calling this native, but specialize
2201 // callNativeGetterResult.
2202 EmitCallGetterResultGuards(writer
, dv
, holder
, id
, *prop
, objId
, mode_
);
2203 writer
.guardHasAttachedArrayBuffer(objId
);
2205 if (dv
->byteOffset() <= INT32_MAX
) {
2206 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2208 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2210 trackAttached("GetProp.DataViewByteOffset");
2212 if (dv
->byteLength() <= INT32_MAX
) {
2213 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2215 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2217 trackAttached("GetProp.DataViewByteLength");
2219 writer
.returnFromIC();
2221 return AttachDecision::Attach
;
2224 AttachDecision
GetPropIRGenerator::tryAttachArrayBufferMaybeShared(
2225 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2226 if (!obj
->is
<ArrayBufferObjectMaybeShared
>()) {
2227 return AttachDecision::NoAction
;
2229 auto* buf
= &obj
->as
<ArrayBufferObjectMaybeShared
>();
2231 // TODO: Support resizable buffers. (bug 1842999)
2232 if (buf
->isResizable()) {
2233 return AttachDecision::NoAction
;
2236 if (mode_
!= ICState::Mode::Specialized
) {
2237 return AttachDecision::NoAction
;
2240 // Receiver should be the object.
2242 return AttachDecision::NoAction
;
2245 if (!id
.isAtom(cx_
->names().byteLength
)) {
2246 return AttachDecision::NoAction
;
2249 NativeObject
* holder
= nullptr;
2250 Maybe
<PropertyInfo
> prop
;
2251 NativeGetPropKind kind
=
2252 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2253 if (kind
!= NativeGetPropKind::NativeGetter
) {
2254 return AttachDecision::NoAction
;
2257 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2258 if (buf
->is
<ArrayBufferObject
>()) {
2259 if (!ArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2260 return AttachDecision::NoAction
;
2263 if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2264 return AttachDecision::NoAction
;
2268 maybeEmitIdGuard(id
);
2269 // Emit all the normal guards for calling this native, but specialize
2270 // callNativeGetterResult.
2271 EmitCallGetterResultGuards(writer
, buf
, holder
, id
, *prop
, objId
, mode_
);
2272 if (buf
->byteLength() <= INT32_MAX
) {
2273 writer
.loadArrayBufferByteLengthInt32Result(objId
);
2275 writer
.loadArrayBufferByteLengthDoubleResult(objId
);
2277 writer
.returnFromIC();
2279 trackAttached("GetProp.ArrayBufferMaybeSharedByteLength");
2280 return AttachDecision::Attach
;
2283 AttachDecision
GetPropIRGenerator::tryAttachRegExp(HandleObject obj
,
2286 if (!obj
->is
<RegExpObject
>()) {
2287 return AttachDecision::NoAction
;
2289 auto* regExp
= &obj
->as
<RegExpObject
>();
2291 if (mode_
!= ICState::Mode::Specialized
) {
2292 return AttachDecision::NoAction
;
2295 // Receiver should be the object.
2297 return AttachDecision::NoAction
;
2300 NativeObject
* holder
= nullptr;
2301 Maybe
<PropertyInfo
> prop
;
2302 NativeGetPropKind kind
=
2303 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2304 if (kind
!= NativeGetPropKind::NativeGetter
) {
2305 return AttachDecision::NoAction
;
2308 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2309 JS::RegExpFlags flags
= JS::RegExpFlag::NoFlags
;
2310 if (!RegExpObject::isOriginalFlagGetter(fun
.native(), &flags
)) {
2311 return AttachDecision::NoAction
;
2314 maybeEmitIdGuard(id
);
2315 // Emit all the normal guards for calling this native, but specialize
2316 // callNativeGetterResult.
2317 EmitCallGetterResultGuards(writer
, regExp
, holder
, id
, *prop
, objId
, mode_
);
2319 writer
.regExpFlagResult(objId
, flags
.value());
2320 writer
.returnFromIC();
2322 trackAttached("GetProp.RegExpFlag");
2323 return AttachDecision::Attach
;
2326 AttachDecision
GetPropIRGenerator::tryAttachMap(HandleObject obj
,
2329 if (!obj
->is
<MapObject
>()) {
2330 return AttachDecision::NoAction
;
2332 auto* mapObj
= &obj
->as
<MapObject
>();
2334 if (mode_
!= ICState::Mode::Specialized
) {
2335 return AttachDecision::NoAction
;
2338 // Receiver should be the object.
2340 return AttachDecision::NoAction
;
2343 if (!id
.isAtom(cx_
->names().size
)) {
2344 return AttachDecision::NoAction
;
2347 NativeObject
* holder
= nullptr;
2348 Maybe
<PropertyInfo
> prop
;
2349 NativeGetPropKind kind
=
2350 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2351 if (kind
!= NativeGetPropKind::NativeGetter
) {
2352 return AttachDecision::NoAction
;
2355 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2356 if (!MapObject::isOriginalSizeGetter(fun
.native())) {
2357 return AttachDecision::NoAction
;
2360 maybeEmitIdGuard(id
);
2362 // Emit all the normal guards for calling this native, but specialize
2363 // callNativeGetterResult.
2364 EmitCallGetterResultGuards(writer
, mapObj
, holder
, id
, *prop
, objId
, mode_
);
2366 writer
.mapSizeResult(objId
);
2367 writer
.returnFromIC();
2369 trackAttached("GetProp.MapSize");
2370 return AttachDecision::Attach
;
2373 AttachDecision
GetPropIRGenerator::tryAttachSet(HandleObject obj
,
2376 if (!obj
->is
<SetObject
>()) {
2377 return AttachDecision::NoAction
;
2379 auto* setObj
= &obj
->as
<SetObject
>();
2381 if (mode_
!= ICState::Mode::Specialized
) {
2382 return AttachDecision::NoAction
;
2385 // Receiver should be the object.
2387 return AttachDecision::NoAction
;
2390 if (!id
.isAtom(cx_
->names().size
)) {
2391 return AttachDecision::NoAction
;
2394 NativeObject
* holder
= nullptr;
2395 Maybe
<PropertyInfo
> prop
;
2396 NativeGetPropKind kind
=
2397 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2398 if (kind
!= NativeGetPropKind::NativeGetter
) {
2399 return AttachDecision::NoAction
;
2402 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2403 if (!SetObject::isOriginalSizeGetter(fun
.native())) {
2404 return AttachDecision::NoAction
;
2407 maybeEmitIdGuard(id
);
2409 // Emit all the normal guards for calling this native, but specialize
2410 // callNativeGetterResult.
2411 EmitCallGetterResultGuards(writer
, setObj
, holder
, id
, *prop
, objId
, mode_
);
2413 writer
.setSizeResult(objId
);
2414 writer
.returnFromIC();
2416 trackAttached("GetProp.SetSize");
2417 return AttachDecision::Attach
;
2420 AttachDecision
GetPropIRGenerator::tryAttachFunction(HandleObject obj
,
2423 // Function properties are lazily resolved so they might not be defined yet.
2424 // And we might end up in a situation where we always have a fresh function
2425 // object during the IC generation.
2426 if (!obj
->is
<JSFunction
>()) {
2427 return AttachDecision::NoAction
;
2430 bool isLength
= id
.isAtom(cx_
->names().length
);
2431 if (!isLength
&& !id
.isAtom(cx_
->names().name
)) {
2432 return AttachDecision::NoAction
;
2435 NativeObject
* holder
= nullptr;
2436 PropertyResult prop
;
2437 // If this property exists already, don't attach the stub.
2438 if (LookupPropertyPure(cx_
, obj
, id
, &holder
, &prop
)) {
2439 return AttachDecision::NoAction
;
2442 JSFunction
* fun
= &obj
->as
<JSFunction
>();
2445 // length was probably deleted from the function.
2446 if (fun
->hasResolvedLength()) {
2447 return AttachDecision::NoAction
;
2450 // Lazy functions don't store the length.
2451 if (!fun
->hasBytecode()) {
2452 return AttachDecision::NoAction
;
2455 // name was probably deleted from the function.
2456 if (fun
->hasResolvedName()) {
2457 return AttachDecision::NoAction
;
2461 maybeEmitIdGuard(id
);
2462 writer
.guardClass(objId
, GuardClassKind::JSFunction
);
2464 writer
.loadFunctionLengthResult(objId
);
2465 writer
.returnFromIC();
2466 trackAttached("GetProp.FunctionLength");
2468 writer
.loadFunctionNameResult(objId
);
2469 writer
.returnFromIC();
2470 trackAttached("GetProp.FunctionName");
2472 return AttachDecision::Attach
;
2475 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectIterator(
2476 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2477 if (!obj
->is
<ArgumentsObject
>()) {
2478 return AttachDecision::NoAction
;
2481 if (!id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
2482 return AttachDecision::NoAction
;
2485 Handle
<ArgumentsObject
*> args
= obj
.as
<ArgumentsObject
>();
2486 if (args
->hasOverriddenIterator()) {
2487 return AttachDecision::NoAction
;
2490 AssertArgumentsCustomDataProp(args
, id
);
2492 RootedValue
iterator(cx_
);
2493 if (!ArgumentsObject::getArgumentsIterator(cx_
, &iterator
)) {
2494 cx_
->recoverFromOutOfMemory();
2495 return AttachDecision::NoAction
;
2497 MOZ_ASSERT(iterator
.isObject());
2499 maybeEmitIdGuard(id
);
2500 if (args
->is
<MappedArgumentsObject
>()) {
2501 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2503 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2504 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2506 uint32_t flags
= ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
;
2507 writer
.guardArgumentsObjectFlags(objId
, flags
);
2509 ObjOperandId iterId
= writer
.loadObject(&iterator
.toObject());
2510 writer
.loadObjectResult(iterId
);
2511 writer
.returnFromIC();
2513 trackAttached("GetProp.ArgumentsObjectIterator");
2514 return AttachDecision::Attach
;
2517 AttachDecision
GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj
,
2520 if (!obj
->is
<ModuleNamespaceObject
>()) {
2521 return AttachDecision::NoAction
;
2524 auto* ns
= &obj
->as
<ModuleNamespaceObject
>();
2525 ModuleEnvironmentObject
* env
= nullptr;
2526 Maybe
<PropertyInfo
> prop
;
2527 if (!ns
->bindings().lookup(id
, &env
, &prop
)) {
2528 return AttachDecision::NoAction
;
2531 // Don't emit a stub until the target binding has been initialized.
2532 if (env
->getSlot(prop
->slot()).isMagic(JS_UNINITIALIZED_LEXICAL
)) {
2533 return AttachDecision::NoAction
;
2536 // Check for the specific namespace object.
2537 maybeEmitIdGuard(id
);
2538 writer
.guardSpecificObject(objId
, ns
);
2540 ObjOperandId envId
= writer
.loadObject(env
);
2541 EmitLoadSlotResult(writer
, envId
, env
, *prop
);
2542 writer
.returnFromIC();
2544 trackAttached("GetProp.ModuleNamespace");
2545 return AttachDecision::Attach
;
2548 AttachDecision
GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId
,
2550 MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
2552 JSProtoKey protoKey
;
2553 switch (val_
.type()) {
2554 case ValueType::String
:
2555 if (id
.isAtom(cx_
->names().length
)) {
2556 // String length is special-cased, see js::GetProperty.
2557 return AttachDecision::NoAction
;
2559 protoKey
= JSProto_String
;
2561 case ValueType::Int32
:
2562 case ValueType::Double
:
2563 protoKey
= JSProto_Number
;
2565 case ValueType::Boolean
:
2566 protoKey
= JSProto_Boolean
;
2568 case ValueType::Symbol
:
2569 protoKey
= JSProto_Symbol
;
2571 case ValueType::BigInt
:
2572 protoKey
= JSProto_BigInt
;
2574 case ValueType::Null
:
2575 case ValueType::Undefined
:
2576 case ValueType::Magic
:
2577 return AttachDecision::NoAction
;
2578 #ifdef ENABLE_RECORD_TUPLE
2579 case ValueType::ExtendedPrimitive
:
2581 case ValueType::Object
:
2582 case ValueType::PrivateGCThing
:
2583 MOZ_CRASH("unexpected type");
2586 JSObject
* proto
= GlobalObject::getOrCreatePrototype(cx_
, protoKey
);
2588 cx_
->recoverFromOutOfMemory();
2589 return AttachDecision::NoAction
;
2592 NativeObject
* holder
= nullptr;
2593 Maybe
<PropertyInfo
> prop
;
2594 NativeGetPropKind kind
=
2595 CanAttachNativeGetProp(cx_
, proto
, id
, &holder
, &prop
, pc_
);
2597 case NativeGetPropKind::None
:
2598 return AttachDecision::NoAction
;
2599 case NativeGetPropKind::Missing
:
2600 case NativeGetPropKind::Slot
: {
2601 auto* nproto
= &proto
->as
<NativeObject
>();
2603 if (val_
.isNumber()) {
2604 writer
.guardIsNumber(valId
);
2606 writer
.guardNonDoubleType(valId
, val_
.type());
2608 maybeEmitIdGuard(id
);
2610 ObjOperandId protoId
= writer
.loadObject(nproto
);
2611 if (kind
== NativeGetPropKind::Slot
) {
2612 EmitReadSlotResult(writer
, nproto
, holder
, *prop
, protoId
);
2613 writer
.returnFromIC();
2614 trackAttached("GetProp.PrimitiveSlot");
2616 EmitMissingPropResult(writer
, nproto
, protoId
);
2617 writer
.returnFromIC();
2618 trackAttached("GetProp.PrimitiveMissing");
2620 return AttachDecision::Attach
;
2622 case NativeGetPropKind::ScriptedGetter
:
2623 case NativeGetPropKind::NativeGetter
: {
2624 auto* nproto
= &proto
->as
<NativeObject
>();
2626 if (val_
.isNumber()) {
2627 writer
.guardIsNumber(valId
);
2629 writer
.guardNonDoubleType(valId
, val_
.type());
2631 maybeEmitIdGuard(id
);
2633 ObjOperandId protoId
= writer
.loadObject(nproto
);
2634 EmitCallGetterResult(cx_
, writer
, kind
, nproto
, holder
, id
, *prop
,
2635 protoId
, valId
, mode_
);
2637 trackAttached("GetProp.PrimitiveGetter");
2638 return AttachDecision::Attach
;
2642 MOZ_CRASH("Bad NativeGetPropKind");
2645 AttachDecision
GetPropIRGenerator::tryAttachStringLength(ValOperandId valId
,
2647 if (!val_
.isString() || !id
.isAtom(cx_
->names().length
)) {
2648 return AttachDecision::NoAction
;
2651 StringOperandId strId
= writer
.guardToString(valId
);
2652 maybeEmitIdGuard(id
);
2653 writer
.loadStringLengthResult(strId
);
2654 writer
.returnFromIC();
2656 trackAttached("GetProp.StringLength");
2657 return AttachDecision::Attach
;
2660 enum class AttachStringChar
{ No
, Yes
, Linearize
, OutOfBounds
};
2662 static AttachStringChar
CanAttachStringChar(const Value
& val
,
2665 if (!val
.isString() || !idVal
.isInt32()) {
2666 return AttachStringChar::No
;
2669 JSString
* str
= val
.toString();
2670 int32_t index
= idVal
.toInt32();
2672 if (index
< 0 && kind
== StringChar::At
) {
2673 static_assert(JSString::MAX_LENGTH
<= INT32_MAX
,
2674 "string length fits in int32");
2675 index
+= int32_t(str
->length());
2678 if (index
< 0 || size_t(index
) >= str
->length()) {
2679 return AttachStringChar::OutOfBounds
;
2682 // This follows JSString::getChar and MacroAssembler::loadStringChar.
2683 if (str
->isRope()) {
2684 JSRope
* rope
= &str
->asRope();
2685 if (size_t(index
) < rope
->leftChild()->length()) {
2686 str
= rope
->leftChild();
2688 // MacroAssembler::loadStringChar doesn't support surrogate pairs which
2689 // are split between the left and right child of a rope.
2690 if (kind
== StringChar::CodePointAt
&&
2691 size_t(index
) + 1 == str
->length() && str
->isLinear()) {
2692 // Linearize the string when the last character of the left child is a
2693 // a lead surrogate.
2694 char16_t ch
= str
->asLinear().latin1OrTwoByteChar(index
);
2695 if (unicode::IsLeadSurrogate(ch
)) {
2696 return AttachStringChar::Linearize
;
2700 str
= rope
->rightChild();
2704 if (!str
->isLinear()) {
2705 return AttachStringChar::Linearize
;
2708 return AttachStringChar::Yes
;
2711 AttachDecision
GetPropIRGenerator::tryAttachStringChar(ValOperandId valId
,
2712 ValOperandId indexId
) {
2713 MOZ_ASSERT(idVal_
.isInt32());
2715 auto attach
= CanAttachStringChar(val_
, idVal_
, StringChar::CharAt
);
2716 if (attach
== AttachStringChar::No
) {
2717 return AttachDecision::NoAction
;
2720 // Can't attach for out-of-bounds access without guarding that indexed
2721 // properties aren't present along the prototype chain of |String.prototype|.
2722 if (attach
== AttachStringChar::OutOfBounds
) {
2723 return AttachDecision::NoAction
;
2726 StringOperandId strId
= writer
.guardToString(valId
);
2727 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
2728 if (attach
== AttachStringChar::Linearize
) {
2729 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
2731 writer
.loadStringCharResult(strId
, int32IndexId
, /* handleOOB = */ false);
2732 writer
.returnFromIC();
2734 trackAttached("GetProp.StringChar");
2735 return AttachDecision::Attach
;
2738 static bool ClassCanHaveExtraProperties(const JSClass
* clasp
) {
2739 return clasp
->getResolve() || clasp
->getOpsLookupProperty() ||
2740 clasp
->getOpsGetProperty() || IsTypedArrayClass(clasp
);
2743 enum class OwnProperty
: bool { No
, Yes
};
2744 enum class AllowIndexedReceiver
: bool { No
, Yes
};
2745 enum class AllowExtraReceiverProperties
: bool { No
, Yes
};
2747 static bool CanAttachDenseElementHole(
2748 NativeObject
* obj
, OwnProperty ownProp
,
2749 AllowIndexedReceiver allowIndexedReceiver
= AllowIndexedReceiver::No
,
2750 AllowExtraReceiverProperties allowExtraReceiverProperties
=
2751 AllowExtraReceiverProperties::No
) {
2752 // Make sure the objects on the prototype don't have any indexed properties
2753 // or that such properties can't appear without a shape change.
2754 // Otherwise returning undefined for holes would obviously be incorrect,
2755 // because we would have to lookup a property on the prototype instead.
2757 // The first two checks are also relevant to the receiver object.
2758 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
2761 allowIndexedReceiver
= AllowIndexedReceiver::No
;
2763 if (allowExtraReceiverProperties
== AllowExtraReceiverProperties::No
&&
2764 ClassCanHaveExtraProperties(obj
->getClass())) {
2767 allowExtraReceiverProperties
= AllowExtraReceiverProperties::No
;
2769 // Don't need to check prototype for OwnProperty checks
2770 if (ownProp
== OwnProperty::Yes
) {
2774 JSObject
* proto
= obj
->staticPrototype();
2779 if (!proto
->is
<NativeObject
>()) {
2783 // Make sure objects on the prototype don't have dense elements.
2784 if (proto
->as
<NativeObject
>().getDenseInitializedLength() != 0) {
2788 obj
= &proto
->as
<NativeObject
>();
2794 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArg(
2795 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2796 Int32OperandId indexId
) {
2797 if (!obj
->is
<ArgumentsObject
>()) {
2798 return AttachDecision::NoAction
;
2800 auto* args
= &obj
->as
<ArgumentsObject
>();
2802 // No elements must have been overridden or deleted.
2803 if (args
->hasOverriddenElement()) {
2804 return AttachDecision::NoAction
;
2808 if (index
>= args
->initialLength()) {
2809 return AttachDecision::NoAction
;
2812 AssertArgumentsCustomDataProp(args
, PropertyKey::Int(index
));
2814 // And finally also check that the argument isn't forwarded.
2815 if (args
->argIsForwarded(index
)) {
2816 return AttachDecision::NoAction
;
2819 if (args
->is
<MappedArgumentsObject
>()) {
2820 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2822 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2823 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2826 writer
.loadArgumentsObjectArgResult(objId
, indexId
);
2827 writer
.returnFromIC();
2829 trackAttached("GetProp.ArgumentsObjectArg");
2830 return AttachDecision::Attach
;
2833 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
2834 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2835 Int32OperandId indexId
) {
2836 if (!obj
->is
<ArgumentsObject
>()) {
2837 return AttachDecision::NoAction
;
2839 auto* args
= &obj
->as
<ArgumentsObject
>();
2841 // No elements must have been overridden or deleted.
2842 if (args
->hasOverriddenElement()) {
2843 return AttachDecision::NoAction
;
2846 // And also check that the argument isn't forwarded.
2847 if (index
< args
->initialLength() && args
->argIsForwarded(index
)) {
2848 return AttachDecision::NoAction
;
2851 if (!CanAttachDenseElementHole(args
, OwnProperty::No
,
2852 AllowIndexedReceiver::Yes
,
2853 AllowExtraReceiverProperties::Yes
)) {
2854 return AttachDecision::NoAction
;
2857 // We don't need to guard on the shape, because we check if any element is
2858 // overridden. Elements are marked as overridden iff any element is defined,
2859 // irrespective of whether the element is in-bounds or out-of-bounds. So when
2860 // that flag isn't set, we can guarantee that the arguments object doesn't
2861 // have any additional own elements.
2863 if (args
->is
<MappedArgumentsObject
>()) {
2864 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2866 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2867 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2870 GeneratePrototypeHoleGuards(writer
, args
, objId
,
2871 /* alwaysGuardFirstProto = */ true);
2873 writer
.loadArgumentsObjectArgHoleResult(objId
, indexId
);
2874 writer
.returnFromIC();
2876 trackAttached("GetProp.ArgumentsObjectArgHole");
2877 return AttachDecision::Attach
;
2880 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectCallee(
2881 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2882 // Only mapped arguments objects have a `callee` property.
2883 if (!obj
->is
<MappedArgumentsObject
>()) {
2884 return AttachDecision::NoAction
;
2887 if (!id
.isAtom(cx_
->names().callee
)) {
2888 return AttachDecision::NoAction
;
2891 // The callee must not have been overridden or deleted.
2892 MappedArgumentsObject
* args
= &obj
->as
<MappedArgumentsObject
>();
2893 if (args
->hasOverriddenCallee()) {
2894 return AttachDecision::NoAction
;
2897 AssertArgumentsCustomDataProp(args
, id
);
2899 maybeEmitIdGuard(id
);
2900 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2902 uint32_t flags
= ArgumentsObject::CALLEE_OVERRIDDEN_BIT
;
2903 writer
.guardArgumentsObjectFlags(objId
, flags
);
2905 writer
.loadFixedSlotResult(objId
,
2906 MappedArgumentsObject::getCalleeSlotOffset());
2907 writer
.returnFromIC();
2909 trackAttached("GetProp.ArgumentsObjectCallee");
2910 return AttachDecision::Attach
;
2913 AttachDecision
GetPropIRGenerator::tryAttachDenseElement(
2914 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2915 Int32OperandId indexId
) {
2916 if (!obj
->is
<NativeObject
>()) {
2917 return AttachDecision::NoAction
;
2920 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2921 if (!nobj
->containsDenseElement(index
)) {
2922 return AttachDecision::NoAction
;
2925 if (mode_
== ICState::Mode::Megamorphic
) {
2926 writer
.guardIsNativeObject(objId
);
2928 TestMatchingNativeReceiver(writer
, nobj
, objId
);
2930 writer
.loadDenseElementResult(objId
, indexId
);
2931 writer
.returnFromIC();
2933 trackAttached("GetProp.DenseElement");
2934 return AttachDecision::Attach
;
2937 AttachDecision
GetPropIRGenerator::tryAttachDenseElementHole(
2938 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2939 Int32OperandId indexId
) {
2940 if (!obj
->is
<NativeObject
>()) {
2941 return AttachDecision::NoAction
;
2944 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2945 if (nobj
->containsDenseElement(index
)) {
2946 return AttachDecision::NoAction
;
2948 if (!CanAttachDenseElementHole(nobj
, OwnProperty::No
)) {
2949 return AttachDecision::NoAction
;
2952 // Guard on the shape, to prevent non-dense elements from appearing.
2953 TestMatchingNativeReceiver(writer
, nobj
, objId
);
2954 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
2955 /* alwaysGuardFirstProto = */ false);
2956 writer
.loadDenseElementHoleResult(objId
, indexId
);
2957 writer
.returnFromIC();
2959 trackAttached("GetProp.DenseElementHole");
2960 return AttachDecision::Attach
;
2963 AttachDecision
GetPropIRGenerator::tryAttachSparseElement(
2964 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2965 Int32OperandId indexId
) {
2966 if (!obj
->is
<NativeObject
>()) {
2967 return AttachDecision::NoAction
;
2969 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2971 // Stub doesn't handle negative indices.
2972 if (index
> INT32_MAX
) {
2973 return AttachDecision::NoAction
;
2976 // The object must have sparse elements.
2977 if (!nobj
->isIndexed()) {
2978 return AttachDecision::NoAction
;
2981 // The index must not be for a dense element.
2982 if (nobj
->containsDenseElement(index
)) {
2983 return AttachDecision::NoAction
;
2986 // Only handle ArrayObject and PlainObject in this stub.
2987 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
2988 return AttachDecision::NoAction
;
2991 // GetSparseElementHelper assumes that the target and the receiver
2994 return AttachDecision::NoAction
;
2997 // Here, we ensure that the prototype chain does not define any sparse
2998 // indexed properties on the shape lineage. This allows us to guard on
2999 // the shapes up the prototype chain to ensure that no indexed properties
3000 // exist outside of the dense elements.
3002 // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
3003 // as well as ensure that no prototypes contain dense elements, allowing
3004 // us to perform a pure shape-search for out-of-bounds integer-indexed
3005 // properties on the receiver object.
3006 if (PrototypeMayHaveIndexedProperties(nobj
)) {
3007 return AttachDecision::NoAction
;
3010 // Ensure that obj is an ArrayObject or PlainObject.
3011 if (nobj
->is
<ArrayObject
>()) {
3012 writer
.guardClass(objId
, GuardClassKind::Array
);
3014 MOZ_ASSERT(nobj
->is
<PlainObject
>());
3015 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
3018 // The helper we are going to call only applies to non-dense elements.
3019 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3021 // Ensures we are able to efficiently able to map to an integral jsid.
3022 writer
.guardInt32IsNonNegative(indexId
);
3024 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
3025 // The helper function also ensures that the index does not appear within the
3026 // dense element set of the prototypes.
3027 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3028 /* alwaysGuardFirstProto = */ true);
3030 // At this point, we are guaranteed that the indexed property will not
3031 // be found on one of the prototypes. We are assured that we only have
3032 // to check that the receiving object has the property.
3034 writer
.callGetSparseElementResult(objId
, indexId
);
3035 writer
.returnFromIC();
3037 trackAttached("GetProp.SparseElement");
3038 return AttachDecision::Attach
;
3041 // For Uint32Array we let the stub return an Int32 if we have not seen a
3042 // double, to allow better codegen in Warp while avoiding bailout loops.
3043 static bool ForceDoubleForUint32Array(TypedArrayObject
* tarr
, uint64_t index
) {
3044 MOZ_ASSERT(index
< tarr
->length());
3046 if (tarr
->type() != Scalar::Type::Uint32
) {
3047 // Return value is only relevant for Uint32Array.
3052 MOZ_ALWAYS_TRUE(tarr
->getElementPure(index
, &res
));
3053 MOZ_ASSERT(res
.isNumber());
3054 return res
.isDouble();
3057 AttachDecision
GetPropIRGenerator::tryAttachTypedArrayElement(
3058 HandleObject obj
, ObjOperandId objId
) {
3059 if (!obj
->is
<TypedArrayObject
>()) {
3060 return AttachDecision::NoAction
;
3063 if (!idVal_
.isNumber()) {
3064 return AttachDecision::NoAction
;
3067 TypedArrayObject
* tarr
= &obj
->as
<TypedArrayObject
>();
3069 bool handleOOB
= false;
3071 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
3072 uint64_t(indexInt64
) >= tarr
->length()) {
3076 // If the number is not representable as an integer the result will be
3077 // |undefined| so we leave |forceDoubleForUint32| as false.
3078 bool forceDoubleForUint32
= false;
3080 uint64_t index
= uint64_t(indexInt64
);
3081 forceDoubleForUint32
= ForceDoubleForUint32Array(tarr
, index
);
3084 writer
.guardShapeForClass(objId
, tarr
->shape());
3086 ValOperandId keyId
= getElemKeyValueId();
3087 IntPtrOperandId intPtrIndexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
3089 writer
.loadTypedArrayElementResult(objId
, intPtrIndexId
, tarr
->type(),
3090 handleOOB
, forceDoubleForUint32
);
3091 writer
.returnFromIC();
3093 trackAttached("GetProp.TypedElement");
3094 return AttachDecision::Attach
;
3097 AttachDecision
GetPropIRGenerator::tryAttachGenericElement(
3098 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3099 Int32OperandId indexId
, ValOperandId receiverId
) {
3100 if (!obj
->is
<NativeObject
>()) {
3101 return AttachDecision::NoAction
;
3104 #ifdef JS_CODEGEN_X86
3106 // There aren't enough registers available on x86.
3107 return AttachDecision::NoAction
;
3111 // To allow other types to attach in the non-megamorphic case we test the
3112 // specific matching native receiver; however, once megamorphic we can attach
3114 if (mode_
== ICState::Mode::Megamorphic
) {
3115 writer
.guardIsNativeObject(objId
);
3117 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3118 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3120 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3122 writer
.callNativeGetElementSuperResult(objId
, indexId
, receiverId
);
3124 writer
.callNativeGetElementResult(objId
, indexId
);
3126 writer
.returnFromIC();
3128 trackAttached(mode_
== ICState::Mode::Megamorphic
3129 ? "GenericElementMegamorphic"
3130 : "GenericElement");
3131 return AttachDecision::Attach
;
3134 AttachDecision
GetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3135 ObjOperandId objId
) {
3136 if (!obj
->is
<ProxyObject
>()) {
3137 return AttachDecision::NoAction
;
3140 // The proxy stubs don't currently support |super| access.
3142 return AttachDecision::NoAction
;
3146 auto proxy
= obj
.as
<ProxyObject
>();
3147 if (proxy
->handler()->isScripted()) {
3148 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, JS::VoidHandlePropertyKey
));
3152 writer
.guardIsProxy(objId
);
3154 // We are not guarding against DOM proxies here, because there is no other
3155 // specialized DOM IC we could attach.
3156 // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
3157 // but for GetElem we prefer to attach a stub that can handle any Value
3158 // so we don't attach a new stub for every id.
3159 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
3160 MOZ_ASSERT(!isSuper());
3161 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
3162 writer
.returnFromIC();
3164 trackAttached("GetProp.ProxyElement");
3165 return AttachDecision::Attach
;
3168 void GetPropIRGenerator::trackAttached(const char* name
) {
3169 stubName_
= name
? name
: "NotAttached";
3170 #ifdef JS_CACHEIR_SPEW
3171 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3172 sp
.valueProperty("base", val_
);
3173 sp
.valueProperty("property", idVal_
);
3178 void IRGenerator::emitIdGuard(ValOperandId valId
, const Value
& idVal
, jsid id
) {
3179 if (id
.isSymbol()) {
3180 MOZ_ASSERT(idVal
.toSymbol() == id
.toSymbol());
3181 SymbolOperandId symId
= writer
.guardToSymbol(valId
);
3182 writer
.guardSpecificSymbol(symId
, id
.toSymbol());
3184 MOZ_ASSERT(id
.isAtom());
3185 if (idVal
.isUndefined()) {
3186 MOZ_ASSERT(id
.isAtom(cx_
->names().undefined
));
3187 writer
.guardIsUndefined(valId
);
3188 } else if (idVal
.isNull()) {
3189 MOZ_ASSERT(id
.isAtom(cx_
->names().null
));
3190 writer
.guardIsNull(valId
);
3192 MOZ_ASSERT(idVal
.isString());
3193 StringOperandId strId
= writer
.guardToString(valId
);
3194 writer
.guardSpecificAtom(strId
, id
.toAtom());
3199 void GetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3200 if (cacheKind_
== CacheKind::GetProp
||
3201 cacheKind_
== CacheKind::GetPropSuper
) {
3202 // Constant PropertyName, no guards necessary.
3203 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3207 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
3208 cacheKind_
== CacheKind::GetElemSuper
);
3209 emitIdGuard(getElemKeyValueId(), idVal_
, id
);
3212 void SetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3213 if (cacheKind_
== CacheKind::SetProp
) {
3214 // Constant PropertyName, no guards necessary.
3215 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3219 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
3220 emitIdGuard(setElemKeyValueId(), idVal_
, id
);
3223 GetNameIRGenerator::GetNameIRGenerator(JSContext
* cx
, HandleScript script
,
3224 jsbytecode
* pc
, ICState state
,
3226 Handle
<PropertyName
*> name
)
3227 : IRGenerator(cx
, script
, pc
, CacheKind::GetName
, state
),
3231 AttachDecision
GetNameIRGenerator::tryAttachStub() {
3232 MOZ_ASSERT(cacheKind_
== CacheKind::GetName
);
3234 AutoAssertNoPendingException
aanpe(cx_
);
3236 ObjOperandId
envId(writer
.setInputOperandId(0));
3237 RootedId
id(cx_
, NameToId(name_
));
3239 TRY_ATTACH(tryAttachGlobalNameValue(envId
, id
));
3240 TRY_ATTACH(tryAttachGlobalNameGetter(envId
, id
));
3241 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3243 trackAttached(IRGenerator::NotAttached
);
3244 return AttachDecision::NoAction
;
3247 static bool CanAttachGlobalName(JSContext
* cx
,
3248 GlobalLexicalEnvironmentObject
* globalLexical
,
3249 PropertyKey id
, NativeObject
** holder
,
3250 Maybe
<PropertyInfo
>* prop
) {
3251 // The property must be found, and it must be found as a normal data property.
3252 NativeObject
* current
= globalLexical
;
3254 *prop
= current
->lookup(cx
, id
);
3255 if (prop
->isSome()) {
3259 if (current
== globalLexical
) {
3260 current
= &globalLexical
->global();
3262 // In the browser the global prototype chain should be immutable.
3263 if (!current
->staticPrototypeIsImmutable()) {
3267 JSObject
* proto
= current
->staticPrototype();
3268 if (!proto
|| !proto
->is
<NativeObject
>()) {
3272 current
= &proto
->as
<NativeObject
>();
3280 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId
,
3282 if (!IsGlobalOp(JSOp(*pc_
))) {
3283 return AttachDecision::NoAction
;
3285 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3287 auto* globalLexical
= &env_
->as
<GlobalLexicalEnvironmentObject
>();
3289 NativeObject
* holder
= nullptr;
3290 Maybe
<PropertyInfo
> prop
;
3291 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3292 return AttachDecision::NoAction
;
3295 // The property must be found, and it must be found as a normal data property.
3296 if (!prop
->isDataProperty()) {
3297 return AttachDecision::NoAction
;
3300 // This might still be an uninitialized lexical.
3301 if (holder
->getSlot(prop
->slot()).isMagic()) {
3302 return AttachDecision::NoAction
;
3305 if (holder
== globalLexical
) {
3306 // There is no need to guard on the shape. Lexical bindings are
3307 // non-configurable, and this stub cannot be shared across globals.
3308 size_t dynamicSlotOffset
=
3309 holder
->dynamicSlotIndex(prop
->slot()) * sizeof(Value
);
3310 writer
.loadDynamicSlotResult(objId
, dynamicSlotOffset
);
3311 } else if (holder
== &globalLexical
->global()) {
3312 MOZ_ASSERT(globalLexical
->global().isGenerationCountedGlobal());
3313 writer
.guardGlobalGeneration(
3314 globalLexical
->global().generationCount(),
3315 globalLexical
->global().addressOfGenerationCount());
3316 ObjOperandId holderId
= writer
.loadObject(holder
);
3318 writer
.assertPropertyLookup(holderId
, id
, prop
->slot());
3320 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3322 // Check the prototype chain from the global to the holder
3323 // prototype. Ignore the global lexical scope as it doesn't figure
3324 // into the prototype chain. We guard on the global lexical
3325 // scope's shape independently.
3326 if (!IsCacheableGetPropSlot(&globalLexical
->global(), holder
, *prop
)) {
3327 return AttachDecision::NoAction
;
3330 // Shape guard for global lexical.
3331 writer
.guardShape(objId
, globalLexical
->shape());
3333 // Guard on the shape of the GlobalObject.
3334 ObjOperandId globalId
= writer
.loadObject(&globalLexical
->global());
3335 writer
.guardShape(globalId
, globalLexical
->global().shape());
3337 // Shape guard holder.
3338 ObjOperandId holderId
= writer
.loadObject(holder
);
3339 writer
.guardShape(holderId
, holder
->shape());
3341 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3344 writer
.returnFromIC();
3346 trackAttached("GetName.GlobalNameValue");
3347 return AttachDecision::Attach
;
3350 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId
,
3352 if (!IsGlobalOp(JSOp(*pc_
))) {
3353 return AttachDecision::NoAction
;
3355 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3357 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3358 env_
.as
<GlobalLexicalEnvironmentObject
>();
3359 MOZ_ASSERT(globalLexical
->isGlobal());
3361 NativeObject
* holder
= nullptr;
3362 Maybe
<PropertyInfo
> prop
;
3363 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3364 return AttachDecision::NoAction
;
3367 if (holder
== globalLexical
) {
3368 return AttachDecision::NoAction
;
3371 GlobalObject
* global
= &globalLexical
->global();
3373 NativeGetPropKind kind
= IsCacheableGetPropCall(global
, holder
, *prop
);
3374 if (kind
!= NativeGetPropKind::NativeGetter
&&
3375 kind
!= NativeGetPropKind::ScriptedGetter
) {
3376 return AttachDecision::NoAction
;
3379 bool needsWindowProxy
=
3380 IsWindow(global
) && GetterNeedsWindowProxyThis(holder
, *prop
);
3382 // Shape guard for global lexical.
3383 writer
.guardShape(objId
, globalLexical
->shape());
3385 // Guard on the shape of the GlobalObject.
3386 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3387 writer
.guardShape(globalId
, global
->shape());
3389 if (holder
!= global
) {
3390 // Shape guard holder.
3391 ObjOperandId holderId
= writer
.loadObject(holder
);
3392 writer
.guardShape(holderId
, holder
->shape());
3393 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
3394 /* holderIsConstant = */ true);
3396 // Note: pass true for |holderIsConstant| because the holder must be the
3397 // current global object.
3398 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, globalId
,
3399 /* holderIsConstant = */ true);
3402 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, global
, holder
, *prop
,
3404 // The global shape guard above ensures the instance JSClass is correct.
3405 MOZ_ASSERT(!needsWindowProxy
);
3406 EmitCallDOMGetterResultNoGuards(writer
, holder
, *prop
, globalId
);
3407 trackAttached("GetName.GlobalNameDOMGetter");
3409 ObjOperandId receiverObjId
;
3410 if (needsWindowProxy
) {
3411 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
3412 receiverObjId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
3414 receiverObjId
= globalId
;
3416 ValOperandId receiverId
= writer
.boxObject(receiverObjId
);
3417 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, global
, holder
, *prop
,
3419 trackAttached("GetName.GlobalNameGetter");
3422 return AttachDecision::Attach
;
3425 static bool NeedEnvironmentShapeGuard(JSContext
* cx
, JSObject
* envObj
) {
3426 if (!envObj
->is
<CallObject
>()) {
3430 // We can skip a guard on the call object if the script's bindings are
3431 // guaranteed to be immutable (and thus cannot introduce shadowing variables).
3432 // If the function is a relazified self-hosted function it has no BaseScript
3433 // and we pessimistically create the guard.
3434 CallObject
* callObj
= &envObj
->as
<CallObject
>();
3435 JSFunction
* fun
= &callObj
->callee();
3436 if (!fun
->hasBaseScript() || fun
->baseScript()->funHasExtensibleScope() ||
3437 DebugEnvironments::hasDebugEnvironment(cx
, *callObj
)) {
3444 AttachDecision
GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3446 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3447 return AttachDecision::NoAction
;
3450 JSObject
* env
= env_
;
3451 Maybe
<PropertyInfo
> prop
;
3452 NativeObject
* holder
= nullptr;
3455 if (env
->is
<GlobalObject
>()) {
3456 prop
= env
->as
<GlobalObject
>().lookup(cx_
, id
);
3457 if (prop
.isSome()) {
3460 return AttachDecision::NoAction
;
3463 if (!env
->is
<EnvironmentObject
>() || env
->is
<WithEnvironmentObject
>()) {
3464 return AttachDecision::NoAction
;
3467 // Check for an 'own' property on the env. There is no need to
3468 // check the prototype as non-with scopes do not inherit properties
3469 // from any prototype.
3470 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3471 if (prop
.isSome()) {
3475 env
= env
->enclosingEnvironment();
3478 holder
= &env
->as
<NativeObject
>();
3479 if (!IsCacheableGetPropSlot(holder
, holder
, *prop
)) {
3480 return AttachDecision::NoAction
;
3482 if (holder
->getSlot(prop
->slot()).isMagic()) {
3483 MOZ_ASSERT(holder
->is
<EnvironmentObject
>());
3484 return AttachDecision::NoAction
;
3487 ObjOperandId lastObjId
= objId
;
3490 if (NeedEnvironmentShapeGuard(cx_
, env
)) {
3491 writer
.guardShape(lastObjId
, env
->shape());
3494 if (env
== holder
) {
3498 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3499 env
= env
->enclosingEnvironment();
3502 ValOperandId resId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3503 if (holder
->is
<EnvironmentObject
>()) {
3504 writer
.guardIsNotUninitializedLexical(resId
);
3506 writer
.loadOperandResult(resId
);
3507 writer
.returnFromIC();
3509 trackAttached("GetName.EnvironmentName");
3510 return AttachDecision::Attach
;
3513 void GetNameIRGenerator::trackAttached(const char* name
) {
3514 stubName_
= name
? name
: "NotAttached";
3515 #ifdef JS_CACHEIR_SPEW
3516 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3517 sp
.valueProperty("base", ObjectValue(*env_
));
3518 sp
.valueProperty("property", StringValue(name_
));
3523 BindNameIRGenerator::BindNameIRGenerator(JSContext
* cx
, HandleScript script
,
3524 jsbytecode
* pc
, ICState state
,
3526 Handle
<PropertyName
*> name
)
3527 : IRGenerator(cx
, script
, pc
, CacheKind::BindName
, state
),
3531 AttachDecision
BindNameIRGenerator::tryAttachStub() {
3532 MOZ_ASSERT(cacheKind_
== CacheKind::BindName
);
3534 AutoAssertNoPendingException
aanpe(cx_
);
3536 ObjOperandId
envId(writer
.setInputOperandId(0));
3537 RootedId
id(cx_
, NameToId(name_
));
3539 TRY_ATTACH(tryAttachGlobalName(envId
, id
));
3540 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3542 trackAttached(IRGenerator::NotAttached
);
3543 return AttachDecision::NoAction
;
3546 AttachDecision
BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId
,
3548 if (!IsGlobalOp(JSOp(*pc_
))) {
3549 return AttachDecision::NoAction
;
3551 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3553 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3554 env_
.as
<GlobalLexicalEnvironmentObject
>();
3555 MOZ_ASSERT(globalLexical
->isGlobal());
3557 JSObject
* result
= nullptr;
3558 if (Maybe
<PropertyInfo
> prop
= globalLexical
->lookup(cx_
, id
)) {
3559 // If this is an uninitialized lexical or a const, we need to return a
3560 // RuntimeLexicalErrorObject.
3561 if (globalLexical
->getSlot(prop
->slot()).isMagic() || !prop
->writable()) {
3562 return AttachDecision::NoAction
;
3564 result
= globalLexical
;
3566 result
= &globalLexical
->global();
3569 if (result
== globalLexical
) {
3570 // Lexical bindings are non-configurable so we can just return the
3572 writer
.loadObjectResult(objId
);
3574 // If the property exists on the global and is non-configurable, it cannot
3575 // be shadowed by the lexical scope so we can just return the global without
3577 Maybe
<PropertyInfo
> prop
= result
->as
<GlobalObject
>().lookup(cx_
, id
);
3578 if (prop
.isNothing() || prop
->configurable()) {
3579 writer
.guardShape(objId
, globalLexical
->shape());
3581 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3582 writer
.loadObjectResult(globalId
);
3584 writer
.returnFromIC();
3586 trackAttached("BindName.GlobalName");
3587 return AttachDecision::Attach
;
3590 AttachDecision
BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3592 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3593 return AttachDecision::NoAction
;
3596 JSObject
* env
= env_
;
3597 Maybe
<PropertyInfo
> prop
;
3599 if (!env
->is
<GlobalObject
>() && !env
->is
<EnvironmentObject
>()) {
3600 return AttachDecision::NoAction
;
3602 if (env
->is
<WithEnvironmentObject
>()) {
3603 return AttachDecision::NoAction
;
3606 // When we reach an unqualified variables object (like the global) we
3607 // have to stop looking and return that object.
3608 if (env
->isUnqualifiedVarObj()) {
3612 // Check for an 'own' property on the env. There is no need to
3613 // check the prototype as non-with scopes do not inherit properties
3614 // from any prototype.
3615 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3616 if (prop
.isSome()) {
3620 env
= env
->enclosingEnvironment();
3623 // If this is an uninitialized lexical or a const, we need to return a
3624 // RuntimeLexicalErrorObject.
3625 auto* holder
= &env
->as
<NativeObject
>();
3626 if (prop
.isSome() && holder
->is
<EnvironmentObject
>() &&
3627 (holder
->getSlot(prop
->slot()).isMagic() || !prop
->writable())) {
3628 return AttachDecision::NoAction
;
3631 ObjOperandId lastObjId
= objId
;
3634 if (NeedEnvironmentShapeGuard(cx_
, env
) && !env
->is
<GlobalObject
>()) {
3635 writer
.guardShape(lastObjId
, env
->shape());
3638 if (env
== holder
) {
3642 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3643 env
= env
->enclosingEnvironment();
3646 if (prop
.isSome() && holder
->is
<EnvironmentObject
>()) {
3647 ValOperandId valId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3648 writer
.guardIsNotUninitializedLexical(valId
);
3651 writer
.loadObjectResult(lastObjId
);
3652 writer
.returnFromIC();
3654 trackAttached("BindName.EnvironmentName");
3655 return AttachDecision::Attach
;
3658 void BindNameIRGenerator::trackAttached(const char* name
) {
3659 stubName_
= name
? name
: "NotAttached";
3660 #ifdef JS_CACHEIR_SPEW
3661 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3662 sp
.valueProperty("base", ObjectValue(*env_
));
3663 sp
.valueProperty("property", StringValue(name_
));
3668 HasPropIRGenerator::HasPropIRGenerator(JSContext
* cx
, HandleScript script
,
3669 jsbytecode
* pc
, ICState state
,
3670 CacheKind cacheKind
, HandleValue idVal
,
3672 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
3674 AttachDecision
HasPropIRGenerator::tryAttachDense(HandleObject obj
,
3677 Int32OperandId indexId
) {
3678 if (!obj
->is
<NativeObject
>()) {
3679 return AttachDecision::NoAction
;
3682 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3683 if (!nobj
->containsDenseElement(index
)) {
3684 return AttachDecision::NoAction
;
3687 if (mode_
== ICState::Mode::Megamorphic
) {
3688 writer
.guardIsNativeObject(objId
);
3690 // Guard shape to ensure object class is NativeObject.
3691 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3693 writer
.loadDenseElementExistsResult(objId
, indexId
);
3694 writer
.returnFromIC();
3696 trackAttached("HasProp.Dense");
3697 return AttachDecision::Attach
;
3700 AttachDecision
HasPropIRGenerator::tryAttachDenseHole(HandleObject obj
,
3703 Int32OperandId indexId
) {
3704 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3705 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3707 if (!obj
->is
<NativeObject
>()) {
3708 return AttachDecision::NoAction
;
3711 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3712 if (nobj
->containsDenseElement(index
)) {
3713 return AttachDecision::NoAction
;
3715 if (!CanAttachDenseElementHole(nobj
, ownProp
)) {
3716 return AttachDecision::NoAction
;
3719 // Guard shape to ensure class is NativeObject and to prevent non-dense
3720 // elements being added. Also ensures prototype doesn't change if dynamic
3721 // checks aren't emitted.
3722 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3724 // Generate prototype guards if needed. This includes monitoring that
3725 // properties were not added in the chain.
3727 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3728 /* alwaysGuardFirstProto = */ false);
3731 writer
.loadDenseElementHoleExistsResult(objId
, indexId
);
3732 writer
.returnFromIC();
3734 trackAttached("HasProp.DenseHole");
3735 return AttachDecision::Attach
;
3738 AttachDecision
HasPropIRGenerator::tryAttachSparse(HandleObject obj
,
3740 Int32OperandId indexId
) {
3741 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3742 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3744 if (!obj
->is
<NativeObject
>()) {
3745 return AttachDecision::NoAction
;
3747 auto* nobj
= &obj
->as
<NativeObject
>();
3749 if (!nobj
->isIndexed()) {
3750 return AttachDecision::NoAction
;
3752 if (!CanAttachDenseElementHole(nobj
, ownProp
, AllowIndexedReceiver::Yes
)) {
3753 return AttachDecision::NoAction
;
3756 // Guard that this is a native object.
3757 writer
.guardIsNativeObject(objId
);
3759 // Generate prototype guards if needed. This includes monitoring that
3760 // properties were not added in the chain.
3762 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3763 /* alwaysGuardFirstProto = */ true);
3766 // Because of the prototype guard we know that the prototype chain
3767 // does not include any dense or sparse (i.e indexed) properties.
3768 writer
.callObjectHasSparseElementResult(objId
, indexId
);
3769 writer
.returnFromIC();
3771 trackAttached("HasProp.Sparse");
3772 return AttachDecision::Attach
;
3775 AttachDecision
HasPropIRGenerator::tryAttachArgumentsObjectArg(
3776 HandleObject obj
, ObjOperandId objId
, Int32OperandId indexId
) {
3777 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3778 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3780 if (!obj
->is
<ArgumentsObject
>()) {
3781 return AttachDecision::NoAction
;
3783 auto* args
= &obj
->as
<ArgumentsObject
>();
3785 // No elements must have been overridden or deleted.
3786 if (args
->hasOverriddenElement()) {
3787 return AttachDecision::NoAction
;
3790 if (!CanAttachDenseElementHole(args
, ownProp
, AllowIndexedReceiver::Yes
,
3791 AllowExtraReceiverProperties::Yes
)) {
3792 return AttachDecision::NoAction
;
3795 if (args
->is
<MappedArgumentsObject
>()) {
3796 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3798 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
3799 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
3803 GeneratePrototypeHoleGuards(writer
, args
, objId
,
3804 /* alwaysGuardFirstProto = */ true);
3807 writer
.loadArgumentsObjectArgExistsResult(objId
, indexId
);
3808 writer
.returnFromIC();
3810 trackAttached("HasProp.ArgumentsObjectArg");
3811 return AttachDecision::Attach
;
3814 AttachDecision
HasPropIRGenerator::tryAttachNamedProp(HandleObject obj
,
3817 ValOperandId keyId
) {
3818 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3820 NativeObject
* holder
= nullptr;
3821 PropertyResult prop
;
3824 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
3825 return AttachDecision::NoAction
;
3828 holder
= &obj
->as
<NativeObject
>();
3830 if (!LookupPropertyPure(cx_
, obj
, key
, &holder
, &prop
)) {
3831 return AttachDecision::NoAction
;
3834 if (prop
.isNotFound()) {
3835 return AttachDecision::NoAction
;
3837 auto* nobj
= &obj
->as
<NativeObject
>();
3839 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
3840 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
, holder
));
3842 return AttachDecision::NoAction
;
3845 AttachDecision
HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId
,
3846 ValOperandId keyId
) {
3847 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3849 if (mode_
!= ICState::Mode::Megamorphic
) {
3850 return AttachDecision::NoAction
;
3853 writer
.megamorphicHasPropResult(objId
, keyId
, hasOwn
);
3854 writer
.returnFromIC();
3855 trackAttached("HasProp.Megamorphic");
3856 return AttachDecision::Attach
;
3859 AttachDecision
HasPropIRGenerator::tryAttachNative(NativeObject
* obj
,
3860 ObjOperandId objId
, jsid key
,
3862 PropertyResult prop
,
3863 NativeObject
* holder
) {
3864 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
3866 if (!prop
.isNativeProperty()) {
3867 return AttachDecision::NoAction
;
3870 emitIdGuard(keyId
, idVal_
, key
);
3871 EmitReadSlotGuard(writer
, obj
, holder
, objId
);
3872 writer
.loadBooleanResult(true);
3873 writer
.returnFromIC();
3875 trackAttached("HasProp.Native");
3876 return AttachDecision::Attach
;
3879 AttachDecision
HasPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
3881 ValOperandId keyId
) {
3882 if (!obj
->is
<TypedArrayObject
>()) {
3883 return AttachDecision::NoAction
;
3887 if (!ValueIsInt64Index(idVal_
, &index
)) {
3888 return AttachDecision::NoAction
;
3891 writer
.guardIsTypedArray(objId
);
3892 IntPtrOperandId intPtrIndexId
=
3893 guardToIntPtrIndex(idVal_
, keyId
, /* supportOOB = */ true);
3894 writer
.loadTypedArrayElementExistsResult(objId
, intPtrIndexId
);
3895 writer
.returnFromIC();
3897 trackAttached("HasProp.TypedArrayObject");
3898 return AttachDecision::Attach
;
3901 AttachDecision
HasPropIRGenerator::tryAttachSlotDoesNotExist(
3902 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
3903 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3905 emitIdGuard(keyId
, idVal_
, key
);
3907 TestMatchingNativeReceiver(writer
, obj
, objId
);
3909 EmitMissingPropGuard(writer
, obj
, objId
);
3911 writer
.loadBooleanResult(false);
3912 writer
.returnFromIC();
3914 trackAttached("HasProp.DoesNotExist");
3915 return AttachDecision::Attach
;
3918 AttachDecision
HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj
,
3921 ValOperandId keyId
) {
3922 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3924 // Check that property doesn't exist on |obj| or it's prototype chain. These
3925 // checks allow NativeObjects with a NativeObject prototype chain. They return
3926 // NoAction if unknown such as resolve hooks or proxies.
3928 if (!CheckHasNoSuchOwnProperty(cx_
, obj
, key
)) {
3929 return AttachDecision::NoAction
;
3932 if (!CheckHasNoSuchProperty(cx_
, obj
, key
)) {
3933 return AttachDecision::NoAction
;
3936 auto* nobj
= &obj
->as
<NativeObject
>();
3938 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
3939 TRY_ATTACH(tryAttachSlotDoesNotExist(nobj
, objId
, key
, keyId
));
3941 return AttachDecision::NoAction
;
3944 AttachDecision
HasPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3946 ValOperandId keyId
) {
3947 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3949 if (!obj
->is
<ProxyObject
>()) {
3950 return AttachDecision::NoAction
;
3953 writer
.guardIsProxy(objId
);
3954 writer
.proxyHasPropResult(objId
, keyId
, hasOwn
);
3955 writer
.returnFromIC();
3957 trackAttached("HasProp.ProxyElement");
3958 return AttachDecision::Attach
;
3961 AttachDecision
HasPropIRGenerator::tryAttachStub() {
3962 MOZ_ASSERT(cacheKind_
== CacheKind::In
|| cacheKind_
== CacheKind::HasOwn
);
3964 AutoAssertNoPendingException
aanpe(cx_
);
3966 // NOTE: Argument order is PROPERTY, OBJECT
3967 ValOperandId
keyId(writer
.setInputOperandId(0));
3968 ValOperandId
valId(writer
.setInputOperandId(1));
3970 if (!val_
.isObject()) {
3971 trackAttached(IRGenerator::NotAttached
);
3972 return AttachDecision::NoAction
;
3974 RootedObject
obj(cx_
, &val_
.toObject());
3975 ObjOperandId objId
= writer
.guardToObject(valId
);
3978 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, keyId
));
3982 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
3983 cx_
->clearPendingException();
3984 return AttachDecision::NoAction
;
3988 TRY_ATTACH(tryAttachNamedProp(obj
, objId
, id
, keyId
));
3989 TRY_ATTACH(tryAttachDoesNotExist(obj
, objId
, id
, keyId
));
3991 trackAttached(IRGenerator::NotAttached
);
3992 return AttachDecision::NoAction
;
3995 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, keyId
));
3998 Int32OperandId indexId
;
3999 if (maybeGuardInt32Index(idVal_
, keyId
, &index
, &indexId
)) {
4000 TRY_ATTACH(tryAttachDense(obj
, objId
, index
, indexId
));
4001 TRY_ATTACH(tryAttachDenseHole(obj
, objId
, index
, indexId
));
4002 TRY_ATTACH(tryAttachSparse(obj
, objId
, indexId
));
4003 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, indexId
));
4005 trackAttached(IRGenerator::NotAttached
);
4006 return AttachDecision::NoAction
;
4009 trackAttached(IRGenerator::NotAttached
);
4010 return AttachDecision::NoAction
;
4013 void HasPropIRGenerator::trackAttached(const char* name
) {
4014 stubName_
= name
? name
: "NotAttached";
4015 #ifdef JS_CACHEIR_SPEW
4016 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4017 sp
.valueProperty("base", val_
);
4018 sp
.valueProperty("property", idVal_
);
4023 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
4024 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
4025 CacheKind cacheKind
, HandleValue idVal
, HandleValue val
)
4026 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {
4027 MOZ_ASSERT(idVal
.isSymbol() && idVal
.toSymbol()->isPrivateName());
4030 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachStub() {
4031 AutoAssertNoPendingException
aanpe(cx_
);
4033 ValOperandId
valId(writer
.setInputOperandId(0));
4034 ValOperandId
keyId(writer
.setInputOperandId(1));
4036 if (!val_
.isObject()) {
4037 trackAttached(IRGenerator::NotAttached
);
4038 return AttachDecision::NoAction
;
4040 JSObject
* obj
= &val_
.toObject();
4041 ObjOperandId objId
= writer
.guardToObject(valId
);
4042 PropertyKey key
= PropertyKey::Symbol(idVal_
.toSymbol());
4044 ThrowCondition condition
;
4045 ThrowMsgKind msgKind
;
4046 GetCheckPrivateFieldOperands(pc_
, &condition
, &msgKind
);
4048 PropertyResult prop
;
4049 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
4050 return AttachDecision::NoAction
;
4053 if (CheckPrivateFieldWillThrow(condition
, prop
.isFound())) {
4054 // Don't attach a stub if the operation will throw.
4055 return AttachDecision::NoAction
;
4058 auto* nobj
= &obj
->as
<NativeObject
>();
4060 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
));
4062 return AttachDecision::NoAction
;
4065 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachNative(
4066 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
,
4067 PropertyResult prop
) {
4068 MOZ_ASSERT(prop
.isNativeProperty() || prop
.isNotFound());
4070 emitIdGuard(keyId
, idVal_
, key
);
4071 TestMatchingNativeReceiver(writer
, obj
, objId
);
4072 writer
.loadBooleanResult(prop
.isFound());
4073 writer
.returnFromIC();
4075 trackAttached("CheckPrivateField.Native");
4076 return AttachDecision::Attach
;
4079 void CheckPrivateFieldIRGenerator::trackAttached(const char* name
) {
4080 stubName_
= name
? name
: "NotAttached";
4081 #ifdef JS_CACHEIR_SPEW
4082 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4083 sp
.valueProperty("base", val_
);
4084 sp
.valueProperty("property", idVal_
);
4089 bool IRGenerator::maybeGuardInt32Index(const Value
& index
, ValOperandId indexId
,
4090 uint32_t* int32Index
,
4091 Int32OperandId
* int32IndexId
) {
4092 if (index
.isNumber()) {
4093 int32_t indexSigned
;
4094 if (index
.isInt32()) {
4095 indexSigned
= index
.toInt32();
4097 // We allow negative zero here.
4098 if (!mozilla::NumberEqualsInt32(index
.toDouble(), &indexSigned
)) {
4103 if (indexSigned
< 0) {
4107 *int32Index
= uint32_t(indexSigned
);
4108 *int32IndexId
= writer
.guardToInt32Index(indexId
);
4112 if (index
.isString()) {
4113 int32_t indexSigned
= GetIndexFromString(index
.toString());
4114 if (indexSigned
< 0) {
4118 StringOperandId strId
= writer
.guardToString(indexId
);
4119 *int32Index
= uint32_t(indexSigned
);
4120 *int32IndexId
= writer
.guardStringToIndex(strId
);
4127 SetPropIRGenerator::SetPropIRGenerator(JSContext
* cx
, HandleScript script
,
4128 jsbytecode
* pc
, CacheKind cacheKind
,
4129 ICState state
, HandleValue lhsVal
,
4130 HandleValue idVal
, HandleValue rhsVal
)
4131 : IRGenerator(cx
, script
, pc
, cacheKind
, state
),
4136 AttachDecision
SetPropIRGenerator::tryAttachStub() {
4137 AutoAssertNoPendingException
aanpe(cx_
);
4139 ValOperandId
objValId(writer
.setInputOperandId(0));
4140 ValOperandId rhsValId
;
4141 if (cacheKind_
== CacheKind::SetProp
) {
4142 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
4144 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4145 MOZ_ASSERT(setElemKeyValueId().id() == 1);
4146 writer
.setInputOperandId(1);
4147 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
4152 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4153 cx_
->clearPendingException();
4154 return AttachDecision::NoAction
;
4157 if (lhsVal_
.isObject()) {
4158 RootedObject
obj(cx_
, &lhsVal_
.toObject());
4160 ObjOperandId objId
= writer
.guardToObject(objValId
);
4161 if (IsPropertySetOp(JSOp(*pc_
))) {
4162 TRY_ATTACH(tryAttachMegamorphicSetElement(obj
, objId
, rhsValId
));
4165 TRY_ATTACH(tryAttachNativeSetSlot(obj
, objId
, id
, rhsValId
));
4166 if (IsPropertySetOp(JSOp(*pc_
))) {
4167 TRY_ATTACH(tryAttachSetArrayLength(obj
, objId
, id
, rhsValId
));
4168 TRY_ATTACH(tryAttachSetter(obj
, objId
, id
, rhsValId
));
4169 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
, rhsValId
));
4170 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, rhsValId
));
4171 TRY_ATTACH(tryAttachMegamorphicSetSlot(obj
, objId
, id
, rhsValId
));
4173 if (canAttachAddSlotStub(obj
, id
)) {
4174 deferType_
= DeferType::AddSlot
;
4175 return AttachDecision::Deferred
;
4177 return AttachDecision::NoAction
;
4180 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4182 if (IsPropertySetOp(JSOp(*pc_
))) {
4183 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, rhsValId
));
4186 TRY_ATTACH(tryAttachSetTypedArrayElement(obj
, objId
, rhsValId
));
4189 Int32OperandId indexId
;
4190 if (maybeGuardInt32Index(idVal_
, setElemKeyValueId(), &index
, &indexId
)) {
4192 tryAttachSetDenseElement(obj
, objId
, index
, indexId
, rhsValId
));
4194 tryAttachSetDenseElementHole(obj
, objId
, index
, indexId
, rhsValId
));
4195 TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj
, objId
, index
, indexId
,
4197 return AttachDecision::NoAction
;
4200 return AttachDecision::NoAction
;
4203 static void EmitStoreSlotAndReturn(CacheIRWriter
& writer
, ObjOperandId objId
,
4204 NativeObject
* nobj
, PropertyInfo prop
,
4205 ValOperandId rhsId
) {
4206 if (nobj
->isFixedSlot(prop
.slot())) {
4207 size_t offset
= NativeObject::getFixedSlotOffset(prop
.slot());
4208 writer
.storeFixedSlot(objId
, offset
, rhsId
);
4210 size_t offset
= nobj
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
4211 writer
.storeDynamicSlot(objId
, offset
, rhsId
);
4213 writer
.returnFromIC();
4216 static Maybe
<PropertyInfo
> LookupShapeForSetSlot(JSOp op
, NativeObject
* obj
,
4218 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(id
);
4219 if (prop
.isNothing() || !prop
->isDataProperty() || !prop
->writable()) {
4220 return mozilla::Nothing();
4223 // If this is a property init operation, the property's attributes may have to
4224 // be changed too, so make sure the current flags match.
4225 if (IsPropertyInitOp(op
)) {
4226 // Don't support locked init operations.
4227 if (IsLockedInitOp(op
)) {
4228 return mozilla::Nothing();
4231 // Can't redefine a non-configurable property.
4232 if (!prop
->configurable()) {
4233 return mozilla::Nothing();
4236 // Make sure the enumerable flag matches the init operation.
4237 if (IsHiddenInitOp(op
) == prop
->enumerable()) {
4238 return mozilla::Nothing();
4245 static bool CanAttachNativeSetSlot(JSOp op
, JSObject
* obj
, PropertyKey id
,
4246 Maybe
<PropertyInfo
>* prop
) {
4247 if (!obj
->is
<NativeObject
>()) {
4251 if (Watchtower::watchesPropertyModification(&obj
->as
<NativeObject
>())) {
4255 *prop
= LookupShapeForSetSlot(op
, &obj
->as
<NativeObject
>(), id
);
4256 return prop
->isSome();
4259 // There is no need to guard on the shape. Global lexical bindings are
4260 // non-configurable and can not be shadowed.
4261 static bool IsGlobalLexicalSetGName(JSOp op
, NativeObject
* obj
,
4262 PropertyInfo prop
) {
4263 // Ensure that the env can't change.
4264 if (op
!= JSOp::SetGName
&& op
!= JSOp::StrictSetGName
) {
4268 if (!obj
->is
<GlobalLexicalEnvironmentObject
>()) {
4272 // Uninitialized let bindings use a RuntimeLexicalErrorObject.
4273 MOZ_ASSERT(!obj
->getSlot(prop
.slot()).isMagic());
4274 MOZ_ASSERT(prop
.writable());
4275 MOZ_ASSERT(!prop
.configurable());
4279 AttachDecision
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj
,
4282 ValOperandId rhsId
) {
4283 Maybe
<PropertyInfo
> prop
;
4284 if (!CanAttachNativeSetSlot(JSOp(*pc_
), obj
, id
, &prop
)) {
4285 return AttachDecision::NoAction
;
4288 if (mode_
== ICState::Mode::Megamorphic
&& cacheKind_
== CacheKind::SetProp
&&
4289 IsPropertySetOp(JSOp(*pc_
))) {
4290 return AttachDecision::NoAction
;
4293 maybeEmitIdGuard(id
);
4295 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4296 if (!IsGlobalLexicalSetGName(JSOp(*pc_
), nobj
, *prop
)) {
4297 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4299 EmitStoreSlotAndReturn(writer
, objId
, nobj
, *prop
, rhsId
);
4301 trackAttached("SetProp.NativeSlot");
4302 return AttachDecision::Attach
;
4305 OperandId
IRGenerator::emitNumericGuard(ValOperandId valId
, Scalar::Type type
) {
4310 case Scalar::Uint16
:
4312 case Scalar::Uint32
:
4313 return writer
.guardToInt32ModUint32(valId
);
4315 case Scalar::Float32
:
4316 case Scalar::Float64
:
4317 return writer
.guardIsNumber(valId
);
4319 case Scalar::Uint8Clamped
:
4320 return writer
.guardToUint8Clamped(valId
);
4322 case Scalar::BigInt64
:
4323 case Scalar::BigUint64
:
4324 return writer
.guardToBigInt(valId
);
4326 case Scalar::MaxTypedArrayViewType
:
4328 case Scalar::Simd128
:
4331 MOZ_CRASH("Unsupported TypedArray type");
4334 static bool ValueIsNumeric(Scalar::Type type
, const Value
& val
) {
4335 if (Scalar::isBigIntType(type
)) {
4336 return val
.isBigInt();
4338 return val
.isNumber();
4341 void SetPropIRGenerator::trackAttached(const char* name
) {
4342 stubName_
= name
? name
: "NotAttached";
4343 #ifdef JS_CACHEIR_SPEW
4344 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4345 sp
.opcodeProperty("op", JSOp(*pc_
));
4346 sp
.valueProperty("base", lhsVal_
);
4347 sp
.valueProperty("property", idVal_
);
4348 sp
.valueProperty("value", rhsVal_
);
4353 static bool IsCacheableSetPropCallNative(NativeObject
* obj
,
4354 NativeObject
* holder
,
4355 PropertyInfo prop
) {
4356 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4358 if (!prop
.isAccessorProperty()) {
4362 JSObject
* setterObject
= holder
->getSetter(prop
);
4363 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4367 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4368 if (!setter
.isNativeWithoutJitEntry()) {
4372 if (setter
.isClassConstructor()) {
4379 static bool IsCacheableSetPropCallScripted(NativeObject
* obj
,
4380 NativeObject
* holder
,
4381 PropertyInfo prop
) {
4382 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4384 if (!prop
.isAccessorProperty()) {
4388 JSObject
* setterObject
= holder
->getSetter(prop
);
4389 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4393 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4394 if (setter
.isClassConstructor()) {
4398 // Scripted functions and natives with JIT entry can use the scripted path.
4399 return setter
.hasJitEntry();
4402 static bool CanAttachSetter(JSContext
* cx
, jsbytecode
* pc
, JSObject
* obj
,
4403 PropertyKey id
, NativeObject
** holder
,
4404 Maybe
<PropertyInfo
>* propInfo
) {
4405 // Don't attach a setter stub for ops like JSOp::InitElem.
4406 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
4408 PropertyResult prop
;
4409 if (!LookupPropertyPure(cx
, obj
, id
, holder
, &prop
)) {
4412 auto* nobj
= &obj
->as
<NativeObject
>();
4414 if (!prop
.isNativeProperty()) {
4418 if (!IsCacheableSetPropCallScripted(nobj
, *holder
, prop
.propertyInfo()) &&
4419 !IsCacheableSetPropCallNative(nobj
, *holder
, prop
.propertyInfo())) {
4423 *propInfo
= mozilla::Some(prop
.propertyInfo());
4427 static void EmitCallSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4428 NativeObject
* obj
, NativeObject
* holder
,
4429 PropertyInfo prop
, ObjOperandId receiverId
,
4430 ValOperandId rhsId
) {
4431 JSFunction
* target
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4432 bool sameRealm
= cx
->realm() == target
->realm();
4434 if (target
->isNativeWithoutJitEntry()) {
4435 MOZ_ASSERT(IsCacheableSetPropCallNative(obj
, holder
, prop
));
4436 writer
.callNativeSetter(receiverId
, target
, rhsId
, sameRealm
);
4437 writer
.returnFromIC();
4441 MOZ_ASSERT(IsCacheableSetPropCallScripted(obj
, holder
, prop
));
4442 writer
.callScriptedSetter(receiverId
, target
, rhsId
, sameRealm
);
4443 writer
.returnFromIC();
4446 static void EmitCallDOMSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4447 NativeObject
* holder
, PropertyInfo prop
,
4448 ObjOperandId objId
, ValOperandId rhsId
) {
4449 JSFunction
* setter
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4450 MOZ_ASSERT(cx
->realm() == setter
->realm());
4452 writer
.callDOMSetter(objId
, setter
->jitInfo(), rhsId
);
4453 writer
.returnFromIC();
4456 AttachDecision
SetPropIRGenerator::tryAttachSetter(HandleObject obj
,
4459 ValOperandId rhsId
) {
4460 // Don't attach a setter stub for ops like JSOp::InitElem.
4461 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4463 NativeObject
* holder
= nullptr;
4464 Maybe
<PropertyInfo
> prop
;
4465 if (!CanAttachSetter(cx_
, pc_
, obj
, id
, &holder
, &prop
)) {
4466 return AttachDecision::NoAction
;
4468 auto* nobj
= &obj
->as
<NativeObject
>();
4470 bool needsWindowProxy
=
4471 IsWindow(nobj
) && SetterNeedsWindowProxyThis(holder
, *prop
);
4473 maybeEmitIdGuard(id
);
4475 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
4476 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
4477 // require outerizing).
4478 if (mode_
== ICState::Mode::Specialized
|| IsWindow(nobj
)) {
4479 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4481 if (nobj
!= holder
) {
4482 GeneratePrototypeGuards(writer
, nobj
, holder
, objId
);
4484 // Guard on the holder's shape.
4485 ObjOperandId holderId
= writer
.loadObject(holder
);
4486 TestMatchingHolder(writer
, holder
, holderId
);
4488 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4489 /* holderIsConstant = */ true);
4491 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, objId
);
4494 GetterSetter
* gs
= holder
->getGetterSetter(*prop
);
4495 writer
.guardHasGetterSetter(objId
, id
, gs
);
4498 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Setter
, nobj
, holder
, *prop
,
4500 MOZ_ASSERT(!needsWindowProxy
);
4501 EmitCallDOMSetterNoGuards(cx_
, writer
, holder
, *prop
, objId
, rhsId
);
4503 trackAttached("SetProp.DOMSetter");
4504 return AttachDecision::Attach
;
4507 ObjOperandId receiverId
;
4508 if (needsWindowProxy
) {
4509 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
4510 receiverId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
4514 EmitCallSetterNoGuards(cx_
, writer
, nobj
, holder
, *prop
, receiverId
, rhsId
);
4516 trackAttached("SetProp.Setter");
4517 return AttachDecision::Attach
;
4520 AttachDecision
SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj
,
4523 ValOperandId rhsId
) {
4524 // Don't attach an array length stub for ops like JSOp::InitElem.
4525 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4527 if (!obj
->is
<ArrayObject
>() || !id
.isAtom(cx_
->names().length
) ||
4528 !obj
->as
<ArrayObject
>().lengthIsWritable()) {
4529 return AttachDecision::NoAction
;
4532 maybeEmitIdGuard(id
);
4533 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
4534 writer
.callSetArrayLength(objId
, IsStrictSetPC(pc_
), rhsId
);
4535 writer
.returnFromIC();
4537 trackAttached("SetProp.ArrayLength");
4538 return AttachDecision::Attach
;
4541 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElement(
4542 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4543 Int32OperandId indexId
, ValOperandId rhsId
) {
4544 if (!obj
->is
<NativeObject
>()) {
4545 return AttachDecision::NoAction
;
4548 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4549 if (!nobj
->containsDenseElement(index
) || nobj
->denseElementsAreFrozen()) {
4550 return AttachDecision::NoAction
;
4553 // Setting holes requires extra code for marking the elements non-packed.
4554 MOZ_ASSERT(!rhsVal_
.isMagic(JS_ELEMENTS_HOLE
));
4556 JSOp op
= JSOp(*pc_
);
4558 // We don't currently emit locked init for any indexed properties.
4559 MOZ_ASSERT(!IsLockedInitOp(op
));
4561 // We don't currently emit hidden init for any existing indexed properties.
4562 MOZ_ASSERT(!IsHiddenInitOp(op
));
4564 // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
4565 // the elements are sealed, we have to throw an exception. Note that we have
4566 // to check !isExtensible instead of denseElementsAreSealed because sealing
4567 // a (non-extensible) object does not necessarily trigger a Shape change.
4568 if (IsPropertyInitOp(op
) && !nobj
->isExtensible()) {
4569 return AttachDecision::NoAction
;
4572 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4574 writer
.storeDenseElement(objId
, indexId
, rhsId
);
4575 writer
.returnFromIC();
4577 trackAttached("SetProp.DenseElement");
4578 return AttachDecision::Attach
;
4581 static bool CanAttachAddElement(NativeObject
* obj
, bool isInit
,
4582 AllowIndexedReceiver allowIndexedReceiver
) {
4583 // Make sure the receiver doesn't have any indexed properties and that such
4584 // properties can't appear without a shape change.
4585 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
4590 // This check is also relevant for the receiver object.
4591 const JSClass
* clasp
= obj
->getClass();
4592 if (clasp
!= &ArrayObject::class_
&&
4593 (clasp
->getAddProperty() || clasp
->getResolve() ||
4594 clasp
->getOpsLookupProperty() || clasp
->getOpsSetProperty())) {
4598 // If we're initializing a property instead of setting one, the objects
4599 // on the prototype are not relevant.
4604 JSObject
* proto
= obj
->staticPrototype();
4609 if (!proto
->is
<NativeObject
>()) {
4613 NativeObject
* nproto
= &proto
->as
<NativeObject
>();
4614 if (nproto
->isIndexed()) {
4618 // We have to make sure the proto has no non-writable (frozen) elements
4619 // because we're not allowed to shadow them.
4620 if (nproto
->denseElementsAreFrozen() &&
4621 nproto
->getDenseInitializedLength() > 0) {
4631 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElementHole(
4632 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4633 Int32OperandId indexId
, ValOperandId rhsId
) {
4634 if (!obj
->is
<NativeObject
>()) {
4635 return AttachDecision::NoAction
;
4638 // Setting holes requires extra code for marking the elements non-packed.
4639 if (rhsVal_
.isMagic(JS_ELEMENTS_HOLE
)) {
4640 return AttachDecision::NoAction
;
4643 JSOp op
= JSOp(*pc_
);
4644 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4646 // We don't currently emit locked init for any indexed properties.
4647 MOZ_ASSERT(!IsLockedInitOp(op
));
4649 // Hidden init can be emitted for absent indexed properties.
4650 if (IsHiddenInitOp(op
)) {
4651 MOZ_ASSERT(op
== JSOp::InitHiddenElem
);
4652 return AttachDecision::NoAction
;
4655 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4656 if (!nobj
->isExtensible()) {
4657 return AttachDecision::NoAction
;
4660 MOZ_ASSERT(!nobj
->denseElementsAreFrozen(),
4661 "Extensible objects should not have frozen elements");
4663 uint32_t initLength
= nobj
->getDenseInitializedLength();
4665 // Optimize if we're adding an element at initLength or writing to a hole.
4667 // In the case where index > initLength, we need noteHasDenseAdd to be called
4668 // to ensure Ion is aware that writes have occurred to-out-of-bound indexes
4671 // TODO(post-Warp): noteHasDenseAdd (nee: noteArrayWriteHole) no longer exists
4672 bool isAdd
= index
== initLength
;
4673 bool isHoleInBounds
=
4674 index
< initLength
&& !nobj
->containsDenseElement(index
);
4675 if (!isAdd
&& !isHoleInBounds
) {
4676 return AttachDecision::NoAction
;
4679 // Can't add new elements to arrays with non-writable length.
4680 if (isAdd
&& nobj
->is
<ArrayObject
>() &&
4681 !nobj
->as
<ArrayObject
>().lengthIsWritable()) {
4682 return AttachDecision::NoAction
;
4685 // Typed arrays don't have dense elements.
4686 if (nobj
->is
<TypedArrayObject
>()) {
4687 return AttachDecision::NoAction
;
4690 // Check for other indexed properties or class hooks.
4691 if (!CanAttachAddElement(nobj
, IsPropertyInitOp(op
),
4692 AllowIndexedReceiver::No
)) {
4693 return AttachDecision::NoAction
;
4696 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4698 // Also shape guard the proto chain, unless this is an InitElem.
4699 if (IsPropertySetOp(op
)) {
4700 ShapeGuardProtoChain(writer
, nobj
, objId
);
4703 writer
.storeDenseElementHole(objId
, indexId
, rhsId
, isAdd
);
4704 writer
.returnFromIC();
4706 trackAttached(isAdd
? "AddDenseElement" : "StoreDenseElementHole");
4707 return AttachDecision::Attach
;
4710 // Add an IC for adding or updating a sparse element.
4711 AttachDecision
SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
4712 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4713 Int32OperandId indexId
, ValOperandId rhsId
) {
4714 JSOp op
= JSOp(*pc_
);
4715 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4717 if (op
!= JSOp::SetElem
&& op
!= JSOp::StrictSetElem
) {
4718 return AttachDecision::NoAction
;
4721 if (!obj
->is
<NativeObject
>()) {
4722 return AttachDecision::NoAction
;
4724 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4726 // We cannot attach a stub to a non-extensible object
4727 if (!nobj
->isExtensible()) {
4728 return AttachDecision::NoAction
;
4731 // Stub doesn't handle negative indices.
4732 if (index
> INT32_MAX
) {
4733 return AttachDecision::NoAction
;
4736 // The index must not be for a dense element.
4737 if (nobj
->containsDenseElement(index
)) {
4738 return AttachDecision::NoAction
;
4741 // Only handle ArrayObject and PlainObject in this stub.
4742 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
4743 return AttachDecision::NoAction
;
4746 // Don't attach if we're adding to an array with non-writable length.
4747 if (nobj
->is
<ArrayObject
>()) {
4748 ArrayObject
* aobj
= &nobj
->as
<ArrayObject
>();
4749 bool isAdd
= (index
>= aobj
->length());
4750 if (isAdd
&& !aobj
->lengthIsWritable()) {
4751 return AttachDecision::NoAction
;
4755 // Check for class hooks or indexed properties on the prototype chain that
4756 // we're not allowed to shadow.
4757 if (!CanAttachAddElement(nobj
, /* isInit = */ false,
4758 AllowIndexedReceiver::Yes
)) {
4759 return AttachDecision::NoAction
;
4762 // Ensure that obj is an ArrayObject or PlainObject.
4763 if (nobj
->is
<ArrayObject
>()) {
4764 writer
.guardClass(objId
, GuardClassKind::Array
);
4766 MOZ_ASSERT(nobj
->is
<PlainObject
>());
4767 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
4770 // The helper we are going to call only applies to non-dense elements.
4771 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
4773 // Guard extensible: We may be trying to add a new element, and so we'd best
4774 // be able to do so safely.
4775 writer
.guardIsExtensible(objId
);
4777 // Ensures we are able to efficiently able to map to an integral jsid.
4778 writer
.guardInt32IsNonNegative(indexId
);
4780 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
4781 // Guard the prototype of the receiver explicitly, because the receiver's
4782 // shape is not being guarded as a proxy for that.
4783 GuardReceiverProto(writer
, nobj
, objId
);
4785 // Dense elements may appear on the prototype chain (and prototypes may
4786 // have a different notion of which elements are dense), but they can
4787 // only be data properties, so our specialized Set handler is ok to bind
4789 if (IsPropertySetOp(op
)) {
4790 ShapeGuardProtoChain(writer
, nobj
, objId
);
4793 // Ensure that if we're adding an element to the object, the object's
4794 // length is writable.
4795 if (nobj
->is
<ArrayObject
>()) {
4796 writer
.guardIndexIsValidUpdateOrAdd(objId
, indexId
);
4799 writer
.callAddOrUpdateSparseElementHelper(
4800 objId
, indexId
, rhsId
,
4801 /* strict = */ op
== JSOp::StrictSetElem
);
4802 writer
.returnFromIC();
4804 trackAttached("SetProp.AddOrUpdateSparseElement");
4805 return AttachDecision::Attach
;
4808 AttachDecision
SetPropIRGenerator::tryAttachSetTypedArrayElement(
4809 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
4810 if (!obj
->is
<TypedArrayObject
>()) {
4811 return AttachDecision::NoAction
;
4813 if (!idVal_
.isNumber()) {
4814 return AttachDecision::NoAction
;
4817 TypedArrayObject
* tarr
= &obj
->as
<TypedArrayObject
>();
4818 Scalar::Type elementType
= tarr
->type();
4820 // Don't attach if the input type doesn't match the guard added below.
4821 if (!ValueIsNumeric(elementType
, rhsVal_
)) {
4822 return AttachDecision::NoAction
;
4825 bool handleOOB
= false;
4827 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
4828 uint64_t(indexInt64
) >= tarr
->length()) {
4832 JSOp op
= JSOp(*pc_
);
4834 // The only expected property init operation is InitElem.
4835 MOZ_ASSERT_IF(IsPropertyInitOp(op
), op
== JSOp::InitElem
);
4837 // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
4838 if (handleOOB
&& IsPropertyInitOp(op
)) {
4839 return AttachDecision::NoAction
;
4842 writer
.guardShapeForClass(objId
, tarr
->shape());
4844 OperandId rhsValId
= emitNumericGuard(rhsId
, elementType
);
4846 ValOperandId keyId
= setElemKeyValueId();
4847 IntPtrOperandId indexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
4849 writer
.storeTypedArrayElement(objId
, elementType
, indexId
, rhsValId
,
4851 writer
.returnFromIC();
4853 trackAttached(handleOOB
? "SetTypedElementOOB" : "SetTypedElement");
4854 return AttachDecision::Attach
;
4857 AttachDecision
SetPropIRGenerator::tryAttachGenericProxy(
4858 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4859 ValOperandId rhsId
, bool handleDOMProxies
) {
4860 // Don't attach a proxy stub for ops like JSOp::InitElem.
4861 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4863 writer
.guardIsProxy(objId
);
4865 if (!handleDOMProxies
) {
4866 // Ensure that the incoming object is not a DOM proxy, so that we can
4867 // get to the specialized stubs. If handleDOMProxies is true, we were
4868 // unable to attach a specialized DOM stub, so we just handle all
4870 writer
.guardIsNotDOMProxy(objId
);
4873 if (cacheKind_
== CacheKind::SetProp
|| mode_
== ICState::Mode::Specialized
) {
4874 maybeEmitIdGuard(id
);
4875 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
4877 // Attach a stub that handles every id.
4878 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4879 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
4880 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
,
4881 IsStrictSetPC(pc_
));
4884 writer
.returnFromIC();
4886 trackAttached("SetProp.GenericProxy");
4887 return AttachDecision::Attach
;
4890 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyShadowed(
4891 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4892 ValOperandId rhsId
) {
4893 // Don't attach a proxy stub for ops like JSOp::InitElem.
4894 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4896 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
4898 maybeEmitIdGuard(id
);
4899 TestMatchingProxyReceiver(writer
, obj
, objId
);
4900 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
4901 writer
.returnFromIC();
4903 trackAttached("SetProp.DOMProxyShadowed");
4904 return AttachDecision::Attach
;
4907 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
4908 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4909 ValOperandId rhsId
) {
4910 // Don't attach a proxy stub for ops like JSOp::InitElem.
4911 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4913 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
4915 JSObject
* proto
= obj
->staticPrototype();
4917 return AttachDecision::NoAction
;
4920 NativeObject
* holder
= nullptr;
4921 Maybe
<PropertyInfo
> prop
;
4922 if (!CanAttachSetter(cx_
, pc_
, proto
, id
, &holder
, &prop
)) {
4923 return AttachDecision::NoAction
;
4925 auto* nproto
= &proto
->as
<NativeObject
>();
4927 maybeEmitIdGuard(id
);
4929 // Guard that our proxy (expando) object hasn't started shadowing this
4931 TestMatchingProxyReceiver(writer
, obj
, objId
);
4932 bool canOptimizeMissing
= false;
4933 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
4935 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
4937 // Guard on the holder of the property.
4938 ObjOperandId holderId
= writer
.loadObject(holder
);
4939 TestMatchingHolder(writer
, holder
, holderId
);
4941 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4942 /* holderIsConstant = */ true);
4944 // EmitCallSetterNoGuards expects |obj| to be the object the property is
4945 // on to do some checks. Since we actually looked at proto, and no extra
4946 // guards will be generated, we can just pass that instead.
4947 EmitCallSetterNoGuards(cx_
, writer
, nproto
, holder
, *prop
, objId
, rhsId
);
4949 trackAttached("SetProp.DOMProxyUnshadowed");
4950 return AttachDecision::Attach
;
4953 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyExpando(
4954 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4955 ValOperandId rhsId
) {
4956 // Don't attach a proxy stub for ops like JSOp::InitElem.
4957 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4959 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
4961 Value expandoVal
= GetProxyPrivate(obj
);
4962 JSObject
* expandoObj
;
4963 if (expandoVal
.isObject()) {
4964 expandoObj
= &expandoVal
.toObject();
4966 MOZ_ASSERT(!expandoVal
.isUndefined(),
4967 "How did a missing expando manage to shadow things?");
4968 auto expandoAndGeneration
=
4969 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
4970 MOZ_ASSERT(expandoAndGeneration
);
4971 expandoObj
= &expandoAndGeneration
->expando
.toObject();
4974 Maybe
<PropertyInfo
> prop
;
4975 if (CanAttachNativeSetSlot(JSOp(*pc_
), expandoObj
, id
, &prop
)) {
4976 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
4978 maybeEmitIdGuard(id
);
4979 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
4980 obj
, objId
, expandoVal
, nativeExpandoObj
);
4982 EmitStoreSlotAndReturn(writer
, expandoObjId
, nativeExpandoObj
, *prop
,
4984 trackAttached("SetProp.DOMProxyExpandoSlot");
4985 return AttachDecision::Attach
;
4988 NativeObject
* holder
= nullptr;
4989 if (CanAttachSetter(cx_
, pc_
, expandoObj
, id
, &holder
, &prop
)) {
4990 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
4992 // Call the setter. Note that we pass objId, the DOM proxy, as |this|
4993 // and not the expando object.
4994 maybeEmitIdGuard(id
);
4995 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
4996 obj
, objId
, expandoVal
, nativeExpandoObj
);
4998 MOZ_ASSERT(holder
== nativeExpandoObj
);
4999 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
5000 EmitCallSetterNoGuards(cx_
, writer
, nativeExpandoObj
, nativeExpandoObj
,
5001 *prop
, objId
, rhsId
);
5002 trackAttached("SetProp.DOMProxyExpandoSetter");
5003 return AttachDecision::Attach
;
5006 return AttachDecision::NoAction
;
5009 AttachDecision
SetPropIRGenerator::tryAttachProxy(HandleObject obj
,
5012 ValOperandId rhsId
) {
5013 // Don't attach a proxy stub for ops like JSOp::InitElem.
5014 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5016 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
5017 if (type
== ProxyStubType::None
) {
5018 return AttachDecision::NoAction
;
5020 auto proxy
= obj
.as
<ProxyObject
>();
5022 if (mode_
== ICState::Mode::Megamorphic
) {
5023 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5024 /* handleDOMProxies = */ true);
5028 case ProxyStubType::None
:
5030 case ProxyStubType::DOMExpando
:
5031 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, rhsId
));
5032 [[fallthrough
]]; // Fall through to the generic shadowed case.
5033 case ProxyStubType::DOMShadowed
:
5034 return tryAttachDOMProxyShadowed(proxy
, objId
, id
, rhsId
);
5035 case ProxyStubType::DOMUnshadowed
:
5036 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, rhsId
));
5037 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5038 /* handleDOMProxies = */ true);
5039 case ProxyStubType::Generic
:
5040 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5041 /* handleDOMProxies = */ false);
5044 MOZ_CRASH("Unexpected ProxyStubType");
5047 AttachDecision
SetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
5049 ValOperandId rhsId
) {
5050 // Don't attach a proxy stub for ops like JSOp::InitElem.
5051 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5053 if (!obj
->is
<ProxyObject
>()) {
5054 return AttachDecision::NoAction
;
5057 writer
.guardIsProxy(objId
);
5059 // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
5060 // proxies here as we don't have specialized DOM stubs for this.
5061 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5062 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
, IsStrictSetPC(pc_
));
5063 writer
.returnFromIC();
5065 trackAttached("SetProp.ProxyElement");
5066 return AttachDecision::Attach
;
5069 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetElement(
5070 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5071 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5073 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetElem
) {
5074 return AttachDecision::NoAction
;
5077 // The generic proxy stubs are faster.
5078 if (obj
->is
<ProxyObject
>()) {
5079 return AttachDecision::NoAction
;
5082 writer
.megamorphicSetElement(objId
, setElemKeyValueId(), rhsId
,
5083 IsStrictSetPC(pc_
));
5084 writer
.returnFromIC();
5086 trackAttached("SetProp.MegamorphicSetElement");
5087 return AttachDecision::Attach
;
5090 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetSlot(
5091 HandleObject obj
, ObjOperandId objId
, HandleId id
, ValOperandId rhsId
) {
5092 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetProp
) {
5093 return AttachDecision::NoAction
;
5096 writer
.megamorphicStoreSlot(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5097 writer
.returnFromIC();
5098 trackAttached("SetProp.MegamorphicNativeSlot");
5099 return AttachDecision::Attach
;
5102 AttachDecision
SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
5105 ValOperandId rhsId
) {
5106 // Don't attach a window proxy stub for ops like JSOp::InitElem.
5107 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5109 // Attach a stub when the receiver is a WindowProxy and we can do the set
5110 // on the Window (the global object).
5112 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
5113 return AttachDecision::NoAction
;
5116 // If we're megamorphic prefer a generic proxy stub that handles a lot more
5118 if (mode_
== ICState::Mode::Megamorphic
) {
5119 return AttachDecision::NoAction
;
5122 // Now try to do the set on the Window (the current global).
5123 GlobalObject
* windowObj
= cx_
->global();
5125 Maybe
<PropertyInfo
> prop
;
5126 if (!CanAttachNativeSetSlot(JSOp(*pc_
), windowObj
, id
, &prop
)) {
5127 return AttachDecision::NoAction
;
5130 maybeEmitIdGuard(id
);
5132 ObjOperandId windowObjId
=
5133 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
5134 writer
.guardShape(windowObjId
, windowObj
->shape());
5136 EmitStoreSlotAndReturn(writer
, windowObjId
, windowObj
, *prop
, rhsId
);
5138 trackAttached("SetProp.WindowProxySlot");
5139 return AttachDecision::Attach
;
5142 // Detect if |id| refers to the 'prototype' property of a function object. This
5143 // property is special-cased in canAttachAddSlotStub().
5144 static bool IsFunctionPrototype(const JSAtomState
& names
, JSObject
* obj
,
5146 return obj
->is
<JSFunction
>() && id
.isAtom(names
.prototype
);
5149 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj
, HandleId id
) {
5150 if (!obj
->is
<NativeObject
>()) {
5153 auto* nobj
= &obj
->as
<NativeObject
>();
5155 // Special-case JSFunction resolve hook to allow redefining the 'prototype'
5156 // property without triggering lazy expansion of property and object
5158 if (IsFunctionPrototype(cx_
->names(), nobj
, id
)) {
5159 MOZ_ASSERT(ClassMayResolveId(cx_
->names(), nobj
->getClass(), id
, nobj
));
5161 // We're only interested in functions that have a builtin .prototype
5162 // property (needsPrototypeProperty). The stub will guard on this because
5163 // the builtin .prototype property is non-configurable/non-enumerable and it
5164 // would be wrong to add a property with those attributes to a function that
5165 // doesn't have a builtin .prototype.
5167 // Inlining needsPrototypeProperty in JIT code is complicated so we use
5168 // isNonBuiltinConstructor as a stronger condition that's easier to check
5170 JSFunction
* fun
= &nobj
->as
<JSFunction
>();
5171 if (!fun
->isNonBuiltinConstructor()) {
5174 MOZ_ASSERT(fun
->needsPrototypeProperty());
5176 // If property exists this isn't an "add".
5177 if (fun
->lookupPure(id
)) {
5181 // Normal Case: If property exists this isn't an "add"
5182 PropertyResult prop
;
5183 if (!LookupOwnPropertyPure(cx_
, nobj
, id
, &prop
)) {
5186 if (prop
.isFound()) {
5191 // For now we don't optimize Watchtower-monitored objects.
5192 if (Watchtower::watchesPropertyAdd(nobj
)) {
5196 // Object must be extensible, or we must be initializing a private
5198 bool canAddNewProperty
= nobj
->isExtensible() || id
.isPrivateName();
5199 if (!canAddNewProperty
) {
5203 JSOp op
= JSOp(*pc_
);
5204 if (IsPropertyInitOp(op
)) {
5208 MOZ_ASSERT(IsPropertySetOp(op
));
5210 // Walk up the object prototype chain and ensure that all prototypes are
5211 // native, and that all prototypes have no setter defined on the property.
5212 for (JSObject
* proto
= nobj
->staticPrototype(); proto
;
5213 proto
= proto
->staticPrototype()) {
5214 if (!proto
->is
<NativeObject
>()) {
5218 // If prototype defines this property in a non-plain way, don't optimize.
5219 Maybe
<PropertyInfo
> protoProp
= proto
->as
<NativeObject
>().lookup(cx_
, id
);
5220 if (protoProp
.isSome() && !protoProp
->isDataProperty()) {
5224 // Otherwise, if there's no such property, watch out for a resolve hook
5225 // that would need to be invoked and thus prevent inlining of property
5226 // addition. Allow the JSFunction resolve hook as it only defines plain
5227 // data properties and we don't need to invoke it for objects on the
5229 if (ClassMayResolveId(cx_
->names(), proto
->getClass(), id
, proto
) &&
5230 !proto
->is
<JSFunction
>()) {
5238 static PropertyFlags
SetPropertyFlags(JSOp op
, bool isFunctionPrototype
) {
5239 // Locked properties are non-writable, non-enumerable, and non-configurable.
5240 if (IsLockedInitOp(op
)) {
5244 // Hidden properties are writable, non-enumerable, and configurable.
5245 if (IsHiddenInitOp(op
)) {
5247 PropertyFlag::Writable
,
5248 PropertyFlag::Configurable
,
5252 // This is a special case to overwrite an unresolved function.prototype
5253 // property. The initial property flags of this property are writable,
5254 // non-enumerable, and non-configurable. See canAttachAddSlotStub.
5255 if (isFunctionPrototype
) {
5257 PropertyFlag::Writable
,
5261 // Other properties are writable, enumerable, and configurable.
5262 return PropertyFlags::defaultDataPropFlags
;
5265 AttachDecision
SetPropIRGenerator::tryAttachAddSlotStub(
5266 Handle
<Shape
*> oldShape
) {
5267 ValOperandId
objValId(writer
.setInputOperandId(0));
5268 ValOperandId rhsValId
;
5269 if (cacheKind_
== CacheKind::SetProp
) {
5270 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
5272 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5273 MOZ_ASSERT(setElemKeyValueId().id() == 1);
5274 writer
.setInputOperandId(1);
5275 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
5280 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
5281 cx_
->clearPendingException();
5282 return AttachDecision::NoAction
;
5285 if (!lhsVal_
.isObject() || !nameOrSymbol
) {
5286 return AttachDecision::NoAction
;
5289 JSObject
* obj
= &lhsVal_
.toObject();
5291 PropertyResult prop
;
5292 if (!LookupOwnPropertyPure(cx_
, obj
, id
, &prop
)) {
5293 return AttachDecision::NoAction
;
5295 if (prop
.isNotFound()) {
5296 return AttachDecision::NoAction
;
5299 if (!obj
->is
<NativeObject
>()) {
5300 return AttachDecision::NoAction
;
5302 auto* nobj
= &obj
->as
<NativeObject
>();
5304 PropertyInfo propInfo
= prop
.propertyInfo();
5305 NativeObject
* holder
= nobj
;
5307 if (holder
->inDictionaryMode()) {
5308 return AttachDecision::NoAction
;
5311 SharedShape
* oldSharedShape
= &oldShape
->asShared();
5313 // The property must be the last added property of the object.
5314 SharedShape
* newShape
= holder
->sharedShape();
5315 MOZ_RELEASE_ASSERT(newShape
->lastProperty() == propInfo
);
5318 // Verify exactly one property was added by comparing the property map
5320 if (oldSharedShape
->propMapLength() == PropMap::Capacity
) {
5321 MOZ_ASSERT(newShape
->propMapLength() == 1);
5323 MOZ_ASSERT(newShape
->propMapLength() ==
5324 oldSharedShape
->propMapLength() + 1);
5328 bool isFunctionPrototype
= IsFunctionPrototype(cx_
->names(), nobj
, id
);
5330 JSOp op
= JSOp(*pc_
);
5331 PropertyFlags flags
= SetPropertyFlags(op
, isFunctionPrototype
);
5333 // Basic property checks.
5334 if (!propInfo
.isDataProperty() || propInfo
.flags() != flags
) {
5335 return AttachDecision::NoAction
;
5338 ObjOperandId objId
= writer
.guardToObject(objValId
);
5339 maybeEmitIdGuard(id
);
5341 // Shape guard the object.
5342 writer
.guardShape(objId
, oldShape
);
5344 // If this is the special function.prototype case, we need to guard the
5345 // function is a non-builtin constructor. See canAttachAddSlotStub.
5346 if (isFunctionPrototype
) {
5347 MOZ_ASSERT(nobj
->as
<JSFunction
>().isNonBuiltinConstructor());
5348 writer
.guardFunctionIsNonBuiltinCtor(objId
);
5351 // Also shape guard the proto chain, unless this is an InitElem.
5352 if (IsPropertySetOp(op
)) {
5353 ShapeGuardProtoChain(writer
, nobj
, objId
);
5356 // If the JSClass has an addProperty hook, we need to call a VM function to
5357 // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
5358 // anything for non-index properties.
5359 DebugOnly
<uint32_t> index
;
5360 MOZ_ASSERT_IF(obj
->is
<ArrayObject
>(), !IdIsIndex(id
, &index
));
5361 bool mustCallAddPropertyHook
=
5362 obj
->getClass()->getAddProperty() && !obj
->is
<ArrayObject
>();
5364 if (mustCallAddPropertyHook
) {
5365 writer
.addSlotAndCallAddPropHook(objId
, rhsValId
, newShape
);
5366 trackAttached("SetProp.AddSlotWithAddPropertyHook");
5367 } else if (holder
->isFixedSlot(propInfo
.slot())) {
5368 size_t offset
= NativeObject::getFixedSlotOffset(propInfo
.slot());
5369 writer
.addAndStoreFixedSlot(objId
, offset
, rhsValId
, newShape
);
5370 trackAttached("SetProp.AddSlotFixed");
5372 size_t offset
= holder
->dynamicSlotIndex(propInfo
.slot()) * sizeof(Value
);
5373 uint32_t numOldSlots
= NativeObject::calculateDynamicSlots(oldSharedShape
);
5374 uint32_t numNewSlots
= holder
->numDynamicSlots();
5375 if (numOldSlots
== numNewSlots
) {
5376 writer
.addAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
);
5377 trackAttached("SetProp.AddSlotDynamic");
5379 MOZ_ASSERT(numNewSlots
> numOldSlots
);
5380 writer
.allocateAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
,
5382 trackAttached("SetProp.AllocateSlot");
5385 writer
.returnFromIC();
5387 return AttachDecision::Attach
;
5390 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext
* cx
, HandleScript script
,
5391 jsbytecode
* pc
, ICState state
,
5392 HandleValue lhs
, HandleObject rhs
)
5393 : IRGenerator(cx
, script
, pc
, CacheKind::InstanceOf
, state
),
5397 AttachDecision
InstanceOfIRGenerator::tryAttachStub() {
5398 MOZ_ASSERT(cacheKind_
== CacheKind::InstanceOf
);
5399 AutoAssertNoPendingException
aanpe(cx_
);
5401 // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
5403 if (!rhsObj_
->is
<JSFunction
>()) {
5404 trackAttached(IRGenerator::NotAttached
);
5405 return AttachDecision::NoAction
;
5408 HandleFunction fun
= rhsObj_
.as
<JSFunction
>();
5410 // Look up the @@hasInstance property, and check that Function.__proto__ is
5411 // the property holder, and that no object further down the prototype chain
5412 // (including this function) has shadowed it; together with the fact that
5413 // Function.__proto__[@@hasInstance] is immutable, this ensures that the
5414 // hasInstance hook will not change without the need to guard on the actual
5416 PropertyResult hasInstanceProp
;
5417 NativeObject
* hasInstanceHolder
= nullptr;
5418 jsid hasInstanceID
= PropertyKey::Symbol(cx_
->wellKnownSymbols().hasInstance
);
5419 if (!LookupPropertyPure(cx_
, fun
, hasInstanceID
, &hasInstanceHolder
,
5420 &hasInstanceProp
) ||
5421 !hasInstanceProp
.isNativeProperty()) {
5422 trackAttached(IRGenerator::NotAttached
);
5423 return AttachDecision::NoAction
;
5426 JSObject
& funProto
= cx_
->global()->getPrototype(JSProto_Function
);
5427 if (hasInstanceHolder
!= &funProto
) {
5428 trackAttached(IRGenerator::NotAttached
);
5429 return AttachDecision::NoAction
;
5432 // If the above succeeded, then these should be true about @@hasInstance,
5433 // because the property on Function.__proto__ is an immutable data property:
5434 MOZ_ASSERT(hasInstanceProp
.propertyInfo().isDataProperty());
5435 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().configurable());
5436 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().writable());
5438 MOZ_ASSERT(IsCacheableProtoChain(fun
, hasInstanceHolder
));
5440 // Ensure that the function's prototype slot is the same.
5441 Maybe
<PropertyInfo
> prop
= fun
->lookupPure(cx_
->names().prototype
);
5442 if (prop
.isNothing() || !prop
->isDataProperty()) {
5443 trackAttached(IRGenerator::NotAttached
);
5444 return AttachDecision::NoAction
;
5447 uint32_t slot
= prop
->slot();
5448 MOZ_ASSERT(slot
>= fun
->numFixedSlots(), "Stub code relies on this");
5449 if (!fun
->getSlot(slot
).isObject()) {
5450 trackAttached(IRGenerator::NotAttached
);
5451 return AttachDecision::NoAction
;
5455 ValOperandId
lhs(writer
.setInputOperandId(0));
5456 ValOperandId
rhs(writer
.setInputOperandId(1));
5458 ObjOperandId rhsId
= writer
.guardToObject(rhs
);
5459 writer
.guardShape(rhsId
, fun
->shape());
5461 // Ensure that the shapes up the prototype chain for the RHS remain the same
5462 // so that @@hasInstance is not shadowed by some intermediate prototype
5464 if (hasInstanceHolder
!= fun
) {
5465 GeneratePrototypeGuards(writer
, fun
, hasInstanceHolder
, rhsId
);
5466 ObjOperandId holderId
= writer
.loadObject(hasInstanceHolder
);
5467 TestMatchingHolder(writer
, hasInstanceHolder
, holderId
);
5470 // Load the .prototype value and ensure it's an object.
5471 ValOperandId protoValId
=
5472 writer
.loadDynamicSlot(rhsId
, slot
- fun
->numFixedSlots());
5473 ObjOperandId protoId
= writer
.guardToObject(protoValId
);
5475 // Needn't guard LHS is object, because the actual stub can handle that
5476 // and correctly return false.
5477 writer
.loadInstanceOfObjectResult(lhs
, protoId
);
5478 writer
.returnFromIC();
5479 trackAttached("InstanceOf");
5480 return AttachDecision::Attach
;
5483 void InstanceOfIRGenerator::trackAttached(const char* name
) {
5484 stubName_
= name
? name
: "NotAttached";
5485 #ifdef JS_CACHEIR_SPEW
5486 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5487 sp
.valueProperty("lhs", lhsVal_
);
5488 sp
.valueProperty("rhs", ObjectValue(*rhsObj_
));
5491 // Silence Clang -Wunused-private-field warning.
5496 TypeOfIRGenerator::TypeOfIRGenerator(JSContext
* cx
, HandleScript script
,
5497 jsbytecode
* pc
, ICState state
,
5499 : IRGenerator(cx
, script
, pc
, CacheKind::TypeOf
, state
), val_(value
) {}
5501 void TypeOfIRGenerator::trackAttached(const char* name
) {
5502 stubName_
= name
? name
: "NotAttached";
5503 #ifdef JS_CACHEIR_SPEW
5504 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5505 sp
.valueProperty("val", val_
);
5510 AttachDecision
TypeOfIRGenerator::tryAttachStub() {
5511 MOZ_ASSERT(cacheKind_
== CacheKind::TypeOf
);
5513 AutoAssertNoPendingException
aanpe(cx_
);
5515 ValOperandId
valId(writer
.setInputOperandId(0));
5517 TRY_ATTACH(tryAttachPrimitive(valId
));
5518 TRY_ATTACH(tryAttachObject(valId
));
5520 MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
5521 return AttachDecision::NoAction
;
5524 AttachDecision
TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId
) {
5525 if (!val_
.isPrimitive()) {
5526 return AttachDecision::NoAction
;
5529 // Note: we don't use GuardIsNumber for int32 values because it's less
5530 // efficient in Warp (unboxing to double instead of int32).
5531 if (val_
.isDouble()) {
5532 writer
.guardIsNumber(valId
);
5534 writer
.guardNonDoubleType(valId
, val_
.type());
5537 writer
.loadConstantStringResult(
5538 TypeName(js::TypeOfValue(val_
), cx_
->names()));
5539 writer
.returnFromIC();
5540 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5541 trackAttached("TypeOf.Primitive");
5542 return AttachDecision::Attach
;
5545 AttachDecision
TypeOfIRGenerator::tryAttachObject(ValOperandId valId
) {
5546 if (!val_
.isObject()) {
5547 return AttachDecision::NoAction
;
5550 ObjOperandId objId
= writer
.guardToObject(valId
);
5551 writer
.loadTypeOfObjectResult(objId
);
5552 writer
.returnFromIC();
5553 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5554 trackAttached("TypeOf.Object");
5555 return AttachDecision::Attach
;
5558 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext
* cx
,
5559 HandleScript script
,
5560 jsbytecode
* pc
, ICState state
,
5562 : IRGenerator(cx
, script
, pc
, CacheKind::GetIterator
, state
), val_(value
) {}
5564 AttachDecision
GetIteratorIRGenerator::tryAttachStub() {
5565 MOZ_ASSERT(cacheKind_
== CacheKind::GetIterator
);
5567 AutoAssertNoPendingException
aanpe(cx_
);
5569 ValOperandId
valId(writer
.setInputOperandId(0));
5571 TRY_ATTACH(tryAttachObject(valId
));
5572 TRY_ATTACH(tryAttachNullOrUndefined(valId
));
5573 TRY_ATTACH(tryAttachGeneric(valId
));
5575 trackAttached(IRGenerator::NotAttached
);
5576 return AttachDecision::NoAction
;
5579 AttachDecision
GetIteratorIRGenerator::tryAttachObject(ValOperandId valId
) {
5580 if (!val_
.isObject()) {
5581 return AttachDecision::NoAction
;
5584 MOZ_ASSERT(val_
.toObject().compartment() == cx_
->compartment());
5586 ObjOperandId objId
= writer
.guardToObject(valId
);
5587 writer
.objectToIteratorResult(objId
, cx_
->compartment()->enumeratorsAddr());
5588 writer
.returnFromIC();
5590 trackAttached("GetIterator.Object");
5591 return AttachDecision::Attach
;
5594 AttachDecision
GetIteratorIRGenerator::tryAttachNullOrUndefined(
5595 ValOperandId valId
) {
5596 MOZ_ASSERT(JSOp(*pc_
) == JSOp::Iter
);
5598 // For null/undefined we can simply return the empty iterator singleton. This
5599 // works because this iterator is unlinked and immutable.
5601 if (!val_
.isNullOrUndefined()) {
5602 return AttachDecision::NoAction
;
5605 PropertyIteratorObject
* emptyIter
=
5606 GlobalObject::getOrCreateEmptyIterator(cx_
);
5608 cx_
->recoverFromOutOfMemory();
5609 return AttachDecision::NoAction
;
5612 writer
.guardIsNullOrUndefined(valId
);
5614 ObjOperandId iterId
= writer
.loadObject(emptyIter
);
5615 writer
.loadObjectResult(iterId
);
5616 writer
.returnFromIC();
5618 trackAttached("GetIterator.NullOrUndefined");
5619 return AttachDecision::Attach
;
5622 AttachDecision
GetIteratorIRGenerator::tryAttachGeneric(ValOperandId valId
) {
5623 writer
.valueToIteratorResult(valId
);
5624 writer
.returnFromIC();
5626 trackAttached("GetIterator.Generic");
5627 return AttachDecision::Attach
;
5630 void GetIteratorIRGenerator::trackAttached(const char* name
) {
5631 stubName_
= name
? name
: "NotAttached";
5632 #ifdef JS_CACHEIR_SPEW
5633 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5634 sp
.valueProperty("val", val_
);
5639 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
5640 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
5642 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeSpreadCall
, state
),
5645 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachStub() {
5646 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeSpreadCall
);
5648 AutoAssertNoPendingException
aanpe(cx_
);
5650 TRY_ATTACH(tryAttachArray());
5651 TRY_ATTACH(tryAttachArguments());
5652 TRY_ATTACH(tryAttachNotOptimizable());
5654 trackAttached(IRGenerator::NotAttached
);
5655 return AttachDecision::NoAction
;
5658 static bool IsArrayInstanceOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5659 MutableHandle
<NativeObject
*> arrProto
) {
5660 // Prototype must be Array.prototype.
5661 auto* proto
= cx
->global()->maybeGetArrayPrototype();
5662 if (!proto
|| arr
->staticPrototype() != proto
) {
5665 arrProto
.set(proto
);
5667 // The object must not have an own @@iterator property.
5668 PropertyKey iteratorKey
=
5669 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5670 return !arr
->lookupPure(iteratorKey
);
5673 static bool IsArrayPrototypeOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5674 Handle
<NativeObject
*> arrProto
,
5676 MutableHandle
<JSFunction
*> iterFun
) {
5677 PropertyKey iteratorKey
=
5678 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5679 // Ensure that Array.prototype's @@iterator slot is unchanged.
5680 Maybe
<PropertyInfo
> prop
= arrProto
->lookupPure(iteratorKey
);
5681 if (prop
.isNothing() || !prop
->isDataProperty()) {
5685 *slot
= prop
->slot();
5686 MOZ_ASSERT(arrProto
->numFixedSlots() == 0, "Stub code relies on this");
5688 const Value
& iterVal
= arrProto
->getSlot(*slot
);
5689 if (!iterVal
.isObject() || !iterVal
.toObject().is
<JSFunction
>()) {
5693 iterFun
.set(&iterVal
.toObject().as
<JSFunction
>());
5694 return IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
);
5697 enum class AllowIteratorReturn
: bool {
5701 static bool IsArrayIteratorPrototypeOptimizable(
5702 JSContext
* cx
, AllowIteratorReturn allowReturn
,
5703 MutableHandle
<NativeObject
*> arrIterProto
, uint32_t* slot
,
5704 MutableHandle
<JSFunction
*> nextFun
) {
5705 NativeObject
* proto
= nullptr;
5707 AutoEnterOOMUnsafeRegion oom
;
5708 proto
= GlobalObject::getOrCreateArrayIteratorPrototype(cx
, cx
->global());
5710 oom
.crash("failed to allocate Array iterator prototype");
5713 arrIterProto
.set(proto
);
5715 // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
5716 Maybe
<PropertyInfo
> prop
= proto
->lookupPure(cx
->names().next
);
5717 if (prop
.isNothing() || !prop
->isDataProperty()) {
5721 *slot
= prop
->slot();
5722 MOZ_ASSERT(proto
->numFixedSlots() == 0, "Stub code relies on this");
5724 const Value
& nextVal
= proto
->getSlot(*slot
);
5725 if (!nextVal
.isObject() || !nextVal
.toObject().is
<JSFunction
>()) {
5729 nextFun
.set(&nextVal
.toObject().as
<JSFunction
>());
5730 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
5734 if (allowReturn
== AllowIteratorReturn::No
) {
5735 // Ensure that %ArrayIteratorPrototype% doesn't define "return".
5736 if (!CheckHasNoSuchProperty(cx
, proto
, NameToId(cx
->names().return_
))) {
5744 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArray() {
5745 if (!isFirstStub_
) {
5746 return AttachDecision::NoAction
;
5749 // The value must be a packed array.
5750 if (!val_
.isObject()) {
5751 return AttachDecision::NoAction
;
5753 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
5754 if (!IsPackedArray(obj
)) {
5755 return AttachDecision::NoAction
;
5758 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
5760 Rooted
<NativeObject
*> arrProto(cx_
);
5761 uint32_t arrProtoIterSlot
;
5762 Rooted
<JSFunction
*> iterFun(cx_
);
5763 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
5764 return AttachDecision::NoAction
;
5767 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
5768 &arrProtoIterSlot
, &iterFun
)) {
5769 return AttachDecision::NoAction
;
5772 // %ArrayIteratorPrototype%.next must not be modified.
5773 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
5774 uint32_t iterNextSlot
;
5775 Rooted
<JSFunction
*> nextFun(cx_
);
5776 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
5777 &arrayIteratorProto
, &iterNextSlot
,
5779 return AttachDecision::NoAction
;
5782 ValOperandId
valId(writer
.setInputOperandId(0));
5783 ObjOperandId objId
= writer
.guardToObject(valId
);
5785 // Guard the object is a packed array with Array.prototype as proto.
5786 MOZ_ASSERT(obj
->is
<ArrayObject
>());
5787 writer
.guardShape(objId
, obj
->shape());
5788 writer
.guardArrayIsPacked(objId
);
5790 // Guard on Array.prototype[@@iterator].
5791 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
5792 ObjOperandId iterId
= writer
.loadObject(iterFun
);
5793 writer
.guardShape(arrProtoId
, arrProto
->shape());
5794 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
, arrProtoIterSlot
);
5796 // Guard on %ArrayIteratorPrototype%.next.
5797 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
5798 ObjOperandId nextId
= writer
.loadObject(nextFun
);
5799 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
5800 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, iterNextSlot
);
5802 writer
.loadObjectResult(objId
);
5803 writer
.returnFromIC();
5805 trackAttached("OptimizeSpreadCall.Array");
5806 return AttachDecision::Attach
;
5809 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArguments() {
5810 // The value must be an arguments object.
5811 if (!val_
.isObject()) {
5812 return AttachDecision::NoAction
;
5814 RootedObject
obj(cx_
, &val_
.toObject());
5815 if (!obj
->is
<ArgumentsObject
>()) {
5816 return AttachDecision::NoAction
;
5818 auto args
= obj
.as
<ArgumentsObject
>();
5820 // Ensure neither elements, nor the length, nor the iterator has been
5821 // overridden. Also ensure no args are forwarded to allow reading them
5822 // directly from the frame.
5823 if (args
->hasOverriddenElement() || args
->hasOverriddenLength() ||
5824 args
->hasOverriddenIterator() || args
->anyArgIsForwarded()) {
5825 return AttachDecision::NoAction
;
5828 Rooted
<Shape
*> shape(cx_
, GlobalObject::getArrayShapeWithDefaultProto(cx_
));
5830 cx_
->clearPendingException();
5831 return AttachDecision::NoAction
;
5834 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
5836 Rooted
<JSFunction
*> nextFun(cx_
);
5837 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
5838 &arrayIteratorProto
, &slot
,
5840 return AttachDecision::NoAction
;
5843 ValOperandId
valId(writer
.setInputOperandId(0));
5844 ObjOperandId objId
= writer
.guardToObject(valId
);
5846 if (args
->is
<MappedArgumentsObject
>()) {
5847 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
5849 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
5850 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
5852 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
5853 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
5854 ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
|
5855 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
5856 writer
.guardArgumentsObjectFlags(objId
, flags
);
5858 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
5859 ObjOperandId nextId
= writer
.loadObject(nextFun
);
5861 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
5863 // Ensure that proto[slot] == nextFun.
5864 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
5866 writer
.arrayFromArgumentsObjectResult(objId
, shape
);
5867 writer
.returnFromIC();
5869 trackAttached("OptimizeSpreadCall.Arguments");
5870 return AttachDecision::Attach
;
5873 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
5874 ValOperandId
valId(writer
.setInputOperandId(0));
5876 writer
.loadUndefinedResult();
5877 writer
.returnFromIC();
5879 trackAttached("OptimizeSpreadCall.NotOptimizable");
5880 return AttachDecision::Attach
;
5883 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name
) {
5884 stubName_
= name
? name
: "NotAttached";
5885 #ifdef JS_CACHEIR_SPEW
5886 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5887 sp
.valueProperty("val", val_
);
5892 CallIRGenerator::CallIRGenerator(JSContext
* cx
, HandleScript script
,
5893 jsbytecode
* pc
, JSOp op
, ICState state
,
5894 uint32_t argc
, HandleValue callee
,
5895 HandleValue thisval
, HandleValue newTarget
,
5896 HandleValueArray args
)
5897 : IRGenerator(cx
, script
, pc
, CacheKind::Call
, state
),
5902 newTarget_(newTarget
),
5905 void InlinableNativeIRGenerator::emitNativeCalleeGuard() {
5906 // Note: we rely on GuardSpecificFunction to also guard against the same
5907 // native from a different realm.
5908 MOZ_ASSERT(callee_
->isNativeWithoutJitEntry());
5910 ObjOperandId calleeObjId
;
5911 if (flags_
.getArgFormat() == CallFlags::Standard
) {
5912 ValOperandId calleeValId
=
5913 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
5914 calleeObjId
= writer
.guardToObject(calleeValId
);
5915 } else if (flags_
.getArgFormat() == CallFlags::Spread
) {
5916 ValOperandId calleeValId
=
5917 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
5918 calleeObjId
= writer
.guardToObject(calleeValId
);
5919 } else if (flags_
.getArgFormat() == CallFlags::FunCall
) {
5920 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
5922 Int32OperandId
argcId(0);
5923 calleeObjId
= generator_
.emitFunCallOrApplyGuard(argcId
);
5925 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
5926 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
5928 Int32OperandId
argcId(0);
5929 calleeObjId
= generator_
.emitFunApplyGuard(argcId
);
5932 writer
.guardSpecificFunction(calleeObjId
, callee_
);
5934 // If we're constructing we also need to guard newTarget == callee.
5935 if (flags_
.isConstructing()) {
5936 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
5937 MOZ_ASSERT(&newTarget_
.toObject() == callee_
);
5939 ValOperandId newTargetValId
=
5940 writer
.loadArgumentFixedSlot(ArgumentKind::NewTarget
, argc_
, flags_
);
5941 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
5942 writer
.guardSpecificFunction(newTargetObjId
, callee_
);
5946 ObjOperandId
InlinableNativeIRGenerator::emitLoadArgsArray() {
5947 if (flags_
.getArgFormat() == CallFlags::Spread
) {
5948 return writer
.loadSpreadArgs();
5951 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
5952 return generator_
.emitFunApplyArgsGuard(flags_
.getArgFormat()).ref();
5955 void IRGenerator::emitCalleeGuard(ObjOperandId calleeId
, JSFunction
* callee
) {
5956 // Guarding on the callee JSFunction* is most efficient, but doesn't work well
5957 // for lambda clones (multiple functions with the same BaseScript). We guard
5958 // on the function's BaseScript if the callee is scripted and this isn't the
5961 // Self-hosted functions are more complicated: top-level functions can be
5962 // relazified using SelfHostedLazyScript and this means they don't have a
5963 // stable BaseScript pointer. These functions are never lambda clones, though,
5964 // so we can just always guard on the JSFunction*. Self-hosted lambdas are
5965 // never relazified so there we use the normal heuristics.
5966 if (isFirstStub_
|| !callee
->hasBaseScript() ||
5967 (callee
->isSelfHostedBuiltin() && !callee
->isLambda())) {
5968 writer
.guardSpecificFunction(calleeId
, callee
);
5970 MOZ_ASSERT_IF(callee
->isSelfHostedBuiltin(),
5971 !callee
->baseScript()->allowRelazify());
5972 writer
.guardClass(calleeId
, GuardClassKind::JSFunction
);
5973 writer
.guardFunctionScript(calleeId
, callee
->baseScript());
5977 ObjOperandId
CallIRGenerator::emitFunCallOrApplyGuard(Int32OperandId argcId
) {
5978 JSFunction
* callee
= &callee_
.toObject().as
<JSFunction
>();
5979 MOZ_ASSERT(callee
->native() == fun_call
|| callee
->native() == fun_apply
);
5981 // Guard that callee is the |fun_call| or |fun_apply| native function.
5982 ValOperandId calleeValId
=
5983 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
);
5984 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
5985 writer
.guardSpecificFunction(calleeObjId
, callee
);
5987 // Guard that |this| is an object.
5988 ValOperandId thisValId
=
5989 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
);
5990 return writer
.guardToObject(thisValId
);
5993 ObjOperandId
CallIRGenerator::emitFunCallGuard(Int32OperandId argcId
) {
5994 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_call
);
5996 return emitFunCallOrApplyGuard(argcId
);
5999 ObjOperandId
CallIRGenerator::emitFunApplyGuard(Int32OperandId argcId
) {
6000 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_apply
);
6002 return emitFunCallOrApplyGuard(argcId
);
6005 Maybe
<ObjOperandId
> CallIRGenerator::emitFunApplyArgsGuard(
6006 CallFlags::ArgFormat format
) {
6007 MOZ_ASSERT(argc_
== 2);
6009 ValOperandId argValId
=
6010 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6012 if (format
== CallFlags::FunApplyArgsObj
) {
6013 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6014 if (args_
[1].toObject().is
<MappedArgumentsObject
>()) {
6015 writer
.guardClass(argObjId
, GuardClassKind::MappedArguments
);
6017 MOZ_ASSERT(args_
[1].toObject().is
<UnmappedArgumentsObject
>());
6018 writer
.guardClass(argObjId
, GuardClassKind::UnmappedArguments
);
6020 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6021 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6022 writer
.guardArgumentsObjectFlags(argObjId
, flags
);
6023 return mozilla::Some(argObjId
);
6026 if (format
== CallFlags::FunApplyArray
) {
6027 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6028 emitOptimisticClassGuard(argObjId
, &args_
[1].toObject(),
6029 GuardClassKind::Array
);
6030 writer
.guardArrayIsPacked(argObjId
);
6031 return mozilla::Some(argObjId
);
6034 MOZ_ASSERT(format
== CallFlags::FunApplyNullUndefined
);
6035 writer
.guardIsNullOrUndefined(argValId
);
6036 return mozilla::Nothing();
6039 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPush() {
6040 // Only optimize on obj.push(val);
6041 if (argc_
!= 1 || !thisval_
.isObject()) {
6042 return AttachDecision::NoAction
;
6045 // Where |obj| is a native array.
6046 JSObject
* thisobj
= &thisval_
.toObject();
6047 if (!thisobj
->is
<ArrayObject
>()) {
6048 return AttachDecision::NoAction
;
6051 auto* thisarray
= &thisobj
->as
<ArrayObject
>();
6053 // Check for other indexed properties or class hooks.
6054 if (!CanAttachAddElement(thisarray
, /* isInit = */ false,
6055 AllowIndexedReceiver::No
)) {
6056 return AttachDecision::NoAction
;
6059 // Can't add new elements to arrays with non-writable length.
6060 if (!thisarray
->lengthIsWritable()) {
6061 return AttachDecision::NoAction
;
6064 // Check that array is extensible.
6065 if (!thisarray
->isExtensible()) {
6066 return AttachDecision::NoAction
;
6069 // Check that the array is completely initialized (no holes).
6070 if (thisarray
->getDenseInitializedLength() != thisarray
->length()) {
6071 return AttachDecision::NoAction
;
6074 MOZ_ASSERT(!thisarray
->denseElementsAreFrozen(),
6075 "Extensible arrays should not have frozen elements");
6077 // After this point, we can generate code fine.
6079 // Initialize the input operand.
6080 initializeInputOperand();
6082 // Guard callee is the 'push' native function.
6083 emitNativeCalleeGuard();
6085 // Guard this is an array object.
6086 ValOperandId thisValId
=
6087 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6088 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6090 // Guard that the shape matches.
6091 TestMatchingNativeReceiver(writer
, thisarray
, thisObjId
);
6093 // Guard proto chain shapes.
6094 ShapeGuardProtoChain(writer
, thisarray
, thisObjId
);
6096 // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
6097 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6098 writer
.arrayPush(thisObjId
, argId
);
6100 writer
.returnFromIC();
6102 trackAttached("ArrayPush");
6103 return AttachDecision::Attach
;
6106 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPopShift(
6107 InlinableNative native
) {
6108 // Expecting no arguments.
6110 return AttachDecision::NoAction
;
6113 // Only optimize if |this| is a packed array.
6114 if (!thisval_
.isObject() || !IsPackedArray(&thisval_
.toObject())) {
6115 return AttachDecision::NoAction
;
6118 // Other conditions:
6120 // * The array length needs to be writable because we're changing it.
6121 // * The array must be extensible. Non-extensible arrays require preserving
6122 // the |initializedLength == capacity| invariant on ObjectElements.
6123 // See NativeObject::shrinkCapacityToInitializedLength.
6124 // This also ensures the elements aren't sealed/frozen.
6125 // * There must not be a for-in iterator for the elements because the IC stub
6126 // does not suppress deleted properties.
6127 ArrayObject
* arr
= &thisval_
.toObject().as
<ArrayObject
>();
6128 if (!arr
->lengthIsWritable() || !arr
->isExtensible() ||
6129 arr
->denseElementsHaveMaybeInIterationFlag()) {
6130 return AttachDecision::NoAction
;
6133 // Initialize the input operand.
6134 initializeInputOperand();
6136 // Guard callee is the 'pop' or 'shift' native function.
6137 emitNativeCalleeGuard();
6139 ValOperandId thisValId
=
6140 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6141 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6142 emitOptimisticClassGuard(objId
, arr
, GuardClassKind::Array
);
6144 if (native
== InlinableNative::ArrayPop
) {
6145 writer
.packedArrayPopResult(objId
);
6147 MOZ_ASSERT(native
== InlinableNative::ArrayShift
);
6148 writer
.packedArrayShiftResult(objId
);
6151 writer
.returnFromIC();
6153 trackAttached("ArrayPopShift");
6154 return AttachDecision::Attach
;
6157 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayJoin() {
6158 // Only handle argc <= 1.
6160 return AttachDecision::NoAction
;
6163 // Only optimize if |this| is an array.
6164 if (!thisval_
.isObject() || !thisval_
.toObject().is
<ArrayObject
>()) {
6165 return AttachDecision::NoAction
;
6168 // The separator argument must be a string, if present.
6169 if (argc_
> 0 && !args_
[0].isString()) {
6170 return AttachDecision::NoAction
;
6173 // IC stub code can handle non-packed array.
6175 // Initialize the input operand.
6176 initializeInputOperand();
6178 // Guard callee is the 'join' native function.
6179 emitNativeCalleeGuard();
6181 // Guard this is an array object.
6182 ValOperandId thisValId
=
6183 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6184 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6185 emitOptimisticClassGuard(thisObjId
, &thisval_
.toObject(),
6186 GuardClassKind::Array
);
6188 StringOperandId sepId
;
6190 // If argcount is 1, guard that the argument is a string.
6191 ValOperandId argValId
=
6192 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6193 sepId
= writer
.guardToString(argValId
);
6195 sepId
= writer
.loadConstantString(cx_
->names().comma_
);
6199 writer
.arrayJoinResult(thisObjId
, sepId
);
6201 writer
.returnFromIC();
6203 trackAttached("ArrayJoin");
6204 return AttachDecision::Attach
;
6207 AttachDecision
InlinableNativeIRGenerator::tryAttachArraySlice() {
6208 // Only handle argc <= 2.
6210 return AttachDecision::NoAction
;
6213 // Only optimize if |this| is a packed array or an arguments object.
6214 if (!thisval_
.isObject()) {
6215 return AttachDecision::NoAction
;
6218 bool isPackedArray
= IsPackedArray(&thisval_
.toObject());
6219 if (!isPackedArray
) {
6220 if (!thisval_
.toObject().is
<ArgumentsObject
>()) {
6221 return AttachDecision::NoAction
;
6223 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6225 // No elements must have been overridden or deleted.
6226 if (args
->hasOverriddenElement()) {
6227 return AttachDecision::NoAction
;
6230 // The length property mustn't be overridden.
6231 if (args
->hasOverriddenLength()) {
6232 return AttachDecision::NoAction
;
6235 // And finally also check that no argument is forwarded.
6236 if (args
->anyArgIsForwarded()) {
6237 return AttachDecision::NoAction
;
6241 // Arguments for the sliced region must be integers.
6242 if (argc_
> 0 && !args_
[0].isInt32()) {
6243 return AttachDecision::NoAction
;
6245 if (argc_
> 1 && !args_
[1].isInt32()) {
6246 return AttachDecision::NoAction
;
6249 JSObject
* templateObj
= NewDenseFullyAllocatedArray(cx_
, 0, TenuredObject
);
6251 cx_
->recoverFromOutOfMemory();
6252 return AttachDecision::NoAction
;
6255 // Initialize the input operand.
6256 initializeInputOperand();
6258 // Guard callee is the 'slice' native function.
6259 emitNativeCalleeGuard();
6261 ValOperandId thisValId
=
6262 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6263 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6265 if (isPackedArray
) {
6266 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6267 GuardClassKind::Array
);
6269 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6271 if (args
->is
<MappedArgumentsObject
>()) {
6272 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6274 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6275 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6278 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6279 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6280 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6281 writer
.guardArgumentsObjectFlags(objId
, flags
);
6284 Int32OperandId int32BeginId
;
6286 ValOperandId beginId
=
6287 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6288 int32BeginId
= writer
.guardToInt32(beginId
);
6290 int32BeginId
= writer
.loadInt32Constant(0);
6293 Int32OperandId int32EndId
;
6295 ValOperandId endId
=
6296 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6297 int32EndId
= writer
.guardToInt32(endId
);
6298 } else if (isPackedArray
) {
6299 int32EndId
= writer
.loadInt32ArrayLength(objId
);
6301 int32EndId
= writer
.loadArgumentsObjectLength(objId
);
6304 if (isPackedArray
) {
6305 writer
.packedArraySliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6307 writer
.argumentsSliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6309 writer
.returnFromIC();
6311 trackAttached(isPackedArray
? "ArraySlice" : "ArgumentsSlice");
6312 return AttachDecision::Attach
;
6315 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayIsArray() {
6316 // Need a single argument.
6318 return AttachDecision::NoAction
;
6321 // Initialize the input operand.
6322 initializeInputOperand();
6324 // Guard callee is the 'isArray' native function.
6325 emitNativeCalleeGuard();
6327 // Check if the argument is an Array and return result.
6328 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6329 writer
.isArrayResult(argId
);
6330 writer
.returnFromIC();
6332 trackAttached("ArrayIsArray");
6333 return AttachDecision::Attach
;
6336 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewGet(
6337 Scalar::Type type
) {
6338 // Ensure |this| is a DataViewObject.
6339 // TODO: Support resizable dataviews. (bug 1842999)
6340 if (!thisval_
.isObject() ||
6341 !thisval_
.toObject().is
<FixedLengthDataViewObject
>()) {
6342 return AttachDecision::NoAction
;
6345 // Expected arguments: offset (number), optional littleEndian (boolean).
6346 if (argc_
< 1 || argc_
> 2) {
6347 return AttachDecision::NoAction
;
6349 int64_t offsetInt64
;
6350 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6351 return AttachDecision::NoAction
;
6353 if (argc_
> 1 && !args_
[1].isBoolean()) {
6354 return AttachDecision::NoAction
;
6357 auto* dv
= &thisval_
.toObject().as
<FixedLengthDataViewObject
>();
6359 // Bounds check the offset.
6360 if (offsetInt64
< 0 ||
6361 !dv
->offsetIsInBounds(Scalar::byteSize(type
), offsetInt64
)) {
6362 return AttachDecision::NoAction
;
6365 // For getUint32 we let the stub return an Int32 if we have not seen a
6366 // double, to allow better codegen in Warp while avoiding bailout loops.
6367 bool forceDoubleForUint32
= false;
6368 if (type
== Scalar::Uint32
) {
6369 bool isLittleEndian
= argc_
> 1 && args_
[1].toBoolean();
6370 uint32_t res
= dv
->read
<uint32_t>(offsetInt64
, isLittleEndian
);
6371 forceDoubleForUint32
= res
>= INT32_MAX
;
6374 // Initialize the input operand.
6375 initializeInputOperand();
6377 // Guard callee is this DataView native function.
6378 emitNativeCalleeGuard();
6380 // Guard |this| is a DataViewObject.
6381 ValOperandId thisValId
=
6382 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6383 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6384 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6385 GuardClassKind::FixedLengthDataView
);
6387 // Convert offset to intPtr.
6388 ValOperandId offsetId
=
6389 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6390 IntPtrOperandId intPtrOffsetId
=
6391 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6393 BooleanOperandId boolLittleEndianId
;
6395 ValOperandId littleEndianId
=
6396 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6397 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6399 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6402 writer
.loadDataViewValueResult(objId
, intPtrOffsetId
, boolLittleEndianId
,
6403 type
, forceDoubleForUint32
);
6404 writer
.returnFromIC();
6406 trackAttached("DataViewGet");
6407 return AttachDecision::Attach
;
6410 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewSet(
6411 Scalar::Type type
) {
6412 // Ensure |this| is a DataViewObject.
6413 // TODO: Support resizable dataviews. (bug 1842999)
6414 if (!thisval_
.isObject() ||
6415 !thisval_
.toObject().is
<FixedLengthDataViewObject
>()) {
6416 return AttachDecision::NoAction
;
6419 // Expected arguments: offset (number), value, optional littleEndian (boolean)
6420 if (argc_
< 2 || argc_
> 3) {
6421 return AttachDecision::NoAction
;
6423 int64_t offsetInt64
;
6424 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6425 return AttachDecision::NoAction
;
6427 if (!ValueIsNumeric(type
, args_
[1])) {
6428 return AttachDecision::NoAction
;
6430 if (argc_
> 2 && !args_
[2].isBoolean()) {
6431 return AttachDecision::NoAction
;
6434 auto* dv
= &thisval_
.toObject().as
<FixedLengthDataViewObject
>();
6436 // Bounds check the offset.
6437 if (offsetInt64
< 0 ||
6438 !dv
->offsetIsInBounds(Scalar::byteSize(type
), offsetInt64
)) {
6439 return AttachDecision::NoAction
;
6442 // Initialize the input operand.
6443 initializeInputOperand();
6445 // Guard callee is this DataView native function.
6446 emitNativeCalleeGuard();
6448 // Guard |this| is a DataViewObject.
6449 ValOperandId thisValId
=
6450 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6451 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6452 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6453 GuardClassKind::FixedLengthDataView
);
6455 // Convert offset to intPtr.
6456 ValOperandId offsetId
=
6457 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6458 IntPtrOperandId intPtrOffsetId
=
6459 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6461 // Convert value to number or BigInt.
6462 ValOperandId valueId
=
6463 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6464 OperandId numericValueId
= emitNumericGuard(valueId
, type
);
6466 BooleanOperandId boolLittleEndianId
;
6468 ValOperandId littleEndianId
=
6469 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6470 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6472 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6475 writer
.storeDataViewValueResult(objId
, intPtrOffsetId
, numericValueId
,
6476 boolLittleEndianId
, type
);
6477 writer
.returnFromIC();
6479 trackAttached("DataViewSet");
6480 return AttachDecision::Attach
;
6483 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeGetReservedSlot(
6484 InlinableNative native
) {
6485 // Self-hosted code calls this with (object, int32) arguments.
6486 MOZ_ASSERT(argc_
== 2);
6487 MOZ_ASSERT(args_
[0].isObject());
6488 MOZ_ASSERT(args_
[1].isInt32());
6489 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6491 uint32_t slot
= uint32_t(args_
[1].toInt32());
6492 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6493 return AttachDecision::NoAction
;
6495 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6497 // Initialize the input operand.
6498 initializeInputOperand();
6500 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6502 // Guard that the first argument is an object.
6503 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6504 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6506 // BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot ensures that the
6507 // slot argument is constant. (At least for direct calls)
6510 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
6511 writer
.loadFixedSlotResult(objId
, offset
);
6513 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
6514 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Object
);
6516 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
6517 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Int32
);
6519 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
6520 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::String
);
6523 MOZ_CRASH("unexpected native");
6526 writer
.returnFromIC();
6528 trackAttached("UnsafeGetReservedSlot");
6529 return AttachDecision::Attach
;
6532 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeSetReservedSlot() {
6533 // Self-hosted code calls this with (object, int32, value) arguments.
6534 MOZ_ASSERT(argc_
== 3);
6535 MOZ_ASSERT(args_
[0].isObject());
6536 MOZ_ASSERT(args_
[1].isInt32());
6537 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6539 uint32_t slot
= uint32_t(args_
[1].toInt32());
6540 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6541 return AttachDecision::NoAction
;
6543 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6545 // Initialize the input operand.
6546 initializeInputOperand();
6548 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6550 // Guard that the first argument is an object.
6551 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6552 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6554 // BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot ensures that the
6555 // slot argument is constant. (At least for direct calls)
6557 // Get the value to set.
6558 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6560 // Set the fixed slot and return undefined.
6561 writer
.storeFixedSlotUndefinedResult(objId
, offset
, valId
);
6563 // This stub always returns undefined.
6564 writer
.returnFromIC();
6566 trackAttached("UnsafeSetReservedSlot");
6567 return AttachDecision::Attach
;
6570 AttachDecision
InlinableNativeIRGenerator::tryAttachIsSuspendedGenerator() {
6571 // The IsSuspendedGenerator intrinsic is only called in
6572 // self-hosted code, so it's safe to assume we have a single
6573 // argument and the callee is our intrinsic.
6575 MOZ_ASSERT(argc_
== 1);
6577 initializeInputOperand();
6579 // Stack layout here is (bottom to top):
6582 // 0: Arg <-- Top of stack.
6583 // We only care about the argument.
6584 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6586 // Check whether the argument is a suspended generator.
6587 // We don't need guards, because IsSuspendedGenerator returns
6588 // false for values that are not generator objects.
6589 writer
.callIsSuspendedGeneratorResult(valId
);
6590 writer
.returnFromIC();
6592 trackAttached("IsSuspendedGenerator");
6593 return AttachDecision::Attach
;
6596 AttachDecision
InlinableNativeIRGenerator::tryAttachToObject() {
6597 // Self-hosted code calls this with a single argument.
6598 MOZ_ASSERT(argc_
== 1);
6600 // Need a single object argument.
6601 // TODO(Warp): Support all or more conversions to object.
6602 if (!args_
[0].isObject()) {
6603 return AttachDecision::NoAction
;
6606 // Initialize the input operand.
6607 initializeInputOperand();
6609 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6611 // Guard that the argument is an object.
6612 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6613 ObjOperandId objId
= writer
.guardToObject(argId
);
6615 // Return the object.
6616 writer
.loadObjectResult(objId
);
6617 writer
.returnFromIC();
6619 trackAttached("ToObject");
6620 return AttachDecision::Attach
;
6623 AttachDecision
InlinableNativeIRGenerator::tryAttachToInteger() {
6624 // Self-hosted code calls this with a single argument.
6625 MOZ_ASSERT(argc_
== 1);
6627 // Need a single int32 argument.
6628 // TODO(Warp): Support all or more conversions to integer.
6629 // Make sure to update this code correctly if we ever start
6630 // returning non-int32 integers.
6631 if (!args_
[0].isInt32()) {
6632 return AttachDecision::NoAction
;
6635 // Initialize the input operand.
6636 initializeInputOperand();
6638 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6640 // Guard that the argument is an int32.
6641 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6642 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
6644 // Return the int32.
6645 writer
.loadInt32Result(int32Id
);
6646 writer
.returnFromIC();
6648 trackAttached("ToInteger");
6649 return AttachDecision::Attach
;
6652 AttachDecision
InlinableNativeIRGenerator::tryAttachToLength() {
6653 // Self-hosted code calls this with a single argument.
6654 MOZ_ASSERT(argc_
== 1);
6656 // Need a single int32 argument.
6657 if (!args_
[0].isInt32()) {
6658 return AttachDecision::NoAction
;
6661 // Initialize the input operand.
6662 initializeInputOperand();
6664 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6666 // ToLength(int32) is equivalent to max(int32, 0).
6667 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6668 Int32OperandId int32ArgId
= writer
.guardToInt32(argId
);
6669 Int32OperandId zeroId
= writer
.loadInt32Constant(0);
6671 Int32OperandId maxId
= writer
.int32MinMax(isMax
, int32ArgId
, zeroId
);
6672 writer
.loadInt32Result(maxId
);
6673 writer
.returnFromIC();
6675 trackAttached("ToLength");
6676 return AttachDecision::Attach
;
6679 AttachDecision
InlinableNativeIRGenerator::tryAttachIsObject() {
6680 // Self-hosted code calls this with a single argument.
6681 MOZ_ASSERT(argc_
== 1);
6683 // Initialize the input operand.
6684 initializeInputOperand();
6686 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6688 // Type check the argument and return result.
6689 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6690 writer
.isObjectResult(argId
);
6691 writer
.returnFromIC();
6693 trackAttached("IsObject");
6694 return AttachDecision::Attach
;
6697 AttachDecision
InlinableNativeIRGenerator::tryAttachIsPackedArray() {
6698 // Self-hosted code calls this with a single object argument.
6699 MOZ_ASSERT(argc_
== 1);
6700 MOZ_ASSERT(args_
[0].isObject());
6702 // Initialize the input operand.
6703 initializeInputOperand();
6705 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6707 // Check if the argument is packed and return result.
6708 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6709 ObjOperandId objArgId
= writer
.guardToObject(argId
);
6710 writer
.isPackedArrayResult(objArgId
);
6711 writer
.returnFromIC();
6713 trackAttached("IsPackedArray");
6714 return AttachDecision::Attach
;
6717 AttachDecision
InlinableNativeIRGenerator::tryAttachIsCallable() {
6718 // Self-hosted code calls this with a single argument.
6719 MOZ_ASSERT(argc_
== 1);
6721 // Initialize the input operand.
6722 initializeInputOperand();
6724 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6726 // Check if the argument is callable and return result.
6727 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6728 writer
.isCallableResult(argId
);
6729 writer
.returnFromIC();
6731 trackAttached("IsCallable");
6732 return AttachDecision::Attach
;
6735 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructor() {
6736 // Self-hosted code calls this with a single argument.
6737 MOZ_ASSERT(argc_
== 1);
6739 // Need a single object argument.
6740 if (!args_
[0].isObject()) {
6741 return AttachDecision::NoAction
;
6744 // Initialize the input operand.
6745 initializeInputOperand();
6747 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6749 // Guard that the argument is an object.
6750 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6751 ObjOperandId objId
= writer
.guardToObject(argId
);
6753 // Check if the argument is a constructor and return result.
6754 writer
.isConstructorResult(objId
);
6755 writer
.returnFromIC();
6757 trackAttached("IsConstructor");
6758 return AttachDecision::Attach
;
6762 InlinableNativeIRGenerator::tryAttachIsCrossRealmArrayConstructor() {
6763 // Self-hosted code calls this with an object argument.
6764 MOZ_ASSERT(argc_
== 1);
6765 MOZ_ASSERT(args_
[0].isObject());
6767 if (args_
[0].toObject().is
<ProxyObject
>()) {
6768 return AttachDecision::NoAction
;
6771 // Initialize the input operand.
6772 initializeInputOperand();
6774 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6776 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6777 ObjOperandId objId
= writer
.guardToObject(argId
);
6778 writer
.guardIsNotProxy(objId
);
6779 writer
.isCrossRealmArrayConstructorResult(objId
);
6780 writer
.returnFromIC();
6782 trackAttached("IsCrossRealmArrayConstructor");
6783 return AttachDecision::Attach
;
6786 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
6787 InlinableNative native
) {
6788 // Self-hosted code calls this with an object argument.
6789 MOZ_ASSERT(argc_
== 1);
6790 MOZ_ASSERT(args_
[0].isObject());
6792 // Class must match.
6793 const JSClass
* clasp
= InlinableNativeGuardToClass(native
);
6794 if (args_
[0].toObject().getClass() != clasp
) {
6795 return AttachDecision::NoAction
;
6798 // Initialize the input operand.
6799 initializeInputOperand();
6801 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6803 // Guard that the argument is an object.
6804 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6805 ObjOperandId objId
= writer
.guardToObject(argId
);
6807 // Guard that the object has the correct class.
6808 writer
.guardAnyClass(objId
, clasp
);
6810 // Return the object.
6811 writer
.loadObjectResult(objId
);
6812 writer
.returnFromIC();
6814 trackAttached("GuardToClass");
6815 return AttachDecision::Attach
;
6818 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() {
6819 // TODO: Support resizable ArrayBuffers (bug 1842999), for now simply
6820 // pass through to tryAttachGuardToClass which guards on
6821 // FixedLengthArrayBufferObject.
6822 return tryAttachGuardToClass(InlinableNative::IntrinsicGuardToArrayBuffer
);
6825 AttachDecision
InlinableNativeIRGenerator::tryAttachHasClass(
6826 const JSClass
* clasp
, bool isPossiblyWrapped
) {
6827 // Self-hosted code calls this with an object argument.
6828 MOZ_ASSERT(argc_
== 1);
6829 MOZ_ASSERT(args_
[0].isObject());
6831 // Only optimize when the object isn't a proxy.
6832 if (isPossiblyWrapped
&& args_
[0].toObject().is
<ProxyObject
>()) {
6833 return AttachDecision::NoAction
;
6836 // Initialize the input operand.
6837 initializeInputOperand();
6839 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6841 // Perform the Class check.
6842 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6843 ObjOperandId objId
= writer
.guardToObject(argId
);
6845 if (isPossiblyWrapped
) {
6846 writer
.guardIsNotProxy(objId
);
6849 writer
.hasClassResult(objId
, clasp
);
6850 writer
.returnFromIC();
6852 trackAttached("HasClass");
6853 return AttachDecision::Attach
;
6856 // Returns whether the .lastIndex property is a non-negative int32 value and is
6858 static bool HasOptimizableLastIndexSlot(RegExpObject
* regexp
, JSContext
* cx
) {
6859 auto lastIndexProp
= regexp
->lookupPure(cx
->names().lastIndex
);
6860 MOZ_ASSERT(lastIndexProp
->isDataProperty());
6861 if (!lastIndexProp
->writable()) {
6864 Value lastIndex
= regexp
->getLastIndex();
6865 if (!lastIndex
.isInt32() || lastIndex
.toInt32() < 0) {
6871 // Returns the RegExp stub used by the optimized code path for this intrinsic.
6872 // We store a pointer to this in the IC stub to ensure GC doesn't discard it.
6873 static JitCode
* GetOrCreateRegExpStub(JSContext
* cx
, InlinableNative native
) {
6874 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
6877 // The stubs assume the global has non-null RegExpStatics and match result
6879 if (!GlobalObject::getRegExpStatics(cx
, cx
->global()) ||
6880 !cx
->global()->regExpRealm().getOrCreateMatchResultShape(cx
)) {
6881 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
6882 cx
->clearPendingException();
6887 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
6888 case InlinableNative::IntrinsicRegExpExecForTest
:
6889 code
= cx
->zone()->jitZone()->ensureRegExpExecTestStubExists(cx
);
6891 case InlinableNative::IntrinsicRegExpBuiltinExec
:
6892 case InlinableNative::IntrinsicRegExpExec
:
6893 code
= cx
->zone()->jitZone()->ensureRegExpExecMatchStubExists(cx
);
6895 case InlinableNative::RegExpMatcher
:
6896 code
= cx
->zone()->jitZone()->ensureRegExpMatcherStubExists(cx
);
6898 case InlinableNative::RegExpSearcher
:
6899 code
= cx
->zone()->jitZone()->ensureRegExpSearcherStubExists(cx
);
6902 MOZ_CRASH("Unexpected native");
6905 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
6906 cx
->clearPendingException();
6913 static void EmitGuardLastIndexIsNonNegativeInt32(CacheIRWriter
& writer
,
6914 ObjOperandId regExpId
) {
6916 NativeObject::getFixedSlotOffset(RegExpObject::lastIndexSlot());
6917 ValOperandId lastIndexValId
= writer
.loadFixedSlot(regExpId
, offset
);
6918 Int32OperandId lastIndexId
= writer
.guardToInt32(lastIndexValId
);
6919 writer
.guardInt32IsNonNegative(lastIndexId
);
6922 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpBuiltinExec(
6923 InlinableNative native
) {
6924 // Self-hosted code calls this with (regexp, string) arguments.
6925 MOZ_ASSERT(argc_
== 2);
6926 MOZ_ASSERT(args_
[0].isObject());
6927 MOZ_ASSERT(args_
[1].isString());
6929 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
6931 return AttachDecision::NoAction
;
6934 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
6935 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
6936 return AttachDecision::NoAction
;
6939 // Initialize the input operand.
6940 initializeInputOperand();
6942 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6944 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6945 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
6946 writer
.guardShape(regExpId
, re
->shape());
6947 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
6949 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6950 StringOperandId inputId
= writer
.guardToString(arg1Id
);
6952 if (native
== InlinableNative::IntrinsicRegExpBuiltinExecForTest
) {
6953 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
6955 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
6957 writer
.returnFromIC();
6959 trackAttached("IntrinsicRegExpBuiltinExec");
6960 return AttachDecision::Attach
;
6963 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpExec(
6964 InlinableNative native
) {
6965 // Self-hosted code calls this with (object, string) arguments.
6966 MOZ_ASSERT(argc_
== 2);
6967 MOZ_ASSERT(args_
[0].isObject());
6968 MOZ_ASSERT(args_
[1].isString());
6970 if (!args_
[0].toObject().is
<RegExpObject
>()) {
6971 return AttachDecision::NoAction
;
6974 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
6976 return AttachDecision::NoAction
;
6979 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
6980 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
6981 return AttachDecision::NoAction
;
6984 // Ensure regexp.exec is the original RegExp.prototype.exec function on the
6986 if (re
->containsPure(cx_
->names().exec
)) {
6987 return AttachDecision::NoAction
;
6989 MOZ_ASSERT(cx_
->global()->maybeGetRegExpPrototype());
6991 &cx_
->global()->maybeGetRegExpPrototype()->as
<NativeObject
>();
6992 if (re
->staticPrototype() != regExpProto
) {
6993 return AttachDecision::NoAction
;
6995 auto execProp
= regExpProto
->as
<NativeObject
>().lookupPure(cx_
->names().exec
);
6996 if (!execProp
|| !execProp
->isDataProperty()) {
6997 return AttachDecision::NoAction
;
6999 // It should be stored in a dynamic slot. We assert this in
7000 // FinishRegExpClassInit.
7001 if (regExpProto
->isFixedSlot(execProp
->slot())) {
7002 return AttachDecision::NoAction
;
7004 Value execVal
= regExpProto
->getSlot(execProp
->slot());
7005 PropertyName
* execName
= cx_
->names().RegExp_prototype_Exec
;
7006 if (!IsSelfHostedFunctionWithName(execVal
, execName
)) {
7007 return AttachDecision::NoAction
;
7009 JSFunction
* execFunction
= &execVal
.toObject().as
<JSFunction
>();
7011 // Initialize the input operand.
7012 initializeInputOperand();
7014 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7016 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7017 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7018 writer
.guardShape(regExpId
, re
->shape());
7019 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7021 // Emit guards for the RegExp.prototype.exec property.
7022 ObjOperandId regExpProtoId
= writer
.loadObject(regExpProto
);
7023 writer
.guardShape(regExpProtoId
, regExpProto
->shape());
7025 regExpProto
->dynamicSlotIndex(execProp
->slot()) * sizeof(Value
);
7026 writer
.guardDynamicSlotValue(regExpProtoId
, offset
,
7027 ObjectValue(*execFunction
));
7029 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7030 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7032 if (native
== InlinableNative::IntrinsicRegExpExecForTest
) {
7033 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7035 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7037 writer
.returnFromIC();
7039 trackAttached("IntrinsicRegExpExec");
7040 return AttachDecision::Attach
;
7043 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpMatcherSearcher(
7044 InlinableNative native
) {
7045 // Self-hosted code calls this with (object, string, number) arguments.
7046 MOZ_ASSERT(argc_
== 3);
7047 MOZ_ASSERT(args_
[0].isObject());
7048 MOZ_ASSERT(args_
[1].isString());
7049 MOZ_ASSERT(args_
[2].isNumber());
7051 // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
7052 if (!args_
[2].isInt32()) {
7053 return AttachDecision::NoAction
;
7056 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7058 return AttachDecision::NoAction
;
7061 // Initialize the input operand.
7062 initializeInputOperand();
7064 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7066 // Guard argument types.
7067 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7068 ObjOperandId reId
= writer
.guardToObject(arg0Id
);
7070 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7071 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7073 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7074 Int32OperandId lastIndexId
= writer
.guardToInt32(arg2Id
);
7077 case InlinableNative::RegExpMatcher
:
7078 writer
.callRegExpMatcherResult(reId
, inputId
, lastIndexId
, stub
);
7079 writer
.returnFromIC();
7080 trackAttached("RegExpMatcher");
7083 case InlinableNative::RegExpSearcher
:
7084 writer
.callRegExpSearcherResult(reId
, inputId
, lastIndexId
, stub
);
7085 writer
.returnFromIC();
7086 trackAttached("RegExpSearcher");
7090 MOZ_CRASH("Unexpected native");
7093 return AttachDecision::Attach
;
7096 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
7097 // Self-hosted code calls this with a string argument that's only used for an
7099 MOZ_ASSERT(argc_
== 1);
7100 MOZ_ASSERT(args_
[0].isString());
7102 // Initialize the input operand.
7103 initializeInputOperand();
7105 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7107 writer
.regExpSearcherLastLimitResult();
7108 writer
.returnFromIC();
7110 trackAttached("RegExpSearcherLastLimit");
7111 return AttachDecision::Attach
;
7114 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
7115 // Self-hosted code calls this with object and string arguments.
7116 MOZ_ASSERT(argc_
== 2);
7117 MOZ_ASSERT(args_
[0].toObject().is
<RegExpObject
>());
7118 MOZ_ASSERT(args_
[1].isString());
7120 // Initialize the input operand.
7121 initializeInputOperand();
7123 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7125 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7126 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7128 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7129 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7131 writer
.regExpHasCaptureGroupsResult(objId
, inputId
);
7132 writer
.returnFromIC();
7134 trackAttached("RegExpHasCaptureGroups");
7135 return AttachDecision::Attach
;
7139 InlinableNativeIRGenerator::tryAttachRegExpPrototypeOptimizable() {
7140 // Self-hosted code calls this with a single object argument.
7141 MOZ_ASSERT(argc_
== 1);
7142 MOZ_ASSERT(args_
[0].isObject());
7144 // Initialize the input operand.
7145 initializeInputOperand();
7147 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7149 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7150 ObjOperandId protoId
= writer
.guardToObject(arg0Id
);
7152 writer
.regExpPrototypeOptimizableResult(protoId
);
7153 writer
.returnFromIC();
7155 trackAttached("RegExpPrototypeOptimizable");
7156 return AttachDecision::Attach
;
7160 InlinableNativeIRGenerator::tryAttachRegExpInstanceOptimizable() {
7161 // Self-hosted code calls this with two object arguments.
7162 MOZ_ASSERT(argc_
== 2);
7163 MOZ_ASSERT(args_
[0].isObject());
7164 MOZ_ASSERT(args_
[1].isObject());
7166 // Initialize the input operand.
7167 initializeInputOperand();
7169 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7171 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7172 ObjOperandId regexpId
= writer
.guardToObject(arg0Id
);
7174 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7175 ObjOperandId protoId
= writer
.guardToObject(arg1Id
);
7177 writer
.regExpInstanceOptimizableResult(regexpId
, protoId
);
7178 writer
.returnFromIC();
7180 trackAttached("RegExpInstanceOptimizable");
7181 return AttachDecision::Attach
;
7184 AttachDecision
InlinableNativeIRGenerator::tryAttachGetFirstDollarIndex() {
7185 // Self-hosted code calls this with a single string argument.
7186 MOZ_ASSERT(argc_
== 1);
7187 MOZ_ASSERT(args_
[0].isString());
7189 // Initialize the input operand.
7190 initializeInputOperand();
7192 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7194 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7195 StringOperandId strId
= writer
.guardToString(arg0Id
);
7197 writer
.getFirstDollarIndexResult(strId
);
7198 writer
.returnFromIC();
7200 trackAttached("GetFirstDollarIndex");
7201 return AttachDecision::Attach
;
7204 AttachDecision
InlinableNativeIRGenerator::tryAttachSubstringKernel() {
7205 // Self-hosted code calls this with (string, int32, int32) arguments.
7206 MOZ_ASSERT(argc_
== 3);
7207 MOZ_ASSERT(args_
[0].isString());
7208 MOZ_ASSERT(args_
[1].isInt32());
7209 MOZ_ASSERT(args_
[2].isInt32());
7211 // Initialize the input operand.
7212 initializeInputOperand();
7214 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7216 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7217 StringOperandId strId
= writer
.guardToString(arg0Id
);
7219 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7220 Int32OperandId beginId
= writer
.guardToInt32(arg1Id
);
7222 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7223 Int32OperandId lengthId
= writer
.guardToInt32(arg2Id
);
7225 writer
.callSubstringKernelResult(strId
, beginId
, lengthId
);
7226 writer
.returnFromIC();
7228 trackAttached("SubstringKernel");
7229 return AttachDecision::Attach
;
7232 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectHasPrototype() {
7233 // Self-hosted code calls this with (object, object) arguments.
7234 MOZ_ASSERT(argc_
== 2);
7235 MOZ_ASSERT(args_
[0].isObject());
7236 MOZ_ASSERT(args_
[1].isObject());
7238 auto* obj
= &args_
[0].toObject().as
<NativeObject
>();
7239 auto* proto
= &args_
[1].toObject().as
<NativeObject
>();
7241 // Only attach when obj.__proto__ is proto.
7242 if (obj
->staticPrototype() != proto
) {
7243 return AttachDecision::NoAction
;
7246 // Initialize the input operand.
7247 initializeInputOperand();
7249 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7251 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7252 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7254 writer
.guardProto(objId
, proto
);
7255 writer
.loadBooleanResult(true);
7256 writer
.returnFromIC();
7258 trackAttached("ObjectHasPrototype");
7259 return AttachDecision::Attach
;
7262 static bool CanConvertToString(const Value
& v
) {
7263 return v
.isString() || v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
7266 AttachDecision
InlinableNativeIRGenerator::tryAttachString() {
7267 // Need a single argument that is or can be converted to a string.
7268 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7269 return AttachDecision::NoAction
;
7272 // Initialize the input operand.
7273 initializeInputOperand();
7275 // Guard callee is the 'String' function.
7276 emitNativeCalleeGuard();
7278 // Guard that the argument is a string or can be converted to one.
7279 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7280 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7282 // Return the string.
7283 writer
.loadStringResult(strId
);
7284 writer
.returnFromIC();
7286 trackAttached("String");
7287 return AttachDecision::Attach
;
7290 AttachDecision
InlinableNativeIRGenerator::tryAttachStringConstructor() {
7291 // Need a single argument that is or can be converted to a string.
7292 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7293 return AttachDecision::NoAction
;
7296 RootedString
emptyString(cx_
, cx_
->runtime()->emptyString
);
7297 JSObject
* templateObj
= StringObject::create(
7298 cx_
, emptyString
, /* proto = */ nullptr, TenuredObject
);
7300 cx_
->recoverFromOutOfMemory();
7301 return AttachDecision::NoAction
;
7304 // Initialize the input operand.
7305 initializeInputOperand();
7307 // Guard callee is the 'String' function.
7308 emitNativeCalleeGuard();
7310 // Guard on number and convert to string.
7311 ValOperandId argId
=
7312 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
7313 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7315 writer
.newStringObjectResult(templateObj
, strId
);
7316 writer
.returnFromIC();
7318 trackAttached("StringConstructor");
7319 return AttachDecision::Attach
;
7322 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToStringValueOf() {
7323 // Expecting no arguments.
7325 return AttachDecision::NoAction
;
7328 // Ensure |this| is a primitive string value.
7329 if (!thisval_
.isString()) {
7330 return AttachDecision::NoAction
;
7333 // Initialize the input operand.
7334 initializeInputOperand();
7336 // Guard callee is the 'toString' OR 'valueOf' native function.
7337 emitNativeCalleeGuard();
7339 // Guard |this| is a string.
7340 ValOperandId thisValId
=
7341 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7342 StringOperandId strId
= writer
.guardToString(thisValId
);
7344 // Return the string
7345 writer
.loadStringResult(strId
);
7346 writer
.returnFromIC();
7348 trackAttached("StringToStringValueOf");
7349 return AttachDecision::Attach
;
7352 AttachDecision
InlinableNativeIRGenerator::tryAttachStringReplaceString() {
7353 // Self-hosted code calls this with (string, string, string) arguments.
7354 MOZ_ASSERT(argc_
== 3);
7355 MOZ_ASSERT(args_
[0].isString());
7356 MOZ_ASSERT(args_
[1].isString());
7357 MOZ_ASSERT(args_
[2].isString());
7359 // Initialize the input operand.
7360 initializeInputOperand();
7362 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7364 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7365 StringOperandId strId
= writer
.guardToString(arg0Id
);
7367 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7368 StringOperandId patternId
= writer
.guardToString(arg1Id
);
7370 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7371 StringOperandId replacementId
= writer
.guardToString(arg2Id
);
7373 writer
.stringReplaceStringResult(strId
, patternId
, replacementId
);
7374 writer
.returnFromIC();
7376 trackAttached("StringReplaceString");
7377 return AttachDecision::Attach
;
7380 AttachDecision
InlinableNativeIRGenerator::tryAttachStringSplitString() {
7381 // Self-hosted code calls this with (string, string) arguments.
7382 MOZ_ASSERT(argc_
== 2);
7383 MOZ_ASSERT(args_
[0].isString());
7384 MOZ_ASSERT(args_
[1].isString());
7386 // Initialize the input operand.
7387 initializeInputOperand();
7389 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7391 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7392 StringOperandId strId
= writer
.guardToString(arg0Id
);
7394 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7395 StringOperandId separatorId
= writer
.guardToString(arg1Id
);
7397 writer
.stringSplitStringResult(strId
, separatorId
);
7398 writer
.returnFromIC();
7400 trackAttached("StringSplitString");
7401 return AttachDecision::Attach
;
7404 AttachDecision
InlinableNativeIRGenerator::tryAttachStringChar(
7406 // Need one argument.
7408 return AttachDecision::NoAction
;
7411 auto attach
= CanAttachStringChar(thisval_
, args_
[0], kind
);
7412 if (attach
== AttachStringChar::No
) {
7413 return AttachDecision::NoAction
;
7416 bool handleOOB
= attach
== AttachStringChar::OutOfBounds
;
7418 // Initialize the input operand.
7419 initializeInputOperand();
7421 // Guard callee is the 'charCodeAt', 'codePointAt', 'charAt', or 'at' native
7423 emitNativeCalleeGuard();
7425 // Guard this is a string.
7426 ValOperandId thisValId
=
7427 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7428 StringOperandId strId
= writer
.guardToString(thisValId
);
7430 // Guard int32 index.
7431 ValOperandId indexId
=
7432 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7433 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
7435 // Handle relative string indices, if necessary.
7436 if (kind
== StringChar::At
) {
7437 int32IndexId
= writer
.toRelativeStringIndex(int32IndexId
, strId
);
7440 // Linearize the string.
7442 // AttachStringChar doesn't have a separate state when OOB access happens on
7443 // a string which needs to be linearized, so just linearize unconditionally
7444 // for out-of-bounds accesses.
7445 if (attach
== AttachStringChar::Linearize
||
7446 attach
== AttachStringChar::OutOfBounds
) {
7448 case StringChar::CharCodeAt
:
7449 case StringChar::CharAt
:
7450 case StringChar::At
:
7451 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
7453 case StringChar::CodePointAt
:
7454 strId
= writer
.linearizeForCodePointAccess(strId
, int32IndexId
);
7459 // Load string char or code.
7461 case StringChar::CharCodeAt
:
7462 writer
.loadStringCharCodeResult(strId
, int32IndexId
, handleOOB
);
7464 case StringChar::CodePointAt
:
7465 writer
.loadStringCodePointResult(strId
, int32IndexId
, handleOOB
);
7467 case StringChar::CharAt
:
7468 writer
.loadStringCharResult(strId
, int32IndexId
, handleOOB
);
7470 case StringChar::At
:
7471 writer
.loadStringAtResult(strId
, int32IndexId
, handleOOB
);
7475 writer
.returnFromIC();
7478 case StringChar::CharCodeAt
:
7479 trackAttached("StringCharCodeAt");
7481 case StringChar::CodePointAt
:
7482 trackAttached("StringCodePointAt");
7484 case StringChar::CharAt
:
7485 trackAttached("StringCharAt");
7487 case StringChar::At
:
7488 trackAttached("StringAt");
7492 return AttachDecision::Attach
;
7495 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharCodeAt() {
7496 return tryAttachStringChar(StringChar::CharCodeAt
);
7499 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCodePointAt() {
7500 return tryAttachStringChar(StringChar::CodePointAt
);
7503 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharAt() {
7504 return tryAttachStringChar(StringChar::CharAt
);
7507 AttachDecision
InlinableNativeIRGenerator::tryAttachStringAt() {
7508 return tryAttachStringChar(StringChar::At
);
7511 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCharCode() {
7512 // Need one number argument.
7513 if (argc_
!= 1 || !args_
[0].isNumber()) {
7514 return AttachDecision::NoAction
;
7517 // Initialize the input operand.
7518 initializeInputOperand();
7520 // Guard callee is the 'fromCharCode' native function.
7521 emitNativeCalleeGuard();
7523 // Guard int32 argument.
7524 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7525 Int32OperandId codeId
;
7526 if (args_
[0].isInt32()) {
7527 codeId
= writer
.guardToInt32(argId
);
7529 // 'fromCharCode' performs ToUint16 on its input. We can use Uint32
7530 // semantics, because ToUint16(ToUint32(v)) == ToUint16(v).
7531 codeId
= writer
.guardToInt32ModUint32(argId
);
7534 // Return string created from code.
7535 writer
.stringFromCharCodeResult(codeId
);
7536 writer
.returnFromIC();
7538 trackAttached("StringFromCharCode");
7539 return AttachDecision::Attach
;
7542 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCodePoint() {
7543 // Need one int32 argument.
7544 if (argc_
!= 1 || !args_
[0].isInt32()) {
7545 return AttachDecision::NoAction
;
7548 // String.fromCodePoint throws for invalid code points.
7549 int32_t codePoint
= args_
[0].toInt32();
7550 if (codePoint
< 0 || codePoint
> int32_t(unicode::NonBMPMax
)) {
7551 return AttachDecision::NoAction
;
7554 // Initialize the input operand.
7555 initializeInputOperand();
7557 // Guard callee is the 'fromCodePoint' native function.
7558 emitNativeCalleeGuard();
7560 // Guard int32 argument.
7561 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7562 Int32OperandId codeId
= writer
.guardToInt32(argId
);
7564 // Return string created from code point.
7565 writer
.stringFromCodePointResult(codeId
);
7566 writer
.returnFromIC();
7568 trackAttached("StringFromCodePoint");
7569 return AttachDecision::Attach
;
7572 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIncludes() {
7573 // Need one string argument.
7574 if (argc_
!= 1 || !args_
[0].isString()) {
7575 return AttachDecision::NoAction
;
7578 // Ensure |this| is a primitive string value.
7579 if (!thisval_
.isString()) {
7580 return AttachDecision::NoAction
;
7583 // Initialize the input operand.
7584 initializeInputOperand();
7586 // Guard callee is the 'includes' native function.
7587 emitNativeCalleeGuard();
7589 // Guard this is a string.
7590 ValOperandId thisValId
=
7591 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7592 StringOperandId strId
= writer
.guardToString(thisValId
);
7594 // Guard string argument.
7595 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7596 StringOperandId searchStrId
= writer
.guardToString(argId
);
7598 writer
.stringIncludesResult(strId
, searchStrId
);
7599 writer
.returnFromIC();
7601 trackAttached("StringIncludes");
7602 return AttachDecision::Attach
;
7605 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIndexOf() {
7606 // Need one string argument.
7607 if (argc_
!= 1 || !args_
[0].isString()) {
7608 return AttachDecision::NoAction
;
7611 // Ensure |this| is a primitive string value.
7612 if (!thisval_
.isString()) {
7613 return AttachDecision::NoAction
;
7616 // Initialize the input operand.
7617 initializeInputOperand();
7619 // Guard callee is the 'indexOf' native function.
7620 emitNativeCalleeGuard();
7622 // Guard this is a string.
7623 ValOperandId thisValId
=
7624 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7625 StringOperandId strId
= writer
.guardToString(thisValId
);
7627 // Guard string argument.
7628 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7629 StringOperandId searchStrId
= writer
.guardToString(argId
);
7631 writer
.stringIndexOfResult(strId
, searchStrId
);
7632 writer
.returnFromIC();
7634 trackAttached("StringIndexOf");
7635 return AttachDecision::Attach
;
7638 AttachDecision
InlinableNativeIRGenerator::tryAttachStringLastIndexOf() {
7639 // Need one string argument.
7640 if (argc_
!= 1 || !args_
[0].isString()) {
7641 return AttachDecision::NoAction
;
7644 // Ensure |this| is a primitive string value.
7645 if (!thisval_
.isString()) {
7646 return AttachDecision::NoAction
;
7649 // Initialize the input operand.
7650 initializeInputOperand();
7652 // Guard callee is the 'lastIndexOf' native function.
7653 emitNativeCalleeGuard();
7655 // Guard this is a string.
7656 ValOperandId thisValId
=
7657 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7658 StringOperandId strId
= writer
.guardToString(thisValId
);
7660 // Guard string argument.
7661 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7662 StringOperandId searchStrId
= writer
.guardToString(argId
);
7664 writer
.stringLastIndexOfResult(strId
, searchStrId
);
7665 writer
.returnFromIC();
7667 trackAttached("StringLastIndexOf");
7668 return AttachDecision::Attach
;
7671 AttachDecision
InlinableNativeIRGenerator::tryAttachStringStartsWith() {
7672 // Need one string argument.
7673 if (argc_
!= 1 || !args_
[0].isString()) {
7674 return AttachDecision::NoAction
;
7677 // Ensure |this| is a primitive string value.
7678 if (!thisval_
.isString()) {
7679 return AttachDecision::NoAction
;
7682 // Initialize the input operand.
7683 initializeInputOperand();
7685 // Guard callee is the 'startsWith' native function.
7686 emitNativeCalleeGuard();
7688 // Guard this is a string.
7689 ValOperandId thisValId
=
7690 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7691 StringOperandId strId
= writer
.guardToString(thisValId
);
7693 // Guard string argument.
7694 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7695 StringOperandId searchStrId
= writer
.guardToString(argId
);
7697 writer
.stringStartsWithResult(strId
, searchStrId
);
7698 writer
.returnFromIC();
7700 trackAttached("StringStartsWith");
7701 return AttachDecision::Attach
;
7704 AttachDecision
InlinableNativeIRGenerator::tryAttachStringEndsWith() {
7705 // Need one string argument.
7706 if (argc_
!= 1 || !args_
[0].isString()) {
7707 return AttachDecision::NoAction
;
7710 // Ensure |this| is a primitive string value.
7711 if (!thisval_
.isString()) {
7712 return AttachDecision::NoAction
;
7715 // Initialize the input operand.
7716 initializeInputOperand();
7718 // Guard callee is the 'endsWith' native function.
7719 emitNativeCalleeGuard();
7721 // Guard this is a string.
7722 ValOperandId thisValId
=
7723 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7724 StringOperandId strId
= writer
.guardToString(thisValId
);
7726 // Guard string argument.
7727 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7728 StringOperandId searchStrId
= writer
.guardToString(argId
);
7730 writer
.stringEndsWithResult(strId
, searchStrId
);
7731 writer
.returnFromIC();
7733 trackAttached("StringEndsWith");
7734 return AttachDecision::Attach
;
7737 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToLowerCase() {
7738 // Expecting no arguments.
7740 return AttachDecision::NoAction
;
7743 // Ensure |this| is a primitive string value.
7744 if (!thisval_
.isString()) {
7745 return AttachDecision::NoAction
;
7748 // Initialize the input operand.
7749 initializeInputOperand();
7751 // Guard callee is the 'toLowerCase' native function.
7752 emitNativeCalleeGuard();
7754 // Guard this is a string.
7755 ValOperandId thisValId
=
7756 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7757 StringOperandId strId
= writer
.guardToString(thisValId
);
7759 // Return string converted to lower-case.
7760 writer
.stringToLowerCaseResult(strId
);
7761 writer
.returnFromIC();
7763 trackAttached("StringToLowerCase");
7764 return AttachDecision::Attach
;
7767 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToUpperCase() {
7768 // Expecting no arguments.
7770 return AttachDecision::NoAction
;
7773 // Ensure |this| is a primitive string value.
7774 if (!thisval_
.isString()) {
7775 return AttachDecision::NoAction
;
7778 // Initialize the input operand.
7779 initializeInputOperand();
7781 // Guard callee is the 'toUpperCase' native function.
7782 emitNativeCalleeGuard();
7784 // Guard this is a string.
7785 ValOperandId thisValId
=
7786 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7787 StringOperandId strId
= writer
.guardToString(thisValId
);
7789 // Return string converted to upper-case.
7790 writer
.stringToUpperCaseResult(strId
);
7791 writer
.returnFromIC();
7793 trackAttached("StringToUpperCase");
7794 return AttachDecision::Attach
;
7797 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrim() {
7798 // Expecting no arguments.
7800 return AttachDecision::NoAction
;
7803 // Ensure |this| is a primitive string value.
7804 if (!thisval_
.isString()) {
7805 return AttachDecision::NoAction
;
7808 // Initialize the input operand.
7809 initializeInputOperand();
7811 // Guard callee is the 'trim' native function.
7812 emitNativeCalleeGuard();
7814 // Guard this is a string.
7815 ValOperandId thisValId
=
7816 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7817 StringOperandId strId
= writer
.guardToString(thisValId
);
7819 writer
.stringTrimResult(strId
);
7820 writer
.returnFromIC();
7822 trackAttached("StringTrim");
7823 return AttachDecision::Attach
;
7826 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimStart() {
7827 // Expecting no arguments.
7829 return AttachDecision::NoAction
;
7832 // Ensure |this| is a primitive string value.
7833 if (!thisval_
.isString()) {
7834 return AttachDecision::NoAction
;
7837 // Initialize the input operand.
7838 initializeInputOperand();
7840 // Guard callee is the 'trimStart' native function.
7841 emitNativeCalleeGuard();
7843 // Guard this is a string.
7844 ValOperandId thisValId
=
7845 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7846 StringOperandId strId
= writer
.guardToString(thisValId
);
7848 writer
.stringTrimStartResult(strId
);
7849 writer
.returnFromIC();
7851 trackAttached("StringTrimStart");
7852 return AttachDecision::Attach
;
7855 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimEnd() {
7856 // Expecting no arguments.
7858 return AttachDecision::NoAction
;
7861 // Ensure |this| is a primitive string value.
7862 if (!thisval_
.isString()) {
7863 return AttachDecision::NoAction
;
7866 // Initialize the input operand.
7867 initializeInputOperand();
7869 // Guard callee is the 'trimEnd' native function.
7870 emitNativeCalleeGuard();
7872 // Guard this is a string.
7873 ValOperandId thisValId
=
7874 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7875 StringOperandId strId
= writer
.guardToString(thisValId
);
7877 writer
.stringTrimEndResult(strId
);
7878 writer
.returnFromIC();
7880 trackAttached("StringTrimEnd");
7881 return AttachDecision::Attach
;
7884 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRandom() {
7885 // Expecting no arguments.
7887 return AttachDecision::NoAction
;
7890 MOZ_ASSERT(cx_
->realm() == callee_
->realm(),
7891 "Shouldn't inline cross-realm Math.random because per-realm RNG");
7893 // Initialize the input operand.
7894 initializeInputOperand();
7896 // Guard callee is the 'random' native function.
7897 emitNativeCalleeGuard();
7899 mozilla::non_crypto::XorShift128PlusRNG
* rng
=
7900 &cx_
->realm()->getOrCreateRandomNumberGenerator();
7901 writer
.mathRandomResult(rng
);
7903 writer
.returnFromIC();
7905 trackAttached("MathRandom");
7906 return AttachDecision::Attach
;
7909 AttachDecision
InlinableNativeIRGenerator::tryAttachMathAbs() {
7910 // Need one argument.
7912 return AttachDecision::NoAction
;
7915 if (!args_
[0].isNumber()) {
7916 return AttachDecision::NoAction
;
7919 // Initialize the input operand.
7920 initializeInputOperand();
7922 // Guard callee is the 'abs' native function.
7923 emitNativeCalleeGuard();
7925 ValOperandId argumentId
=
7926 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7928 // abs(INT_MIN) is a double.
7929 if (args_
[0].isInt32() && args_
[0].toInt32() != INT_MIN
) {
7930 Int32OperandId int32Id
= writer
.guardToInt32(argumentId
);
7931 writer
.mathAbsInt32Result(int32Id
);
7933 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
7934 writer
.mathAbsNumberResult(numberId
);
7937 writer
.returnFromIC();
7939 trackAttached("MathAbs");
7940 return AttachDecision::Attach
;
7943 AttachDecision
InlinableNativeIRGenerator::tryAttachMathClz32() {
7944 // Need one (number) argument.
7945 if (argc_
!= 1 || !args_
[0].isNumber()) {
7946 return AttachDecision::NoAction
;
7949 // Initialize the input operand.
7950 initializeInputOperand();
7952 // Guard callee is the 'clz32' native function.
7953 emitNativeCalleeGuard();
7955 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7957 Int32OperandId int32Id
;
7958 if (args_
[0].isInt32()) {
7959 int32Id
= writer
.guardToInt32(argId
);
7961 MOZ_ASSERT(args_
[0].isDouble());
7962 NumberOperandId numId
= writer
.guardIsNumber(argId
);
7963 int32Id
= writer
.truncateDoubleToUInt32(numId
);
7965 writer
.mathClz32Result(int32Id
);
7966 writer
.returnFromIC();
7968 trackAttached("MathClz32");
7969 return AttachDecision::Attach
;
7972 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSign() {
7973 // Need one (number) argument.
7974 if (argc_
!= 1 || !args_
[0].isNumber()) {
7975 return AttachDecision::NoAction
;
7978 // Initialize the input operand.
7979 initializeInputOperand();
7981 // Guard callee is the 'sign' native function.
7982 emitNativeCalleeGuard();
7984 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7986 if (args_
[0].isInt32()) {
7987 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
7988 writer
.mathSignInt32Result(int32Id
);
7990 // Math.sign returns a double only if the input is -0 or NaN so try to
7991 // optimize the common Number => Int32 case.
7992 double d
= math_sign_impl(args_
[0].toDouble());
7994 bool resultIsInt32
= mozilla::NumberIsInt32(d
, &unused
);
7996 NumberOperandId numId
= writer
.guardIsNumber(argId
);
7997 if (resultIsInt32
) {
7998 writer
.mathSignNumberToInt32Result(numId
);
8000 writer
.mathSignNumberResult(numId
);
8004 writer
.returnFromIC();
8006 trackAttached("MathSign");
8007 return AttachDecision::Attach
;
8010 AttachDecision
InlinableNativeIRGenerator::tryAttachMathImul() {
8011 // Need two (number) arguments.
8012 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8013 return AttachDecision::NoAction
;
8016 // Initialize the input operand.
8017 initializeInputOperand();
8019 // Guard callee is the 'imul' native function.
8020 emitNativeCalleeGuard();
8022 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8023 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8025 Int32OperandId int32Arg0Id
, int32Arg1Id
;
8026 if (args_
[0].isInt32() && args_
[1].isInt32()) {
8027 int32Arg0Id
= writer
.guardToInt32(arg0Id
);
8028 int32Arg1Id
= writer
.guardToInt32(arg1Id
);
8030 // Treat both arguments as numbers if at least one of them is non-int32.
8031 NumberOperandId numArg0Id
= writer
.guardIsNumber(arg0Id
);
8032 NumberOperandId numArg1Id
= writer
.guardIsNumber(arg1Id
);
8033 int32Arg0Id
= writer
.truncateDoubleToUInt32(numArg0Id
);
8034 int32Arg1Id
= writer
.truncateDoubleToUInt32(numArg1Id
);
8036 writer
.mathImulResult(int32Arg0Id
, int32Arg1Id
);
8037 writer
.returnFromIC();
8039 trackAttached("MathImul");
8040 return AttachDecision::Attach
;
8043 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFloor() {
8044 // Need one (number) argument.
8045 if (argc_
!= 1 || !args_
[0].isNumber()) {
8046 return AttachDecision::NoAction
;
8049 // Check if the result fits in int32.
8050 double res
= math_floor_impl(args_
[0].toNumber());
8052 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8054 // Initialize the input operand.
8055 initializeInputOperand();
8057 // Guard callee is the 'floor' native function.
8058 emitNativeCalleeGuard();
8060 ValOperandId argumentId
=
8061 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8063 if (args_
[0].isInt32()) {
8064 MOZ_ASSERT(resultIsInt32
);
8066 // Use an indirect truncation to inform the optimizer it needs to preserve
8067 // a bailout when the input can't be represented as an int32, even if the
8068 // final result is fully truncated.
8069 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8070 writer
.indirectTruncateInt32Result(intId
);
8072 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8074 if (resultIsInt32
) {
8075 writer
.mathFloorToInt32Result(numberId
);
8077 writer
.mathFloorNumberResult(numberId
);
8081 writer
.returnFromIC();
8083 trackAttached("MathFloor");
8084 return AttachDecision::Attach
;
8087 AttachDecision
InlinableNativeIRGenerator::tryAttachMathCeil() {
8088 // Need one (number) argument.
8089 if (argc_
!= 1 || !args_
[0].isNumber()) {
8090 return AttachDecision::NoAction
;
8093 // Check if the result fits in int32.
8094 double res
= math_ceil_impl(args_
[0].toNumber());
8096 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8098 // Initialize the input operand.
8099 initializeInputOperand();
8101 // Guard callee is the 'ceil' native function.
8102 emitNativeCalleeGuard();
8104 ValOperandId argumentId
=
8105 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8107 if (args_
[0].isInt32()) {
8108 MOZ_ASSERT(resultIsInt32
);
8110 // Use an indirect truncation to inform the optimizer it needs to preserve
8111 // a bailout when the input can't be represented as an int32, even if the
8112 // final result is fully truncated.
8113 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8114 writer
.indirectTruncateInt32Result(intId
);
8116 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8118 if (resultIsInt32
) {
8119 writer
.mathCeilToInt32Result(numberId
);
8121 writer
.mathCeilNumberResult(numberId
);
8125 writer
.returnFromIC();
8127 trackAttached("MathCeil");
8128 return AttachDecision::Attach
;
8131 AttachDecision
InlinableNativeIRGenerator::tryAttachMathTrunc() {
8132 // Need one (number) argument.
8133 if (argc_
!= 1 || !args_
[0].isNumber()) {
8134 return AttachDecision::NoAction
;
8137 // Check if the result fits in int32.
8138 double res
= math_trunc_impl(args_
[0].toNumber());
8140 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8142 // Initialize the input operand.
8143 initializeInputOperand();
8145 // Guard callee is the 'trunc' native function.
8146 emitNativeCalleeGuard();
8148 ValOperandId argumentId
=
8149 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8151 if (args_
[0].isInt32()) {
8152 MOZ_ASSERT(resultIsInt32
);
8154 // We don't need an indirect truncation barrier here, because Math.trunc
8155 // always truncates, but never rounds its input away from zero.
8156 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8157 writer
.loadInt32Result(intId
);
8159 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8161 if (resultIsInt32
) {
8162 writer
.mathTruncToInt32Result(numberId
);
8164 writer
.mathTruncNumberResult(numberId
);
8168 writer
.returnFromIC();
8170 trackAttached("MathTrunc");
8171 return AttachDecision::Attach
;
8174 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRound() {
8175 // Need one (number) argument.
8176 if (argc_
!= 1 || !args_
[0].isNumber()) {
8177 return AttachDecision::NoAction
;
8180 // Check if the result fits in int32.
8181 double res
= math_round_impl(args_
[0].toNumber());
8183 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8185 // Initialize the input operand.
8186 initializeInputOperand();
8188 // Guard callee is the 'round' native function.
8189 emitNativeCalleeGuard();
8191 ValOperandId argumentId
=
8192 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8194 if (args_
[0].isInt32()) {
8195 MOZ_ASSERT(resultIsInt32
);
8197 // Use an indirect truncation to inform the optimizer it needs to preserve
8198 // a bailout when the input can't be represented as an int32, even if the
8199 // final result is fully truncated.
8200 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8201 writer
.indirectTruncateInt32Result(intId
);
8203 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8205 if (resultIsInt32
) {
8206 writer
.mathRoundToInt32Result(numberId
);
8208 writer
.mathFunctionNumberResult(numberId
, UnaryMathFunction::Round
);
8212 writer
.returnFromIC();
8214 trackAttached("MathRound");
8215 return AttachDecision::Attach
;
8218 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSqrt() {
8219 // Need one (number) argument.
8220 if (argc_
!= 1 || !args_
[0].isNumber()) {
8221 return AttachDecision::NoAction
;
8224 // Initialize the input operand.
8225 initializeInputOperand();
8227 // Guard callee is the 'sqrt' native function.
8228 emitNativeCalleeGuard();
8230 ValOperandId argumentId
=
8231 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8232 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8233 writer
.mathSqrtNumberResult(numberId
);
8234 writer
.returnFromIC();
8236 trackAttached("MathSqrt");
8237 return AttachDecision::Attach
;
8240 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFRound() {
8241 // Need one (number) argument.
8242 if (argc_
!= 1 || !args_
[0].isNumber()) {
8243 return AttachDecision::NoAction
;
8246 // Initialize the input operand.
8247 initializeInputOperand();
8249 // Guard callee is the 'fround' native function.
8250 emitNativeCalleeGuard();
8252 ValOperandId argumentId
=
8253 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8254 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8255 writer
.mathFRoundNumberResult(numberId
);
8256 writer
.returnFromIC();
8258 trackAttached("MathFRound");
8259 return AttachDecision::Attach
;
8262 static bool CanAttachInt32Pow(const Value
& baseVal
, const Value
& powerVal
) {
8263 auto valToInt32
= [](const Value
& v
) {
8267 if (v
.isBoolean()) {
8268 return int32_t(v
.toBoolean());
8270 MOZ_ASSERT(v
.isNull());
8273 int32_t base
= valToInt32(baseVal
);
8274 int32_t power
= valToInt32(powerVal
);
8276 // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
8277 // gets large enough. It's hard to determine when exactly y is "large enough",
8278 // so we don't use Int32PowResult when x != 1 and y < 0.
8279 // Note: it's important for this condition to match the code generated by
8280 // MacroAssembler::pow32 to prevent failure loops.
8285 double res
= powi(base
, power
);
8287 return mozilla::NumberIsInt32(res
, &unused
);
8290 AttachDecision
InlinableNativeIRGenerator::tryAttachMathPow() {
8291 // Need two number arguments.
8292 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8293 return AttachDecision::NoAction
;
8296 // Initialize the input operand.
8297 initializeInputOperand();
8299 // Guard callee is the 'pow' function.
8300 emitNativeCalleeGuard();
8302 ValOperandId baseId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8303 ValOperandId exponentId
=
8304 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8306 if (args_
[0].isInt32() && args_
[1].isInt32() &&
8307 CanAttachInt32Pow(args_
[0], args_
[1])) {
8308 Int32OperandId baseInt32Id
= writer
.guardToInt32(baseId
);
8309 Int32OperandId exponentInt32Id
= writer
.guardToInt32(exponentId
);
8310 writer
.int32PowResult(baseInt32Id
, exponentInt32Id
);
8312 NumberOperandId baseNumberId
= writer
.guardIsNumber(baseId
);
8313 NumberOperandId exponentNumberId
= writer
.guardIsNumber(exponentId
);
8314 writer
.doublePowResult(baseNumberId
, exponentNumberId
);
8317 writer
.returnFromIC();
8319 trackAttached("MathPow");
8320 return AttachDecision::Attach
;
8323 AttachDecision
InlinableNativeIRGenerator::tryAttachMathHypot() {
8324 // Only optimize if there are 2-4 arguments.
8325 if (argc_
< 2 || argc_
> 4) {
8326 return AttachDecision::NoAction
;
8329 for (size_t i
= 0; i
< argc_
; i
++) {
8330 if (!args_
[i
].isNumber()) {
8331 return AttachDecision::NoAction
;
8335 // Initialize the input operand.
8336 initializeInputOperand();
8338 // Guard callee is the 'hypot' native function.
8339 emitNativeCalleeGuard();
8341 ValOperandId firstId
=
8342 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8343 ValOperandId secondId
=
8344 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8346 NumberOperandId firstNumId
= writer
.guardIsNumber(firstId
);
8347 NumberOperandId secondNumId
= writer
.guardIsNumber(secondId
);
8349 ValOperandId thirdId
;
8350 ValOperandId fourthId
;
8351 NumberOperandId thirdNumId
;
8352 NumberOperandId fourthNumId
;
8356 writer
.mathHypot2NumberResult(firstNumId
, secondNumId
);
8359 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8360 thirdNumId
= writer
.guardIsNumber(thirdId
);
8361 writer
.mathHypot3NumberResult(firstNumId
, secondNumId
, thirdNumId
);
8364 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8365 fourthId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8366 thirdNumId
= writer
.guardIsNumber(thirdId
);
8367 fourthNumId
= writer
.guardIsNumber(fourthId
);
8368 writer
.mathHypot4NumberResult(firstNumId
, secondNumId
, thirdNumId
,
8372 MOZ_CRASH("Unexpected number of arguments to hypot function.");
8375 writer
.returnFromIC();
8377 trackAttached("MathHypot");
8378 return AttachDecision::Attach
;
8381 AttachDecision
InlinableNativeIRGenerator::tryAttachMathATan2() {
8382 // Requires two numbers as arguments.
8383 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8384 return AttachDecision::NoAction
;
8387 // Initialize the input operand.
8388 initializeInputOperand();
8390 // Guard callee is the 'atan2' native function.
8391 emitNativeCalleeGuard();
8393 ValOperandId yId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8394 ValOperandId xId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8396 NumberOperandId yNumberId
= writer
.guardIsNumber(yId
);
8397 NumberOperandId xNumberId
= writer
.guardIsNumber(xId
);
8399 writer
.mathAtan2NumberResult(yNumberId
, xNumberId
);
8400 writer
.returnFromIC();
8402 trackAttached("MathAtan2");
8403 return AttachDecision::Attach
;
8406 AttachDecision
InlinableNativeIRGenerator::tryAttachMathMinMax(bool isMax
) {
8407 // For now only optimize if there are 1-4 arguments.
8408 if (argc_
< 1 || argc_
> 4) {
8409 return AttachDecision::NoAction
;
8412 // Ensure all arguments are numbers.
8413 bool allInt32
= true;
8414 for (size_t i
= 0; i
< argc_
; i
++) {
8415 if (!args_
[i
].isNumber()) {
8416 return AttachDecision::NoAction
;
8418 if (!args_
[i
].isInt32()) {
8423 // Initialize the input operand.
8424 initializeInputOperand();
8426 // Guard callee is this Math function.
8427 emitNativeCalleeGuard();
8430 ValOperandId valId
=
8431 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8432 Int32OperandId resId
= writer
.guardToInt32(valId
);
8433 for (size_t i
= 1; i
< argc_
; i
++) {
8434 ValOperandId argId
=
8435 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8436 Int32OperandId argInt32Id
= writer
.guardToInt32(argId
);
8437 resId
= writer
.int32MinMax(isMax
, resId
, argInt32Id
);
8439 writer
.loadInt32Result(resId
);
8441 ValOperandId valId
=
8442 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8443 NumberOperandId resId
= writer
.guardIsNumber(valId
);
8444 for (size_t i
= 1; i
< argc_
; i
++) {
8445 ValOperandId argId
=
8446 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8447 NumberOperandId argNumId
= writer
.guardIsNumber(argId
);
8448 resId
= writer
.numberMinMax(isMax
, resId
, argNumId
);
8450 writer
.loadDoubleResult(resId
);
8453 writer
.returnFromIC();
8455 trackAttached(isMax
? "MathMax" : "MathMin");
8456 return AttachDecision::Attach
;
8459 AttachDecision
InlinableNativeIRGenerator::tryAttachSpreadMathMinMax(
8461 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Spread
||
8462 flags_
.getArgFormat() == CallFlags::FunApplyArray
);
8464 // The result will be an int32 if there is at least one argument,
8465 // and all the arguments are int32.
8466 bool int32Result
= args_
.length() > 0;
8467 for (size_t i
= 0; i
< args_
.length(); i
++) {
8468 if (!args_
[i
].isNumber()) {
8469 return AttachDecision::NoAction
;
8471 if (!args_
[i
].isInt32()) {
8472 int32Result
= false;
8476 // Initialize the input operand.
8477 initializeInputOperand();
8479 // Guard callee is this Math function.
8480 emitNativeCalleeGuard();
8482 // Load the argument array.
8483 ObjOperandId argsId
= emitLoadArgsArray();
8486 writer
.int32MinMaxArrayResult(argsId
, isMax
);
8488 writer
.numberMinMaxArrayResult(argsId
, isMax
);
8491 writer
.returnFromIC();
8493 trackAttached(isMax
? "MathMaxArray" : "MathMinArray");
8494 return AttachDecision::Attach
;
8497 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFunction(
8498 UnaryMathFunction fun
) {
8499 // Need one argument.
8501 return AttachDecision::NoAction
;
8504 if (!args_
[0].isNumber()) {
8505 return AttachDecision::NoAction
;
8508 if (math_use_fdlibm_for_sin_cos_tan() ||
8509 callee_
->realm()->creationOptions().alwaysUseFdlibm()) {
8511 case UnaryMathFunction::SinNative
:
8512 fun
= UnaryMathFunction::SinFdlibm
;
8514 case UnaryMathFunction::CosNative
:
8515 fun
= UnaryMathFunction::CosFdlibm
;
8517 case UnaryMathFunction::TanNative
:
8518 fun
= UnaryMathFunction::TanFdlibm
;
8525 // Initialize the input operand.
8526 initializeInputOperand();
8528 // Guard callee is this Math function.
8529 emitNativeCalleeGuard();
8531 ValOperandId argumentId
=
8532 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8533 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8534 writer
.mathFunctionNumberResult(numberId
, fun
);
8535 writer
.returnFromIC();
8537 trackAttached("MathFunction");
8538 return AttachDecision::Attach
;
8541 AttachDecision
InlinableNativeIRGenerator::tryAttachNumber() {
8542 // Expect a single string argument.
8543 if (argc_
!= 1 || !args_
[0].isString()) {
8544 return AttachDecision::NoAction
;
8548 if (!StringToNumber(cx_
, args_
[0].toString(), &num
)) {
8549 cx_
->recoverFromOutOfMemory();
8550 return AttachDecision::NoAction
;
8553 // Initialize the input operand.
8554 initializeInputOperand();
8556 // Guard callee is the `Number` function.
8557 emitNativeCalleeGuard();
8559 // Guard that the argument is a string.
8560 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8561 StringOperandId strId
= writer
.guardToString(argId
);
8563 // Return either an Int32 or Double result.
8565 if (mozilla::NumberIsInt32(num
, &unused
)) {
8566 Int32OperandId resultId
= writer
.guardStringToInt32(strId
);
8567 writer
.loadInt32Result(resultId
);
8569 NumberOperandId resultId
= writer
.guardStringToNumber(strId
);
8570 writer
.loadDoubleResult(resultId
);
8572 writer
.returnFromIC();
8574 trackAttached("Number");
8575 return AttachDecision::Attach
;
8578 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberParseInt() {
8579 // Expected arguments: input (string or number), optional radix (int32).
8580 if (argc_
< 1 || argc_
> 2) {
8581 return AttachDecision::NoAction
;
8583 if (!args_
[0].isString() && !args_
[0].isNumber()) {
8584 return AttachDecision::NoAction
;
8586 if (args_
[0].isDouble()) {
8587 double d
= args_
[0].toDouble();
8589 // See num_parseInt for why we have to reject numbers smaller than 1.0e-6.
8590 // Negative numbers in the exclusive range (-1, -0) return -0.
8591 bool canTruncateToInt32
=
8592 (DOUBLE_DECIMAL_IN_SHORTEST_LOW
<= d
&& d
<= double(INT32_MAX
)) ||
8593 (double(INT32_MIN
) <= d
&& d
<= -1.0) || (d
== 0.0);
8594 if (!canTruncateToInt32
) {
8595 return AttachDecision::NoAction
;
8598 if (argc_
> 1 && !args_
[1].isInt32(10)) {
8599 return AttachDecision::NoAction
;
8602 // Initialize the input operand.
8603 initializeInputOperand();
8605 // Guard callee is the 'parseInt' native function.
8606 emitNativeCalleeGuard();
8608 auto guardRadix
= [&]() {
8609 ValOperandId radixId
=
8610 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8611 Int32OperandId intRadixId
= writer
.guardToInt32(radixId
);
8612 writer
.guardSpecificInt32(intRadixId
, 10);
8616 ValOperandId inputId
=
8617 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8619 if (args_
[0].isString()) {
8620 StringOperandId strId
= writer
.guardToString(inputId
);
8622 Int32OperandId intRadixId
;
8624 intRadixId
= guardRadix();
8626 intRadixId
= writer
.loadInt32Constant(0);
8629 writer
.numberParseIntResult(strId
, intRadixId
);
8630 } else if (args_
[0].isInt32()) {
8631 Int32OperandId intId
= writer
.guardToInt32(inputId
);
8635 writer
.loadInt32Result(intId
);
8637 MOZ_ASSERT(args_
[0].isDouble());
8639 NumberOperandId numId
= writer
.guardIsNumber(inputId
);
8643 writer
.doubleParseIntResult(numId
);
8646 writer
.returnFromIC();
8648 trackAttached("NumberParseInt");
8649 return AttachDecision::Attach
;
8652 StringOperandId
IRGenerator::emitToStringGuard(ValOperandId id
,
8654 MOZ_ASSERT(CanConvertToString(v
));
8656 return writer
.guardToString(id
);
8658 if (v
.isBoolean()) {
8659 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
8660 return writer
.booleanToString(boolId
);
8663 writer
.guardIsNull(id
);
8664 return writer
.loadConstantString(cx_
->names().null
);
8666 if (v
.isUndefined()) {
8667 writer
.guardIsUndefined(id
);
8668 return writer
.loadConstantString(cx_
->names().undefined
);
8671 Int32OperandId intId
= writer
.guardToInt32(id
);
8672 return writer
.callInt32ToString(intId
);
8674 // At this point we are creating an IC that will handle
8675 // both Int32 and Double cases.
8676 MOZ_ASSERT(v
.isNumber());
8677 NumberOperandId numId
= writer
.guardIsNumber(id
);
8678 return writer
.callNumberToString(numId
);
8681 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberToString() {
8682 // Expecting no arguments or a single int32 argument.
8684 return AttachDecision::NoAction
;
8686 if (argc_
== 1 && !args_
[0].isInt32()) {
8687 return AttachDecision::NoAction
;
8690 // Ensure |this| is a primitive number value.
8691 if (!thisval_
.isNumber()) {
8692 return AttachDecision::NoAction
;
8695 // No arguments means base 10.
8698 base
= args_
[0].toInt32();
8699 if (base
< 2 || base
> 36) {
8700 return AttachDecision::NoAction
;
8703 // Non-decimal bases currently only support int32 inputs.
8704 if (base
!= 10 && !thisval_
.isInt32()) {
8705 return AttachDecision::NoAction
;
8708 MOZ_ASSERT(2 <= base
&& base
<= 36);
8710 // Initialize the input operand.
8711 initializeInputOperand();
8713 // Guard callee is the 'toString' native function.
8714 emitNativeCalleeGuard();
8716 // Initialize the |this| operand.
8717 ValOperandId thisValId
=
8718 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8720 // Guard on number and convert to string.
8722 // If an explicit base was passed, guard its value.
8724 // Guard the `base` argument is an int32.
8725 ValOperandId baseId
=
8726 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8727 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
8729 // Guard `base` is 10 for decimal toString representation.
8730 writer
.guardSpecificInt32(intBaseId
, 10);
8733 StringOperandId strId
= emitToStringGuard(thisValId
, thisval_
);
8735 // Return the string.
8736 writer
.loadStringResult(strId
);
8738 MOZ_ASSERT(argc_
> 0);
8740 // Guard the |this| value is an int32.
8741 Int32OperandId thisIntId
= writer
.guardToInt32(thisValId
);
8743 // Guard the `base` argument is an int32.
8744 ValOperandId baseId
=
8745 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8746 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
8748 // Return the string.
8749 writer
.int32ToStringWithBaseResult(thisIntId
, intBaseId
);
8752 writer
.returnFromIC();
8754 trackAttached("NumberToString");
8755 return AttachDecision::Attach
;
8758 AttachDecision
InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
8759 // Need one argument.
8761 return AttachDecision::NoAction
;
8764 if (!args_
[0].isObject()) {
8765 return AttachDecision::NoAction
;
8768 // Initialize the input operand.
8769 initializeInputOperand();
8771 // Guard callee is the 'getPrototypeOf' native function.
8772 emitNativeCalleeGuard();
8774 ValOperandId argumentId
=
8775 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8776 ObjOperandId objId
= writer
.guardToObject(argumentId
);
8778 writer
.reflectGetPrototypeOfResult(objId
);
8779 writer
.returnFromIC();
8781 trackAttached("ReflectGetPrototypeOf");
8782 return AttachDecision::Attach
;
8785 static bool AtomicsMeetsPreconditions(TypedArrayObject
* typedArray
,
8786 const Value
& index
) {
8787 switch (typedArray
->type()) {
8791 case Scalar::Uint16
:
8793 case Scalar::Uint32
:
8794 case Scalar::BigInt64
:
8795 case Scalar::BigUint64
:
8798 case Scalar::Float32
:
8799 case Scalar::Float64
:
8800 case Scalar::Uint8Clamped
:
8801 // Exclude floating types and Uint8Clamped.
8804 case Scalar::MaxTypedArrayViewType
:
8806 case Scalar::Simd128
:
8807 MOZ_CRASH("Unsupported TypedArray type");
8810 // Bounds check the index argument.
8812 if (!ValueIsInt64Index(index
, &indexInt64
)) {
8815 if (indexInt64
< 0 || uint64_t(indexInt64
) >= typedArray
->length()) {
8822 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
8823 if (!JitSupportsAtomics()) {
8824 return AttachDecision::NoAction
;
8827 // Need four arguments.
8829 return AttachDecision::NoAction
;
8832 // Arguments: typedArray, index (number), expected, replacement.
8833 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
8834 return AttachDecision::NoAction
;
8836 if (!args_
[1].isNumber()) {
8837 return AttachDecision::NoAction
;
8840 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8841 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
8842 return AttachDecision::NoAction
;
8845 Scalar::Type elementType
= typedArray
->type();
8846 if (!ValueIsNumeric(elementType
, args_
[2])) {
8847 return AttachDecision::NoAction
;
8849 if (!ValueIsNumeric(elementType
, args_
[3])) {
8850 return AttachDecision::NoAction
;
8853 // Initialize the input operand.
8854 initializeInputOperand();
8856 // Guard callee is the `compareExchange` native function.
8857 emitNativeCalleeGuard();
8859 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8860 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
8861 writer
.guardShapeForClass(objId
, typedArray
->shape());
8863 // Convert index to intPtr.
8864 ValOperandId indexId
=
8865 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8866 IntPtrOperandId intPtrIndexId
=
8867 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
8869 // Convert expected value to int32/BigInt.
8870 ValOperandId expectedId
=
8871 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8872 OperandId numericExpectedId
= emitNumericGuard(expectedId
, elementType
);
8874 // Convert replacement value to int32/BigInt.
8875 ValOperandId replacementId
=
8876 writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8877 OperandId numericReplacementId
= emitNumericGuard(replacementId
, elementType
);
8879 writer
.atomicsCompareExchangeResult(objId
, intPtrIndexId
, numericExpectedId
,
8880 numericReplacementId
, typedArray
->type());
8881 writer
.returnFromIC();
8883 trackAttached("AtomicsCompareExchange");
8884 return AttachDecision::Attach
;
8887 bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
8888 if (!JitSupportsAtomics()) {
8892 // Need three arguments.
8897 // Arguments: typedArray, index (number), value.
8898 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
8901 if (!args_
[1].isNumber()) {
8905 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8906 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
8909 if (!ValueIsNumeric(typedArray
->type(), args_
[2])) {
8915 InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands
8916 InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() {
8917 MOZ_ASSERT(canAttachAtomicsReadWriteModify());
8919 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8921 // Initialize the input operand.
8922 initializeInputOperand();
8924 // Guard callee is this Atomics function.
8925 emitNativeCalleeGuard();
8927 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8928 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
8929 writer
.guardShapeForClass(objId
, typedArray
->shape());
8931 // Convert index to intPtr.
8932 ValOperandId indexId
=
8933 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8934 IntPtrOperandId intPtrIndexId
=
8935 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
8937 // Convert value to int32/BigInt.
8938 ValOperandId valueId
=
8939 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8940 OperandId numericValueId
= emitNumericGuard(valueId
, typedArray
->type());
8942 return {objId
, intPtrIndexId
, numericValueId
};
8945 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsExchange() {
8946 if (!canAttachAtomicsReadWriteModify()) {
8947 return AttachDecision::NoAction
;
8950 auto [objId
, intPtrIndexId
, numericValueId
] =
8951 emitAtomicsReadWriteModifyOperands();
8953 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8955 writer
.atomicsExchangeResult(objId
, intPtrIndexId
, numericValueId
,
8956 typedArray
->type());
8957 writer
.returnFromIC();
8959 trackAttached("AtomicsExchange");
8960 return AttachDecision::Attach
;
8963 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAdd() {
8964 if (!canAttachAtomicsReadWriteModify()) {
8965 return AttachDecision::NoAction
;
8968 auto [objId
, intPtrIndexId
, numericValueId
] =
8969 emitAtomicsReadWriteModifyOperands();
8971 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8972 bool forEffect
= ignoresResult();
8974 writer
.atomicsAddResult(objId
, intPtrIndexId
, numericValueId
,
8975 typedArray
->type(), forEffect
);
8976 writer
.returnFromIC();
8978 trackAttached("AtomicsAdd");
8979 return AttachDecision::Attach
;
8982 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsSub() {
8983 if (!canAttachAtomicsReadWriteModify()) {
8984 return AttachDecision::NoAction
;
8987 auto [objId
, intPtrIndexId
, numericValueId
] =
8988 emitAtomicsReadWriteModifyOperands();
8990 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
8991 bool forEffect
= ignoresResult();
8993 writer
.atomicsSubResult(objId
, intPtrIndexId
, numericValueId
,
8994 typedArray
->type(), forEffect
);
8995 writer
.returnFromIC();
8997 trackAttached("AtomicsSub");
8998 return AttachDecision::Attach
;
9001 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAnd() {
9002 if (!canAttachAtomicsReadWriteModify()) {
9003 return AttachDecision::NoAction
;
9006 auto [objId
, intPtrIndexId
, numericValueId
] =
9007 emitAtomicsReadWriteModifyOperands();
9009 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9010 bool forEffect
= ignoresResult();
9012 writer
.atomicsAndResult(objId
, intPtrIndexId
, numericValueId
,
9013 typedArray
->type(), forEffect
);
9014 writer
.returnFromIC();
9016 trackAttached("AtomicsAnd");
9017 return AttachDecision::Attach
;
9020 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsOr() {
9021 if (!canAttachAtomicsReadWriteModify()) {
9022 return AttachDecision::NoAction
;
9025 auto [objId
, intPtrIndexId
, numericValueId
] =
9026 emitAtomicsReadWriteModifyOperands();
9028 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9029 bool forEffect
= ignoresResult();
9031 writer
.atomicsOrResult(objId
, intPtrIndexId
, numericValueId
,
9032 typedArray
->type(), forEffect
);
9033 writer
.returnFromIC();
9035 trackAttached("AtomicsOr");
9036 return AttachDecision::Attach
;
9039 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsXor() {
9040 if (!canAttachAtomicsReadWriteModify()) {
9041 return AttachDecision::NoAction
;
9044 auto [objId
, intPtrIndexId
, numericValueId
] =
9045 emitAtomicsReadWriteModifyOperands();
9047 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9048 bool forEffect
= ignoresResult();
9050 writer
.atomicsXorResult(objId
, intPtrIndexId
, numericValueId
,
9051 typedArray
->type(), forEffect
);
9052 writer
.returnFromIC();
9054 trackAttached("AtomicsXor");
9055 return AttachDecision::Attach
;
9058 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
9059 if (!JitSupportsAtomics()) {
9060 return AttachDecision::NoAction
;
9063 // Need two arguments.
9065 return AttachDecision::NoAction
;
9068 // Arguments: typedArray, index (number).
9069 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9070 return AttachDecision::NoAction
;
9072 if (!args_
[1].isNumber()) {
9073 return AttachDecision::NoAction
;
9076 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9077 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9078 return AttachDecision::NoAction
;
9081 // Initialize the input operand.
9082 initializeInputOperand();
9084 // Guard callee is the `load` native function.
9085 emitNativeCalleeGuard();
9087 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9088 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9089 writer
.guardShapeForClass(objId
, typedArray
->shape());
9091 // Convert index to intPtr.
9092 ValOperandId indexId
=
9093 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9094 IntPtrOperandId intPtrIndexId
=
9095 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9097 writer
.atomicsLoadResult(objId
, intPtrIndexId
, typedArray
->type());
9098 writer
.returnFromIC();
9100 trackAttached("AtomicsLoad");
9101 return AttachDecision::Attach
;
9104 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsStore() {
9105 if (!JitSupportsAtomics()) {
9106 return AttachDecision::NoAction
;
9109 // Need three arguments.
9111 return AttachDecision::NoAction
;
9114 // Atomics.store() is annoying because it returns the result of converting the
9115 // value by ToInteger(), not the input value, nor the result of converting the
9116 // value by ToInt32(). It is especially annoying because almost nobody uses
9117 // the result value.
9119 // As an expedient compromise, therefore, we inline only if the result is
9120 // obviously unused or if the argument is already Int32 and thus requires no
9123 // Arguments: typedArray, index (number), value.
9124 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9125 return AttachDecision::NoAction
;
9127 if (!args_
[1].isNumber()) {
9128 return AttachDecision::NoAction
;
9131 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9132 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9133 return AttachDecision::NoAction
;
9136 Scalar::Type elementType
= typedArray
->type();
9137 if (!ValueIsNumeric(elementType
, args_
[2])) {
9138 return AttachDecision::NoAction
;
9141 bool guardIsInt32
= !Scalar::isBigIntType(elementType
) && !ignoresResult();
9143 if (guardIsInt32
&& !args_
[2].isInt32()) {
9144 return AttachDecision::NoAction
;
9147 // Initialize the input operand.
9148 initializeInputOperand();
9150 // Guard callee is the `store` native function.
9151 emitNativeCalleeGuard();
9153 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9154 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9155 writer
.guardShapeForClass(objId
, typedArray
->shape());
9157 // Convert index to intPtr.
9158 ValOperandId indexId
=
9159 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9160 IntPtrOperandId intPtrIndexId
=
9161 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9163 // Ensure value is int32 or BigInt.
9164 ValOperandId valueId
=
9165 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9166 OperandId numericValueId
;
9168 numericValueId
= writer
.guardToInt32(valueId
);
9170 numericValueId
= emitNumericGuard(valueId
, elementType
);
9173 writer
.atomicsStoreResult(objId
, intPtrIndexId
, numericValueId
,
9174 typedArray
->type());
9175 writer
.returnFromIC();
9177 trackAttached("AtomicsStore");
9178 return AttachDecision::Attach
;
9181 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsIsLockFree() {
9182 // Need one argument.
9184 return AttachDecision::NoAction
;
9187 if (!args_
[0].isInt32()) {
9188 return AttachDecision::NoAction
;
9191 // Initialize the input operand.
9192 initializeInputOperand();
9194 // Guard callee is the `isLockFree` native function.
9195 emitNativeCalleeGuard();
9197 // Ensure value is int32.
9198 ValOperandId valueId
=
9199 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9200 Int32OperandId int32ValueId
= writer
.guardToInt32(valueId
);
9202 writer
.atomicsIsLockFreeResult(int32ValueId
);
9203 writer
.returnFromIC();
9205 trackAttached("AtomicsIsLockFree");
9206 return AttachDecision::Attach
;
9209 AttachDecision
InlinableNativeIRGenerator::tryAttachBoolean() {
9210 // Need zero or one argument.
9212 return AttachDecision::NoAction
;
9215 // Initialize the input operand.
9216 initializeInputOperand();
9218 // Guard callee is the 'Boolean' native function.
9219 emitNativeCalleeGuard();
9222 writer
.loadBooleanResult(false);
9224 ValOperandId valId
=
9225 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9227 writer
.loadValueTruthyResult(valId
);
9230 writer
.returnFromIC();
9232 trackAttached("Boolean");
9233 return AttachDecision::Attach
;
9236 AttachDecision
InlinableNativeIRGenerator::tryAttachBailout() {
9237 // Expecting no arguments.
9239 return AttachDecision::NoAction
;
9242 // Initialize the input operand.
9243 initializeInputOperand();
9245 // Guard callee is the 'bailout' native function.
9246 emitNativeCalleeGuard();
9249 writer
.loadUndefinedResult();
9250 writer
.returnFromIC();
9252 trackAttached("Bailout");
9253 return AttachDecision::Attach
;
9256 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertFloat32() {
9257 // Expecting two arguments.
9259 return AttachDecision::NoAction
;
9262 // Initialize the input operand.
9263 initializeInputOperand();
9265 // Guard callee is the 'assertFloat32' native function.
9266 emitNativeCalleeGuard();
9268 // TODO: Warp doesn't yet optimize Float32 (bug 1655773).
9270 // NOP when not in IonMonkey.
9271 writer
.loadUndefinedResult();
9272 writer
.returnFromIC();
9274 trackAttached("AssertFloat32");
9275 return AttachDecision::Attach
;
9278 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertRecoveredOnBailout() {
9279 // Expecting two arguments.
9281 return AttachDecision::NoAction
;
9284 // (Fuzzing unsafe) testing function which must be called with a constant
9285 // boolean as its second argument.
9286 bool mustBeRecovered
= args_
[1].toBoolean();
9288 // Initialize the input operand.
9289 initializeInputOperand();
9291 // Guard callee is the 'assertRecoveredOnBailout' native function.
9292 emitNativeCalleeGuard();
9294 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9296 writer
.assertRecoveredOnBailoutResult(valId
, mustBeRecovered
);
9297 writer
.returnFromIC();
9299 trackAttached("AssertRecoveredOnBailout");
9300 return AttachDecision::Attach
;
9303 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIs() {
9304 // Need two arguments.
9306 return AttachDecision::NoAction
;
9309 // Initialize the input operand.
9310 initializeInputOperand();
9312 // Guard callee is the `is` native function.
9313 emitNativeCalleeGuard();
9315 ValOperandId lhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9316 ValOperandId rhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9318 HandleValue lhs
= args_
[0];
9319 HandleValue rhs
= args_
[1];
9321 if (!isFirstStub()) {
9322 writer
.sameValueResult(lhsId
, rhsId
);
9323 } else if (lhs
.isNumber() && rhs
.isNumber() &&
9324 !(lhs
.isInt32() && rhs
.isInt32())) {
9325 NumberOperandId lhsNumId
= writer
.guardIsNumber(lhsId
);
9326 NumberOperandId rhsNumId
= writer
.guardIsNumber(rhsId
);
9327 writer
.compareDoubleSameValueResult(lhsNumId
, rhsNumId
);
9328 } else if (!SameType(lhs
, rhs
)) {
9329 // Compare tags for strictly different types.
9330 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
9331 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
9332 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
9333 writer
.loadBooleanResult(false);
9335 MOZ_ASSERT(lhs
.type() == rhs
.type());
9336 MOZ_ASSERT(lhs
.type() != JS::ValueType::Double
);
9338 switch (lhs
.type()) {
9339 case JS::ValueType::Int32
: {
9340 Int32OperandId lhsIntId
= writer
.guardToInt32(lhsId
);
9341 Int32OperandId rhsIntId
= writer
.guardToInt32(rhsId
);
9342 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9345 case JS::ValueType::Boolean
: {
9346 Int32OperandId lhsIntId
= writer
.guardBooleanToInt32(lhsId
);
9347 Int32OperandId rhsIntId
= writer
.guardBooleanToInt32(rhsId
);
9348 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9351 case JS::ValueType::Undefined
: {
9352 writer
.guardIsUndefined(lhsId
);
9353 writer
.guardIsUndefined(rhsId
);
9354 writer
.loadBooleanResult(true);
9357 case JS::ValueType::Null
: {
9358 writer
.guardIsNull(lhsId
);
9359 writer
.guardIsNull(rhsId
);
9360 writer
.loadBooleanResult(true);
9363 case JS::ValueType::String
: {
9364 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
9365 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
9366 writer
.compareStringResult(JSOp::StrictEq
, lhsStrId
, rhsStrId
);
9369 case JS::ValueType::Symbol
: {
9370 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
9371 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
9372 writer
.compareSymbolResult(JSOp::StrictEq
, lhsSymId
, rhsSymId
);
9375 case JS::ValueType::BigInt
: {
9376 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
9377 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
9378 writer
.compareBigIntResult(JSOp::StrictEq
, lhsBigIntId
, rhsBigIntId
);
9381 case JS::ValueType::Object
: {
9382 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
9383 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
9384 writer
.compareObjectResult(JSOp::StrictEq
, lhsObjId
, rhsObjId
);
9388 #ifdef ENABLE_RECORD_TUPLE
9389 case ValueType::ExtendedPrimitive
:
9391 case JS::ValueType::Double
:
9392 case JS::ValueType::Magic
:
9393 case JS::ValueType::PrivateGCThing
:
9394 MOZ_CRASH("Unexpected type");
9398 writer
.returnFromIC();
9400 trackAttached("ObjectIs");
9401 return AttachDecision::Attach
;
9404 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIsPrototypeOf() {
9405 // Ensure |this| is an object.
9406 if (!thisval_
.isObject()) {
9407 return AttachDecision::NoAction
;
9410 // Need a single argument.
9412 return AttachDecision::NoAction
;
9415 // Initialize the input operand.
9416 initializeInputOperand();
9418 // Guard callee is the `isPrototypeOf` native function.
9419 emitNativeCalleeGuard();
9421 // Guard that |this| is an object.
9422 ValOperandId thisValId
=
9423 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9424 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9426 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9428 writer
.loadInstanceOfObjectResult(argId
, thisObjId
);
9429 writer
.returnFromIC();
9431 trackAttached("ObjectIsPrototypeOf");
9432 return AttachDecision::Attach
;
9435 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectKeys() {
9436 // Only handle argc <= 1.
9438 return AttachDecision::NoAction
;
9441 // Do not attach any IC if the argument is not an object.
9442 if (!args_
[0].isObject()) {
9443 return AttachDecision::NoAction
;
9445 // Do not attach any IC if the argument is a Proxy. While implementation could
9446 // work with proxies the goal of this implementation is to provide an
9447 // optimization for calls of `Object.keys(obj)` where there is no side-effect,
9448 // and where the computation of the array of property name can be moved.
9449 const JSClass
* clasp
= args_
[0].toObject().getClass();
9450 if (clasp
->isProxyObject()) {
9451 return AttachDecision::NoAction
;
9454 // Generate cache IR code to attach a new inline cache which will delegate the
9455 // call to Object.keys to the native function.
9456 initializeInputOperand();
9458 // Guard callee is the 'keys' native function.
9459 emitNativeCalleeGuard();
9461 // Implicit: Note `Object.keys` is a property of the `Object` global. The fact
9462 // that we are in this function implies that we already identify the function
9463 // as being the proper one. Thus there should not be any need to validate that
9464 // this is the proper function. (test: ion/object-keys-05)
9466 // Guard `arg0` is an object.
9467 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9468 ObjOperandId argObjId
= writer
.guardToObject(argId
);
9470 // Guard against proxies.
9471 writer
.guardIsNotProxy(argObjId
);
9473 // Compute the keys array.
9474 writer
.objectKeysResult(argObjId
);
9476 writer
.returnFromIC();
9478 trackAttached("ObjectKeys");
9479 return AttachDecision::Attach
;
9482 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectToString() {
9483 // Expecting no arguments.
9485 return AttachDecision::NoAction
;
9488 // Ensure |this| is an object.
9489 if (!thisval_
.isObject()) {
9490 return AttachDecision::NoAction
;
9493 // Don't attach if the object has @@toStringTag or is a proxy.
9494 if (!ObjectClassToString(cx_
, &thisval_
.toObject())) {
9495 return AttachDecision::NoAction
;
9498 // Initialize the input operand.
9499 initializeInputOperand();
9501 // Guard callee is the 'toString' native function.
9502 emitNativeCalleeGuard();
9504 // Guard that |this| is an object.
9505 ValOperandId thisValId
=
9506 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9507 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9509 writer
.objectToStringResult(thisObjId
);
9510 writer
.returnFromIC();
9512 trackAttached("ObjectToString");
9513 return AttachDecision::Attach
;
9516 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsIntN() {
9517 // Need two arguments (Int32, BigInt).
9518 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9519 return AttachDecision::NoAction
;
9522 // Negative bits throws an error.
9523 if (args_
[0].toInt32() < 0) {
9524 return AttachDecision::NoAction
;
9527 // Initialize the input operand.
9528 initializeInputOperand();
9530 // Guard callee is the 'BigInt.asIntN' native function.
9531 emitNativeCalleeGuard();
9533 // Convert bits to int32.
9534 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9535 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9537 // Number of bits mustn't be negative.
9538 writer
.guardInt32IsNonNegative(int32BitsId
);
9540 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9541 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9543 writer
.bigIntAsIntNResult(int32BitsId
, bigIntId
);
9544 writer
.returnFromIC();
9546 trackAttached("BigIntAsIntN");
9547 return AttachDecision::Attach
;
9550 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsUintN() {
9551 // Need two arguments (Int32, BigInt).
9552 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9553 return AttachDecision::NoAction
;
9556 // Negative bits throws an error.
9557 if (args_
[0].toInt32() < 0) {
9558 return AttachDecision::NoAction
;
9561 // Initialize the input operand.
9562 initializeInputOperand();
9564 // Guard callee is the 'BigInt.asUintN' native function.
9565 emitNativeCalleeGuard();
9567 // Convert bits to int32.
9568 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9569 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9571 // Number of bits mustn't be negative.
9572 writer
.guardInt32IsNonNegative(int32BitsId
);
9574 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9575 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9577 writer
.bigIntAsUintNResult(int32BitsId
, bigIntId
);
9578 writer
.returnFromIC();
9580 trackAttached("BigIntAsUintN");
9581 return AttachDecision::Attach
;
9584 AttachDecision
InlinableNativeIRGenerator::tryAttachSetHas() {
9585 // Ensure |this| is a SetObject.
9586 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
9587 return AttachDecision::NoAction
;
9590 // Need a single argument.
9592 return AttachDecision::NoAction
;
9595 // Initialize the input operand.
9596 initializeInputOperand();
9598 // Guard callee is the 'has' native function.
9599 emitNativeCalleeGuard();
9601 // Guard |this| is a SetObject.
9602 ValOperandId thisValId
=
9603 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9604 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9605 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Set
);
9607 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9609 #ifndef JS_CODEGEN_X86
9610 // Assume the hash key will likely always have the same type when attaching
9611 // the first stub. If the call is polymorphic on the hash key, attach a stub
9612 // which handles any value.
9613 if (isFirstStub()) {
9614 switch (args_
[0].type()) {
9615 case ValueType::Double
:
9616 case ValueType::Int32
:
9617 case ValueType::Boolean
:
9618 case ValueType::Undefined
:
9619 case ValueType::Null
: {
9620 writer
.guardToNonGCThing(argId
);
9621 writer
.setHasNonGCThingResult(objId
, argId
);
9624 case ValueType::String
: {
9625 StringOperandId strId
= writer
.guardToString(argId
);
9626 writer
.setHasStringResult(objId
, strId
);
9629 case ValueType::Symbol
: {
9630 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9631 writer
.setHasSymbolResult(objId
, symId
);
9634 case ValueType::BigInt
: {
9635 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9636 writer
.setHasBigIntResult(objId
, bigIntId
);
9639 case ValueType::Object
: {
9640 // Currently only supported on 64-bit platforms.
9642 ObjOperandId valId
= writer
.guardToObject(argId
);
9643 writer
.setHasObjectResult(objId
, valId
);
9645 writer
.setHasResult(objId
, argId
);
9650 # ifdef ENABLE_RECORD_TUPLE
9651 case ValueType::ExtendedPrimitive
:
9653 case ValueType::Magic
:
9654 case ValueType::PrivateGCThing
:
9655 MOZ_CRASH("Unexpected type");
9658 writer
.setHasResult(objId
, argId
);
9661 // The optimized versions require too many registers on x86.
9662 writer
.setHasResult(objId
, argId
);
9665 writer
.returnFromIC();
9667 trackAttached("SetHas");
9668 return AttachDecision::Attach
;
9671 AttachDecision
InlinableNativeIRGenerator::tryAttachSetSize() {
9672 // Ensure |this| is a SetObject.
9673 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
9674 return AttachDecision::NoAction
;
9677 // Expecting no arguments.
9679 return AttachDecision::NoAction
;
9682 // Initialize the input operand.
9683 initializeInputOperand();
9685 // Guard callee is the 'size' native function.
9686 emitNativeCalleeGuard();
9688 // Guard |this| is a SetObject.
9689 ValOperandId thisValId
=
9690 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9691 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9692 writer
.guardClass(objId
, GuardClassKind::Set
);
9694 writer
.setSizeResult(objId
);
9695 writer
.returnFromIC();
9697 trackAttached("SetSize");
9698 return AttachDecision::Attach
;
9701 AttachDecision
InlinableNativeIRGenerator::tryAttachMapHas() {
9702 // Ensure |this| is a MapObject.
9703 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
9704 return AttachDecision::NoAction
;
9707 // Need a single argument.
9709 return AttachDecision::NoAction
;
9712 // Initialize the input operand.
9713 initializeInputOperand();
9715 // Guard callee is the 'has' native function.
9716 emitNativeCalleeGuard();
9718 // Guard |this| is a MapObject.
9719 ValOperandId thisValId
=
9720 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9721 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9722 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
9724 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9726 #ifndef JS_CODEGEN_X86
9727 // Assume the hash key will likely always have the same type when attaching
9728 // the first stub. If the call is polymorphic on the hash key, attach a stub
9729 // which handles any value.
9730 if (isFirstStub()) {
9731 switch (args_
[0].type()) {
9732 case ValueType::Double
:
9733 case ValueType::Int32
:
9734 case ValueType::Boolean
:
9735 case ValueType::Undefined
:
9736 case ValueType::Null
: {
9737 writer
.guardToNonGCThing(argId
);
9738 writer
.mapHasNonGCThingResult(objId
, argId
);
9741 case ValueType::String
: {
9742 StringOperandId strId
= writer
.guardToString(argId
);
9743 writer
.mapHasStringResult(objId
, strId
);
9746 case ValueType::Symbol
: {
9747 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9748 writer
.mapHasSymbolResult(objId
, symId
);
9751 case ValueType::BigInt
: {
9752 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9753 writer
.mapHasBigIntResult(objId
, bigIntId
);
9756 case ValueType::Object
: {
9757 // Currently only supported on 64-bit platforms.
9759 ObjOperandId valId
= writer
.guardToObject(argId
);
9760 writer
.mapHasObjectResult(objId
, valId
);
9762 writer
.mapHasResult(objId
, argId
);
9767 # ifdef ENABLE_RECORD_TUPLE
9768 case ValueType::ExtendedPrimitive
:
9770 case ValueType::Magic
:
9771 case ValueType::PrivateGCThing
:
9772 MOZ_CRASH("Unexpected type");
9775 writer
.mapHasResult(objId
, argId
);
9778 // The optimized versions require too many registers on x86.
9779 writer
.mapHasResult(objId
, argId
);
9782 writer
.returnFromIC();
9784 trackAttached("MapHas");
9785 return AttachDecision::Attach
;
9788 AttachDecision
InlinableNativeIRGenerator::tryAttachMapGet() {
9789 // Ensure |this| is a MapObject.
9790 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
9791 return AttachDecision::NoAction
;
9794 // Need a single argument.
9796 return AttachDecision::NoAction
;
9799 // Initialize the input operand.
9800 initializeInputOperand();
9802 // Guard callee is the 'get' native function.
9803 emitNativeCalleeGuard();
9805 // Guard |this| is a MapObject.
9806 ValOperandId thisValId
=
9807 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9808 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9809 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
9811 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9813 #ifndef JS_CODEGEN_X86
9814 // Assume the hash key will likely always have the same type when attaching
9815 // the first stub. If the call is polymorphic on the hash key, attach a stub
9816 // which handles any value.
9817 if (isFirstStub()) {
9818 switch (args_
[0].type()) {
9819 case ValueType::Double
:
9820 case ValueType::Int32
:
9821 case ValueType::Boolean
:
9822 case ValueType::Undefined
:
9823 case ValueType::Null
: {
9824 writer
.guardToNonGCThing(argId
);
9825 writer
.mapGetNonGCThingResult(objId
, argId
);
9828 case ValueType::String
: {
9829 StringOperandId strId
= writer
.guardToString(argId
);
9830 writer
.mapGetStringResult(objId
, strId
);
9833 case ValueType::Symbol
: {
9834 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9835 writer
.mapGetSymbolResult(objId
, symId
);
9838 case ValueType::BigInt
: {
9839 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9840 writer
.mapGetBigIntResult(objId
, bigIntId
);
9843 case ValueType::Object
: {
9844 // Currently only supported on 64-bit platforms.
9846 ObjOperandId valId
= writer
.guardToObject(argId
);
9847 writer
.mapGetObjectResult(objId
, valId
);
9849 writer
.mapGetResult(objId
, argId
);
9854 # ifdef ENABLE_RECORD_TUPLE
9855 case ValueType::ExtendedPrimitive
:
9857 case ValueType::Magic
:
9858 case ValueType::PrivateGCThing
:
9859 MOZ_CRASH("Unexpected type");
9862 writer
.mapGetResult(objId
, argId
);
9865 // The optimized versions require too many registers on x86.
9866 writer
.mapGetResult(objId
, argId
);
9869 writer
.returnFromIC();
9871 trackAttached("MapGet");
9872 return AttachDecision::Attach
;
9875 AttachDecision
CallIRGenerator::tryAttachFunCall(HandleFunction callee
) {
9876 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
9878 if (callee
->native() != fun_call
) {
9879 return AttachDecision::NoAction
;
9882 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
9883 return AttachDecision::NoAction
;
9885 RootedFunction
target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
9887 bool isScripted
= target
->hasJitEntry();
9888 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
9890 if (target
->isClassConstructor()) {
9891 return AttachDecision::NoAction
;
9893 Int32OperandId
argcId(writer
.setInputOperandId(0));
9895 CallFlags
targetFlags(CallFlags::FunCall
);
9896 if (mode_
== ICState::Mode::Specialized
) {
9897 if (cx_
->realm() == target
->realm()) {
9898 targetFlags
.setIsSameRealm();
9902 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&& argc_
> 0) {
9903 // The stack layout is already in the correct form for calls with at least
9908 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
9909 // Callee <-- argc+1
9910 // ThisValue <-- argc
9911 // Args: | Arg0 | <-- argc-1
9912 // | Arg1 | <-- argc-2
9916 // When passing |argc-1| as the number of arguments, we get:
9918 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
9919 // Callee <-- (argc-1)+1 = argc = ThisValue
9920 // ThisValue <-- (argc-1) = argc-1 = Arg0
9921 // Args: | Arg0 | <-- (argc-1)-1 = argc-2 = Arg1
9922 // | Arg1 | <-- (argc-1)-2 = argc-3 = Arg2
9927 // This allows to call |loadArgumentFixedSlot(ArgumentKind::Arg0)| and we
9928 // still load the correct argument index from |ArgumentKind::Arg1|.
9930 // When no arguments are passed, i.e. |argc==0|, we have to replace
9931 // |ArgumentKind::Arg0| with the undefined value. But we don't yet support
9933 HandleValue newTarget
= NullHandleValue
;
9934 HandleValue thisValue
= args_
[0];
9935 HandleValueArray args
=
9936 HandleValueArray::subarray(args_
, 1, args_
.length() - 1);
9938 // Check for specific native-function optimizations.
9939 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
9941 TRY_ATTACH(nativeGen
.tryAttachStub());
9944 ObjOperandId thisObjId
= emitFunCallGuard(argcId
);
9946 if (mode_
== ICState::Mode::Specialized
) {
9947 // Ensure that |this| is the expected target function.
9948 emitCalleeGuard(thisObjId
, target
);
9951 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
9952 ClampFixedArgc(argc_
));
9954 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
9955 ClampFixedArgc(argc_
));
9958 // Guard that |this| is a function.
9959 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
9961 // Guard that function is not a class constructor.
9962 writer
.guardNotClassConstructor(thisObjId
);
9965 writer
.guardFunctionHasJitEntry(thisObjId
, /*isConstructing =*/false);
9966 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
9967 ClampFixedArgc(argc_
));
9969 writer
.guardFunctionHasNoJitEntry(thisObjId
);
9970 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
,
9971 ClampFixedArgc(argc_
));
9975 writer
.returnFromIC();
9978 trackAttached("Scripted fun_call");
9980 trackAttached("Native fun_call");
9983 return AttachDecision::Attach
;
9986 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArray(
9987 bool isPossiblyWrapped
) {
9988 // Self-hosted code calls this with a single object argument.
9989 MOZ_ASSERT(argc_
== 1);
9990 MOZ_ASSERT(args_
[0].isObject());
9992 // Initialize the input operand.
9993 initializeInputOperand();
9995 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
9997 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9998 ObjOperandId objArgId
= writer
.guardToObject(argId
);
9999 writer
.isTypedArrayResult(objArgId
, isPossiblyWrapped
);
10000 writer
.returnFromIC();
10002 trackAttached(isPossiblyWrapped
? "IsPossiblyWrappedTypedArray"
10004 return AttachDecision::Attach
;
10007 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArrayConstructor() {
10008 // Self-hosted code calls this with a single object argument.
10009 MOZ_ASSERT(argc_
== 1);
10010 MOZ_ASSERT(args_
[0].isObject());
10012 // Initialize the input operand.
10013 initializeInputOperand();
10015 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10017 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10018 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10019 writer
.isTypedArrayConstructorResult(objArgId
);
10020 writer
.returnFromIC();
10022 trackAttached("IsTypedArrayConstructor");
10023 return AttachDecision::Attach
;
10026 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() {
10027 // Self-hosted code calls this with a single TypedArrayObject argument.
10028 MOZ_ASSERT(argc_
== 1);
10029 MOZ_ASSERT(args_
[0].isObject());
10030 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10032 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10034 // Initialize the input operand.
10035 initializeInputOperand();
10037 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10039 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10040 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10041 if (tarr
->byteOffset() <= INT32_MAX
) {
10042 writer
.arrayBufferViewByteOffsetInt32Result(objArgId
);
10044 writer
.arrayBufferViewByteOffsetDoubleResult(objArgId
);
10046 writer
.returnFromIC();
10048 trackAttached("IntrinsicTypedArrayByteOffset");
10049 return AttachDecision::Attach
;
10052 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayElementSize() {
10053 // Self-hosted code calls this with a single TypedArrayObject argument.
10054 MOZ_ASSERT(argc_
== 1);
10055 MOZ_ASSERT(args_
[0].isObject());
10056 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10058 // Initialize the input operand.
10059 initializeInputOperand();
10061 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10063 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10064 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10065 writer
.typedArrayElementSizeResult(objArgId
);
10066 writer
.returnFromIC();
10068 trackAttached("TypedArrayElementSize");
10069 return AttachDecision::Attach
;
10072 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayLength(
10073 bool isPossiblyWrapped
) {
10074 // Self-hosted code calls this with a single, possibly wrapped,
10075 // TypedArrayObject argument.
10076 MOZ_ASSERT(argc_
== 1);
10077 MOZ_ASSERT(args_
[0].isObject());
10079 // Only optimize when the object isn't a wrapper.
10080 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10081 return AttachDecision::NoAction
;
10084 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10086 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10088 // Initialize the input operand.
10089 initializeInputOperand();
10091 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10093 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10094 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10096 if (isPossiblyWrapped
) {
10097 writer
.guardIsNotProxy(objArgId
);
10100 if (tarr
->length() <= INT32_MAX
) {
10101 writer
.loadArrayBufferViewLengthInt32Result(objArgId
);
10103 writer
.loadArrayBufferViewLengthDoubleResult(objArgId
);
10105 writer
.returnFromIC();
10107 trackAttached("IntrinsicTypedArrayLength");
10108 return AttachDecision::Attach
;
10111 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayBufferByteLength(
10112 bool isPossiblyWrapped
) {
10113 // Self-hosted code calls this with a single, possibly wrapped,
10114 // ArrayBufferObject argument.
10115 MOZ_ASSERT(argc_
== 1);
10116 MOZ_ASSERT(args_
[0].isObject());
10118 // Only optimize when the object isn't a wrapper.
10119 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10120 return AttachDecision::NoAction
;
10123 MOZ_ASSERT(args_
[0].toObject().is
<ArrayBufferObject
>());
10125 auto* buffer
= &args_
[0].toObject().as
<ArrayBufferObject
>();
10127 // TODO: Support resizable buffers. (bug 1842999)
10128 if (buffer
->isResizable()) {
10129 return AttachDecision::NoAction
;
10132 // Initialize the input operand.
10133 initializeInputOperand();
10135 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10137 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10138 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10140 if (isPossiblyWrapped
) {
10141 writer
.guardIsNotProxy(objArgId
);
10144 if (buffer
->byteLength() <= INT32_MAX
) {
10145 writer
.loadArrayBufferByteLengthInt32Result(objArgId
);
10147 writer
.loadArrayBufferByteLengthDoubleResult(objArgId
);
10149 writer
.returnFromIC();
10151 trackAttached("ArrayBufferByteLength");
10152 return AttachDecision::Attach
;
10155 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructing() {
10156 // Self-hosted code calls this with no arguments in function scripts.
10157 MOZ_ASSERT(argc_
== 0);
10158 MOZ_ASSERT(script()->isFunction());
10160 // Initialize the input operand.
10161 initializeInputOperand();
10163 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10165 writer
.frameIsConstructingResult();
10166 writer
.returnFromIC();
10168 trackAttached("IsConstructing");
10169 return AttachDecision::Attach
;
10173 InlinableNativeIRGenerator::tryAttachGetNextMapSetEntryForIterator(bool isMap
) {
10174 // Self-hosted code calls this with two objects.
10175 MOZ_ASSERT(argc_
== 2);
10177 MOZ_ASSERT(args_
[0].toObject().is
<MapIteratorObject
>());
10179 MOZ_ASSERT(args_
[0].toObject().is
<SetIteratorObject
>());
10181 MOZ_ASSERT(args_
[1].toObject().is
<ArrayObject
>());
10183 // Initialize the input operand.
10184 initializeInputOperand();
10186 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10188 ValOperandId iterId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10189 ObjOperandId objIterId
= writer
.guardToObject(iterId
);
10191 ValOperandId resultArrId
=
10192 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10193 ObjOperandId objResultArrId
= writer
.guardToObject(resultArrId
);
10195 writer
.getNextMapSetEntryForIteratorResult(objIterId
, objResultArrId
, isMap
);
10196 writer
.returnFromIC();
10198 trackAttached("GetNextMapSetEntryForIterator");
10199 return AttachDecision::Attach
;
10202 AttachDecision
InlinableNativeIRGenerator::tryAttachNewArrayIterator() {
10203 // Self-hosted code calls this without any arguments
10204 MOZ_ASSERT(argc_
== 0);
10206 JSObject
* templateObj
= NewArrayIteratorTemplate(cx_
);
10207 if (!templateObj
) {
10208 cx_
->recoverFromOutOfMemory();
10209 return AttachDecision::NoAction
;
10212 // Initialize the input operand.
10213 initializeInputOperand();
10215 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10217 writer
.newArrayIteratorResult(templateObj
);
10218 writer
.returnFromIC();
10220 trackAttached("NewArrayIterator");
10221 return AttachDecision::Attach
;
10224 AttachDecision
InlinableNativeIRGenerator::tryAttachNewStringIterator() {
10225 // Self-hosted code calls this without any arguments
10226 MOZ_ASSERT(argc_
== 0);
10228 JSObject
* templateObj
= NewStringIteratorTemplate(cx_
);
10229 if (!templateObj
) {
10230 cx_
->recoverFromOutOfMemory();
10231 return AttachDecision::NoAction
;
10234 // Initialize the input operand.
10235 initializeInputOperand();
10237 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10239 writer
.newStringIteratorResult(templateObj
);
10240 writer
.returnFromIC();
10242 trackAttached("NewStringIterator");
10243 return AttachDecision::Attach
;
10246 AttachDecision
InlinableNativeIRGenerator::tryAttachNewRegExpStringIterator() {
10247 // Self-hosted code calls this without any arguments
10248 MOZ_ASSERT(argc_
== 0);
10250 JSObject
* templateObj
= NewRegExpStringIteratorTemplate(cx_
);
10251 if (!templateObj
) {
10252 cx_
->recoverFromOutOfMemory();
10253 return AttachDecision::NoAction
;
10256 // Initialize the input operand.
10257 initializeInputOperand();
10259 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10261 writer
.newRegExpStringIteratorResult(templateObj
);
10262 writer
.returnFromIC();
10264 trackAttached("NewRegExpStringIterator");
10265 return AttachDecision::Attach
;
10269 InlinableNativeIRGenerator::tryAttachArrayIteratorPrototypeOptimizable() {
10270 // Self-hosted code calls this without any arguments
10271 MOZ_ASSERT(argc_
== 0);
10273 if (!isFirstStub()) {
10274 // Attach only once to prevent slowdowns for polymorphic calls.
10275 return AttachDecision::NoAction
;
10278 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
10280 Rooted
<JSFunction
*> nextFun(cx_
);
10281 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
10282 &arrayIteratorProto
, &slot
,
10284 return AttachDecision::NoAction
;
10287 // Initialize the input operand.
10288 initializeInputOperand();
10290 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10292 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
10293 ObjOperandId nextId
= writer
.loadObject(nextFun
);
10295 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
10297 // Ensure that proto[slot] == nextFun.
10298 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
10299 writer
.loadBooleanResult(true);
10300 writer
.returnFromIC();
10302 trackAttached("ArrayIteratorPrototypeOptimizable");
10303 return AttachDecision::Attach
;
10306 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectCreate() {
10307 // Need a single object-or-null argument.
10308 if (argc_
!= 1 || !args_
[0].isObjectOrNull()) {
10309 return AttachDecision::NoAction
;
10312 if (!isFirstStub()) {
10313 // Attach only once to prevent slowdowns for polymorphic calls.
10314 return AttachDecision::NoAction
;
10317 RootedObject
proto(cx_
, args_
[0].toObjectOrNull());
10318 JSObject
* templateObj
= ObjectCreateImpl(cx_
, proto
, TenuredObject
);
10319 if (!templateObj
) {
10320 cx_
->recoverFromOutOfMemory();
10321 return AttachDecision::NoAction
;
10324 // Initialize the input operand.
10325 initializeInputOperand();
10327 // Guard callee is the 'create' native function.
10328 emitNativeCalleeGuard();
10330 // Guard on the proto argument.
10331 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10333 ObjOperandId protoId
= writer
.guardToObject(argId
);
10334 writer
.guardSpecificObject(protoId
, proto
);
10336 writer
.guardIsNull(argId
);
10339 writer
.objectCreateResult(templateObj
);
10340 writer
.returnFromIC();
10342 trackAttached("ObjectCreate");
10343 return AttachDecision::Attach
;
10346 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectConstructor() {
10347 // Expecting no arguments or a single object argument.
10348 // TODO(Warp): Support all or more conversions to object.
10350 return AttachDecision::NoAction
;
10352 if (argc_
== 1 && !args_
[0].isObject()) {
10353 return AttachDecision::NoAction
;
10356 PlainObject
* templateObj
= nullptr;
10358 // Stub doesn't support metadata builder
10359 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
10360 return AttachDecision::NoAction
;
10363 // Create a temporary object to act as the template object.
10364 templateObj
= NewPlainObjectWithAllocKind(cx_
, NewObjectGCKind());
10365 if (!templateObj
) {
10366 cx_
->recoverFromOutOfMemory();
10367 return AttachDecision::NoAction
;
10371 // Initialize the input operand.
10372 initializeInputOperand();
10374 // Guard callee and newTarget (if constructing) are this Object constructor
10376 emitNativeCalleeGuard();
10379 // TODO: Support pre-tenuring.
10380 gc::AllocSite
* site
=
10381 script()->zone()->unknownAllocSite(JS::TraceKind::Object
);
10384 uint32_t numFixedSlots
= templateObj
->numUsedFixedSlots();
10385 uint32_t numDynamicSlots
= templateObj
->numDynamicSlots();
10386 gc::AllocKind allocKind
= templateObj
->allocKindForTenure();
10387 Shape
* shape
= templateObj
->shape();
10389 writer
.guardNoAllocationMetadataBuilder(
10390 cx_
->realm()->addressOfMetadataBuilder());
10391 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
,
10394 // Use standard call flags when this is an inline Function.prototype.call(),
10395 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10396 CallFlags flags
= flags_
;
10397 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10398 flags
= CallFlags(CallFlags::Standard
);
10401 // Guard that the argument is an object.
10402 ValOperandId argId
=
10403 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10404 ObjOperandId objId
= writer
.guardToObject(argId
);
10406 // Return the object.
10407 writer
.loadObjectResult(objId
);
10410 writer
.returnFromIC();
10412 trackAttached("ObjectConstructor");
10413 return AttachDecision::Attach
;
10416 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayConstructor() {
10417 // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
10418 // for now. Note that self-hosted code calls this without |new| via std_Array.
10420 return AttachDecision::NoAction
;
10422 if (argc_
== 1 && !args_
[0].isInt32()) {
10423 return AttachDecision::NoAction
;
10426 int32_t length
= (argc_
== 1) ? args_
[0].toInt32() : 0;
10427 if (length
< 0 || uint32_t(length
) > ArrayObject::EagerAllocationMaxLength
) {
10428 return AttachDecision::NoAction
;
10431 // We allow inlining this function across realms so make sure the template
10432 // object is allocated in that realm. See CanInlineNativeCrossRealm.
10433 JSObject
* templateObj
;
10435 AutoRealm
ar(cx_
, callee_
);
10436 templateObj
= NewDenseFullyAllocatedArray(cx_
, length
, TenuredObject
);
10437 if (!templateObj
) {
10438 cx_
->clearPendingException();
10439 return AttachDecision::NoAction
;
10443 // Initialize the input operand.
10444 initializeInputOperand();
10446 // Guard callee and newTarget (if constructing) are this Array constructor
10448 emitNativeCalleeGuard();
10450 Int32OperandId lengthId
;
10452 // Use standard call flags when this is an inline Function.prototype.call(),
10453 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10454 CallFlags flags
= flags_
;
10455 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10456 flags
= CallFlags(CallFlags::Standard
);
10459 ValOperandId arg0Id
=
10460 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10461 lengthId
= writer
.guardToInt32(arg0Id
);
10463 MOZ_ASSERT(argc_
== 0);
10464 lengthId
= writer
.loadInt32Constant(0);
10467 writer
.newArrayFromLengthResult(templateObj
, lengthId
);
10468 writer
.returnFromIC();
10470 trackAttached("ArrayConstructor");
10471 return AttachDecision::Attach
;
10474 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() {
10475 MOZ_ASSERT(flags_
.isConstructing());
10477 if (argc_
== 0 || argc_
> 3) {
10478 return AttachDecision::NoAction
;
10481 if (!isFirstStub()) {
10482 // Attach only once to prevent slowdowns for polymorphic calls.
10483 return AttachDecision::NoAction
;
10486 // The first argument must be int32 or a non-proxy object.
10487 if (!args_
[0].isInt32() && !args_
[0].isObject()) {
10488 return AttachDecision::NoAction
;
10490 if (args_
[0].isObject() && args_
[0].toObject().is
<ProxyObject
>()) {
10491 return AttachDecision::NoAction
;
10493 if (args_
[0].isObject() &&
10494 args_
[0].toObject().is
<ResizableArrayBufferObject
>()) {
10495 return AttachDecision::NoAction
;
10498 #ifdef JS_CODEGEN_X86
10499 // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
10500 // we can easily support on 32-bit x86 for now.
10501 if (args_
[0].isObject() &&
10502 args_
[0].toObject().is
<ArrayBufferObjectMaybeShared
>()) {
10503 return AttachDecision::NoAction
;
10507 RootedObject
templateObj(cx_
);
10508 if (!TypedArrayObject::GetTemplateObjectForNative(cx_
, callee_
->native(),
10509 args_
, &templateObj
)) {
10510 cx_
->recoverFromOutOfMemory();
10511 return AttachDecision::NoAction
;
10514 if (!templateObj
) {
10515 // This can happen for large length values.
10516 MOZ_ASSERT(args_
[0].isInt32());
10517 return AttachDecision::NoAction
;
10520 // Initialize the input operand.
10521 initializeInputOperand();
10523 // Guard callee and newTarget are this TypedArray constructor function.
10524 emitNativeCalleeGuard();
10526 ValOperandId arg0Id
=
10527 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
10529 if (args_
[0].isInt32()) {
10531 Int32OperandId lengthId
= writer
.guardToInt32(arg0Id
);
10532 writer
.newTypedArrayFromLengthResult(templateObj
, lengthId
);
10534 JSObject
* obj
= &args_
[0].toObject();
10535 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
10537 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
10538 // From ArrayBuffer.
10539 if (obj
->is
<FixedLengthArrayBufferObject
>()) {
10540 writer
.guardClass(objId
, GuardClassKind::FixedLengthArrayBuffer
);
10542 MOZ_ASSERT(obj
->is
<SharedArrayBufferObject
>());
10543 writer
.guardClass(objId
, GuardClassKind::SharedArrayBuffer
);
10545 ValOperandId byteOffsetId
;
10548 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
, flags_
);
10550 byteOffsetId
= writer
.loadUndefined();
10552 ValOperandId lengthId
;
10555 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
, flags_
);
10557 lengthId
= writer
.loadUndefined();
10559 writer
.newTypedArrayFromArrayBufferResult(templateObj
, objId
,
10560 byteOffsetId
, lengthId
);
10562 // From Array-like.
10563 writer
.guardIsNotArrayBufferMaybeShared(objId
);
10564 writer
.guardIsNotProxy(objId
);
10565 writer
.newTypedArrayFromArrayResult(templateObj
, objId
);
10569 writer
.returnFromIC();
10571 trackAttached("TypedArrayConstructor");
10572 return AttachDecision::Attach
;
10575 AttachDecision
InlinableNativeIRGenerator::tryAttachSpecializedFunctionBind(
10576 Handle
<JSObject
*> target
, Handle
<BoundFunctionObject
*> templateObj
) {
10577 // Try to attach a faster stub that's more specialized than what we emit in
10578 // tryAttachFunctionBind. This lets us allocate and initialize a bound
10579 // function object in Ion without calling into C++.
10581 // We can do this if:
10583 // * The target's prototype is Function.prototype, because that's the proto we
10584 // use for the template object.
10585 // * All bound arguments can be stored inline.
10586 // * The `.name`, `.length`, and `IsConstructor` values match `target`.
10588 // We initialize the template object with the bound function's name, length,
10589 // and flags. At runtime we then only have to clone the template object and
10590 // initialize the slots for the target, the bound `this` and the bound
10593 if (!isFirstStub()) {
10594 return AttachDecision::NoAction
;
10596 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
10597 return AttachDecision::NoAction
;
10599 if (target
->staticPrototype() != &cx_
->global()->getFunctionPrototype()) {
10600 return AttachDecision::NoAction
;
10602 size_t numBoundArgs
= argc_
> 0 ? argc_
- 1 : 0;
10603 if (numBoundArgs
> BoundFunctionObject::MaxInlineBoundArgs
) {
10604 return AttachDecision::NoAction
;
10607 const bool targetIsConstructor
= target
->isConstructor();
10608 Rooted
<JSAtom
*> targetName(cx_
);
10609 uint32_t targetLength
= 0;
10611 if (target
->is
<JSFunction
>()) {
10612 Rooted
<JSFunction
*> fun(cx_
, &target
->as
<JSFunction
>());
10613 if (fun
->isNativeFun()) {
10614 return AttachDecision::NoAction
;
10616 if (fun
->hasResolvedLength() || fun
->hasResolvedName()) {
10617 return AttachDecision::NoAction
;
10620 if (!JSFunction::getUnresolvedLength(cx_
, fun
, &len
)) {
10621 cx_
->clearPendingException();
10622 return AttachDecision::NoAction
;
10624 targetName
= fun
->getUnresolvedName(cx_
);
10626 cx_
->clearPendingException();
10627 return AttachDecision::NoAction
;
10630 targetLength
= len
;
10632 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
10633 if (!targetIsConstructor
) {
10634 // Only support constructors for now. This lets us use
10635 // GuardBoundFunctionIsConstructor.
10636 return AttachDecision::NoAction
;
10638 Shape
* initialShape
=
10639 cx_
->global()->maybeBoundFunctionShapeWithDefaultProto();
10640 if (bound
->shape() != initialShape
) {
10641 return AttachDecision::NoAction
;
10643 Value lenVal
= bound
->getLengthForInitialShape();
10644 Value nameVal
= bound
->getNameForInitialShape();
10645 if (!lenVal
.isInt32() || lenVal
.toInt32() < 0 || !nameVal
.isString() ||
10646 !nameVal
.toString()->isAtom()) {
10647 return AttachDecision::NoAction
;
10649 targetName
= &nameVal
.toString()->asAtom();
10650 targetLength
= uint32_t(lenVal
.toInt32());
10653 if (!templateObj
->initTemplateSlotsForSpecializedBind(
10654 cx_
, numBoundArgs
, targetIsConstructor
, targetLength
, targetName
)) {
10655 cx_
->recoverFromOutOfMemory();
10656 return AttachDecision::NoAction
;
10659 initializeInputOperand();
10660 emitNativeCalleeGuard();
10662 ValOperandId thisValId
=
10663 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10664 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
10666 // Ensure the JSClass and proto match, and that the `length` and `name`
10667 // properties haven't been redefined.
10668 writer
.guardShape(targetId
, target
->shape());
10670 // Emit guards for the `IsConstructor`, `.length`, and `.name` values.
10671 if (target
->is
<JSFunction
>()) {
10673 // * The BaseScript (because that's what JSFunction uses for the `length`).
10674 // Because MGuardFunctionScript doesn't support self-hosted functions yet,
10675 // we use GuardSpecificFunction instead in this case.
10676 // See assertion in MGuardFunctionScript::getAliasSet.
10677 // * The flags slot (for the CONSTRUCTOR, RESOLVED_NAME, RESOLVED_LENGTH,
10678 // HAS_INFERRED_NAME, and HAS_GUESSED_ATOM flags).
10679 // * The atom slot.
10680 JSFunction
* fun
= &target
->as
<JSFunction
>();
10681 if (fun
->isSelfHostedBuiltin()) {
10682 writer
.guardSpecificFunction(targetId
, fun
);
10684 writer
.guardFunctionScript(targetId
, fun
->baseScript());
10686 writer
.guardFixedSlotValue(
10687 targetId
, JSFunction::offsetOfFlagsAndArgCount(),
10688 fun
->getReservedSlot(JSFunction::FlagsAndArgCountSlot
));
10689 writer
.guardFixedSlotValue(targetId
, JSFunction::offsetOfAtom(),
10690 fun
->getReservedSlot(JSFunction::AtomSlot
));
10692 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
10693 writer
.guardBoundFunctionIsConstructor(targetId
);
10694 writer
.guardFixedSlotValue(targetId
,
10695 BoundFunctionObject::offsetOfLengthSlot(),
10696 bound
->getLengthForInitialShape());
10697 writer
.guardFixedSlotValue(targetId
,
10698 BoundFunctionObject::offsetOfNameSlot(),
10699 bound
->getNameForInitialShape());
10702 writer
.specializedBindFunctionResult(targetId
, argc_
, templateObj
);
10703 writer
.returnFromIC();
10705 trackAttached("SpecializedFunctionBind");
10706 return AttachDecision::Attach
;
10709 AttachDecision
InlinableNativeIRGenerator::tryAttachFunctionBind() {
10710 // Ensure |this| (the target) is a function object or a bound function object.
10711 // We could support other callables too, but note that we rely on the target
10712 // having a static prototype in BoundFunctionObject::functionBindImpl.
10713 if (!thisval_
.isObject()) {
10714 return AttachDecision::NoAction
;
10716 Rooted
<JSObject
*> target(cx_
, &thisval_
.toObject());
10717 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
10718 return AttachDecision::NoAction
;
10721 // Only support standard, non-spread calls.
10722 if (flags_
.getArgFormat() != CallFlags::Standard
) {
10723 return AttachDecision::NoAction
;
10726 // Only optimize if the number of arguments is small. This ensures we don't
10727 // compile a lot of different stubs (because we bake in argc) and that we
10728 // don't get anywhere near ARGS_LENGTH_MAX.
10729 static constexpr size_t MaxArguments
= 6;
10730 if (argc_
> MaxArguments
) {
10731 return AttachDecision::NoAction
;
10734 Rooted
<BoundFunctionObject
*> templateObj(
10735 cx_
, BoundFunctionObject::createTemplateObject(cx_
));
10736 if (!templateObj
) {
10737 cx_
->recoverFromOutOfMemory();
10738 return AttachDecision::NoAction
;
10741 TRY_ATTACH(tryAttachSpecializedFunctionBind(target
, templateObj
));
10743 initializeInputOperand();
10745 emitNativeCalleeGuard();
10747 // Guard |this| is a function object or a bound function object.
10748 ValOperandId thisValId
=
10749 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10750 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
10751 if (target
->is
<JSFunction
>()) {
10752 writer
.guardClass(targetId
, GuardClassKind::JSFunction
);
10754 MOZ_ASSERT(target
->is
<BoundFunctionObject
>());
10755 writer
.guardClass(targetId
, GuardClassKind::BoundFunction
);
10758 writer
.bindFunctionResult(targetId
, argc_
, templateObj
);
10759 writer
.returnFromIC();
10761 trackAttached("FunctionBind");
10762 return AttachDecision::Attach
;
10765 AttachDecision
CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc
) {
10766 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
10768 if (calleeFunc
->native() != fun_apply
) {
10769 return AttachDecision::NoAction
;
10773 return AttachDecision::NoAction
;
10776 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
10777 return AttachDecision::NoAction
;
10779 Rooted
<JSFunction
*> target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
10781 bool isScripted
= target
->hasJitEntry();
10782 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
10784 if (target
->isClassConstructor()) {
10785 return AttachDecision::NoAction
;
10788 CallFlags::ArgFormat format
= CallFlags::Standard
;
10790 // |fun.apply()| and |fun.apply(thisValue)| are equivalent to |fun.call()|
10791 // resp. |fun.call(thisValue)|.
10792 format
= CallFlags::FunCall
;
10793 } else if (args_
[1].isNullOrUndefined()) {
10794 // |fun.apply(thisValue, null)| and |fun.apply(thisValue, undefined)| are
10795 // also equivalent to |fun.call(thisValue)|, but we can't use FunCall
10796 // because we have to discard the second argument.
10797 format
= CallFlags::FunApplyNullUndefined
;
10798 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArgumentsObject
>()) {
10799 auto* argsObj
= &args_
[1].toObject().as
<ArgumentsObject
>();
10800 if (argsObj
->hasOverriddenElement() || argsObj
->anyArgIsForwarded() ||
10801 argsObj
->hasOverriddenLength() ||
10802 argsObj
->initialLength() > JIT_ARGS_LENGTH_MAX
) {
10803 return AttachDecision::NoAction
;
10805 format
= CallFlags::FunApplyArgsObj
;
10806 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArrayObject
>() &&
10807 args_
[1].toObject().as
<ArrayObject
>().length() <=
10808 JIT_ARGS_LENGTH_MAX
&&
10809 IsPackedArray(&args_
[1].toObject())) {
10810 format
= CallFlags::FunApplyArray
;
10812 return AttachDecision::NoAction
;
10815 Int32OperandId
argcId(writer
.setInputOperandId(0));
10817 CallFlags
targetFlags(format
);
10818 if (mode_
== ICState::Mode::Specialized
) {
10819 if (cx_
->realm() == target
->realm()) {
10820 targetFlags
.setIsSameRealm();
10824 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
10825 format
== CallFlags::FunApplyArray
) {
10826 HandleValue newTarget
= NullHandleValue
;
10827 HandleValue thisValue
= args_
[0];
10828 Rooted
<ArrayObject
*> aobj(cx_
, &args_
[1].toObject().as
<ArrayObject
>());
10829 HandleValueArray args
= HandleValueArray::fromMarkedLocation(
10830 aobj
->length(), aobj
->getDenseElements());
10832 // Check for specific native-function optimizations.
10833 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10834 args
, targetFlags
);
10835 TRY_ATTACH(nativeGen
.tryAttachStub());
10838 // Don't inline when no arguments are passed, cf. |tryAttachFunCall()|.
10839 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
10840 format
== CallFlags::FunCall
&& argc_
> 0) {
10841 MOZ_ASSERT(argc_
== 1);
10843 HandleValue newTarget
= NullHandleValue
;
10844 HandleValue thisValue
= args_
[0];
10845 HandleValueArray args
= HandleValueArray::empty();
10847 // Check for specific native-function optimizations.
10848 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10849 args
, targetFlags
);
10850 TRY_ATTACH(nativeGen
.tryAttachStub());
10853 ObjOperandId thisObjId
= emitFunApplyGuard(argcId
);
10855 uint32_t fixedArgc
;
10856 if (format
== CallFlags::FunApplyArray
||
10857 format
== CallFlags::FunApplyArgsObj
||
10858 format
== CallFlags::FunApplyNullUndefined
) {
10859 emitFunApplyArgsGuard(format
);
10861 // We always use MaxUnrolledArgCopy here because the fixed argc is
10862 // meaningless in a FunApply case.
10863 fixedArgc
= MaxUnrolledArgCopy
;
10865 MOZ_ASSERT(format
== CallFlags::FunCall
);
10867 // Whereas for the FunCall case we need to use the actual fixed argc value.
10868 fixedArgc
= ClampFixedArgc(argc_
);
10871 if (mode_
== ICState::Mode::Specialized
) {
10872 // Ensure that |this| is the expected target function.
10873 emitCalleeGuard(thisObjId
, target
);
10876 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10878 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
10882 // Guard that |this| is a function.
10883 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
10885 // Guard that function is not a class constructor.
10886 writer
.guardNotClassConstructor(thisObjId
);
10889 // Guard that function is scripted.
10890 writer
.guardFunctionHasJitEntry(thisObjId
, /*constructing =*/false);
10891 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10893 // Guard that function is native.
10894 writer
.guardFunctionHasNoJitEntry(thisObjId
);
10895 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10899 writer
.returnFromIC();
10902 trackAttached("Call.ScriptedFunApply");
10904 trackAttached("Call.NativeFunApply");
10907 return AttachDecision::Attach
;
10910 AttachDecision
CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc
) {
10911 // Try to optimize calls into Wasm code by emitting the CallWasmFunction
10912 // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
10913 // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
10916 // Note: some code refers to these optimized Wasm calls as "inlined" calls.
10918 MOZ_ASSERT(calleeFunc
->isWasmWithJitEntry());
10920 if (!JitOptions
.enableWasmIonFastCalls
) {
10921 return AttachDecision::NoAction
;
10923 if (!isFirstStub_
) {
10924 return AttachDecision::NoAction
;
10926 JSOp op
= JSOp(*pc_
);
10927 if (op
!= JSOp::Call
&& op
!= JSOp::CallContent
&&
10928 op
!= JSOp::CallIgnoresRv
) {
10929 return AttachDecision::NoAction
;
10931 if (cx_
->realm() != calleeFunc
->realm()) {
10932 return AttachDecision::NoAction
;
10935 wasm::Instance
& inst
= wasm::ExportedFunctionToInstance(calleeFunc
);
10936 uint32_t funcIndex
= inst
.code().getFuncIndex(calleeFunc
);
10938 auto bestTier
= inst
.code().bestTier();
10939 const wasm::FuncExport
& funcExport
=
10940 inst
.metadata(bestTier
).lookupFuncExport(funcIndex
);
10941 const wasm::FuncType
& sig
= inst
.metadata().getFuncExportType(funcExport
);
10943 MOZ_ASSERT(!IsInsideNursery(inst
.object()));
10944 MOZ_ASSERT(sig
.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
10946 // If there are too many arguments, don't optimize (we won't be able to store
10947 // the arguments in the LIR node).
10948 static_assert(wasm::MaxArgsForJitInlineCall
<= ArgumentKindArgIndexLimit
);
10949 if (sig
.args().length() > wasm::MaxArgsForJitInlineCall
||
10950 argc_
> ArgumentKindArgIndexLimit
) {
10951 return AttachDecision::NoAction
;
10954 // If there are too many results, don't optimize as Warp currently doesn't
10955 // have code to handle this.
10956 if (sig
.results().length() > wasm::MaxResultsForJitInlineCall
) {
10957 return AttachDecision::NoAction
;
10960 // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
10961 // because it is more difficult (because it requires multiple LIR arguments
10964 // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
10965 // spilled to the stack because it causes problems with register allocation.
10967 constexpr bool optimizeWithI64
= true;
10969 constexpr bool optimizeWithI64
= false;
10971 ABIArgGenerator abi
;
10972 for (const auto& valType
: sig
.args()) {
10973 MIRType mirType
= valType
.toMIRType();
10974 ABIArg abiArg
= abi
.next(mirType
);
10975 if (mirType
!= MIRType::Int64
) {
10978 if (!optimizeWithI64
|| abiArg
.kind() == ABIArg::Stack
) {
10979 return AttachDecision::NoAction
;
10983 // Check that all arguments can be converted to the Wasm type in Warp code
10984 // without bailing out.
10985 for (size_t i
= 0; i
< sig
.args().length(); i
++) {
10986 Value argVal
= i
< argc_
? args_
[i
] : UndefinedValue();
10987 switch (sig
.args()[i
].kind()) {
10988 case wasm::ValType::I32
:
10989 case wasm::ValType::F32
:
10990 case wasm::ValType::F64
:
10991 if (!argVal
.isNumber() && !argVal
.isBoolean() &&
10992 !argVal
.isUndefined()) {
10993 return AttachDecision::NoAction
;
10996 case wasm::ValType::I64
:
10997 if (!argVal
.isBigInt() && !argVal
.isBoolean() && !argVal
.isString()) {
10998 return AttachDecision::NoAction
;
11001 case wasm::ValType::V128
:
11002 MOZ_CRASH("Function should not have a Wasm JitEntry");
11003 case wasm::ValType::Ref
:
11004 // canHaveJitEntry restricts args to externref, where all JS values are
11005 // valid and can be boxed.
11006 MOZ_ASSERT(sig
.args()[i
].refType().isExtern(),
11007 "Unexpected type for Wasm JitEntry");
11012 CallFlags
flags(/* isConstructing = */ false, /* isSpread = */ false,
11013 /* isSameRealm = */ true);
11016 Int32OperandId
argcId(writer
.setInputOperandId(0));
11018 // Load the callee and ensure it is an object
11019 ValOperandId calleeValId
=
11020 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags
);
11021 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11023 // Ensure the callee is this Wasm function.
11024 emitCalleeGuard(calleeObjId
, calleeFunc
);
11026 // Guard the argument types.
11027 uint32_t guardedArgs
= std::min
<uint32_t>(sig
.args().length(), argc_
);
11028 for (uint32_t i
= 0; i
< guardedArgs
; i
++) {
11029 ArgumentKind argKind
= ArgumentKindForArgIndex(i
);
11030 ValOperandId argId
= writer
.loadArgumentFixedSlot(argKind
, argc_
, flags
);
11031 writer
.guardWasmArg(argId
, sig
.args()[i
].kind());
11034 writer
.callWasmFunction(calleeObjId
, argcId
, flags
, ClampFixedArgc(argc_
),
11035 &funcExport
, inst
.object());
11036 writer
.returnFromIC();
11038 trackAttached("Call.WasmCall");
11040 return AttachDecision::Attach
;
11043 AttachDecision
CallIRGenerator::tryAttachInlinableNative(HandleFunction callee
,
11045 MOZ_ASSERT(mode_
== ICState::Mode::Specialized
);
11046 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
11047 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
||
11048 flags
.getArgFormat() == CallFlags::Spread
);
11050 // Special case functions are only optimized for normal calls.
11051 if (op_
!= JSOp::Call
&& op_
!= JSOp::CallContent
&& op_
!= JSOp::New
&&
11052 op_
!= JSOp::NewContent
&& op_
!= JSOp::CallIgnoresRv
&&
11053 op_
!= JSOp::SpreadCall
) {
11054 return AttachDecision::NoAction
;
11057 InlinableNativeIRGenerator
nativeGen(*this, callee
, newTarget_
, thisval_
,
11059 return nativeGen
.tryAttachStub();
11062 #ifdef FUZZING_JS_FUZZILLI
11063 AttachDecision
InlinableNativeIRGenerator::tryAttachFuzzilliHash() {
11065 return AttachDecision::NoAction
;
11068 // Initialize the input operand.
11069 initializeInputOperand();
11071 // Guard callee is the 'fuzzilli_hash' native function.
11072 emitNativeCalleeGuard();
11074 ValOperandId argValId
=
11075 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
11077 writer
.fuzzilliHashResult(argValId
);
11078 writer
.returnFromIC();
11080 trackAttached("FuzzilliHash");
11081 return AttachDecision::Attach
;
11085 AttachDecision
InlinableNativeIRGenerator::tryAttachStub() {
11086 if (!callee_
->hasJitInfo() ||
11087 callee_
->jitInfo()->type() != JSJitInfo::InlinableNative
) {
11088 return AttachDecision::NoAction
;
11091 InlinableNative native
= callee_
->jitInfo()->inlinableNative
;
11093 // Not all natives can be inlined cross-realm.
11094 if (cx_
->realm() != callee_
->realm() && !CanInlineNativeCrossRealm(native
)) {
11095 return AttachDecision::NoAction
;
11098 // Check for special-cased native constructors.
11099 if (flags_
.isConstructing()) {
11100 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
11102 // newTarget must match the callee. CacheIR for this is emitted in
11103 // emitNativeCalleeGuard.
11104 if (ObjectValue(*callee_
) != newTarget_
) {
11105 return AttachDecision::NoAction
;
11108 case InlinableNative::Array
:
11109 return tryAttachArrayConstructor();
11110 case InlinableNative::TypedArrayConstructor
:
11111 return tryAttachTypedArrayConstructor();
11112 case InlinableNative::String
:
11113 return tryAttachStringConstructor();
11114 case InlinableNative::Object
:
11115 return tryAttachObjectConstructor();
11119 return AttachDecision::NoAction
;
11122 // Check for special-cased native spread calls.
11123 if (flags_
.getArgFormat() == CallFlags::Spread
||
11124 flags_
.getArgFormat() == CallFlags::FunApplyArray
) {
11126 case InlinableNative::MathMin
:
11127 return tryAttachSpreadMathMinMax(/*isMax = */ false);
11128 case InlinableNative::MathMax
:
11129 return tryAttachSpreadMathMinMax(/*isMax = */ true);
11133 return AttachDecision::NoAction
;
11136 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
||
11137 flags_
.getArgFormat() == CallFlags::FunCall
);
11139 // Check for special-cased native functions.
11142 case InlinableNative::Array
:
11143 return tryAttachArrayConstructor();
11144 case InlinableNative::ArrayPush
:
11145 return tryAttachArrayPush();
11146 case InlinableNative::ArrayPop
:
11147 case InlinableNative::ArrayShift
:
11148 return tryAttachArrayPopShift(native
);
11149 case InlinableNative::ArrayJoin
:
11150 return tryAttachArrayJoin();
11151 case InlinableNative::ArraySlice
:
11152 return tryAttachArraySlice();
11153 case InlinableNative::ArrayIsArray
:
11154 return tryAttachArrayIsArray();
11156 // DataView natives.
11157 case InlinableNative::DataViewGetInt8
:
11158 return tryAttachDataViewGet(Scalar::Int8
);
11159 case InlinableNative::DataViewGetUint8
:
11160 return tryAttachDataViewGet(Scalar::Uint8
);
11161 case InlinableNative::DataViewGetInt16
:
11162 return tryAttachDataViewGet(Scalar::Int16
);
11163 case InlinableNative::DataViewGetUint16
:
11164 return tryAttachDataViewGet(Scalar::Uint16
);
11165 case InlinableNative::DataViewGetInt32
:
11166 return tryAttachDataViewGet(Scalar::Int32
);
11167 case InlinableNative::DataViewGetUint32
:
11168 return tryAttachDataViewGet(Scalar::Uint32
);
11169 case InlinableNative::DataViewGetFloat32
:
11170 return tryAttachDataViewGet(Scalar::Float32
);
11171 case InlinableNative::DataViewGetFloat64
:
11172 return tryAttachDataViewGet(Scalar::Float64
);
11173 case InlinableNative::DataViewGetBigInt64
:
11174 return tryAttachDataViewGet(Scalar::BigInt64
);
11175 case InlinableNative::DataViewGetBigUint64
:
11176 return tryAttachDataViewGet(Scalar::BigUint64
);
11177 case InlinableNative::DataViewSetInt8
:
11178 return tryAttachDataViewSet(Scalar::Int8
);
11179 case InlinableNative::DataViewSetUint8
:
11180 return tryAttachDataViewSet(Scalar::Uint8
);
11181 case InlinableNative::DataViewSetInt16
:
11182 return tryAttachDataViewSet(Scalar::Int16
);
11183 case InlinableNative::DataViewSetUint16
:
11184 return tryAttachDataViewSet(Scalar::Uint16
);
11185 case InlinableNative::DataViewSetInt32
:
11186 return tryAttachDataViewSet(Scalar::Int32
);
11187 case InlinableNative::DataViewSetUint32
:
11188 return tryAttachDataViewSet(Scalar::Uint32
);
11189 case InlinableNative::DataViewSetFloat32
:
11190 return tryAttachDataViewSet(Scalar::Float32
);
11191 case InlinableNative::DataViewSetFloat64
:
11192 return tryAttachDataViewSet(Scalar::Float64
);
11193 case InlinableNative::DataViewSetBigInt64
:
11194 return tryAttachDataViewSet(Scalar::BigInt64
);
11195 case InlinableNative::DataViewSetBigUint64
:
11196 return tryAttachDataViewSet(Scalar::BigUint64
);
11198 // Function natives.
11199 case InlinableNative::FunctionBind
:
11200 return tryAttachFunctionBind();
11203 case InlinableNative::IntlGuardToCollator
:
11204 case InlinableNative::IntlGuardToDateTimeFormat
:
11205 case InlinableNative::IntlGuardToDisplayNames
:
11206 case InlinableNative::IntlGuardToListFormat
:
11207 case InlinableNative::IntlGuardToNumberFormat
:
11208 case InlinableNative::IntlGuardToPluralRules
:
11209 case InlinableNative::IntlGuardToRelativeTimeFormat
:
11210 case InlinableNative::IntlGuardToSegmenter
:
11211 case InlinableNative::IntlGuardToSegments
:
11212 case InlinableNative::IntlGuardToSegmentIterator
:
11213 return tryAttachGuardToClass(native
);
11215 // Slot intrinsics.
11216 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
11217 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
11218 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
11219 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
11220 return tryAttachUnsafeGetReservedSlot(native
);
11221 case InlinableNative::IntrinsicUnsafeSetReservedSlot
:
11222 return tryAttachUnsafeSetReservedSlot();
11225 case InlinableNative::IntrinsicIsSuspendedGenerator
:
11226 return tryAttachIsSuspendedGenerator();
11227 case InlinableNative::IntrinsicToObject
:
11228 return tryAttachToObject();
11229 case InlinableNative::IntrinsicToInteger
:
11230 return tryAttachToInteger();
11231 case InlinableNative::IntrinsicToLength
:
11232 return tryAttachToLength();
11233 case InlinableNative::IntrinsicIsObject
:
11234 return tryAttachIsObject();
11235 case InlinableNative::IntrinsicIsPackedArray
:
11236 return tryAttachIsPackedArray();
11237 case InlinableNative::IntrinsicIsCallable
:
11238 return tryAttachIsCallable();
11239 case InlinableNative::IntrinsicIsConstructor
:
11240 return tryAttachIsConstructor();
11241 case InlinableNative::IntrinsicIsCrossRealmArrayConstructor
:
11242 return tryAttachIsCrossRealmArrayConstructor();
11243 case InlinableNative::IntrinsicGuardToArrayIterator
:
11244 case InlinableNative::IntrinsicGuardToMapIterator
:
11245 case InlinableNative::IntrinsicGuardToSetIterator
:
11246 case InlinableNative::IntrinsicGuardToStringIterator
:
11247 case InlinableNative::IntrinsicGuardToRegExpStringIterator
:
11248 case InlinableNative::IntrinsicGuardToWrapForValidIterator
:
11249 case InlinableNative::IntrinsicGuardToIteratorHelper
:
11250 case InlinableNative::IntrinsicGuardToAsyncIteratorHelper
:
11251 return tryAttachGuardToClass(native
);
11252 case InlinableNative::IntrinsicSubstringKernel
:
11253 return tryAttachSubstringKernel();
11254 case InlinableNative::IntrinsicIsConstructing
:
11255 return tryAttachIsConstructing();
11256 case InlinableNative::IntrinsicNewArrayIterator
:
11257 return tryAttachNewArrayIterator();
11258 case InlinableNative::IntrinsicNewStringIterator
:
11259 return tryAttachNewStringIterator();
11260 case InlinableNative::IntrinsicNewRegExpStringIterator
:
11261 return tryAttachNewRegExpStringIterator();
11262 case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable
:
11263 return tryAttachArrayIteratorPrototypeOptimizable();
11264 case InlinableNative::IntrinsicObjectHasPrototype
:
11265 return tryAttachObjectHasPrototype();
11268 case InlinableNative::IsRegExpObject
:
11269 return tryAttachHasClass(&RegExpObject::class_
,
11270 /* isPossiblyWrapped = */ false);
11271 case InlinableNative::IsPossiblyWrappedRegExpObject
:
11272 return tryAttachHasClass(&RegExpObject::class_
,
11273 /* isPossiblyWrapped = */ true);
11274 case InlinableNative::RegExpMatcher
:
11275 case InlinableNative::RegExpSearcher
:
11276 return tryAttachRegExpMatcherSearcher(native
);
11277 case InlinableNative::RegExpSearcherLastLimit
:
11278 return tryAttachRegExpSearcherLastLimit();
11279 case InlinableNative::RegExpHasCaptureGroups
:
11280 return tryAttachRegExpHasCaptureGroups();
11281 case InlinableNative::RegExpPrototypeOptimizable
:
11282 return tryAttachRegExpPrototypeOptimizable();
11283 case InlinableNative::RegExpInstanceOptimizable
:
11284 return tryAttachRegExpInstanceOptimizable();
11285 case InlinableNative::GetFirstDollarIndex
:
11286 return tryAttachGetFirstDollarIndex();
11287 case InlinableNative::IntrinsicRegExpBuiltinExec
:
11288 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
11289 return tryAttachIntrinsicRegExpBuiltinExec(native
);
11290 case InlinableNative::IntrinsicRegExpExec
:
11291 case InlinableNative::IntrinsicRegExpExecForTest
:
11292 return tryAttachIntrinsicRegExpExec(native
);
11295 case InlinableNative::String
:
11296 return tryAttachString();
11297 case InlinableNative::StringToString
:
11298 case InlinableNative::StringValueOf
:
11299 return tryAttachStringToStringValueOf();
11300 case InlinableNative::StringCharCodeAt
:
11301 return tryAttachStringCharCodeAt();
11302 case InlinableNative::StringCodePointAt
:
11303 return tryAttachStringCodePointAt();
11304 case InlinableNative::StringCharAt
:
11305 return tryAttachStringCharAt();
11306 case InlinableNative::StringAt
:
11307 return tryAttachStringAt();
11308 case InlinableNative::StringFromCharCode
:
11309 return tryAttachStringFromCharCode();
11310 case InlinableNative::StringFromCodePoint
:
11311 return tryAttachStringFromCodePoint();
11312 case InlinableNative::StringIncludes
:
11313 return tryAttachStringIncludes();
11314 case InlinableNative::StringIndexOf
:
11315 return tryAttachStringIndexOf();
11316 case InlinableNative::StringLastIndexOf
:
11317 return tryAttachStringLastIndexOf();
11318 case InlinableNative::StringStartsWith
:
11319 return tryAttachStringStartsWith();
11320 case InlinableNative::StringEndsWith
:
11321 return tryAttachStringEndsWith();
11322 case InlinableNative::StringToLowerCase
:
11323 return tryAttachStringToLowerCase();
11324 case InlinableNative::StringToUpperCase
:
11325 return tryAttachStringToUpperCase();
11326 case InlinableNative::StringTrim
:
11327 return tryAttachStringTrim();
11328 case InlinableNative::StringTrimStart
:
11329 return tryAttachStringTrimStart();
11330 case InlinableNative::StringTrimEnd
:
11331 return tryAttachStringTrimEnd();
11332 case InlinableNative::IntrinsicStringReplaceString
:
11333 return tryAttachStringReplaceString();
11334 case InlinableNative::IntrinsicStringSplitString
:
11335 return tryAttachStringSplitString();
11338 case InlinableNative::MathRandom
:
11339 return tryAttachMathRandom();
11340 case InlinableNative::MathAbs
:
11341 return tryAttachMathAbs();
11342 case InlinableNative::MathClz32
:
11343 return tryAttachMathClz32();
11344 case InlinableNative::MathSign
:
11345 return tryAttachMathSign();
11346 case InlinableNative::MathImul
:
11347 return tryAttachMathImul();
11348 case InlinableNative::MathFloor
:
11349 return tryAttachMathFloor();
11350 case InlinableNative::MathCeil
:
11351 return tryAttachMathCeil();
11352 case InlinableNative::MathTrunc
:
11353 return tryAttachMathTrunc();
11354 case InlinableNative::MathRound
:
11355 return tryAttachMathRound();
11356 case InlinableNative::MathSqrt
:
11357 return tryAttachMathSqrt();
11358 case InlinableNative::MathFRound
:
11359 return tryAttachMathFRound();
11360 case InlinableNative::MathHypot
:
11361 return tryAttachMathHypot();
11362 case InlinableNative::MathATan2
:
11363 return tryAttachMathATan2();
11364 case InlinableNative::MathSin
:
11365 return tryAttachMathFunction(UnaryMathFunction::SinNative
);
11366 case InlinableNative::MathTan
:
11367 return tryAttachMathFunction(UnaryMathFunction::TanNative
);
11368 case InlinableNative::MathCos
:
11369 return tryAttachMathFunction(UnaryMathFunction::CosNative
);
11370 case InlinableNative::MathExp
:
11371 return tryAttachMathFunction(UnaryMathFunction::Exp
);
11372 case InlinableNative::MathLog
:
11373 return tryAttachMathFunction(UnaryMathFunction::Log
);
11374 case InlinableNative::MathASin
:
11375 return tryAttachMathFunction(UnaryMathFunction::ASin
);
11376 case InlinableNative::MathATan
:
11377 return tryAttachMathFunction(UnaryMathFunction::ATan
);
11378 case InlinableNative::MathACos
:
11379 return tryAttachMathFunction(UnaryMathFunction::ACos
);
11380 case InlinableNative::MathLog10
:
11381 return tryAttachMathFunction(UnaryMathFunction::Log10
);
11382 case InlinableNative::MathLog2
:
11383 return tryAttachMathFunction(UnaryMathFunction::Log2
);
11384 case InlinableNative::MathLog1P
:
11385 return tryAttachMathFunction(UnaryMathFunction::Log1P
);
11386 case InlinableNative::MathExpM1
:
11387 return tryAttachMathFunction(UnaryMathFunction::ExpM1
);
11388 case InlinableNative::MathCosH
:
11389 return tryAttachMathFunction(UnaryMathFunction::CosH
);
11390 case InlinableNative::MathSinH
:
11391 return tryAttachMathFunction(UnaryMathFunction::SinH
);
11392 case InlinableNative::MathTanH
:
11393 return tryAttachMathFunction(UnaryMathFunction::TanH
);
11394 case InlinableNative::MathACosH
:
11395 return tryAttachMathFunction(UnaryMathFunction::ACosH
);
11396 case InlinableNative::MathASinH
:
11397 return tryAttachMathFunction(UnaryMathFunction::ASinH
);
11398 case InlinableNative::MathATanH
:
11399 return tryAttachMathFunction(UnaryMathFunction::ATanH
);
11400 case InlinableNative::MathCbrt
:
11401 return tryAttachMathFunction(UnaryMathFunction::Cbrt
);
11402 case InlinableNative::MathPow
:
11403 return tryAttachMathPow();
11404 case InlinableNative::MathMin
:
11405 return tryAttachMathMinMax(/* isMax = */ false);
11406 case InlinableNative::MathMax
:
11407 return tryAttachMathMinMax(/* isMax = */ true);
11410 case InlinableNative::IntrinsicGuardToMapObject
:
11411 return tryAttachGuardToClass(native
);
11412 case InlinableNative::IntrinsicGetNextMapEntryForIterator
:
11413 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true);
11416 case InlinableNative::Number
:
11417 return tryAttachNumber();
11418 case InlinableNative::NumberParseInt
:
11419 return tryAttachNumberParseInt();
11420 case InlinableNative::NumberToString
:
11421 return tryAttachNumberToString();
11424 case InlinableNative::Object
:
11425 return tryAttachObjectConstructor();
11426 case InlinableNative::ObjectCreate
:
11427 return tryAttachObjectCreate();
11428 case InlinableNative::ObjectIs
:
11429 return tryAttachObjectIs();
11430 case InlinableNative::ObjectIsPrototypeOf
:
11431 return tryAttachObjectIsPrototypeOf();
11432 case InlinableNative::ObjectKeys
:
11433 return tryAttachObjectKeys();
11434 case InlinableNative::ObjectToString
:
11435 return tryAttachObjectToString();
11438 case InlinableNative::IntrinsicGuardToSetObject
:
11439 return tryAttachGuardToClass(native
);
11440 case InlinableNative::IntrinsicGetNextSetEntryForIterator
:
11441 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false);
11443 // ArrayBuffer intrinsics.
11444 case InlinableNative::IntrinsicGuardToArrayBuffer
:
11445 return tryAttachGuardToArrayBuffer();
11446 case InlinableNative::IntrinsicArrayBufferByteLength
:
11447 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false);
11448 case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength
:
11449 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true);
11451 // SharedArrayBuffer intrinsics.
11452 case InlinableNative::IntrinsicGuardToSharedArrayBuffer
:
11453 return tryAttachGuardToClass(native
);
11455 // TypedArray intrinsics.
11456 case InlinableNative::TypedArrayConstructor
:
11457 return AttachDecision::NoAction
; // Not callable.
11458 case InlinableNative::IntrinsicIsTypedArray
:
11459 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ false);
11460 case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray
:
11461 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ true);
11462 case InlinableNative::IntrinsicIsTypedArrayConstructor
:
11463 return tryAttachIsTypedArrayConstructor();
11464 case InlinableNative::IntrinsicTypedArrayByteOffset
:
11465 return tryAttachTypedArrayByteOffset();
11466 case InlinableNative::IntrinsicTypedArrayElementSize
:
11467 return tryAttachTypedArrayElementSize();
11468 case InlinableNative::IntrinsicTypedArrayLength
:
11469 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false);
11470 case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength
:
11471 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true);
11473 // Reflect natives.
11474 case InlinableNative::ReflectGetPrototypeOf
:
11475 return tryAttachReflectGetPrototypeOf();
11477 // Atomics intrinsics:
11478 case InlinableNative::AtomicsCompareExchange
:
11479 return tryAttachAtomicsCompareExchange();
11480 case InlinableNative::AtomicsExchange
:
11481 return tryAttachAtomicsExchange();
11482 case InlinableNative::AtomicsAdd
:
11483 return tryAttachAtomicsAdd();
11484 case InlinableNative::AtomicsSub
:
11485 return tryAttachAtomicsSub();
11486 case InlinableNative::AtomicsAnd
:
11487 return tryAttachAtomicsAnd();
11488 case InlinableNative::AtomicsOr
:
11489 return tryAttachAtomicsOr();
11490 case InlinableNative::AtomicsXor
:
11491 return tryAttachAtomicsXor();
11492 case InlinableNative::AtomicsLoad
:
11493 return tryAttachAtomicsLoad();
11494 case InlinableNative::AtomicsStore
:
11495 return tryAttachAtomicsStore();
11496 case InlinableNative::AtomicsIsLockFree
:
11497 return tryAttachAtomicsIsLockFree();
11500 case InlinableNative::BigIntAsIntN
:
11501 return tryAttachBigIntAsIntN();
11502 case InlinableNative::BigIntAsUintN
:
11503 return tryAttachBigIntAsUintN();
11505 // Boolean natives.
11506 case InlinableNative::Boolean
:
11507 return tryAttachBoolean();
11510 case InlinableNative::SetHas
:
11511 return tryAttachSetHas();
11512 case InlinableNative::SetSize
:
11513 return tryAttachSetSize();
11516 case InlinableNative::MapHas
:
11517 return tryAttachMapHas();
11518 case InlinableNative::MapGet
:
11519 return tryAttachMapGet();
11521 // Testing functions.
11522 case InlinableNative::TestBailout
:
11523 if (js::SupportDifferentialTesting()) {
11524 return AttachDecision::NoAction
;
11526 return tryAttachBailout();
11527 case InlinableNative::TestAssertFloat32
:
11528 return tryAttachAssertFloat32();
11529 case InlinableNative::TestAssertRecoveredOnBailout
:
11530 if (js::SupportDifferentialTesting()) {
11531 return AttachDecision::NoAction
;
11533 return tryAttachAssertRecoveredOnBailout();
11535 #ifdef FUZZING_JS_FUZZILLI
11536 // Fuzzilli function
11537 case InlinableNative::FuzzilliHash
:
11538 return tryAttachFuzzilliHash();
11541 case InlinableNative::Limit
:
11545 MOZ_CRASH("Shouldn't get here");
11548 // Remember the shape of the this object for any script being called as a
11549 // constructor, for later use during Ion compilation.
11550 ScriptedThisResult
CallIRGenerator::getThisShapeForScripted(
11551 HandleFunction calleeFunc
, Handle
<JSObject
*> newTarget
,
11552 MutableHandle
<Shape
*> result
) {
11553 // Some constructors allocate their own |this| object.
11554 if (calleeFunc
->constructorNeedsUninitializedThis()) {
11555 return ScriptedThisResult::UninitializedThis
;
11558 // Only attach a stub if the newTarget is a function with a
11559 // nonconfigurable prototype.
11560 if (!newTarget
->is
<JSFunction
>() ||
11561 !newTarget
->as
<JSFunction
>().hasNonConfigurablePrototypeDataProperty()) {
11562 return ScriptedThisResult::NoAction
;
11565 AutoRealm
ar(cx_
, calleeFunc
);
11566 Shape
* thisShape
= ThisShapeForFunction(cx_
, calleeFunc
, newTarget
);
11568 cx_
->clearPendingException();
11569 return ScriptedThisResult::NoAction
;
11572 MOZ_ASSERT(thisShape
->realm() == calleeFunc
->realm());
11573 result
.set(thisShape
);
11574 return ScriptedThisResult::PlainObjectShape
;
11577 static bool CanOptimizeScriptedCall(JSFunction
* callee
, bool isConstructing
) {
11578 if (!callee
->hasJitEntry()) {
11582 // If callee is not an interpreted constructor, we have to throw.
11583 if (isConstructing
&& !callee
->isConstructor()) {
11587 // Likewise, if the callee is a class constructor, we have to throw.
11588 if (!isConstructing
&& callee
->isClassConstructor()) {
11595 void CallIRGenerator::emitCallScriptedGuards(ObjOperandId calleeObjId
,
11596 JSFunction
* calleeFunc
,
11597 Int32OperandId argcId
,
11598 CallFlags flags
, Shape
* thisShape
,
11599 bool isBoundFunction
) {
11600 bool isConstructing
= flags
.isConstructing();
11602 if (mode_
== ICState::Mode::Specialized
) {
11603 MOZ_ASSERT_IF(isConstructing
, thisShape
|| flags
.needsUninitializedThis());
11605 // Ensure callee matches this stub's callee
11606 emitCalleeGuard(calleeObjId
, calleeFunc
);
11608 // Emit guards to ensure the newTarget's .prototype property is what we
11609 // expect. Note that getThisForScripted checked newTarget is a function
11610 // with a non-configurable .prototype data property.
11612 JSFunction
* newTarget
;
11613 ObjOperandId newTargetObjId
;
11614 if (isBoundFunction
) {
11615 newTarget
= calleeFunc
;
11616 newTargetObjId
= calleeObjId
;
11618 newTarget
= &newTarget_
.toObject().as
<JSFunction
>();
11619 ValOperandId newTargetValId
= writer
.loadArgumentDynamicSlot(
11620 ArgumentKind::NewTarget
, argcId
, flags
);
11621 newTargetObjId
= writer
.guardToObject(newTargetValId
);
11624 Maybe
<PropertyInfo
> prop
= newTarget
->lookupPure(cx_
->names().prototype
);
11625 MOZ_ASSERT(prop
.isSome());
11626 uint32_t slot
= prop
->slot();
11627 MOZ_ASSERT(slot
>= newTarget
->numFixedSlots(),
11628 "Stub code relies on this");
11630 writer
.guardShape(newTargetObjId
, newTarget
->shape());
11632 const Value
& value
= newTarget
->getSlot(slot
);
11633 if (value
.isObject()) {
11634 JSObject
* prototypeObject
= &value
.toObject();
11636 ObjOperandId protoId
= writer
.loadObject(prototypeObject
);
11637 writer
.guardDynamicSlotIsSpecificObject(
11638 newTargetObjId
, protoId
, slot
- newTarget
->numFixedSlots());
11640 writer
.guardDynamicSlotIsNotObject(newTargetObjId
,
11641 slot
- newTarget
->numFixedSlots());
11644 // Call metaScriptedThisShape before emitting the call, so that Warp can
11645 // use the shape to create the |this| object before transpiling the call.
11646 writer
.metaScriptedThisShape(thisShape
);
11649 // Guard that object is a scripted function
11650 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
11651 writer
.guardFunctionHasJitEntry(calleeObjId
, isConstructing
);
11653 if (isConstructing
) {
11654 // If callee is not a constructor, we have to throw.
11655 writer
.guardFunctionIsConstructor(calleeObjId
);
11657 // If callee is a class constructor, we have to throw.
11658 writer
.guardNotClassConstructor(calleeObjId
);
11663 AttachDecision
CallIRGenerator::tryAttachCallScripted(
11664 HandleFunction calleeFunc
) {
11665 MOZ_ASSERT(calleeFunc
->hasJitEntry());
11667 if (calleeFunc
->isWasmWithJitEntry()) {
11668 TRY_ATTACH(tryAttachWasmCall(calleeFunc
));
11671 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
11673 bool isConstructing
= IsConstructPC(pc_
);
11674 bool isSpread
= IsSpreadPC(pc_
);
11675 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
11676 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
11678 if (!CanOptimizeScriptedCall(calleeFunc
, isConstructing
)) {
11679 return AttachDecision::NoAction
;
11682 if (isConstructing
&& !calleeFunc
->hasJitScript()) {
11683 // If we're constructing, require the callee to have a JitScript. This isn't
11684 // required for correctness but avoids allocating a template object below
11685 // for constructors that aren't hot. See bug 1419758.
11686 return AttachDecision::TemporarilyUnoptimizable
;
11689 // Verify that spread calls have a reasonable number of arguments.
11690 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
11691 return AttachDecision::NoAction
;
11694 Rooted
<Shape
*> thisShape(cx_
);
11695 if (isConstructing
&& isSpecialized
) {
11696 Rooted
<JSObject
*> newTarget(cx_
, &newTarget_
.toObject());
11697 switch (getThisShapeForScripted(calleeFunc
, newTarget
, &thisShape
)) {
11698 case ScriptedThisResult::PlainObjectShape
:
11700 case ScriptedThisResult::UninitializedThis
:
11701 flags
.setNeedsUninitializedThis();
11703 case ScriptedThisResult::NoAction
:
11704 return AttachDecision::NoAction
;
11709 Int32OperandId
argcId(writer
.setInputOperandId(0));
11711 // Load the callee and ensure it is an object
11712 ValOperandId calleeValId
=
11713 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11714 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11716 emitCallScriptedGuards(calleeObjId
, calleeFunc
, argcId
, flags
, thisShape
,
11717 /* isBoundFunction = */ false);
11719 writer
.callScriptedFunction(calleeObjId
, argcId
, flags
,
11720 ClampFixedArgc(argc_
));
11721 writer
.returnFromIC();
11723 if (isSpecialized
) {
11724 trackAttached("Call.CallScripted");
11726 trackAttached("Call.CallAnyScripted");
11729 return AttachDecision::Attach
;
11732 AttachDecision
CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc
) {
11733 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
11735 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
11737 bool isSpread
= IsSpreadPC(pc_
);
11738 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
11739 bool isConstructing
= IsConstructPC(pc_
);
11740 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
11742 if (isConstructing
&& !calleeFunc
->isConstructor()) {
11743 return AttachDecision::NoAction
;
11746 // Verify that spread calls have a reasonable number of arguments.
11747 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
11748 return AttachDecision::NoAction
;
11751 // Check for specific native-function optimizations.
11752 if (isSpecialized
) {
11753 TRY_ATTACH(tryAttachInlinableNative(calleeFunc
, flags
));
11757 Int32OperandId
argcId(writer
.setInputOperandId(0));
11759 // Load the callee and ensure it is an object
11760 ValOperandId calleeValId
=
11761 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11762 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11764 // DOM calls need an additional guard so only try optimizing the first stub.
11765 // Can only optimize normal (non-spread) calls.
11766 if (isFirstStub_
&& !isSpread
&& thisval_
.isObject() &&
11767 CanAttachDOMCall(cx_
, JSJitInfo::Method
, &thisval_
.toObject(), calleeFunc
,
11769 MOZ_ASSERT(!isConstructing
, "DOM functions are not constructors");
11771 // Guard that |this| is an object.
11772 ValOperandId thisValId
=
11773 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
, flags
);
11774 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
11776 // Guard on the |this| shape to make sure it's the right instance. This also
11777 // ensures DOM_OBJECT_SLOT is stored in a fixed slot. See CanAttachDOMCall.
11778 writer
.guardShape(thisObjId
, thisval_
.toObject().shape());
11780 // Ensure callee matches this stub's callee
11781 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
11782 writer
.callDOMFunction(calleeObjId
, argcId
, thisObjId
, calleeFunc
, flags
,
11783 ClampFixedArgc(argc_
));
11785 trackAttached("Call.CallDOM");
11786 } else if (isSpecialized
) {
11787 // Ensure callee matches this stub's callee
11788 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
11789 writer
.callNativeFunction(calleeObjId
, argcId
, op_
, calleeFunc
, flags
,
11790 ClampFixedArgc(argc_
));
11792 trackAttached("Call.CallNative");
11794 // Guard that object is a native function
11795 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
11796 writer
.guardFunctionHasNoJitEntry(calleeObjId
);
11798 if (isConstructing
) {
11799 // If callee is not a constructor, we have to throw.
11800 writer
.guardFunctionIsConstructor(calleeObjId
);
11802 // If callee is a class constructor, we have to throw.
11803 writer
.guardNotClassConstructor(calleeObjId
);
11805 writer
.callAnyNativeFunction(calleeObjId
, argcId
, flags
,
11806 ClampFixedArgc(argc_
));
11808 trackAttached("Call.CallAnyNative");
11811 writer
.returnFromIC();
11813 return AttachDecision::Attach
;
11816 AttachDecision
CallIRGenerator::tryAttachCallHook(HandleObject calleeObj
) {
11817 if (mode_
!= ICState::Mode::Specialized
) {
11818 // We do not have megamorphic call hook stubs.
11819 // TODO: Should we attach specialized call hook stubs in
11820 // megamorphic mode to avoid going generic?
11821 return AttachDecision::NoAction
;
11824 bool isSpread
= IsSpreadPC(pc_
);
11825 bool isConstructing
= IsConstructPC(pc_
);
11826 CallFlags
flags(isConstructing
, isSpread
);
11828 isConstructing
? calleeObj
->constructHook() : calleeObj
->callHook();
11830 return AttachDecision::NoAction
;
11833 // Bound functions have a JSClass construct hook but are not always
11835 if (isConstructing
&& !calleeObj
->isConstructor()) {
11836 return AttachDecision::NoAction
;
11839 // We don't support spread calls in the transpiler yet.
11841 return AttachDecision::NoAction
;
11845 Int32OperandId
argcId(writer
.setInputOperandId(0));
11847 // Load the callee and ensure it is an object
11848 ValOperandId calleeValId
=
11849 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11850 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11852 // Ensure the callee's class matches the one in this stub.
11853 writer
.guardAnyClass(calleeObjId
, calleeObj
->getClass());
11855 if (isConstructing
&& calleeObj
->is
<BoundFunctionObject
>()) {
11856 writer
.guardBoundFunctionIsConstructor(calleeObjId
);
11859 writer
.callClassHook(calleeObjId
, argcId
, hook
, flags
, ClampFixedArgc(argc_
));
11860 writer
.returnFromIC();
11862 trackAttached("Call.CallHook");
11864 return AttachDecision::Attach
;
11867 AttachDecision
CallIRGenerator::tryAttachBoundFunction(
11868 Handle
<BoundFunctionObject
*> calleeObj
) {
11869 // The target must be a JSFunction with a JitEntry.
11870 if (!calleeObj
->getTarget()->is
<JSFunction
>()) {
11871 return AttachDecision::NoAction
;
11874 bool isSpread
= IsSpreadPC(pc_
);
11875 bool isConstructing
= IsConstructPC(pc_
);
11877 // Spread calls are not supported yet.
11879 return AttachDecision::NoAction
;
11882 Rooted
<JSFunction
*> target(cx_
, &calleeObj
->getTarget()->as
<JSFunction
>());
11883 if (!CanOptimizeScriptedCall(target
, isConstructing
)) {
11884 return AttachDecision::NoAction
;
11887 // Limit the number of bound arguments to prevent us from compiling many
11888 // different stubs (we bake in numBoundArgs and it's usually very small).
11889 static constexpr size_t MaxBoundArgs
= 10;
11890 size_t numBoundArgs
= calleeObj
->numBoundArgs();
11891 if (numBoundArgs
> MaxBoundArgs
) {
11892 return AttachDecision::NoAction
;
11895 // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
11896 if (numBoundArgs
+ argc_
> JIT_ARGS_LENGTH_MAX
) {
11897 return AttachDecision::NoAction
;
11900 CallFlags
flags(isConstructing
, isSpread
);
11902 if (mode_
== ICState::Mode::Specialized
) {
11903 if (cx_
->realm() == target
->realm()) {
11904 flags
.setIsSameRealm();
11908 Rooted
<Shape
*> thisShape(cx_
);
11909 if (isConstructing
) {
11910 // Only optimize if newTarget == callee. This is the common case and ensures
11911 // we can always pass the bound function's target as newTarget.
11912 if (newTarget_
!= ObjectValue(*calleeObj
)) {
11913 return AttachDecision::NoAction
;
11916 if (mode_
== ICState::Mode::Specialized
) {
11917 Handle
<JSFunction
*> newTarget
= target
;
11918 switch (getThisShapeForScripted(target
, newTarget
, &thisShape
)) {
11919 case ScriptedThisResult::PlainObjectShape
:
11921 case ScriptedThisResult::UninitializedThis
:
11922 flags
.setNeedsUninitializedThis();
11924 case ScriptedThisResult::NoAction
:
11925 return AttachDecision::NoAction
;
11931 Int32OperandId
argcId(writer
.setInputOperandId(0));
11933 // Load the callee and ensure it's a bound function.
11934 ValOperandId calleeValId
=
11935 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11936 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11937 writer
.guardClass(calleeObjId
, GuardClassKind::BoundFunction
);
11939 // Ensure numBoundArgs matches.
11940 Int32OperandId numBoundArgsId
= writer
.loadBoundFunctionNumArgs(calleeObjId
);
11941 writer
.guardSpecificInt32(numBoundArgsId
, numBoundArgs
);
11943 if (isConstructing
) {
11944 // Guard newTarget == callee. We depend on this in CallBoundScriptedFunction
11945 // and in emitCallScriptedGuards by using boundTarget as newTarget.
11946 ValOperandId newTargetValId
=
11947 writer
.loadArgumentDynamicSlot(ArgumentKind::NewTarget
, argcId
, flags
);
11948 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
11949 writer
.guardObjectIdentity(newTargetObjId
, calleeObjId
);
11952 ObjOperandId targetId
= writer
.loadBoundFunctionTarget(calleeObjId
);
11954 emitCallScriptedGuards(targetId
, target
, argcId
, flags
, thisShape
,
11955 /* isBoundFunction = */ true);
11957 writer
.callBoundScriptedFunction(calleeObjId
, targetId
, argcId
, flags
,
11959 writer
.returnFromIC();
11961 trackAttached("Call.BoundFunction");
11962 return AttachDecision::Attach
;
11965 AttachDecision
CallIRGenerator::tryAttachStub() {
11966 AutoAssertNoPendingException
aanpe(cx_
);
11968 // Some opcodes are not yet supported.
11971 case JSOp::CallContent
:
11972 case JSOp::CallIgnoresRv
:
11973 case JSOp::CallIter
:
11974 case JSOp::CallContentIter
:
11975 case JSOp::SpreadCall
:
11977 case JSOp::NewContent
:
11978 case JSOp::SpreadNew
:
11979 case JSOp::SuperCall
:
11980 case JSOp::SpreadSuperCall
:
11983 return AttachDecision::NoAction
;
11986 MOZ_ASSERT(mode_
!= ICState::Mode::Generic
);
11988 // Ensure callee is a function.
11989 if (!callee_
.isObject()) {
11990 return AttachDecision::NoAction
;
11993 RootedObject
calleeObj(cx_
, &callee_
.toObject());
11994 if (calleeObj
->is
<BoundFunctionObject
>()) {
11995 TRY_ATTACH(tryAttachBoundFunction(calleeObj
.as
<BoundFunctionObject
>()));
11997 if (!calleeObj
->is
<JSFunction
>()) {
11998 return tryAttachCallHook(calleeObj
);
12001 HandleFunction calleeFunc
= calleeObj
.as
<JSFunction
>();
12003 // Check for scripted optimizations.
12004 if (calleeFunc
->hasJitEntry()) {
12005 return tryAttachCallScripted(calleeFunc
);
12008 // Check for native-function optimizations.
12009 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12011 // Try inlining Function.prototype.{call,apply}. We don't use the
12012 // InlinableNative mechanism for this because we want to optimize these more
12013 // aggressively than other natives.
12014 if (op_
== JSOp::Call
|| op_
== JSOp::CallContent
||
12015 op_
== JSOp::CallIgnoresRv
) {
12016 TRY_ATTACH(tryAttachFunCall(calleeFunc
));
12017 TRY_ATTACH(tryAttachFunApply(calleeFunc
));
12020 return tryAttachCallNative(calleeFunc
);
12023 void CallIRGenerator::trackAttached(const char* name
) {
12024 stubName_
= name
? name
: "NotAttached";
12025 #ifdef JS_CACHEIR_SPEW
12026 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12027 sp
.valueProperty("callee", callee_
);
12028 sp
.valueProperty("thisval", thisval_
);
12029 sp
.valueProperty("argc", Int32Value(argc_
));
12031 // Try to log the first two arguments.
12032 if (args_
.length() >= 1) {
12033 sp
.valueProperty("arg0", args_
[0]);
12035 if (args_
.length() >= 2) {
12036 sp
.valueProperty("arg1", args_
[1]);
12042 // Class which holds a shape pointer for use when caches might reference data in
12044 static const JSClass shapeContainerClass
= {"ShapeContainer",
12045 JSCLASS_HAS_RESERVED_SLOTS(1)};
12047 static const size_t SHAPE_CONTAINER_SLOT
= 0;
12049 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
12050 Handle
<NativeObject
*> obj
) {
12051 MOZ_ASSERT(cx
->compartment() != obj
->compartment());
12053 RootedObject
wrapper(cx
);
12055 AutoRealm
ar(cx
, obj
);
12056 wrapper
= NewBuiltinClassInstance(cx
, &shapeContainerClass
);
12060 wrapper
->as
<NativeObject
>().setReservedSlot(
12061 SHAPE_CONTAINER_SLOT
, PrivateGCThingValue(obj
->shape()));
12063 if (!JS_WrapObject(cx
, &wrapper
)) {
12066 MOZ_ASSERT(IsWrapper(wrapper
));
12070 void jit::LoadShapeWrapperContents(MacroAssembler
& masm
, Register obj
,
12071 Register dst
, Label
* failure
) {
12072 masm
.loadPtr(Address(obj
, ProxyObject::offsetOfReservedSlots()), dst
);
12073 Address
privateAddr(dst
,
12074 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
12075 masm
.fallibleUnboxObject(privateAddr
, dst
, failure
);
12076 masm
.unboxNonDouble(
12077 Address(dst
, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT
)), dst
,
12078 JSVAL_TYPE_PRIVATE_GCTHING
);
12081 static bool CanConvertToInt32ForToNumber(const Value
& v
) {
12082 return v
.isInt32() || v
.isBoolean() || v
.isNull();
12085 static Int32OperandId
EmitGuardToInt32ForToNumber(CacheIRWriter
& writer
,
12089 return writer
.guardToInt32(id
);
12092 writer
.guardIsNull(id
);
12093 return writer
.loadInt32Constant(0);
12095 MOZ_ASSERT(v
.isBoolean());
12096 return writer
.guardBooleanToInt32(id
);
12099 static bool CanConvertToDoubleForToNumber(const Value
& v
) {
12100 return v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
12103 static NumberOperandId
EmitGuardToDoubleForToNumber(CacheIRWriter
& writer
,
12106 if (v
.isNumber()) {
12107 return writer
.guardIsNumber(id
);
12109 if (v
.isBoolean()) {
12110 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
12111 return writer
.booleanToNumber(boolId
);
12114 writer
.guardIsNull(id
);
12115 return writer
.loadDoubleConstant(0.0);
12117 MOZ_ASSERT(v
.isUndefined());
12118 writer
.guardIsUndefined(id
);
12119 return writer
.loadDoubleConstant(JS::GenericNaN());
12122 CompareIRGenerator::CompareIRGenerator(JSContext
* cx
, HandleScript script
,
12123 jsbytecode
* pc
, ICState state
, JSOp op
,
12124 HandleValue lhsVal
, HandleValue rhsVal
)
12125 : IRGenerator(cx
, script
, pc
, CacheKind::Compare
, state
),
12130 AttachDecision
CompareIRGenerator::tryAttachString(ValOperandId lhsId
,
12131 ValOperandId rhsId
) {
12132 if (!lhsVal_
.isString() || !rhsVal_
.isString()) {
12133 return AttachDecision::NoAction
;
12136 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
12137 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
12138 writer
.compareStringResult(op_
, lhsStrId
, rhsStrId
);
12139 writer
.returnFromIC();
12141 trackAttached("Compare.String");
12142 return AttachDecision::Attach
;
12145 AttachDecision
CompareIRGenerator::tryAttachObject(ValOperandId lhsId
,
12146 ValOperandId rhsId
) {
12147 MOZ_ASSERT(IsEqualityOp(op_
));
12149 if (!lhsVal_
.isObject() || !rhsVal_
.isObject()) {
12150 return AttachDecision::NoAction
;
12153 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
12154 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
12155 writer
.compareObjectResult(op_
, lhsObjId
, rhsObjId
);
12156 writer
.returnFromIC();
12158 trackAttached("Compare.Object");
12159 return AttachDecision::Attach
;
12162 AttachDecision
CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId
,
12163 ValOperandId rhsId
) {
12164 MOZ_ASSERT(IsEqualityOp(op_
));
12166 if (!lhsVal_
.isSymbol() || !rhsVal_
.isSymbol()) {
12167 return AttachDecision::NoAction
;
12170 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
12171 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
12172 writer
.compareSymbolResult(op_
, lhsSymId
, rhsSymId
);
12173 writer
.returnFromIC();
12175 trackAttached("Compare.Symbol");
12176 return AttachDecision::Attach
;
12179 AttachDecision
CompareIRGenerator::tryAttachStrictDifferentTypes(
12180 ValOperandId lhsId
, ValOperandId rhsId
) {
12181 MOZ_ASSERT(IsEqualityOp(op_
));
12183 if (op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
) {
12184 return AttachDecision::NoAction
;
12187 // Probably can't hit some of these.
12188 if (SameType(lhsVal_
, rhsVal_
) ||
12189 (lhsVal_
.isNumber() && rhsVal_
.isNumber())) {
12190 return AttachDecision::NoAction
;
12194 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
12195 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
12196 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
12198 // Now that we've passed the guard, we know differing types, so return the
12200 writer
.loadBooleanResult(op_
== JSOp::StrictNe
? true : false);
12201 writer
.returnFromIC();
12203 trackAttached("Compare.StrictDifferentTypes");
12204 return AttachDecision::Attach
;
12207 AttachDecision
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId
,
12208 ValOperandId rhsId
) {
12209 if (!CanConvertToInt32ForToNumber(lhsVal_
) ||
12210 !CanConvertToInt32ForToNumber(rhsVal_
)) {
12211 return AttachDecision::NoAction
;
12214 // Strictly different types should have been handed by
12215 // tryAttachStrictDifferentTypes.
12216 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12217 lhsVal_
.type() == rhsVal_
.type());
12219 // Should have been handled by tryAttachAnyNullUndefined.
12220 MOZ_ASSERT_IF(lhsVal_
.isNull() || rhsVal_
.isNull(), !IsEqualityOp(op_
));
12222 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12223 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12225 writer
.compareInt32Result(op_
, lhsIntId
, rhsIntId
);
12226 writer
.returnFromIC();
12228 trackAttached("Compare.Int32");
12229 return AttachDecision::Attach
;
12232 AttachDecision
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId
,
12233 ValOperandId rhsId
) {
12234 if (!CanConvertToDoubleForToNumber(lhsVal_
) ||
12235 !CanConvertToDoubleForToNumber(rhsVal_
)) {
12236 return AttachDecision::NoAction
;
12239 // Strictly different types should have been handed by
12240 // tryAttachStrictDifferentTypes.
12241 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12242 lhsVal_
.type() == rhsVal_
.type() ||
12243 (lhsVal_
.isNumber() && rhsVal_
.isNumber()));
12245 // Should have been handled by tryAttachAnyNullUndefined.
12246 MOZ_ASSERT_IF(lhsVal_
.isNullOrUndefined() || rhsVal_
.isNullOrUndefined(),
12247 !IsEqualityOp(op_
));
12249 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12250 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12251 writer
.compareDoubleResult(op_
, lhs
, rhs
);
12252 writer
.returnFromIC();
12254 trackAttached("Compare.Number");
12255 return AttachDecision::Attach
;
12258 AttachDecision
CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId
,
12259 ValOperandId rhsId
) {
12260 if (!lhsVal_
.isBigInt() || !rhsVal_
.isBigInt()) {
12261 return AttachDecision::NoAction
;
12264 BigIntOperandId lhs
= writer
.guardToBigInt(lhsId
);
12265 BigIntOperandId rhs
= writer
.guardToBigInt(rhsId
);
12267 writer
.compareBigIntResult(op_
, lhs
, rhs
);
12268 writer
.returnFromIC();
12270 trackAttached("Compare.BigInt");
12271 return AttachDecision::Attach
;
12274 AttachDecision
CompareIRGenerator::tryAttachAnyNullUndefined(
12275 ValOperandId lhsId
, ValOperandId rhsId
) {
12276 MOZ_ASSERT(IsEqualityOp(op_
));
12278 // Either RHS or LHS needs to be null/undefined.
12279 if (!lhsVal_
.isNullOrUndefined() && !rhsVal_
.isNullOrUndefined()) {
12280 return AttachDecision::NoAction
;
12283 // We assume that the side with null/undefined is usually constant, in
12284 // code like `if (x === undefined) { x = {}; }`.
12285 // That is why we don't attach when both sides are undefined/null,
12286 // because we would basically need to decide by chance which side is
12287 // the likely constant.
12288 // The actual generated code however handles null/undefined of course.
12289 if (lhsVal_
.isNullOrUndefined() && rhsVal_
.isNullOrUndefined()) {
12290 return AttachDecision::NoAction
;
12293 if (rhsVal_
.isNullOrUndefined()) {
12294 if (rhsVal_
.isNull()) {
12295 writer
.guardIsNull(rhsId
);
12296 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, lhsId
);
12297 trackAttached("Compare.AnyNull");
12299 writer
.guardIsUndefined(rhsId
);
12300 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, lhsId
);
12301 trackAttached("Compare.AnyUndefined");
12304 if (lhsVal_
.isNull()) {
12305 writer
.guardIsNull(lhsId
);
12306 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, rhsId
);
12307 trackAttached("Compare.NullAny");
12309 writer
.guardIsUndefined(lhsId
);
12310 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, rhsId
);
12311 trackAttached("Compare.UndefinedAny");
12315 writer
.returnFromIC();
12316 return AttachDecision::Attach
;
12319 // Handle {null/undefined} x {null,undefined} equality comparisons
12320 AttachDecision
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId
,
12321 ValOperandId rhsId
) {
12322 if (!lhsVal_
.isNullOrUndefined() || !rhsVal_
.isNullOrUndefined()) {
12323 return AttachDecision::NoAction
;
12326 if (op_
== JSOp::Eq
|| op_
== JSOp::Ne
) {
12327 writer
.guardIsNullOrUndefined(lhsId
);
12328 writer
.guardIsNullOrUndefined(rhsId
);
12329 // Sloppy equality means we actually only care about the op:
12330 writer
.loadBooleanResult(op_
== JSOp::Eq
);
12331 trackAttached("Compare.SloppyNullUndefined");
12333 // Strict equality only hits this branch, and only in the
12334 // undef {!,=}== undef and null {!,=}== null cases.
12335 // The other cases should have hit tryAttachStrictDifferentTypes.
12336 MOZ_ASSERT(lhsVal_
.isNull() == rhsVal_
.isNull());
12337 lhsVal_
.isNull() ? writer
.guardIsNull(lhsId
)
12338 : writer
.guardIsUndefined(lhsId
);
12339 rhsVal_
.isNull() ? writer
.guardIsNull(rhsId
)
12340 : writer
.guardIsUndefined(rhsId
);
12341 writer
.loadBooleanResult(op_
== JSOp::StrictEq
);
12342 trackAttached("Compare.StrictNullUndefinedEquality");
12345 writer
.returnFromIC();
12346 return AttachDecision::Attach
;
12349 AttachDecision
CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId
,
12350 ValOperandId rhsId
) {
12351 // Ensure String x {Number, Boolean, Null, Undefined}
12352 if (!(lhsVal_
.isString() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12353 !(rhsVal_
.isString() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12354 return AttachDecision::NoAction
;
12357 // Case should have been handled by tryAttachStrictDifferentTypes
12358 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12360 auto createGuards
= [&](const Value
& v
, ValOperandId vId
) {
12361 if (v
.isString()) {
12362 StringOperandId strId
= writer
.guardToString(vId
);
12363 return writer
.guardStringToNumber(strId
);
12365 return EmitGuardToDoubleForToNumber(writer
, vId
, v
);
12368 NumberOperandId lhsGuardedId
= createGuards(lhsVal_
, lhsId
);
12369 NumberOperandId rhsGuardedId
= createGuards(rhsVal_
, rhsId
);
12370 writer
.compareDoubleResult(op_
, lhsGuardedId
, rhsGuardedId
);
12371 writer
.returnFromIC();
12373 trackAttached("Compare.StringNumber");
12374 return AttachDecision::Attach
;
12377 AttachDecision
CompareIRGenerator::tryAttachPrimitiveSymbol(
12378 ValOperandId lhsId
, ValOperandId rhsId
) {
12379 MOZ_ASSERT(IsEqualityOp(op_
));
12381 // The set of primitive cases we want to handle here (excluding null,
12382 // undefined, and symbol)
12383 auto isPrimitive
= [](const Value
& x
) {
12384 return x
.isString() || x
.isBoolean() || x
.isNumber() || x
.isBigInt();
12387 // Ensure Symbol x {String, Bool, Number, BigInt}.
12388 if (!(lhsVal_
.isSymbol() && isPrimitive(rhsVal_
)) &&
12389 !(rhsVal_
.isSymbol() && isPrimitive(lhsVal_
))) {
12390 return AttachDecision::NoAction
;
12393 auto guardPrimitive
= [&](const Value
& v
, ValOperandId id
) {
12394 MOZ_ASSERT(isPrimitive(v
));
12395 if (v
.isNumber()) {
12396 writer
.guardIsNumber(id
);
12399 switch (v
.extractNonDoubleType()) {
12400 case JSVAL_TYPE_STRING
:
12401 writer
.guardToString(id
);
12403 case JSVAL_TYPE_BOOLEAN
:
12404 writer
.guardToBoolean(id
);
12406 case JSVAL_TYPE_BIGINT
:
12407 writer
.guardToBigInt(id
);
12410 MOZ_CRASH("unexpected type");
12415 if (lhsVal_
.isSymbol()) {
12416 writer
.guardToSymbol(lhsId
);
12417 guardPrimitive(rhsVal_
, rhsId
);
12419 guardPrimitive(lhsVal_
, lhsId
);
12420 writer
.guardToSymbol(rhsId
);
12423 // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
12424 // always be false for other compare ops.
12425 writer
.loadBooleanResult(op_
== JSOp::Ne
|| op_
== JSOp::StrictNe
);
12426 writer
.returnFromIC();
12428 trackAttached("Compare.PrimitiveSymbol");
12429 return AttachDecision::Attach
;
12432 AttachDecision
CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId
,
12433 ValOperandId rhsId
) {
12434 // Ensure BigInt x {Int32, Boolean, Null}.
12435 if (!(lhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(rhsVal_
)) &&
12436 !(rhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(lhsVal_
))) {
12437 return AttachDecision::NoAction
;
12440 // Case should have been handled by tryAttachStrictDifferentTypes
12441 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12443 if (lhsVal_
.isBigInt()) {
12444 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12445 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12447 writer
.compareBigIntInt32Result(op_
, bigIntId
, intId
);
12449 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12450 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12452 writer
.compareBigIntInt32Result(ReverseCompareOp(op_
), bigIntId
, intId
);
12454 writer
.returnFromIC();
12456 trackAttached("Compare.BigIntInt32");
12457 return AttachDecision::Attach
;
12460 AttachDecision
CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId
,
12461 ValOperandId rhsId
) {
12462 // Ensure BigInt x {Number, Undefined}.
12463 if (!(lhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12464 !(rhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12465 return AttachDecision::NoAction
;
12468 // Case should have been handled by tryAttachStrictDifferentTypes
12469 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12471 // Case should have been handled by tryAttachBigIntInt32.
12472 MOZ_ASSERT(!CanConvertToInt32ForToNumber(lhsVal_
));
12473 MOZ_ASSERT(!CanConvertToInt32ForToNumber(rhsVal_
));
12475 if (lhsVal_
.isBigInt()) {
12476 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12477 NumberOperandId numId
=
12478 EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12480 writer
.compareBigIntNumberResult(op_
, bigIntId
, numId
);
12482 NumberOperandId numId
=
12483 EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12484 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12486 writer
.compareBigIntNumberResult(ReverseCompareOp(op_
), bigIntId
, numId
);
12488 writer
.returnFromIC();
12490 trackAttached("Compare.BigIntNumber");
12491 return AttachDecision::Attach
;
12494 AttachDecision
CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId
,
12495 ValOperandId rhsId
) {
12496 // Ensure BigInt x String.
12497 if (!(lhsVal_
.isBigInt() && rhsVal_
.isString()) &&
12498 !(rhsVal_
.isBigInt() && lhsVal_
.isString())) {
12499 return AttachDecision::NoAction
;
12502 // Case should have been handled by tryAttachStrictDifferentTypes
12503 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12505 if (lhsVal_
.isBigInt()) {
12506 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12507 StringOperandId strId
= writer
.guardToString(rhsId
);
12509 writer
.compareBigIntStringResult(op_
, bigIntId
, strId
);
12511 StringOperandId strId
= writer
.guardToString(lhsId
);
12512 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12514 writer
.compareBigIntStringResult(ReverseCompareOp(op_
), bigIntId
, strId
);
12516 writer
.returnFromIC();
12518 trackAttached("Compare.BigIntString");
12519 return AttachDecision::Attach
;
12522 AttachDecision
CompareIRGenerator::tryAttachStub() {
12523 MOZ_ASSERT(cacheKind_
== CacheKind::Compare
);
12524 MOZ_ASSERT(IsEqualityOp(op_
) || IsRelationalOp(op_
));
12526 AutoAssertNoPendingException
aanpe(cx_
);
12528 constexpr uint8_t lhsIndex
= 0;
12529 constexpr uint8_t rhsIndex
= 1;
12531 ValOperandId
lhsId(writer
.setInputOperandId(lhsIndex
));
12532 ValOperandId
rhsId(writer
.setInputOperandId(rhsIndex
));
12534 // For sloppy equality ops, there are cases this IC does not handle:
12535 // - {Object} x {String, Symbol, Bool, Number, BigInt}.
12537 // For relational comparison ops, these cases aren't handled:
12538 // - Object x {String, Bool, Number, BigInt, Object, Null, Undefined}.
12539 // Note: |Symbol x any| always throws, so it doesn't need to be handled.
12541 // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
12542 // already present.)
12544 if (IsEqualityOp(op_
)) {
12545 TRY_ATTACH(tryAttachObject(lhsId
, rhsId
));
12546 TRY_ATTACH(tryAttachSymbol(lhsId
, rhsId
));
12548 // Handles any (non null or undefined) comparison with null/undefined.
12549 TRY_ATTACH(tryAttachAnyNullUndefined(lhsId
, rhsId
));
12551 // This covers -strict- equality/inequality using a type tag check, so
12552 // catches all different type pairs outside of Numbers, which cannot be
12553 // checked on tags alone.
12554 TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId
, rhsId
));
12556 TRY_ATTACH(tryAttachNullUndefined(lhsId
, rhsId
));
12558 TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId
, rhsId
));
12561 // We want these to be last, to allow us to bypass the
12562 // strictly-different-types cases in the below attachment code
12563 TRY_ATTACH(tryAttachInt32(lhsId
, rhsId
));
12564 TRY_ATTACH(tryAttachNumber(lhsId
, rhsId
));
12565 TRY_ATTACH(tryAttachBigInt(lhsId
, rhsId
));
12566 TRY_ATTACH(tryAttachString(lhsId
, rhsId
));
12568 TRY_ATTACH(tryAttachStringNumber(lhsId
, rhsId
));
12570 TRY_ATTACH(tryAttachBigIntInt32(lhsId
, rhsId
));
12571 TRY_ATTACH(tryAttachBigIntNumber(lhsId
, rhsId
));
12572 TRY_ATTACH(tryAttachBigIntString(lhsId
, rhsId
));
12574 // Strict equality is always supported.
12575 MOZ_ASSERT(!IsStrictEqualityOp(op_
));
12577 // Other operations are unsupported iff at least one operand is an object.
12578 MOZ_ASSERT(lhsVal_
.isObject() || rhsVal_
.isObject());
12580 trackAttached(IRGenerator::NotAttached
);
12581 return AttachDecision::NoAction
;
12584 void CompareIRGenerator::trackAttached(const char* name
) {
12585 stubName_
= name
? name
: "NotAttached";
12586 #ifdef JS_CACHEIR_SPEW
12587 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12588 sp
.valueProperty("lhs", lhsVal_
);
12589 sp
.valueProperty("rhs", rhsVal_
);
12590 sp
.opcodeProperty("op", op_
);
12595 ToBoolIRGenerator::ToBoolIRGenerator(JSContext
* cx
, HandleScript script
,
12596 jsbytecode
* pc
, ICState state
,
12598 : IRGenerator(cx
, script
, pc
, CacheKind::ToBool
, state
), val_(val
) {}
12600 void ToBoolIRGenerator::trackAttached(const char* name
) {
12601 stubName_
= name
? name
: "NotAttached";
12602 #ifdef JS_CACHEIR_SPEW
12603 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12604 sp
.valueProperty("val", val_
);
12609 AttachDecision
ToBoolIRGenerator::tryAttachStub() {
12610 AutoAssertNoPendingException
aanpe(cx_
);
12611 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
12613 TRY_ATTACH(tryAttachBool());
12614 TRY_ATTACH(tryAttachInt32());
12615 TRY_ATTACH(tryAttachNumber());
12616 TRY_ATTACH(tryAttachString());
12617 TRY_ATTACH(tryAttachNullOrUndefined());
12618 TRY_ATTACH(tryAttachObject());
12619 TRY_ATTACH(tryAttachSymbol());
12620 TRY_ATTACH(tryAttachBigInt());
12622 trackAttached(IRGenerator::NotAttached
);
12623 return AttachDecision::NoAction
;
12626 AttachDecision
ToBoolIRGenerator::tryAttachBool() {
12627 if (!val_
.isBoolean()) {
12628 return AttachDecision::NoAction
;
12631 ValOperandId
valId(writer
.setInputOperandId(0));
12632 writer
.guardNonDoubleType(valId
, ValueType::Boolean
);
12633 writer
.loadOperandResult(valId
);
12634 writer
.returnFromIC();
12635 trackAttached("ToBool.Bool");
12636 return AttachDecision::Attach
;
12639 AttachDecision
ToBoolIRGenerator::tryAttachInt32() {
12640 if (!val_
.isInt32()) {
12641 return AttachDecision::NoAction
;
12644 ValOperandId
valId(writer
.setInputOperandId(0));
12645 writer
.guardNonDoubleType(valId
, ValueType::Int32
);
12646 writer
.loadInt32TruthyResult(valId
);
12647 writer
.returnFromIC();
12648 trackAttached("ToBool.Int32");
12649 return AttachDecision::Attach
;
12652 AttachDecision
ToBoolIRGenerator::tryAttachNumber() {
12653 if (!val_
.isNumber()) {
12654 return AttachDecision::NoAction
;
12657 ValOperandId
valId(writer
.setInputOperandId(0));
12658 NumberOperandId numId
= writer
.guardIsNumber(valId
);
12659 writer
.loadDoubleTruthyResult(numId
);
12660 writer
.returnFromIC();
12661 trackAttached("ToBool.Number");
12662 return AttachDecision::Attach
;
12665 AttachDecision
ToBoolIRGenerator::tryAttachSymbol() {
12666 if (!val_
.isSymbol()) {
12667 return AttachDecision::NoAction
;
12670 ValOperandId
valId(writer
.setInputOperandId(0));
12671 writer
.guardNonDoubleType(valId
, ValueType::Symbol
);
12672 writer
.loadBooleanResult(true);
12673 writer
.returnFromIC();
12674 trackAttached("ToBool.Symbol");
12675 return AttachDecision::Attach
;
12678 AttachDecision
ToBoolIRGenerator::tryAttachString() {
12679 if (!val_
.isString()) {
12680 return AttachDecision::NoAction
;
12683 ValOperandId
valId(writer
.setInputOperandId(0));
12684 StringOperandId strId
= writer
.guardToString(valId
);
12685 writer
.loadStringTruthyResult(strId
);
12686 writer
.returnFromIC();
12687 trackAttached("ToBool.String");
12688 return AttachDecision::Attach
;
12691 AttachDecision
ToBoolIRGenerator::tryAttachNullOrUndefined() {
12692 if (!val_
.isNullOrUndefined()) {
12693 return AttachDecision::NoAction
;
12696 ValOperandId
valId(writer
.setInputOperandId(0));
12697 writer
.guardIsNullOrUndefined(valId
);
12698 writer
.loadBooleanResult(false);
12699 writer
.returnFromIC();
12700 trackAttached("ToBool.NullOrUndefined");
12701 return AttachDecision::Attach
;
12704 AttachDecision
ToBoolIRGenerator::tryAttachObject() {
12705 if (!val_
.isObject()) {
12706 return AttachDecision::NoAction
;
12709 ValOperandId
valId(writer
.setInputOperandId(0));
12710 ObjOperandId objId
= writer
.guardToObject(valId
);
12711 writer
.loadObjectTruthyResult(objId
);
12712 writer
.returnFromIC();
12713 trackAttached("ToBool.Object");
12714 return AttachDecision::Attach
;
12717 AttachDecision
ToBoolIRGenerator::tryAttachBigInt() {
12718 if (!val_
.isBigInt()) {
12719 return AttachDecision::NoAction
;
12722 ValOperandId
valId(writer
.setInputOperandId(0));
12723 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
12724 writer
.loadBigIntTruthyResult(bigIntId
);
12725 writer
.returnFromIC();
12726 trackAttached("ToBool.BigInt");
12727 return AttachDecision::Attach
;
12730 GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext
* cx
,
12731 HandleScript script
,
12732 jsbytecode
* pc
, ICState state
,
12734 : IRGenerator(cx
, script
, pc
, CacheKind::GetIntrinsic
, state
), val_(val
) {}
12736 void GetIntrinsicIRGenerator::trackAttached(const char* name
) {
12737 stubName_
= name
? name
: "NotAttached";
12738 #ifdef JS_CACHEIR_SPEW
12739 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12740 sp
.valueProperty("val", val_
);
12745 AttachDecision
GetIntrinsicIRGenerator::tryAttachStub() {
12746 AutoAssertNoPendingException
aanpe(cx_
);
12747 writer
.loadValueResult(val_
);
12748 writer
.returnFromIC();
12749 trackAttached("GetIntrinsic");
12750 return AttachDecision::Attach
;
12753 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext
* cx
, HandleScript script
,
12754 jsbytecode
* pc
, ICState state
,
12755 JSOp op
, HandleValue val
,
12757 : IRGenerator(cx
, script
, pc
, CacheKind::UnaryArith
, state
),
12762 void UnaryArithIRGenerator::trackAttached(const char* name
) {
12763 stubName_
= name
? name
: "NotAttached";
12764 #ifdef JS_CACHEIR_SPEW
12765 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12766 sp
.valueProperty("val", val_
);
12767 sp
.valueProperty("res", res_
);
12772 AttachDecision
UnaryArithIRGenerator::tryAttachStub() {
12773 AutoAssertNoPendingException
aanpe(cx_
);
12774 TRY_ATTACH(tryAttachInt32());
12775 TRY_ATTACH(tryAttachNumber());
12776 TRY_ATTACH(tryAttachBitwise());
12777 TRY_ATTACH(tryAttachBigInt());
12778 TRY_ATTACH(tryAttachStringInt32());
12779 TRY_ATTACH(tryAttachStringNumber());
12781 trackAttached(IRGenerator::NotAttached
);
12782 return AttachDecision::NoAction
;
12785 AttachDecision
UnaryArithIRGenerator::tryAttachInt32() {
12786 if (op_
== JSOp::BitNot
) {
12787 return AttachDecision::NoAction
;
12789 if (!CanConvertToInt32ForToNumber(val_
) || !res_
.isInt32()) {
12790 return AttachDecision::NoAction
;
12793 ValOperandId
valId(writer
.setInputOperandId(0));
12795 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, valId
, val_
);
12798 writer
.loadInt32Result(intId
);
12799 trackAttached("UnaryArith.Int32Pos");
12802 writer
.int32NegationResult(intId
);
12803 trackAttached("UnaryArith.Int32Neg");
12806 writer
.int32IncResult(intId
);
12807 trackAttached("UnaryArith.Int32Inc");
12810 writer
.int32DecResult(intId
);
12811 trackAttached("UnaryArith.Int32Dec");
12813 case JSOp::ToNumeric
:
12814 writer
.loadInt32Result(intId
);
12815 trackAttached("UnaryArith.Int32ToNumeric");
12818 MOZ_CRASH("unexpected OP");
12821 writer
.returnFromIC();
12822 return AttachDecision::Attach
;
12825 AttachDecision
UnaryArithIRGenerator::tryAttachNumber() {
12826 if (op_
== JSOp::BitNot
) {
12827 return AttachDecision::NoAction
;
12829 if (!CanConvertToDoubleForToNumber(val_
)) {
12830 return AttachDecision::NoAction
;
12832 MOZ_ASSERT(res_
.isNumber());
12834 ValOperandId
valId(writer
.setInputOperandId(0));
12835 NumberOperandId numId
= EmitGuardToDoubleForToNumber(writer
, valId
, val_
);
12839 writer
.loadDoubleResult(numId
);
12840 trackAttached("UnaryArith.DoublePos");
12843 writer
.doubleNegationResult(numId
);
12844 trackAttached("UnaryArith.DoubleNeg");
12847 writer
.doubleIncResult(numId
);
12848 trackAttached("UnaryArith.DoubleInc");
12851 writer
.doubleDecResult(numId
);
12852 trackAttached("UnaryArith.DoubleDec");
12854 case JSOp::ToNumeric
:
12855 writer
.loadDoubleResult(numId
);
12856 trackAttached("UnaryArith.DoubleToNumeric");
12859 MOZ_CRASH("Unexpected OP");
12862 writer
.returnFromIC();
12863 return AttachDecision::Attach
;
12866 static bool CanTruncateToInt32(const Value
& val
) {
12867 return val
.isNumber() || val
.isBoolean() || val
.isNullOrUndefined() ||
12871 // Convert type into int32 for the bitwise/shift operands.
12872 static Int32OperandId
EmitTruncateToInt32Guard(CacheIRWriter
& writer
,
12874 const Value
& val
) {
12875 MOZ_ASSERT(CanTruncateToInt32(val
));
12876 if (val
.isInt32()) {
12877 return writer
.guardToInt32(id
);
12879 if (val
.isBoolean()) {
12880 return writer
.guardBooleanToInt32(id
);
12882 if (val
.isNullOrUndefined()) {
12883 writer
.guardIsNullOrUndefined(id
);
12884 return writer
.loadInt32Constant(0);
12886 NumberOperandId numId
;
12887 if (val
.isString()) {
12888 StringOperandId strId
= writer
.guardToString(id
);
12889 numId
= writer
.guardStringToNumber(strId
);
12891 MOZ_ASSERT(val
.isDouble());
12892 numId
= writer
.guardIsNumber(id
);
12894 return writer
.truncateDoubleToUInt32(numId
);
12897 AttachDecision
UnaryArithIRGenerator::tryAttachBitwise() {
12898 // Only bitwise operators.
12899 if (op_
!= JSOp::BitNot
) {
12900 return AttachDecision::NoAction
;
12903 // Check guard conditions
12904 if (!CanTruncateToInt32(val_
)) {
12905 return AttachDecision::NoAction
;
12908 // Bitwise operators always produce Int32 values.
12909 MOZ_ASSERT(res_
.isInt32());
12911 ValOperandId
valId(writer
.setInputOperandId(0));
12912 Int32OperandId intId
= EmitTruncateToInt32Guard(writer
, valId
, val_
);
12913 writer
.int32NotResult(intId
);
12914 trackAttached("UnaryArith.BitwiseBitNot");
12916 writer
.returnFromIC();
12917 return AttachDecision::Attach
;
12920 AttachDecision
UnaryArithIRGenerator::tryAttachBigInt() {
12921 if (!val_
.isBigInt()) {
12922 return AttachDecision::NoAction
;
12924 MOZ_ASSERT(res_
.isBigInt());
12926 MOZ_ASSERT(op_
!= JSOp::Pos
,
12927 "Applying the unary + operator on BigInt values throws an error");
12929 ValOperandId
valId(writer
.setInputOperandId(0));
12930 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
12933 writer
.bigIntNotResult(bigIntId
);
12934 trackAttached("UnaryArith.BigIntNot");
12937 writer
.bigIntNegationResult(bigIntId
);
12938 trackAttached("UnaryArith.BigIntNeg");
12941 writer
.bigIntIncResult(bigIntId
);
12942 trackAttached("UnaryArith.BigIntInc");
12945 writer
.bigIntDecResult(bigIntId
);
12946 trackAttached("UnaryArith.BigIntDec");
12948 case JSOp::ToNumeric
:
12949 writer
.loadBigIntResult(bigIntId
);
12950 trackAttached("UnaryArith.BigIntToNumeric");
12953 MOZ_CRASH("Unexpected OP");
12956 writer
.returnFromIC();
12957 return AttachDecision::Attach
;
12960 AttachDecision
UnaryArithIRGenerator::tryAttachStringInt32() {
12961 if (!val_
.isString()) {
12962 return AttachDecision::NoAction
;
12964 MOZ_ASSERT(res_
.isNumber());
12966 // Case should have been handled by tryAttachBitwise.
12967 MOZ_ASSERT(op_
!= JSOp::BitNot
);
12969 if (!res_
.isInt32()) {
12970 return AttachDecision::NoAction
;
12973 ValOperandId
valId(writer
.setInputOperandId(0));
12974 StringOperandId stringId
= writer
.guardToString(valId
);
12975 Int32OperandId intId
= writer
.guardStringToInt32(stringId
);
12979 writer
.loadInt32Result(intId
);
12980 trackAttached("UnaryArith.StringInt32Pos");
12983 writer
.int32NegationResult(intId
);
12984 trackAttached("UnaryArith.StringInt32Neg");
12987 writer
.int32IncResult(intId
);
12988 trackAttached("UnaryArith.StringInt32Inc");
12991 writer
.int32DecResult(intId
);
12992 trackAttached("UnaryArith.StringInt32Dec");
12994 case JSOp::ToNumeric
:
12995 writer
.loadInt32Result(intId
);
12996 trackAttached("UnaryArith.StringInt32ToNumeric");
12999 MOZ_CRASH("Unexpected OP");
13002 writer
.returnFromIC();
13003 return AttachDecision::Attach
;
13006 AttachDecision
UnaryArithIRGenerator::tryAttachStringNumber() {
13007 if (!val_
.isString()) {
13008 return AttachDecision::NoAction
;
13010 MOZ_ASSERT(res_
.isNumber());
13012 // Case should have been handled by tryAttachBitwise.
13013 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13015 ValOperandId
valId(writer
.setInputOperandId(0));
13016 StringOperandId stringId
= writer
.guardToString(valId
);
13017 NumberOperandId numId
= writer
.guardStringToNumber(stringId
);
13019 Int32OperandId truncatedId
;
13022 writer
.loadDoubleResult(numId
);
13023 trackAttached("UnaryArith.StringNumberPos");
13026 writer
.doubleNegationResult(numId
);
13027 trackAttached("UnaryArith.StringNumberNeg");
13030 writer
.doubleIncResult(numId
);
13031 trackAttached("UnaryArith.StringNumberInc");
13034 writer
.doubleDecResult(numId
);
13035 trackAttached("UnaryArith.StringNumberDec");
13037 case JSOp::ToNumeric
:
13038 writer
.loadDoubleResult(numId
);
13039 trackAttached("UnaryArith.StringNumberToNumeric");
13042 MOZ_CRASH("Unexpected OP");
13045 writer
.returnFromIC();
13046 return AttachDecision::Attach
;
13049 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext
* cx
,
13050 HandleScript script
,
13054 : IRGenerator(cx
, script
, pc
, CacheKind::ToPropertyKey
, state
), val_(val
) {}
13056 void ToPropertyKeyIRGenerator::trackAttached(const char* name
) {
13057 stubName_
= name
? name
: "NotAttached";
13058 #ifdef JS_CACHEIR_SPEW
13059 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13060 sp
.valueProperty("val", val_
);
13065 AttachDecision
ToPropertyKeyIRGenerator::tryAttachStub() {
13066 AutoAssertNoPendingException
aanpe(cx_
);
13067 TRY_ATTACH(tryAttachInt32());
13068 TRY_ATTACH(tryAttachNumber());
13069 TRY_ATTACH(tryAttachString());
13070 TRY_ATTACH(tryAttachSymbol());
13072 trackAttached(IRGenerator::NotAttached
);
13073 return AttachDecision::NoAction
;
13076 AttachDecision
ToPropertyKeyIRGenerator::tryAttachInt32() {
13077 if (!val_
.isInt32()) {
13078 return AttachDecision::NoAction
;
13081 ValOperandId
valId(writer
.setInputOperandId(0));
13083 Int32OperandId intId
= writer
.guardToInt32(valId
);
13084 writer
.loadInt32Result(intId
);
13085 writer
.returnFromIC();
13087 trackAttached("ToPropertyKey.Int32");
13088 return AttachDecision::Attach
;
13091 AttachDecision
ToPropertyKeyIRGenerator::tryAttachNumber() {
13092 if (!val_
.isNumber()) {
13093 return AttachDecision::NoAction
;
13096 // We allow negative zero here because ToPropertyKey(-0.0) is 0.
13098 if (!mozilla::NumberEqualsInt32(val_
.toNumber(), &unused
)) {
13099 return AttachDecision::NoAction
;
13102 ValOperandId
valId(writer
.setInputOperandId(0));
13104 Int32OperandId intId
= writer
.guardToInt32Index(valId
);
13105 writer
.loadInt32Result(intId
);
13106 writer
.returnFromIC();
13108 trackAttached("ToPropertyKey.Number");
13109 return AttachDecision::Attach
;
13112 AttachDecision
ToPropertyKeyIRGenerator::tryAttachString() {
13113 if (!val_
.isString()) {
13114 return AttachDecision::NoAction
;
13117 ValOperandId
valId(writer
.setInputOperandId(0));
13119 StringOperandId strId
= writer
.guardToString(valId
);
13120 writer
.loadStringResult(strId
);
13121 writer
.returnFromIC();
13123 trackAttached("ToPropertyKey.String");
13124 return AttachDecision::Attach
;
13127 AttachDecision
ToPropertyKeyIRGenerator::tryAttachSymbol() {
13128 if (!val_
.isSymbol()) {
13129 return AttachDecision::NoAction
;
13132 ValOperandId
valId(writer
.setInputOperandId(0));
13134 SymbolOperandId strId
= writer
.guardToSymbol(valId
);
13135 writer
.loadSymbolResult(strId
);
13136 writer
.returnFromIC();
13138 trackAttached("ToPropertyKey.Symbol");
13139 return AttachDecision::Attach
;
13142 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext
* cx
,
13143 HandleScript script
,
13144 jsbytecode
* pc
, ICState state
,
13145 JSOp op
, HandleValue lhs
,
13146 HandleValue rhs
, HandleValue res
)
13147 : IRGenerator(cx
, script
, pc
, CacheKind::BinaryArith
, state
),
13153 void BinaryArithIRGenerator::trackAttached(const char* name
) {
13154 stubName_
= name
? name
: "NotAttached";
13155 #ifdef JS_CACHEIR_SPEW
13156 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13157 sp
.opcodeProperty("op", op_
);
13158 sp
.valueProperty("rhs", rhs_
);
13159 sp
.valueProperty("lhs", lhs_
);
13164 AttachDecision
BinaryArithIRGenerator::tryAttachStub() {
13165 AutoAssertNoPendingException
aanpe(cx_
);
13166 // Arithmetic operations with Int32 operands
13167 TRY_ATTACH(tryAttachInt32());
13169 // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
13171 TRY_ATTACH(tryAttachBitwise());
13173 // Arithmetic operations with Double operands. This needs to come after
13174 // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
13175 // more specialized Int32 IC if it is possible.
13176 TRY_ATTACH(tryAttachDouble());
13178 // String x {String,Number,Boolean,Null,Undefined}
13179 TRY_ATTACH(tryAttachStringConcat());
13182 TRY_ATTACH(tryAttachStringObjectConcat());
13184 // Arithmetic operations or bitwise operations with BigInt operands
13185 TRY_ATTACH(tryAttachBigInt());
13187 // Arithmetic operations (without addition) with String x Int32.
13188 TRY_ATTACH(tryAttachStringInt32Arith());
13190 // Arithmetic operations (without addition) with String x Number. This needs
13191 // to come after tryAttachStringInt32Arith, as the guards overlap, and we'd
13192 // prefer to attach the more specialized Int32 IC if it is possible.
13193 TRY_ATTACH(tryAttachStringNumberArith());
13195 trackAttached(IRGenerator::NotAttached
);
13196 return AttachDecision::NoAction
;
13199 AttachDecision
BinaryArithIRGenerator::tryAttachBitwise() {
13200 // Only bit-wise and shifts.
13201 if (op_
!= JSOp::BitOr
&& op_
!= JSOp::BitXor
&& op_
!= JSOp::BitAnd
&&
13202 op_
!= JSOp::Lsh
&& op_
!= JSOp::Rsh
&& op_
!= JSOp::Ursh
) {
13203 return AttachDecision::NoAction
;
13206 // Check guard conditions
13207 if (!CanTruncateToInt32(lhs_
) || !CanTruncateToInt32(rhs_
)) {
13208 return AttachDecision::NoAction
;
13211 // All ops, with the exception of Ursh, produce Int32 values.
13212 MOZ_ASSERT_IF(op_
!= JSOp::Ursh
, res_
.isInt32());
13214 ValOperandId
lhsId(writer
.setInputOperandId(0));
13215 ValOperandId
rhsId(writer
.setInputOperandId(1));
13217 Int32OperandId lhsIntId
= EmitTruncateToInt32Guard(writer
, lhsId
, lhs_
);
13218 Int32OperandId rhsIntId
= EmitTruncateToInt32Guard(writer
, rhsId
, rhs_
);
13222 writer
.int32BitOrResult(lhsIntId
, rhsIntId
);
13223 trackAttached("BinaryArith.BitwiseBitOr");
13226 writer
.int32BitXorResult(lhsIntId
, rhsIntId
);
13227 trackAttached("BinaryArith.BitwiseBitXor");
13230 writer
.int32BitAndResult(lhsIntId
, rhsIntId
);
13231 trackAttached("BinaryArith.BitwiseBitAnd");
13234 writer
.int32LeftShiftResult(lhsIntId
, rhsIntId
);
13235 trackAttached("BinaryArith.BitwiseLeftShift");
13238 writer
.int32RightShiftResult(lhsIntId
, rhsIntId
);
13239 trackAttached("BinaryArith.BitwiseRightShift");
13242 writer
.int32URightShiftResult(lhsIntId
, rhsIntId
, res_
.isDouble());
13243 trackAttached("BinaryArith.BitwiseUnsignedRightShift");
13246 MOZ_CRASH("Unhandled op in tryAttachBitwise");
13249 writer
.returnFromIC();
13250 return AttachDecision::Attach
;
13253 AttachDecision
BinaryArithIRGenerator::tryAttachDouble() {
13254 // Check valid opcodes
13255 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13256 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13257 return AttachDecision::NoAction
;
13260 // Check guard conditions.
13261 if (!CanConvertToDoubleForToNumber(lhs_
) ||
13262 !CanConvertToDoubleForToNumber(rhs_
)) {
13263 return AttachDecision::NoAction
;
13266 ValOperandId
lhsId(writer
.setInputOperandId(0));
13267 ValOperandId
rhsId(writer
.setInputOperandId(1));
13269 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhs_
);
13270 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhs_
);
13274 writer
.doubleAddResult(lhs
, rhs
);
13275 trackAttached("BinaryArith.DoubleAdd");
13278 writer
.doubleSubResult(lhs
, rhs
);
13279 trackAttached("BinaryArith.DoubleSub");
13282 writer
.doubleMulResult(lhs
, rhs
);
13283 trackAttached("BinaryArith.DoubleMul");
13286 writer
.doubleDivResult(lhs
, rhs
);
13287 trackAttached("BinaryArith.DoubleDiv");
13290 writer
.doubleModResult(lhs
, rhs
);
13291 trackAttached("BinaryArith.DoubleMod");
13294 writer
.doublePowResult(lhs
, rhs
);
13295 trackAttached("BinaryArith.DoublePow");
13298 MOZ_CRASH("Unhandled Op");
13300 writer
.returnFromIC();
13301 return AttachDecision::Attach
;
13304 AttachDecision
BinaryArithIRGenerator::tryAttachInt32() {
13305 // Check guard conditions.
13306 if (!CanConvertToInt32ForToNumber(lhs_
) ||
13307 !CanConvertToInt32ForToNumber(rhs_
)) {
13308 return AttachDecision::NoAction
;
13311 // These ICs will failure() if result can't be encoded in an Int32:
13312 // If sample result is not Int32, we should avoid IC.
13313 if (!res_
.isInt32()) {
13314 return AttachDecision::NoAction
;
13317 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13318 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13319 return AttachDecision::NoAction
;
13322 if (op_
== JSOp::Pow
&& !CanAttachInt32Pow(lhs_
, rhs_
)) {
13323 return AttachDecision::NoAction
;
13326 ValOperandId
lhsId(writer
.setInputOperandId(0));
13327 ValOperandId
rhsId(writer
.setInputOperandId(1));
13329 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhs_
);
13330 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhs_
);
13334 writer
.int32AddResult(lhsIntId
, rhsIntId
);
13335 trackAttached("BinaryArith.Int32Add");
13338 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13339 trackAttached("BinaryArith.Int32Sub");
13342 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13343 trackAttached("BinaryArith.Int32Mul");
13346 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13347 trackAttached("BinaryArith.Int32Div");
13350 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13351 trackAttached("BinaryArith.Int32Mod");
13354 writer
.int32PowResult(lhsIntId
, rhsIntId
);
13355 trackAttached("BinaryArith.Int32Pow");
13358 MOZ_CRASH("Unhandled op in tryAttachInt32");
13361 writer
.returnFromIC();
13362 return AttachDecision::Attach
;
13365 AttachDecision
BinaryArithIRGenerator::tryAttachStringConcat() {
13367 if (op_
!= JSOp::Add
) {
13368 return AttachDecision::NoAction
;
13371 // One side must be a string, the other side a primitive value we can easily
13372 // convert to a string.
13373 if (!(lhs_
.isString() && CanConvertToString(rhs_
)) &&
13374 !(CanConvertToString(lhs_
) && rhs_
.isString())) {
13375 return AttachDecision::NoAction
;
13378 ValOperandId
lhsId(writer
.setInputOperandId(0));
13379 ValOperandId
rhsId(writer
.setInputOperandId(1));
13381 StringOperandId lhsStrId
= emitToStringGuard(lhsId
, lhs_
);
13382 StringOperandId rhsStrId
= emitToStringGuard(rhsId
, rhs_
);
13384 writer
.callStringConcatResult(lhsStrId
, rhsStrId
);
13386 writer
.returnFromIC();
13387 trackAttached("BinaryArith.StringConcat");
13388 return AttachDecision::Attach
;
13391 AttachDecision
BinaryArithIRGenerator::tryAttachStringObjectConcat() {
13393 if (op_
!= JSOp::Add
) {
13394 return AttachDecision::NoAction
;
13398 if (!(lhs_
.isObject() && rhs_
.isString()) &&
13399 !(lhs_
.isString() && rhs_
.isObject()))
13400 return AttachDecision::NoAction
;
13402 ValOperandId
lhsId(writer
.setInputOperandId(0));
13403 ValOperandId
rhsId(writer
.setInputOperandId(1));
13405 // This guard is actually overly tight, as the runtime
13406 // helper can handle lhs or rhs being a string, so long
13407 // as the other is an object.
13408 if (lhs_
.isString()) {
13409 writer
.guardToString(lhsId
);
13410 writer
.guardToObject(rhsId
);
13412 writer
.guardToObject(lhsId
);
13413 writer
.guardToString(rhsId
);
13416 writer
.callStringObjectConcatResult(lhsId
, rhsId
);
13418 writer
.returnFromIC();
13419 trackAttached("BinaryArith.StringObjectConcat");
13420 return AttachDecision::Attach
;
13423 AttachDecision
BinaryArithIRGenerator::tryAttachBigInt() {
13425 if (!lhs_
.isBigInt() || !rhs_
.isBigInt()) {
13426 return AttachDecision::NoAction
;
13436 // Arithmetic operations.
13444 // Bitwise operations.
13448 return AttachDecision::NoAction
;
13451 ValOperandId
lhsId(writer
.setInputOperandId(0));
13452 ValOperandId
rhsId(writer
.setInputOperandId(1));
13454 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
13455 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
13459 writer
.bigIntAddResult(lhsBigIntId
, rhsBigIntId
);
13460 trackAttached("BinaryArith.BigIntAdd");
13463 writer
.bigIntSubResult(lhsBigIntId
, rhsBigIntId
);
13464 trackAttached("BinaryArith.BigIntSub");
13467 writer
.bigIntMulResult(lhsBigIntId
, rhsBigIntId
);
13468 trackAttached("BinaryArith.BigIntMul");
13471 writer
.bigIntDivResult(lhsBigIntId
, rhsBigIntId
);
13472 trackAttached("BinaryArith.BigIntDiv");
13475 writer
.bigIntModResult(lhsBigIntId
, rhsBigIntId
);
13476 trackAttached("BinaryArith.BigIntMod");
13479 writer
.bigIntPowResult(lhsBigIntId
, rhsBigIntId
);
13480 trackAttached("BinaryArith.BigIntPow");
13483 writer
.bigIntBitOrResult(lhsBigIntId
, rhsBigIntId
);
13484 trackAttached("BinaryArith.BigIntBitOr");
13487 writer
.bigIntBitXorResult(lhsBigIntId
, rhsBigIntId
);
13488 trackAttached("BinaryArith.BigIntBitXor");
13491 writer
.bigIntBitAndResult(lhsBigIntId
, rhsBigIntId
);
13492 trackAttached("BinaryArith.BigIntBitAnd");
13495 writer
.bigIntLeftShiftResult(lhsBigIntId
, rhsBigIntId
);
13496 trackAttached("BinaryArith.BigIntLeftShift");
13499 writer
.bigIntRightShiftResult(lhsBigIntId
, rhsBigIntId
);
13500 trackAttached("BinaryArith.BigIntRightShift");
13503 MOZ_CRASH("Unhandled op in tryAttachBigInt");
13506 writer
.returnFromIC();
13507 return AttachDecision::Attach
;
13510 AttachDecision
BinaryArithIRGenerator::tryAttachStringInt32Arith() {
13511 // Check for either int32 x string or string x int32.
13512 if (!(lhs_
.isInt32() && rhs_
.isString()) &&
13513 !(lhs_
.isString() && rhs_
.isInt32())) {
13514 return AttachDecision::NoAction
;
13517 // The created ICs will fail if the result can't be encoded as as int32.
13518 // Thus skip this IC, if the sample result is not an int32.
13519 if (!res_
.isInt32()) {
13520 return AttachDecision::NoAction
;
13523 // Must _not_ support Add, because it would be string concatenation instead.
13524 // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
13525 // reject that as well.
13526 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13527 op_
!= JSOp::Mod
) {
13528 return AttachDecision::NoAction
;
13531 // The string operand must be convertable to an int32 value.
13532 JSString
* str
= lhs_
.isString() ? lhs_
.toString() : rhs_
.toString();
13535 if (!StringToNumber(cx_
, str
, &num
)) {
13536 cx_
->recoverFromOutOfMemory();
13537 return AttachDecision::NoAction
;
13541 if (!mozilla::NumberIsInt32(num
, &unused
)) {
13542 return AttachDecision::NoAction
;
13545 ValOperandId
lhsId(writer
.setInputOperandId(0));
13546 ValOperandId
rhsId(writer
.setInputOperandId(1));
13548 auto guardToInt32
= [&](ValOperandId id
, const Value
& v
) {
13550 return writer
.guardToInt32(id
);
13553 MOZ_ASSERT(v
.isString());
13554 StringOperandId strId
= writer
.guardToString(id
);
13555 return writer
.guardStringToInt32(strId
);
13558 Int32OperandId lhsIntId
= guardToInt32(lhsId
, lhs_
);
13559 Int32OperandId rhsIntId
= guardToInt32(rhsId
, rhs_
);
13563 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13564 trackAttached("BinaryArith.StringInt32Sub");
13567 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13568 trackAttached("BinaryArith.StringInt32Mul");
13571 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13572 trackAttached("BinaryArith.StringInt32Div");
13575 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13576 trackAttached("BinaryArith.StringInt32Mod");
13579 MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
13582 writer
.returnFromIC();
13583 return AttachDecision::Attach
;
13586 AttachDecision
BinaryArithIRGenerator::tryAttachStringNumberArith() {
13587 // Check for either number x string or string x number.
13588 if (!(lhs_
.isNumber() && rhs_
.isString()) &&
13589 !(lhs_
.isString() && rhs_
.isNumber())) {
13590 return AttachDecision::NoAction
;
13593 // Must _not_ support Add, because it would be string concatenation instead.
13594 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13595 op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13596 return AttachDecision::NoAction
;
13599 ValOperandId
lhsId(writer
.setInputOperandId(0));
13600 ValOperandId
rhsId(writer
.setInputOperandId(1));
13602 auto guardToNumber
= [&](ValOperandId id
, const Value
& v
) {
13603 if (v
.isNumber()) {
13604 return writer
.guardIsNumber(id
);
13607 MOZ_ASSERT(v
.isString());
13608 StringOperandId strId
= writer
.guardToString(id
);
13609 return writer
.guardStringToNumber(strId
);
13612 NumberOperandId lhsIntId
= guardToNumber(lhsId
, lhs_
);
13613 NumberOperandId rhsIntId
= guardToNumber(rhsId
, rhs_
);
13617 writer
.doubleSubResult(lhsIntId
, rhsIntId
);
13618 trackAttached("BinaryArith.StringNumberSub");
13621 writer
.doubleMulResult(lhsIntId
, rhsIntId
);
13622 trackAttached("BinaryArith.StringNumberMul");
13625 writer
.doubleDivResult(lhsIntId
, rhsIntId
);
13626 trackAttached("BinaryArith.StringNumberDiv");
13629 writer
.doubleModResult(lhsIntId
, rhsIntId
);
13630 trackAttached("BinaryArith.StringNumberMod");
13633 writer
.doublePowResult(lhsIntId
, rhsIntId
);
13634 trackAttached("BinaryArith.StringNumberPow");
13637 MOZ_CRASH("Unhandled op in tryAttachStringNumberArith");
13640 writer
.returnFromIC();
13641 return AttachDecision::Attach
;
13644 NewArrayIRGenerator::NewArrayIRGenerator(JSContext
* cx
, HandleScript script
,
13645 jsbytecode
* pc
, ICState state
, JSOp op
,
13646 HandleObject templateObj
,
13647 BaselineFrame
* frame
)
13648 : IRGenerator(cx
, script
, pc
, CacheKind::NewArray
, state
),
13649 #ifdef JS_CACHEIR_SPEW
13652 templateObject_(templateObj
),
13654 MOZ_ASSERT(templateObject_
);
13657 void NewArrayIRGenerator::trackAttached(const char* name
) {
13658 stubName_
= name
? name
: "NotAttached";
13659 #ifdef JS_CACHEIR_SPEW
13660 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13661 sp
.opcodeProperty("op", op_
);
13666 // Allocation sites are usually created during baseline compilation, but we also
13667 // need to create them when an IC stub is added to a baseline compiled script
13668 // and when trial inlining.
13669 static gc::AllocSite
* MaybeCreateAllocSite(jsbytecode
* pc
,
13670 BaselineFrame
* frame
) {
13671 MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc
)));
13673 JSScript
* outerScript
= frame
->outerScript();
13674 bool hasBaselineScript
= outerScript
->hasBaselineScript();
13675 bool isInlined
= frame
->icScript()->isInlined();
13677 if (!hasBaselineScript
&& !isInlined
) {
13678 MOZ_ASSERT(frame
->runningInInterpreter());
13679 return outerScript
->zone()->unknownAllocSite(JS::TraceKind::Object
);
13682 uint32_t pcOffset
= frame
->script()->pcToOffset(pc
);
13683 return frame
->icScript()->getOrCreateAllocSite(outerScript
, pcOffset
);
13686 AttachDecision
NewArrayIRGenerator::tryAttachArrayObject() {
13687 ArrayObject
* arrayObj
= &templateObject_
->as
<ArrayObject
>();
13689 MOZ_ASSERT(arrayObj
->numUsedFixedSlots() == 0);
13690 MOZ_ASSERT(arrayObj
->numDynamicSlots() == 0);
13691 MOZ_ASSERT(!arrayObj
->isSharedMemory());
13693 // The macro assembler only supports creating arrays with fixed elements.
13694 if (arrayObj
->hasDynamicElements()) {
13695 return AttachDecision::NoAction
;
13698 // Stub doesn't support metadata builder
13699 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
13700 return AttachDecision::NoAction
;
13703 writer
.guardNoAllocationMetadataBuilder(
13704 cx_
->realm()->addressOfMetadataBuilder());
13706 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
13708 return AttachDecision::NoAction
;
13711 Shape
* shape
= arrayObj
->shape();
13712 uint32_t length
= arrayObj
->length();
13714 writer
.newArrayObjectResult(length
, shape
, site
);
13716 writer
.returnFromIC();
13718 trackAttached("NewArray.Object");
13719 return AttachDecision::Attach
;
13722 AttachDecision
NewArrayIRGenerator::tryAttachStub() {
13723 AutoAssertNoPendingException
aanpe(cx_
);
13725 TRY_ATTACH(tryAttachArrayObject());
13727 trackAttached(IRGenerator::NotAttached
);
13728 return AttachDecision::NoAction
;
13731 NewObjectIRGenerator::NewObjectIRGenerator(JSContext
* cx
, HandleScript script
,
13732 jsbytecode
* pc
, ICState state
,
13733 JSOp op
, HandleObject templateObj
,
13734 BaselineFrame
* frame
)
13735 : IRGenerator(cx
, script
, pc
, CacheKind::NewObject
, state
),
13736 #ifdef JS_CACHEIR_SPEW
13739 templateObject_(templateObj
),
13741 MOZ_ASSERT(templateObject_
);
13744 void NewObjectIRGenerator::trackAttached(const char* name
) {
13745 stubName_
= name
? name
: "NotAttached";
13746 #ifdef JS_CACHEIR_SPEW
13747 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13748 sp
.opcodeProperty("op", op_
);
13753 AttachDecision
NewObjectIRGenerator::tryAttachPlainObject() {
13754 // Don't optimize allocations with too many dynamic slots. We use an unrolled
13755 // loop when initializing slots and this avoids generating too much code.
13756 static const uint32_t MaxDynamicSlotsToOptimize
= 64;
13758 NativeObject
* nativeObj
= &templateObject_
->as
<NativeObject
>();
13759 MOZ_ASSERT(nativeObj
->is
<PlainObject
>());
13761 // Stub doesn't support metadata builder
13762 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
13763 return AttachDecision::NoAction
;
13766 if (nativeObj
->numDynamicSlots() > MaxDynamicSlotsToOptimize
) {
13767 return AttachDecision::NoAction
;
13770 MOZ_ASSERT(!nativeObj
->hasDynamicElements());
13771 MOZ_ASSERT(!nativeObj
->isSharedMemory());
13773 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
13775 return AttachDecision::NoAction
;
13778 uint32_t numFixedSlots
= nativeObj
->numUsedFixedSlots();
13779 uint32_t numDynamicSlots
= nativeObj
->numDynamicSlots();
13780 gc::AllocKind allocKind
= nativeObj
->allocKindForTenure();
13781 Shape
* shape
= nativeObj
->shape();
13783 writer
.guardNoAllocationMetadataBuilder(
13784 cx_
->realm()->addressOfMetadataBuilder());
13785 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
, shape
,
13788 writer
.returnFromIC();
13790 trackAttached("NewObject.PlainObject");
13791 return AttachDecision::Attach
;
13794 AttachDecision
NewObjectIRGenerator::tryAttachStub() {
13795 AutoAssertNoPendingException
aanpe(cx_
);
13797 TRY_ATTACH(tryAttachPlainObject());
13799 trackAttached(IRGenerator::NotAttached
);
13800 return AttachDecision::NoAction
;
13803 CloseIterIRGenerator::CloseIterIRGenerator(JSContext
* cx
, HandleScript script
,
13804 jsbytecode
* pc
, ICState state
,
13806 CompletionKind kind
)
13807 : IRGenerator(cx
, script
, pc
, CacheKind::CloseIter
, state
),
13811 void CloseIterIRGenerator::trackAttached(const char* name
) {
13812 #ifdef JS_CACHEIR_SPEW
13813 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13814 sp
.valueProperty("iter", ObjectValue(*iter_
));
13819 AttachDecision
CloseIterIRGenerator::tryAttachNoReturnMethod() {
13820 Maybe
<PropertyInfo
> prop
;
13821 NativeObject
* holder
= nullptr;
13823 // If we can guard that the iterator does not have a |return| method,
13824 // then this CloseIter is a no-op.
13825 NativeGetPropKind kind
= CanAttachNativeGetProp(
13826 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
13827 if (kind
!= NativeGetPropKind::Missing
) {
13828 return AttachDecision::NoAction
;
13830 MOZ_ASSERT(!holder
);
13832 ObjOperandId
objId(writer
.setInputOperandId(0));
13834 EmitMissingPropGuard(writer
, &iter_
->as
<NativeObject
>(), objId
);
13836 // There is no return method, so we don't have to do anything.
13837 writer
.returnFromIC();
13839 trackAttached("CloseIter.NoReturn");
13840 return AttachDecision::Attach
;
13843 AttachDecision
CloseIterIRGenerator::tryAttachScriptedReturn() {
13844 Maybe
<PropertyInfo
> prop
;
13845 NativeObject
* holder
= nullptr;
13847 NativeGetPropKind kind
= CanAttachNativeGetProp(
13848 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
13849 if (kind
!= NativeGetPropKind::Slot
) {
13850 return AttachDecision::NoAction
;
13852 MOZ_ASSERT(holder
);
13853 MOZ_ASSERT(prop
->isDataProperty());
13855 size_t slot
= prop
->slot();
13856 Value calleeVal
= holder
->getSlot(slot
);
13857 if (!calleeVal
.isObject() || !calleeVal
.toObject().is
<JSFunction
>()) {
13858 return AttachDecision::NoAction
;
13861 JSFunction
* callee
= &calleeVal
.toObject().as
<JSFunction
>();
13862 if (!callee
->hasJitEntry()) {
13863 return AttachDecision::NoAction
;
13865 if (callee
->isClassConstructor()) {
13866 return AttachDecision::NoAction
;
13869 // We don't support cross-realm |return|.
13870 if (cx_
->realm() != callee
->realm()) {
13871 return AttachDecision::NoAction
;
13874 ObjOperandId
objId(writer
.setInputOperandId(0));
13876 ObjOperandId holderId
=
13877 EmitReadSlotGuard(writer
, &iter_
->as
<NativeObject
>(), holder
, objId
);
13879 ValOperandId calleeValId
= EmitLoadSlot(writer
, holder
, holderId
, slot
);
13880 ObjOperandId calleeId
= writer
.guardToObject(calleeValId
);
13881 emitCalleeGuard(calleeId
, callee
);
13883 writer
.closeIterScriptedResult(objId
, calleeId
, kind_
, callee
->nargs());
13885 writer
.returnFromIC();
13886 trackAttached("CloseIter.ScriptedReturn");
13888 return AttachDecision::Attach
;
13891 AttachDecision
CloseIterIRGenerator::tryAttachStub() {
13892 AutoAssertNoPendingException
aanpe(cx_
);
13894 TRY_ATTACH(tryAttachNoReturnMethod());
13895 TRY_ATTACH(tryAttachScriptedReturn());
13897 trackAttached(IRGenerator::NotAttached
);
13898 return AttachDecision::NoAction
;
13901 OptimizeGetIteratorIRGenerator::OptimizeGetIteratorIRGenerator(
13902 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
13904 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeGetIterator
, state
),
13907 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachStub() {
13908 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeGetIterator
);
13910 AutoAssertNoPendingException
aanpe(cx_
);
13912 TRY_ATTACH(tryAttachArray());
13913 TRY_ATTACH(tryAttachNotOptimizable());
13915 MOZ_CRASH("Failed to attach unoptimizable case.");
13918 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachArray() {
13919 if (!isFirstStub_
) {
13920 return AttachDecision::NoAction
;
13923 // The value must be a packed array.
13924 if (!val_
.isObject()) {
13925 return AttachDecision::NoAction
;
13927 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
13928 if (!IsPackedArray(obj
)) {
13929 return AttachDecision::NoAction
;
13932 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
13934 Rooted
<NativeObject
*> arrProto(cx_
);
13935 uint32_t arrProtoIterSlot
;
13936 Rooted
<JSFunction
*> iterFun(cx_
);
13937 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
13938 return AttachDecision::NoAction
;
13941 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
13942 &arrProtoIterSlot
, &iterFun
)) {
13943 // Fuse should be popped.
13945 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
13946 return AttachDecision::NoAction
;
13949 // %ArrayIteratorPrototype%.next must not be modified and
13950 // %ArrayIteratorPrototype%.return must not be present.
13951 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
13953 Rooted
<JSFunction
*> nextFun(cx_
);
13954 if (!IsArrayIteratorPrototypeOptimizable(
13955 cx_
, AllowIteratorReturn::No
, &arrayIteratorProto
, &slot
, &nextFun
)) {
13956 // Fuse should be popped.
13958 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
13959 return AttachDecision::NoAction
;
13962 ValOperandId
valId(writer
.setInputOperandId(0));
13963 ObjOperandId objId
= writer
.guardToObject(valId
);
13965 // Guard the object is a packed array with Array.prototype as proto.
13966 MOZ_ASSERT(obj
->is
<ArrayObject
>());
13967 writer
.guardShape(objId
, obj
->shape());
13968 writer
.guardArrayIsPacked(objId
);
13969 bool intact
= obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact();
13971 // If the fuse isn't intact but we've still passed all these dynamic checks
13972 // then we can attach a version of the IC that dynamically checks to ensure
13973 // the required invariants still hold.
13975 // As an example of how this could be the case, consider an assignment
13977 // Array.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]
13979 // This assignment pops the fuse, however we can still use the dynamic check
13980 // version of this IC, as the actual -value- is still correct.
13981 bool useDynamicCheck
= !intact
|| !cx_
->options().enableDestructuringFuse();
13982 if (useDynamicCheck
) {
13983 // Guard on Array.prototype[@@iterator].
13984 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
13985 ObjOperandId iterId
= writer
.loadObject(iterFun
);
13986 writer
.guardShape(arrProtoId
, arrProto
->shape());
13987 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
,
13990 // Guard on %ArrayIteratorPrototype%.next.
13991 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
13992 ObjOperandId nextId
= writer
.loadObject(nextFun
);
13993 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
13994 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, slot
);
13996 // Guard on the prototype chain to ensure no "return" method is present.
13997 ShapeGuardProtoChain(writer
, arrayIteratorProto
, iterProtoId
);
13999 // Guard on Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next.
14000 // This fuse also ensures the prototype chain for Array Iterator is
14001 // maintained and that no return method is added.
14002 writer
.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse
);
14005 writer
.loadBooleanResult(true);
14006 writer
.returnFromIC();
14008 if (useDynamicCheck
) {
14009 trackAttached("OptimizeGetIterator.Array.Dynamic");
14011 trackAttached("OptimizeGetIterator.Array.Fuse");
14013 return AttachDecision::Attach
;
14016 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachNotOptimizable() {
14017 ValOperandId
valId(writer
.setInputOperandId(0));
14019 writer
.loadBooleanResult(false);
14020 writer
.returnFromIC();
14022 trackAttached("OptimizeGetIterator.NotOptimizable");
14023 return AttachDecision::Attach
;
14026 void OptimizeGetIteratorIRGenerator::trackAttached(const char* name
) {
14027 stubName_
= name
? name
: "NotAttached";
14029 #ifdef JS_CACHEIR_SPEW
14030 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14031 sp
.valueProperty("val", val_
);
14036 #ifdef JS_SIMULATOR
14037 bool js::jit::CallAnyNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
14038 CallArgs args
= CallArgsFromVp(argc
, vp
);
14039 JSObject
* calleeObj
= &args
.callee();
14041 MOZ_ASSERT(calleeObj
->is
<JSFunction
>());
14042 auto* calleeFunc
= &calleeObj
->as
<JSFunction
>();
14043 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
14045 JSNative native
= calleeFunc
->native();
14046 return native(cx
, args
.length(), args
.base());
14049 const void* js::jit::RedirectedCallAnyNative() {
14050 // The simulator requires native calls to be redirected to a
14051 // special swi instruction. If we are calling an arbitrary native
14052 // function, we can't wrap the real target ahead of time, so we
14053 // call a wrapper function (CallAnyNative) that calls the target
14054 // itself, and redirect that wrapper.
14055 JSNative target
= CallAnyNative
;
14056 void* rawPtr
= JS_FUNC_TO_DATA_PTR(void*, target
);
14057 void* redirected
= Simulator::RedirectNativeFunction(rawPtr
, Args_General3
);