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 // TODO: Support resizable typed arrays. (bug 1842999)
2078 if (!obj
->is
<FixedLengthTypedArrayObject
>()) {
2079 return AttachDecision::NoAction
;
2082 if (mode_
!= ICState::Mode::Specialized
) {
2083 return AttachDecision::NoAction
;
2086 // Receiver should be the object.
2088 return AttachDecision::NoAction
;
2091 bool isLength
= id
.isAtom(cx_
->names().length
);
2092 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2093 if (!isLength
&& !isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2094 return AttachDecision::NoAction
;
2097 NativeObject
* holder
= nullptr;
2098 Maybe
<PropertyInfo
> prop
;
2099 NativeGetPropKind kind
=
2100 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2101 if (kind
!= NativeGetPropKind::NativeGetter
) {
2102 return AttachDecision::NoAction
;
2105 JSFunction
& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2107 if (!TypedArrayObject::isOriginalLengthGetter(fun
.native())) {
2108 return AttachDecision::NoAction
;
2110 } else if (isByteOffset
) {
2111 if (!TypedArrayObject::isOriginalByteOffsetGetter(fun
.native())) {
2112 return AttachDecision::NoAction
;
2115 if (!TypedArrayObject::isOriginalByteLengthGetter(fun
.native())) {
2116 return AttachDecision::NoAction
;
2120 auto* tarr
= &obj
->as
<FixedLengthTypedArrayObject
>();
2122 maybeEmitIdGuard(id
);
2123 // Emit all the normal guards for calling this native, but specialize
2124 // callNativeGetterResult.
2125 EmitCallGetterResultGuards(writer
, tarr
, holder
, id
, *prop
, objId
, mode_
);
2127 if (tarr
->length() <= INT32_MAX
) {
2128 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2130 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2132 trackAttached("GetProp.TypedArrayLength");
2133 } else if (isByteOffset
) {
2134 if (tarr
->byteOffset() <= INT32_MAX
) {
2135 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2137 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2139 trackAttached("GetProp.TypedArrayByteOffset");
2141 if (tarr
->byteLength() <= INT32_MAX
) {
2142 writer
.typedArrayByteLengthInt32Result(objId
);
2144 writer
.typedArrayByteLengthDoubleResult(objId
);
2146 trackAttached("GetProp.TypedArrayByteLength");
2148 writer
.returnFromIC();
2150 return AttachDecision::Attach
;
2153 AttachDecision
GetPropIRGenerator::tryAttachDataView(HandleObject obj
,
2156 // TODO: Support resizable dataviews. (bug 1842999)
2157 if (!obj
->is
<FixedLengthDataViewObject
>()) {
2158 return AttachDecision::NoAction
;
2160 auto* dv
= &obj
->as
<FixedLengthDataViewObject
>();
2162 if (mode_
!= ICState::Mode::Specialized
) {
2163 return AttachDecision::NoAction
;
2166 // Receiver should be the object.
2168 return AttachDecision::NoAction
;
2171 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2172 if (!isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2173 return AttachDecision::NoAction
;
2176 // byteOffset and byteLength both throw when the ArrayBuffer is detached.
2177 if (dv
->hasDetachedBuffer()) {
2178 return AttachDecision::NoAction
;
2181 NativeObject
* holder
= nullptr;
2182 Maybe
<PropertyInfo
> prop
;
2183 NativeGetPropKind kind
=
2184 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2185 if (kind
!= NativeGetPropKind::NativeGetter
) {
2186 return AttachDecision::NoAction
;
2189 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2191 if (!DataViewObject::isOriginalByteOffsetGetter(fun
.native())) {
2192 return AttachDecision::NoAction
;
2195 if (!DataViewObject::isOriginalByteLengthGetter(fun
.native())) {
2196 return AttachDecision::NoAction
;
2200 maybeEmitIdGuard(id
);
2201 // Emit all the normal guards for calling this native, but specialize
2202 // callNativeGetterResult.
2203 EmitCallGetterResultGuards(writer
, dv
, holder
, id
, *prop
, objId
, mode_
);
2204 writer
.guardHasAttachedArrayBuffer(objId
);
2206 if (dv
->byteOffset() <= INT32_MAX
) {
2207 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2209 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2211 trackAttached("GetProp.DataViewByteOffset");
2213 if (dv
->byteLength() <= INT32_MAX
) {
2214 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2216 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2218 trackAttached("GetProp.DataViewByteLength");
2220 writer
.returnFromIC();
2222 return AttachDecision::Attach
;
2225 AttachDecision
GetPropIRGenerator::tryAttachArrayBufferMaybeShared(
2226 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2227 if (!obj
->is
<ArrayBufferObjectMaybeShared
>()) {
2228 return AttachDecision::NoAction
;
2230 auto* buf
= &obj
->as
<ArrayBufferObjectMaybeShared
>();
2232 // TODO: Support resizable buffers. (bug 1842999)
2233 if (buf
->isResizable()) {
2234 return AttachDecision::NoAction
;
2237 if (mode_
!= ICState::Mode::Specialized
) {
2238 return AttachDecision::NoAction
;
2241 // Receiver should be the object.
2243 return AttachDecision::NoAction
;
2246 if (!id
.isAtom(cx_
->names().byteLength
)) {
2247 return AttachDecision::NoAction
;
2250 NativeObject
* holder
= nullptr;
2251 Maybe
<PropertyInfo
> prop
;
2252 NativeGetPropKind kind
=
2253 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2254 if (kind
!= NativeGetPropKind::NativeGetter
) {
2255 return AttachDecision::NoAction
;
2258 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2259 if (buf
->is
<ArrayBufferObject
>()) {
2260 if (!ArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2261 return AttachDecision::NoAction
;
2264 if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2265 return AttachDecision::NoAction
;
2269 maybeEmitIdGuard(id
);
2270 // Emit all the normal guards for calling this native, but specialize
2271 // callNativeGetterResult.
2272 EmitCallGetterResultGuards(writer
, buf
, holder
, id
, *prop
, objId
, mode_
);
2273 if (buf
->byteLength() <= INT32_MAX
) {
2274 writer
.loadArrayBufferByteLengthInt32Result(objId
);
2276 writer
.loadArrayBufferByteLengthDoubleResult(objId
);
2278 writer
.returnFromIC();
2280 trackAttached("GetProp.ArrayBufferMaybeSharedByteLength");
2281 return AttachDecision::Attach
;
2284 AttachDecision
GetPropIRGenerator::tryAttachRegExp(HandleObject obj
,
2287 if (!obj
->is
<RegExpObject
>()) {
2288 return AttachDecision::NoAction
;
2290 auto* regExp
= &obj
->as
<RegExpObject
>();
2292 if (mode_
!= ICState::Mode::Specialized
) {
2293 return AttachDecision::NoAction
;
2296 // Receiver should be the object.
2298 return AttachDecision::NoAction
;
2301 NativeObject
* holder
= nullptr;
2302 Maybe
<PropertyInfo
> prop
;
2303 NativeGetPropKind kind
=
2304 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2305 if (kind
!= NativeGetPropKind::NativeGetter
) {
2306 return AttachDecision::NoAction
;
2309 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2310 JS::RegExpFlags flags
= JS::RegExpFlag::NoFlags
;
2311 if (!RegExpObject::isOriginalFlagGetter(fun
.native(), &flags
)) {
2312 return AttachDecision::NoAction
;
2315 maybeEmitIdGuard(id
);
2316 // Emit all the normal guards for calling this native, but specialize
2317 // callNativeGetterResult.
2318 EmitCallGetterResultGuards(writer
, regExp
, holder
, id
, *prop
, objId
, mode_
);
2320 writer
.regExpFlagResult(objId
, flags
.value());
2321 writer
.returnFromIC();
2323 trackAttached("GetProp.RegExpFlag");
2324 return AttachDecision::Attach
;
2327 AttachDecision
GetPropIRGenerator::tryAttachMap(HandleObject obj
,
2330 if (!obj
->is
<MapObject
>()) {
2331 return AttachDecision::NoAction
;
2333 auto* mapObj
= &obj
->as
<MapObject
>();
2335 if (mode_
!= ICState::Mode::Specialized
) {
2336 return AttachDecision::NoAction
;
2339 // Receiver should be the object.
2341 return AttachDecision::NoAction
;
2344 if (!id
.isAtom(cx_
->names().size
)) {
2345 return AttachDecision::NoAction
;
2348 NativeObject
* holder
= nullptr;
2349 Maybe
<PropertyInfo
> prop
;
2350 NativeGetPropKind kind
=
2351 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2352 if (kind
!= NativeGetPropKind::NativeGetter
) {
2353 return AttachDecision::NoAction
;
2356 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2357 if (!MapObject::isOriginalSizeGetter(fun
.native())) {
2358 return AttachDecision::NoAction
;
2361 maybeEmitIdGuard(id
);
2363 // Emit all the normal guards for calling this native, but specialize
2364 // callNativeGetterResult.
2365 EmitCallGetterResultGuards(writer
, mapObj
, holder
, id
, *prop
, objId
, mode_
);
2367 writer
.mapSizeResult(objId
);
2368 writer
.returnFromIC();
2370 trackAttached("GetProp.MapSize");
2371 return AttachDecision::Attach
;
2374 AttachDecision
GetPropIRGenerator::tryAttachSet(HandleObject obj
,
2377 if (!obj
->is
<SetObject
>()) {
2378 return AttachDecision::NoAction
;
2380 auto* setObj
= &obj
->as
<SetObject
>();
2382 if (mode_
!= ICState::Mode::Specialized
) {
2383 return AttachDecision::NoAction
;
2386 // Receiver should be the object.
2388 return AttachDecision::NoAction
;
2391 if (!id
.isAtom(cx_
->names().size
)) {
2392 return AttachDecision::NoAction
;
2395 NativeObject
* holder
= nullptr;
2396 Maybe
<PropertyInfo
> prop
;
2397 NativeGetPropKind kind
=
2398 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2399 if (kind
!= NativeGetPropKind::NativeGetter
) {
2400 return AttachDecision::NoAction
;
2403 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2404 if (!SetObject::isOriginalSizeGetter(fun
.native())) {
2405 return AttachDecision::NoAction
;
2408 maybeEmitIdGuard(id
);
2410 // Emit all the normal guards for calling this native, but specialize
2411 // callNativeGetterResult.
2412 EmitCallGetterResultGuards(writer
, setObj
, holder
, id
, *prop
, objId
, mode_
);
2414 writer
.setSizeResult(objId
);
2415 writer
.returnFromIC();
2417 trackAttached("GetProp.SetSize");
2418 return AttachDecision::Attach
;
2421 AttachDecision
GetPropIRGenerator::tryAttachFunction(HandleObject obj
,
2424 // Function properties are lazily resolved so they might not be defined yet.
2425 // And we might end up in a situation where we always have a fresh function
2426 // object during the IC generation.
2427 if (!obj
->is
<JSFunction
>()) {
2428 return AttachDecision::NoAction
;
2431 bool isLength
= id
.isAtom(cx_
->names().length
);
2432 if (!isLength
&& !id
.isAtom(cx_
->names().name
)) {
2433 return AttachDecision::NoAction
;
2436 NativeObject
* holder
= nullptr;
2437 PropertyResult prop
;
2438 // If this property exists already, don't attach the stub.
2439 if (LookupPropertyPure(cx_
, obj
, id
, &holder
, &prop
)) {
2440 return AttachDecision::NoAction
;
2443 JSFunction
* fun
= &obj
->as
<JSFunction
>();
2446 // length was probably deleted from the function.
2447 if (fun
->hasResolvedLength()) {
2448 return AttachDecision::NoAction
;
2451 // Lazy functions don't store the length.
2452 if (!fun
->hasBytecode()) {
2453 return AttachDecision::NoAction
;
2456 // name was probably deleted from the function.
2457 if (fun
->hasResolvedName()) {
2458 return AttachDecision::NoAction
;
2462 maybeEmitIdGuard(id
);
2463 writer
.guardClass(objId
, GuardClassKind::JSFunction
);
2465 writer
.loadFunctionLengthResult(objId
);
2466 writer
.returnFromIC();
2467 trackAttached("GetProp.FunctionLength");
2469 writer
.loadFunctionNameResult(objId
);
2470 writer
.returnFromIC();
2471 trackAttached("GetProp.FunctionName");
2473 return AttachDecision::Attach
;
2476 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectIterator(
2477 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2478 if (!obj
->is
<ArgumentsObject
>()) {
2479 return AttachDecision::NoAction
;
2482 if (!id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
2483 return AttachDecision::NoAction
;
2486 Handle
<ArgumentsObject
*> args
= obj
.as
<ArgumentsObject
>();
2487 if (args
->hasOverriddenIterator()) {
2488 return AttachDecision::NoAction
;
2491 AssertArgumentsCustomDataProp(args
, id
);
2493 RootedValue
iterator(cx_
);
2494 if (!ArgumentsObject::getArgumentsIterator(cx_
, &iterator
)) {
2495 cx_
->recoverFromOutOfMemory();
2496 return AttachDecision::NoAction
;
2498 MOZ_ASSERT(iterator
.isObject());
2500 maybeEmitIdGuard(id
);
2501 if (args
->is
<MappedArgumentsObject
>()) {
2502 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2504 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2505 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2507 uint32_t flags
= ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
;
2508 writer
.guardArgumentsObjectFlags(objId
, flags
);
2510 ObjOperandId iterId
= writer
.loadObject(&iterator
.toObject());
2511 writer
.loadObjectResult(iterId
);
2512 writer
.returnFromIC();
2514 trackAttached("GetProp.ArgumentsObjectIterator");
2515 return AttachDecision::Attach
;
2518 AttachDecision
GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj
,
2521 if (!obj
->is
<ModuleNamespaceObject
>()) {
2522 return AttachDecision::NoAction
;
2525 auto* ns
= &obj
->as
<ModuleNamespaceObject
>();
2526 ModuleEnvironmentObject
* env
= nullptr;
2527 Maybe
<PropertyInfo
> prop
;
2528 if (!ns
->bindings().lookup(id
, &env
, &prop
)) {
2529 return AttachDecision::NoAction
;
2532 // Don't emit a stub until the target binding has been initialized.
2533 if (env
->getSlot(prop
->slot()).isMagic(JS_UNINITIALIZED_LEXICAL
)) {
2534 return AttachDecision::NoAction
;
2537 // Check for the specific namespace object.
2538 maybeEmitIdGuard(id
);
2539 writer
.guardSpecificObject(objId
, ns
);
2541 ObjOperandId envId
= writer
.loadObject(env
);
2542 EmitLoadSlotResult(writer
, envId
, env
, *prop
);
2543 writer
.returnFromIC();
2545 trackAttached("GetProp.ModuleNamespace");
2546 return AttachDecision::Attach
;
2549 AttachDecision
GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId
,
2551 MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
2553 JSProtoKey protoKey
;
2554 switch (val_
.type()) {
2555 case ValueType::String
:
2556 if (id
.isAtom(cx_
->names().length
)) {
2557 // String length is special-cased, see js::GetProperty.
2558 return AttachDecision::NoAction
;
2560 protoKey
= JSProto_String
;
2562 case ValueType::Int32
:
2563 case ValueType::Double
:
2564 protoKey
= JSProto_Number
;
2566 case ValueType::Boolean
:
2567 protoKey
= JSProto_Boolean
;
2569 case ValueType::Symbol
:
2570 protoKey
= JSProto_Symbol
;
2572 case ValueType::BigInt
:
2573 protoKey
= JSProto_BigInt
;
2575 case ValueType::Null
:
2576 case ValueType::Undefined
:
2577 case ValueType::Magic
:
2578 return AttachDecision::NoAction
;
2579 #ifdef ENABLE_RECORD_TUPLE
2580 case ValueType::ExtendedPrimitive
:
2582 case ValueType::Object
:
2583 case ValueType::PrivateGCThing
:
2584 MOZ_CRASH("unexpected type");
2587 JSObject
* proto
= GlobalObject::getOrCreatePrototype(cx_
, protoKey
);
2589 cx_
->recoverFromOutOfMemory();
2590 return AttachDecision::NoAction
;
2593 NativeObject
* holder
= nullptr;
2594 Maybe
<PropertyInfo
> prop
;
2595 NativeGetPropKind kind
=
2596 CanAttachNativeGetProp(cx_
, proto
, id
, &holder
, &prop
, pc_
);
2598 case NativeGetPropKind::None
:
2599 return AttachDecision::NoAction
;
2600 case NativeGetPropKind::Missing
:
2601 case NativeGetPropKind::Slot
: {
2602 auto* nproto
= &proto
->as
<NativeObject
>();
2604 if (val_
.isNumber()) {
2605 writer
.guardIsNumber(valId
);
2607 writer
.guardNonDoubleType(valId
, val_
.type());
2609 maybeEmitIdGuard(id
);
2611 ObjOperandId protoId
= writer
.loadObject(nproto
);
2612 if (kind
== NativeGetPropKind::Slot
) {
2613 EmitReadSlotResult(writer
, nproto
, holder
, *prop
, protoId
);
2614 writer
.returnFromIC();
2615 trackAttached("GetProp.PrimitiveSlot");
2617 EmitMissingPropResult(writer
, nproto
, protoId
);
2618 writer
.returnFromIC();
2619 trackAttached("GetProp.PrimitiveMissing");
2621 return AttachDecision::Attach
;
2623 case NativeGetPropKind::ScriptedGetter
:
2624 case NativeGetPropKind::NativeGetter
: {
2625 auto* nproto
= &proto
->as
<NativeObject
>();
2627 if (val_
.isNumber()) {
2628 writer
.guardIsNumber(valId
);
2630 writer
.guardNonDoubleType(valId
, val_
.type());
2632 maybeEmitIdGuard(id
);
2634 ObjOperandId protoId
= writer
.loadObject(nproto
);
2635 EmitCallGetterResult(cx_
, writer
, kind
, nproto
, holder
, id
, *prop
,
2636 protoId
, valId
, mode_
);
2638 trackAttached("GetProp.PrimitiveGetter");
2639 return AttachDecision::Attach
;
2643 MOZ_CRASH("Bad NativeGetPropKind");
2646 AttachDecision
GetPropIRGenerator::tryAttachStringLength(ValOperandId valId
,
2648 if (!val_
.isString() || !id
.isAtom(cx_
->names().length
)) {
2649 return AttachDecision::NoAction
;
2652 StringOperandId strId
= writer
.guardToString(valId
);
2653 maybeEmitIdGuard(id
);
2654 writer
.loadStringLengthResult(strId
);
2655 writer
.returnFromIC();
2657 trackAttached("GetProp.StringLength");
2658 return AttachDecision::Attach
;
2661 enum class AttachStringChar
{ No
, Yes
, Linearize
, OutOfBounds
};
2663 static AttachStringChar
CanAttachStringChar(const Value
& val
,
2666 if (!val
.isString() || !idVal
.isInt32()) {
2667 return AttachStringChar::No
;
2670 JSString
* str
= val
.toString();
2671 int32_t index
= idVal
.toInt32();
2673 if (index
< 0 && kind
== StringChar::At
) {
2674 static_assert(JSString::MAX_LENGTH
<= INT32_MAX
,
2675 "string length fits in int32");
2676 index
+= int32_t(str
->length());
2679 if (index
< 0 || size_t(index
) >= str
->length()) {
2680 return AttachStringChar::OutOfBounds
;
2683 // This follows JSString::getChar and MacroAssembler::loadStringChar.
2684 if (str
->isRope()) {
2685 JSRope
* rope
= &str
->asRope();
2686 if (size_t(index
) < rope
->leftChild()->length()) {
2687 str
= rope
->leftChild();
2689 // MacroAssembler::loadStringChar doesn't support surrogate pairs which
2690 // are split between the left and right child of a rope.
2691 if (kind
== StringChar::CodePointAt
&&
2692 size_t(index
) + 1 == str
->length() && str
->isLinear()) {
2693 // Linearize the string when the last character of the left child is a
2694 // a lead surrogate.
2695 char16_t ch
= str
->asLinear().latin1OrTwoByteChar(index
);
2696 if (unicode::IsLeadSurrogate(ch
)) {
2697 return AttachStringChar::Linearize
;
2701 str
= rope
->rightChild();
2705 if (!str
->isLinear()) {
2706 return AttachStringChar::Linearize
;
2709 return AttachStringChar::Yes
;
2712 AttachDecision
GetPropIRGenerator::tryAttachStringChar(ValOperandId valId
,
2713 ValOperandId indexId
) {
2714 MOZ_ASSERT(idVal_
.isInt32());
2716 auto attach
= CanAttachStringChar(val_
, idVal_
, StringChar::CharAt
);
2717 if (attach
== AttachStringChar::No
) {
2718 return AttachDecision::NoAction
;
2721 // Can't attach for out-of-bounds access without guarding that indexed
2722 // properties aren't present along the prototype chain of |String.prototype|.
2723 if (attach
== AttachStringChar::OutOfBounds
) {
2724 return AttachDecision::NoAction
;
2727 StringOperandId strId
= writer
.guardToString(valId
);
2728 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
2729 if (attach
== AttachStringChar::Linearize
) {
2730 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
2732 writer
.loadStringCharResult(strId
, int32IndexId
, /* handleOOB = */ false);
2733 writer
.returnFromIC();
2735 trackAttached("GetProp.StringChar");
2736 return AttachDecision::Attach
;
2739 static bool ClassCanHaveExtraProperties(const JSClass
* clasp
) {
2740 return clasp
->getResolve() || clasp
->getOpsLookupProperty() ||
2741 clasp
->getOpsGetProperty() || IsTypedArrayClass(clasp
);
2744 enum class OwnProperty
: bool { No
, Yes
};
2745 enum class AllowIndexedReceiver
: bool { No
, Yes
};
2746 enum class AllowExtraReceiverProperties
: bool { No
, Yes
};
2748 static bool CanAttachDenseElementHole(
2749 NativeObject
* obj
, OwnProperty ownProp
,
2750 AllowIndexedReceiver allowIndexedReceiver
= AllowIndexedReceiver::No
,
2751 AllowExtraReceiverProperties allowExtraReceiverProperties
=
2752 AllowExtraReceiverProperties::No
) {
2753 // Make sure the objects on the prototype don't have any indexed properties
2754 // or that such properties can't appear without a shape change.
2755 // Otherwise returning undefined for holes would obviously be incorrect,
2756 // because we would have to lookup a property on the prototype instead.
2758 // The first two checks are also relevant to the receiver object.
2759 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
2762 allowIndexedReceiver
= AllowIndexedReceiver::No
;
2764 if (allowExtraReceiverProperties
== AllowExtraReceiverProperties::No
&&
2765 ClassCanHaveExtraProperties(obj
->getClass())) {
2768 allowExtraReceiverProperties
= AllowExtraReceiverProperties::No
;
2770 // Don't need to check prototype for OwnProperty checks
2771 if (ownProp
== OwnProperty::Yes
) {
2775 JSObject
* proto
= obj
->staticPrototype();
2780 if (!proto
->is
<NativeObject
>()) {
2784 // Make sure objects on the prototype don't have dense elements.
2785 if (proto
->as
<NativeObject
>().getDenseInitializedLength() != 0) {
2789 obj
= &proto
->as
<NativeObject
>();
2795 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArg(
2796 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2797 Int32OperandId indexId
) {
2798 if (!obj
->is
<ArgumentsObject
>()) {
2799 return AttachDecision::NoAction
;
2801 auto* args
= &obj
->as
<ArgumentsObject
>();
2803 // No elements must have been overridden or deleted.
2804 if (args
->hasOverriddenElement()) {
2805 return AttachDecision::NoAction
;
2809 if (index
>= args
->initialLength()) {
2810 return AttachDecision::NoAction
;
2813 AssertArgumentsCustomDataProp(args
, PropertyKey::Int(index
));
2815 // And finally also check that the argument isn't forwarded.
2816 if (args
->argIsForwarded(index
)) {
2817 return AttachDecision::NoAction
;
2820 if (args
->is
<MappedArgumentsObject
>()) {
2821 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2823 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2824 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2827 writer
.loadArgumentsObjectArgResult(objId
, indexId
);
2828 writer
.returnFromIC();
2830 trackAttached("GetProp.ArgumentsObjectArg");
2831 return AttachDecision::Attach
;
2834 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
2835 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2836 Int32OperandId indexId
) {
2837 if (!obj
->is
<ArgumentsObject
>()) {
2838 return AttachDecision::NoAction
;
2840 auto* args
= &obj
->as
<ArgumentsObject
>();
2842 // No elements must have been overridden or deleted.
2843 if (args
->hasOverriddenElement()) {
2844 return AttachDecision::NoAction
;
2847 // And also check that the argument isn't forwarded.
2848 if (index
< args
->initialLength() && args
->argIsForwarded(index
)) {
2849 return AttachDecision::NoAction
;
2852 if (!CanAttachDenseElementHole(args
, OwnProperty::No
,
2853 AllowIndexedReceiver::Yes
,
2854 AllowExtraReceiverProperties::Yes
)) {
2855 return AttachDecision::NoAction
;
2858 // We don't need to guard on the shape, because we check if any element is
2859 // overridden. Elements are marked as overridden iff any element is defined,
2860 // irrespective of whether the element is in-bounds or out-of-bounds. So when
2861 // that flag isn't set, we can guarantee that the arguments object doesn't
2862 // have any additional own elements.
2864 if (args
->is
<MappedArgumentsObject
>()) {
2865 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2867 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2868 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2871 GeneratePrototypeHoleGuards(writer
, args
, objId
,
2872 /* alwaysGuardFirstProto = */ true);
2874 writer
.loadArgumentsObjectArgHoleResult(objId
, indexId
);
2875 writer
.returnFromIC();
2877 trackAttached("GetProp.ArgumentsObjectArgHole");
2878 return AttachDecision::Attach
;
2881 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectCallee(
2882 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2883 // Only mapped arguments objects have a `callee` property.
2884 if (!obj
->is
<MappedArgumentsObject
>()) {
2885 return AttachDecision::NoAction
;
2888 if (!id
.isAtom(cx_
->names().callee
)) {
2889 return AttachDecision::NoAction
;
2892 // The callee must not have been overridden or deleted.
2893 MappedArgumentsObject
* args
= &obj
->as
<MappedArgumentsObject
>();
2894 if (args
->hasOverriddenCallee()) {
2895 return AttachDecision::NoAction
;
2898 AssertArgumentsCustomDataProp(args
, id
);
2900 maybeEmitIdGuard(id
);
2901 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2903 uint32_t flags
= ArgumentsObject::CALLEE_OVERRIDDEN_BIT
;
2904 writer
.guardArgumentsObjectFlags(objId
, flags
);
2906 writer
.loadFixedSlotResult(objId
,
2907 MappedArgumentsObject::getCalleeSlotOffset());
2908 writer
.returnFromIC();
2910 trackAttached("GetProp.ArgumentsObjectCallee");
2911 return AttachDecision::Attach
;
2914 AttachDecision
GetPropIRGenerator::tryAttachDenseElement(
2915 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2916 Int32OperandId indexId
) {
2917 if (!obj
->is
<NativeObject
>()) {
2918 return AttachDecision::NoAction
;
2921 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2922 if (!nobj
->containsDenseElement(index
)) {
2923 return AttachDecision::NoAction
;
2926 if (mode_
== ICState::Mode::Megamorphic
) {
2927 writer
.guardIsNativeObject(objId
);
2929 TestMatchingNativeReceiver(writer
, nobj
, objId
);
2931 writer
.loadDenseElementResult(objId
, indexId
);
2932 writer
.returnFromIC();
2934 trackAttached("GetProp.DenseElement");
2935 return AttachDecision::Attach
;
2938 AttachDecision
GetPropIRGenerator::tryAttachDenseElementHole(
2939 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2940 Int32OperandId indexId
) {
2941 if (!obj
->is
<NativeObject
>()) {
2942 return AttachDecision::NoAction
;
2945 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2946 if (nobj
->containsDenseElement(index
)) {
2947 return AttachDecision::NoAction
;
2949 if (!CanAttachDenseElementHole(nobj
, OwnProperty::No
)) {
2950 return AttachDecision::NoAction
;
2953 // Guard on the shape, to prevent non-dense elements from appearing.
2954 TestMatchingNativeReceiver(writer
, nobj
, objId
);
2955 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
2956 /* alwaysGuardFirstProto = */ false);
2957 writer
.loadDenseElementHoleResult(objId
, indexId
);
2958 writer
.returnFromIC();
2960 trackAttached("GetProp.DenseElementHole");
2961 return AttachDecision::Attach
;
2964 AttachDecision
GetPropIRGenerator::tryAttachSparseElement(
2965 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2966 Int32OperandId indexId
) {
2967 if (!obj
->is
<NativeObject
>()) {
2968 return AttachDecision::NoAction
;
2970 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
2972 // Stub doesn't handle negative indices.
2973 if (index
> INT32_MAX
) {
2974 return AttachDecision::NoAction
;
2977 // The object must have sparse elements.
2978 if (!nobj
->isIndexed()) {
2979 return AttachDecision::NoAction
;
2982 // The index must not be for a dense element.
2983 if (nobj
->containsDenseElement(index
)) {
2984 return AttachDecision::NoAction
;
2987 // Only handle ArrayObject and PlainObject in this stub.
2988 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
2989 return AttachDecision::NoAction
;
2992 // GetSparseElementHelper assumes that the target and the receiver
2995 return AttachDecision::NoAction
;
2998 // Here, we ensure that the prototype chain does not define any sparse
2999 // indexed properties on the shape lineage. This allows us to guard on
3000 // the shapes up the prototype chain to ensure that no indexed properties
3001 // exist outside of the dense elements.
3003 // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
3004 // as well as ensure that no prototypes contain dense elements, allowing
3005 // us to perform a pure shape-search for out-of-bounds integer-indexed
3006 // properties on the receiver object.
3007 if (PrototypeMayHaveIndexedProperties(nobj
)) {
3008 return AttachDecision::NoAction
;
3011 // Ensure that obj is an ArrayObject or PlainObject.
3012 if (nobj
->is
<ArrayObject
>()) {
3013 writer
.guardClass(objId
, GuardClassKind::Array
);
3015 MOZ_ASSERT(nobj
->is
<PlainObject
>());
3016 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
3019 // The helper we are going to call only applies to non-dense elements.
3020 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3022 // Ensures we are able to efficiently able to map to an integral jsid.
3023 writer
.guardInt32IsNonNegative(indexId
);
3025 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
3026 // The helper function also ensures that the index does not appear within the
3027 // dense element set of the prototypes.
3028 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3029 /* alwaysGuardFirstProto = */ true);
3031 // At this point, we are guaranteed that the indexed property will not
3032 // be found on one of the prototypes. We are assured that we only have
3033 // to check that the receiving object has the property.
3035 writer
.callGetSparseElementResult(objId
, indexId
);
3036 writer
.returnFromIC();
3038 trackAttached("GetProp.SparseElement");
3039 return AttachDecision::Attach
;
3042 // For Uint32Array we let the stub return an Int32 if we have not seen a
3043 // double, to allow better codegen in Warp while avoiding bailout loops.
3044 static bool ForceDoubleForUint32Array(FixedLengthTypedArrayObject
* tarr
,
3046 MOZ_ASSERT(index
< tarr
->length());
3048 if (tarr
->type() != Scalar::Type::Uint32
) {
3049 // Return value is only relevant for Uint32Array.
3054 MOZ_ALWAYS_TRUE(tarr
->getElementPure(index
, &res
));
3055 MOZ_ASSERT(res
.isNumber());
3056 return res
.isDouble();
3059 AttachDecision
GetPropIRGenerator::tryAttachTypedArrayElement(
3060 HandleObject obj
, ObjOperandId objId
) {
3061 // TODO: Support resizable typed arrays. (bug 1842999)
3062 if (!obj
->is
<FixedLengthTypedArrayObject
>()) {
3063 return AttachDecision::NoAction
;
3066 if (!idVal_
.isNumber()) {
3067 return AttachDecision::NoAction
;
3070 auto* tarr
= &obj
->as
<FixedLengthTypedArrayObject
>();
3072 bool handleOOB
= false;
3074 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
3075 uint64_t(indexInt64
) >= tarr
->length()) {
3079 // If the number is not representable as an integer the result will be
3080 // |undefined| so we leave |forceDoubleForUint32| as false.
3081 bool forceDoubleForUint32
= false;
3083 uint64_t index
= uint64_t(indexInt64
);
3084 forceDoubleForUint32
= ForceDoubleForUint32Array(tarr
, index
);
3087 writer
.guardShapeForClass(objId
, tarr
->shape());
3089 ValOperandId keyId
= getElemKeyValueId();
3090 IntPtrOperandId intPtrIndexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
3092 writer
.loadTypedArrayElementResult(objId
, intPtrIndexId
, tarr
->type(),
3093 handleOOB
, forceDoubleForUint32
);
3094 writer
.returnFromIC();
3096 trackAttached("GetProp.TypedElement");
3097 return AttachDecision::Attach
;
3100 AttachDecision
GetPropIRGenerator::tryAttachGenericElement(
3101 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3102 Int32OperandId indexId
, ValOperandId receiverId
) {
3103 if (!obj
->is
<NativeObject
>()) {
3104 return AttachDecision::NoAction
;
3107 #ifdef JS_CODEGEN_X86
3109 // There aren't enough registers available on x86.
3110 return AttachDecision::NoAction
;
3114 // To allow other types to attach in the non-megamorphic case we test the
3115 // specific matching native receiver; however, once megamorphic we can attach
3117 if (mode_
== ICState::Mode::Megamorphic
) {
3118 writer
.guardIsNativeObject(objId
);
3120 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3121 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3123 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3125 writer
.callNativeGetElementSuperResult(objId
, indexId
, receiverId
);
3127 writer
.callNativeGetElementResult(objId
, indexId
);
3129 writer
.returnFromIC();
3131 trackAttached(mode_
== ICState::Mode::Megamorphic
3132 ? "GenericElementMegamorphic"
3133 : "GenericElement");
3134 return AttachDecision::Attach
;
3137 AttachDecision
GetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3138 ObjOperandId objId
) {
3139 if (!obj
->is
<ProxyObject
>()) {
3140 return AttachDecision::NoAction
;
3143 // The proxy stubs don't currently support |super| access.
3145 return AttachDecision::NoAction
;
3149 auto proxy
= obj
.as
<ProxyObject
>();
3150 if (proxy
->handler()->isScripted()) {
3151 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, JS::VoidHandlePropertyKey
));
3155 writer
.guardIsProxy(objId
);
3157 // We are not guarding against DOM proxies here, because there is no other
3158 // specialized DOM IC we could attach.
3159 // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
3160 // but for GetElem we prefer to attach a stub that can handle any Value
3161 // so we don't attach a new stub for every id.
3162 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
3163 MOZ_ASSERT(!isSuper());
3164 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
3165 writer
.returnFromIC();
3167 trackAttached("GetProp.ProxyElement");
3168 return AttachDecision::Attach
;
3171 void GetPropIRGenerator::trackAttached(const char* name
) {
3172 stubName_
= name
? name
: "NotAttached";
3173 #ifdef JS_CACHEIR_SPEW
3174 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3175 sp
.valueProperty("base", val_
);
3176 sp
.valueProperty("property", idVal_
);
3181 void IRGenerator::emitIdGuard(ValOperandId valId
, const Value
& idVal
, jsid id
) {
3182 if (id
.isSymbol()) {
3183 MOZ_ASSERT(idVal
.toSymbol() == id
.toSymbol());
3184 SymbolOperandId symId
= writer
.guardToSymbol(valId
);
3185 writer
.guardSpecificSymbol(symId
, id
.toSymbol());
3187 MOZ_ASSERT(id
.isAtom());
3188 if (idVal
.isUndefined()) {
3189 MOZ_ASSERT(id
.isAtom(cx_
->names().undefined
));
3190 writer
.guardIsUndefined(valId
);
3191 } else if (idVal
.isNull()) {
3192 MOZ_ASSERT(id
.isAtom(cx_
->names().null
));
3193 writer
.guardIsNull(valId
);
3195 MOZ_ASSERT(idVal
.isString());
3196 StringOperandId strId
= writer
.guardToString(valId
);
3197 writer
.guardSpecificAtom(strId
, id
.toAtom());
3202 void GetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3203 if (cacheKind_
== CacheKind::GetProp
||
3204 cacheKind_
== CacheKind::GetPropSuper
) {
3205 // Constant PropertyName, no guards necessary.
3206 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3210 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
3211 cacheKind_
== CacheKind::GetElemSuper
);
3212 emitIdGuard(getElemKeyValueId(), idVal_
, id
);
3215 void SetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3216 if (cacheKind_
== CacheKind::SetProp
) {
3217 // Constant PropertyName, no guards necessary.
3218 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3222 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
3223 emitIdGuard(setElemKeyValueId(), idVal_
, id
);
3226 GetNameIRGenerator::GetNameIRGenerator(JSContext
* cx
, HandleScript script
,
3227 jsbytecode
* pc
, ICState state
,
3229 Handle
<PropertyName
*> name
)
3230 : IRGenerator(cx
, script
, pc
, CacheKind::GetName
, state
),
3234 AttachDecision
GetNameIRGenerator::tryAttachStub() {
3235 MOZ_ASSERT(cacheKind_
== CacheKind::GetName
);
3237 AutoAssertNoPendingException
aanpe(cx_
);
3239 ObjOperandId
envId(writer
.setInputOperandId(0));
3240 RootedId
id(cx_
, NameToId(name_
));
3242 TRY_ATTACH(tryAttachGlobalNameValue(envId
, id
));
3243 TRY_ATTACH(tryAttachGlobalNameGetter(envId
, id
));
3244 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3246 trackAttached(IRGenerator::NotAttached
);
3247 return AttachDecision::NoAction
;
3250 static bool CanAttachGlobalName(JSContext
* cx
,
3251 GlobalLexicalEnvironmentObject
* globalLexical
,
3252 PropertyKey id
, NativeObject
** holder
,
3253 Maybe
<PropertyInfo
>* prop
) {
3254 // The property must be found, and it must be found as a normal data property.
3255 NativeObject
* current
= globalLexical
;
3257 *prop
= current
->lookup(cx
, id
);
3258 if (prop
->isSome()) {
3262 if (current
== globalLexical
) {
3263 current
= &globalLexical
->global();
3265 // In the browser the global prototype chain should be immutable.
3266 if (!current
->staticPrototypeIsImmutable()) {
3270 JSObject
* proto
= current
->staticPrototype();
3271 if (!proto
|| !proto
->is
<NativeObject
>()) {
3275 current
= &proto
->as
<NativeObject
>();
3283 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId
,
3285 if (!IsGlobalOp(JSOp(*pc_
))) {
3286 return AttachDecision::NoAction
;
3288 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3290 auto* globalLexical
= &env_
->as
<GlobalLexicalEnvironmentObject
>();
3292 NativeObject
* holder
= nullptr;
3293 Maybe
<PropertyInfo
> prop
;
3294 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3295 return AttachDecision::NoAction
;
3298 // The property must be found, and it must be found as a normal data property.
3299 if (!prop
->isDataProperty()) {
3300 return AttachDecision::NoAction
;
3303 // This might still be an uninitialized lexical.
3304 if (holder
->getSlot(prop
->slot()).isMagic()) {
3305 return AttachDecision::NoAction
;
3308 if (holder
== globalLexical
) {
3309 // There is no need to guard on the shape. Lexical bindings are
3310 // non-configurable, and this stub cannot be shared across globals.
3311 size_t dynamicSlotOffset
=
3312 holder
->dynamicSlotIndex(prop
->slot()) * sizeof(Value
);
3313 writer
.loadDynamicSlotResult(objId
, dynamicSlotOffset
);
3314 } else if (holder
== &globalLexical
->global()) {
3315 MOZ_ASSERT(globalLexical
->global().isGenerationCountedGlobal());
3316 writer
.guardGlobalGeneration(
3317 globalLexical
->global().generationCount(),
3318 globalLexical
->global().addressOfGenerationCount());
3319 ObjOperandId holderId
= writer
.loadObject(holder
);
3321 writer
.assertPropertyLookup(holderId
, id
, prop
->slot());
3323 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3325 // Check the prototype chain from the global to the holder
3326 // prototype. Ignore the global lexical scope as it doesn't figure
3327 // into the prototype chain. We guard on the global lexical
3328 // scope's shape independently.
3329 if (!IsCacheableGetPropSlot(&globalLexical
->global(), holder
, *prop
)) {
3330 return AttachDecision::NoAction
;
3333 // Shape guard for global lexical.
3334 writer
.guardShape(objId
, globalLexical
->shape());
3336 // Guard on the shape of the GlobalObject.
3337 ObjOperandId globalId
= writer
.loadObject(&globalLexical
->global());
3338 writer
.guardShape(globalId
, globalLexical
->global().shape());
3340 // Shape guard holder.
3341 ObjOperandId holderId
= writer
.loadObject(holder
);
3342 writer
.guardShape(holderId
, holder
->shape());
3344 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3347 writer
.returnFromIC();
3349 trackAttached("GetName.GlobalNameValue");
3350 return AttachDecision::Attach
;
3353 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId
,
3355 if (!IsGlobalOp(JSOp(*pc_
))) {
3356 return AttachDecision::NoAction
;
3358 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3360 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3361 env_
.as
<GlobalLexicalEnvironmentObject
>();
3362 MOZ_ASSERT(globalLexical
->isGlobal());
3364 NativeObject
* holder
= nullptr;
3365 Maybe
<PropertyInfo
> prop
;
3366 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3367 return AttachDecision::NoAction
;
3370 if (holder
== globalLexical
) {
3371 return AttachDecision::NoAction
;
3374 GlobalObject
* global
= &globalLexical
->global();
3376 NativeGetPropKind kind
= IsCacheableGetPropCall(global
, holder
, *prop
);
3377 if (kind
!= NativeGetPropKind::NativeGetter
&&
3378 kind
!= NativeGetPropKind::ScriptedGetter
) {
3379 return AttachDecision::NoAction
;
3382 bool needsWindowProxy
=
3383 IsWindow(global
) && GetterNeedsWindowProxyThis(holder
, *prop
);
3385 // Shape guard for global lexical.
3386 writer
.guardShape(objId
, globalLexical
->shape());
3388 // Guard on the shape of the GlobalObject.
3389 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3390 writer
.guardShape(globalId
, global
->shape());
3392 if (holder
!= global
) {
3393 // Shape guard holder.
3394 ObjOperandId holderId
= writer
.loadObject(holder
);
3395 writer
.guardShape(holderId
, holder
->shape());
3396 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
3397 /* holderIsConstant = */ true);
3399 // Note: pass true for |holderIsConstant| because the holder must be the
3400 // current global object.
3401 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, globalId
,
3402 /* holderIsConstant = */ true);
3405 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, global
, holder
, *prop
,
3407 // The global shape guard above ensures the instance JSClass is correct.
3408 MOZ_ASSERT(!needsWindowProxy
);
3409 EmitCallDOMGetterResultNoGuards(writer
, holder
, *prop
, globalId
);
3410 trackAttached("GetName.GlobalNameDOMGetter");
3412 ObjOperandId receiverObjId
;
3413 if (needsWindowProxy
) {
3414 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
3415 receiverObjId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
3417 receiverObjId
= globalId
;
3419 ValOperandId receiverId
= writer
.boxObject(receiverObjId
);
3420 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, global
, holder
, *prop
,
3422 trackAttached("GetName.GlobalNameGetter");
3425 return AttachDecision::Attach
;
3428 static bool NeedEnvironmentShapeGuard(JSContext
* cx
, JSObject
* envObj
) {
3429 if (!envObj
->is
<CallObject
>()) {
3433 // We can skip a guard on the call object if the script's bindings are
3434 // guaranteed to be immutable (and thus cannot introduce shadowing variables).
3435 // If the function is a relazified self-hosted function it has no BaseScript
3436 // and we pessimistically create the guard.
3437 CallObject
* callObj
= &envObj
->as
<CallObject
>();
3438 JSFunction
* fun
= &callObj
->callee();
3439 if (!fun
->hasBaseScript() || fun
->baseScript()->funHasExtensibleScope() ||
3440 DebugEnvironments::hasDebugEnvironment(cx
, *callObj
)) {
3447 AttachDecision
GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3449 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3450 return AttachDecision::NoAction
;
3453 JSObject
* env
= env_
;
3454 Maybe
<PropertyInfo
> prop
;
3455 NativeObject
* holder
= nullptr;
3458 if (env
->is
<GlobalObject
>()) {
3459 prop
= env
->as
<GlobalObject
>().lookup(cx_
, id
);
3460 if (prop
.isSome()) {
3463 return AttachDecision::NoAction
;
3466 if (!env
->is
<EnvironmentObject
>() || env
->is
<WithEnvironmentObject
>()) {
3467 return AttachDecision::NoAction
;
3470 // Check for an 'own' property on the env. There is no need to
3471 // check the prototype as non-with scopes do not inherit properties
3472 // from any prototype.
3473 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3474 if (prop
.isSome()) {
3478 env
= env
->enclosingEnvironment();
3481 holder
= &env
->as
<NativeObject
>();
3482 if (!IsCacheableGetPropSlot(holder
, holder
, *prop
)) {
3483 return AttachDecision::NoAction
;
3485 if (holder
->getSlot(prop
->slot()).isMagic()) {
3486 MOZ_ASSERT(holder
->is
<EnvironmentObject
>());
3487 return AttachDecision::NoAction
;
3490 ObjOperandId lastObjId
= objId
;
3493 if (NeedEnvironmentShapeGuard(cx_
, env
)) {
3494 writer
.guardShape(lastObjId
, env
->shape());
3497 if (env
== holder
) {
3501 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3502 env
= env
->enclosingEnvironment();
3505 ValOperandId resId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3506 if (holder
->is
<EnvironmentObject
>()) {
3507 writer
.guardIsNotUninitializedLexical(resId
);
3509 writer
.loadOperandResult(resId
);
3510 writer
.returnFromIC();
3512 trackAttached("GetName.EnvironmentName");
3513 return AttachDecision::Attach
;
3516 void GetNameIRGenerator::trackAttached(const char* name
) {
3517 stubName_
= name
? name
: "NotAttached";
3518 #ifdef JS_CACHEIR_SPEW
3519 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3520 sp
.valueProperty("base", ObjectValue(*env_
));
3521 sp
.valueProperty("property", StringValue(name_
));
3526 BindNameIRGenerator::BindNameIRGenerator(JSContext
* cx
, HandleScript script
,
3527 jsbytecode
* pc
, ICState state
,
3529 Handle
<PropertyName
*> name
)
3530 : IRGenerator(cx
, script
, pc
, CacheKind::BindName
, state
),
3534 AttachDecision
BindNameIRGenerator::tryAttachStub() {
3535 MOZ_ASSERT(cacheKind_
== CacheKind::BindName
);
3537 AutoAssertNoPendingException
aanpe(cx_
);
3539 ObjOperandId
envId(writer
.setInputOperandId(0));
3540 RootedId
id(cx_
, NameToId(name_
));
3542 TRY_ATTACH(tryAttachGlobalName(envId
, id
));
3543 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3545 trackAttached(IRGenerator::NotAttached
);
3546 return AttachDecision::NoAction
;
3549 AttachDecision
BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId
,
3551 if (!IsGlobalOp(JSOp(*pc_
))) {
3552 return AttachDecision::NoAction
;
3554 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3556 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3557 env_
.as
<GlobalLexicalEnvironmentObject
>();
3558 MOZ_ASSERT(globalLexical
->isGlobal());
3560 JSObject
* result
= nullptr;
3561 if (Maybe
<PropertyInfo
> prop
= globalLexical
->lookup(cx_
, id
)) {
3562 // If this is an uninitialized lexical or a const, we need to return a
3563 // RuntimeLexicalErrorObject.
3564 if (globalLexical
->getSlot(prop
->slot()).isMagic() || !prop
->writable()) {
3565 return AttachDecision::NoAction
;
3567 result
= globalLexical
;
3569 result
= &globalLexical
->global();
3572 if (result
== globalLexical
) {
3573 // Lexical bindings are non-configurable so we can just return the
3575 writer
.loadObjectResult(objId
);
3577 // If the property exists on the global and is non-configurable, it cannot
3578 // be shadowed by the lexical scope so we can just return the global without
3580 Maybe
<PropertyInfo
> prop
= result
->as
<GlobalObject
>().lookup(cx_
, id
);
3581 if (prop
.isNothing() || prop
->configurable()) {
3582 writer
.guardShape(objId
, globalLexical
->shape());
3584 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3585 writer
.loadObjectResult(globalId
);
3587 writer
.returnFromIC();
3589 trackAttached("BindName.GlobalName");
3590 return AttachDecision::Attach
;
3593 AttachDecision
BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3595 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3596 return AttachDecision::NoAction
;
3599 JSObject
* env
= env_
;
3600 Maybe
<PropertyInfo
> prop
;
3602 if (!env
->is
<GlobalObject
>() && !env
->is
<EnvironmentObject
>()) {
3603 return AttachDecision::NoAction
;
3605 if (env
->is
<WithEnvironmentObject
>()) {
3606 return AttachDecision::NoAction
;
3609 // When we reach an unqualified variables object (like the global) we
3610 // have to stop looking and return that object.
3611 if (env
->isUnqualifiedVarObj()) {
3615 // Check for an 'own' property on the env. There is no need to
3616 // check the prototype as non-with scopes do not inherit properties
3617 // from any prototype.
3618 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3619 if (prop
.isSome()) {
3623 env
= env
->enclosingEnvironment();
3626 // If this is an uninitialized lexical or a const, we need to return a
3627 // RuntimeLexicalErrorObject.
3628 auto* holder
= &env
->as
<NativeObject
>();
3629 if (prop
.isSome() && holder
->is
<EnvironmentObject
>() &&
3630 (holder
->getSlot(prop
->slot()).isMagic() || !prop
->writable())) {
3631 return AttachDecision::NoAction
;
3634 ObjOperandId lastObjId
= objId
;
3637 if (NeedEnvironmentShapeGuard(cx_
, env
) && !env
->is
<GlobalObject
>()) {
3638 writer
.guardShape(lastObjId
, env
->shape());
3641 if (env
== holder
) {
3645 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3646 env
= env
->enclosingEnvironment();
3649 if (prop
.isSome() && holder
->is
<EnvironmentObject
>()) {
3650 ValOperandId valId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3651 writer
.guardIsNotUninitializedLexical(valId
);
3654 writer
.loadObjectResult(lastObjId
);
3655 writer
.returnFromIC();
3657 trackAttached("BindName.EnvironmentName");
3658 return AttachDecision::Attach
;
3661 void BindNameIRGenerator::trackAttached(const char* name
) {
3662 stubName_
= name
? name
: "NotAttached";
3663 #ifdef JS_CACHEIR_SPEW
3664 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3665 sp
.valueProperty("base", ObjectValue(*env_
));
3666 sp
.valueProperty("property", StringValue(name_
));
3671 HasPropIRGenerator::HasPropIRGenerator(JSContext
* cx
, HandleScript script
,
3672 jsbytecode
* pc
, ICState state
,
3673 CacheKind cacheKind
, HandleValue idVal
,
3675 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
3677 AttachDecision
HasPropIRGenerator::tryAttachDense(HandleObject obj
,
3680 Int32OperandId indexId
) {
3681 if (!obj
->is
<NativeObject
>()) {
3682 return AttachDecision::NoAction
;
3685 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3686 if (!nobj
->containsDenseElement(index
)) {
3687 return AttachDecision::NoAction
;
3690 if (mode_
== ICState::Mode::Megamorphic
) {
3691 writer
.guardIsNativeObject(objId
);
3693 // Guard shape to ensure object class is NativeObject.
3694 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3696 writer
.loadDenseElementExistsResult(objId
, indexId
);
3697 writer
.returnFromIC();
3699 trackAttached("HasProp.Dense");
3700 return AttachDecision::Attach
;
3703 AttachDecision
HasPropIRGenerator::tryAttachDenseHole(HandleObject obj
,
3706 Int32OperandId indexId
) {
3707 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3708 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3710 if (!obj
->is
<NativeObject
>()) {
3711 return AttachDecision::NoAction
;
3714 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3715 if (nobj
->containsDenseElement(index
)) {
3716 return AttachDecision::NoAction
;
3718 if (!CanAttachDenseElementHole(nobj
, ownProp
)) {
3719 return AttachDecision::NoAction
;
3722 // Guard shape to ensure class is NativeObject and to prevent non-dense
3723 // elements being added. Also ensures prototype doesn't change if dynamic
3724 // checks aren't emitted.
3725 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3727 // Generate prototype guards if needed. This includes monitoring that
3728 // properties were not added in the chain.
3730 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3731 /* alwaysGuardFirstProto = */ false);
3734 writer
.loadDenseElementHoleExistsResult(objId
, indexId
);
3735 writer
.returnFromIC();
3737 trackAttached("HasProp.DenseHole");
3738 return AttachDecision::Attach
;
3741 AttachDecision
HasPropIRGenerator::tryAttachSparse(HandleObject obj
,
3743 Int32OperandId indexId
) {
3744 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3745 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3747 if (!obj
->is
<NativeObject
>()) {
3748 return AttachDecision::NoAction
;
3750 auto* nobj
= &obj
->as
<NativeObject
>();
3752 if (!nobj
->isIndexed()) {
3753 return AttachDecision::NoAction
;
3755 if (!CanAttachDenseElementHole(nobj
, ownProp
, AllowIndexedReceiver::Yes
)) {
3756 return AttachDecision::NoAction
;
3759 // Guard that this is a native object.
3760 writer
.guardIsNativeObject(objId
);
3762 // Generate prototype guards if needed. This includes monitoring that
3763 // properties were not added in the chain.
3765 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3766 /* alwaysGuardFirstProto = */ true);
3769 // Because of the prototype guard we know that the prototype chain
3770 // does not include any dense or sparse (i.e indexed) properties.
3771 writer
.callObjectHasSparseElementResult(objId
, indexId
);
3772 writer
.returnFromIC();
3774 trackAttached("HasProp.Sparse");
3775 return AttachDecision::Attach
;
3778 AttachDecision
HasPropIRGenerator::tryAttachArgumentsObjectArg(
3779 HandleObject obj
, ObjOperandId objId
, Int32OperandId indexId
) {
3780 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3781 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3783 if (!obj
->is
<ArgumentsObject
>()) {
3784 return AttachDecision::NoAction
;
3786 auto* args
= &obj
->as
<ArgumentsObject
>();
3788 // No elements must have been overridden or deleted.
3789 if (args
->hasOverriddenElement()) {
3790 return AttachDecision::NoAction
;
3793 if (!CanAttachDenseElementHole(args
, ownProp
, AllowIndexedReceiver::Yes
,
3794 AllowExtraReceiverProperties::Yes
)) {
3795 return AttachDecision::NoAction
;
3798 if (args
->is
<MappedArgumentsObject
>()) {
3799 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3801 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
3802 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
3806 GeneratePrototypeHoleGuards(writer
, args
, objId
,
3807 /* alwaysGuardFirstProto = */ true);
3810 writer
.loadArgumentsObjectArgExistsResult(objId
, indexId
);
3811 writer
.returnFromIC();
3813 trackAttached("HasProp.ArgumentsObjectArg");
3814 return AttachDecision::Attach
;
3817 AttachDecision
HasPropIRGenerator::tryAttachNamedProp(HandleObject obj
,
3820 ValOperandId keyId
) {
3821 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3823 NativeObject
* holder
= nullptr;
3824 PropertyResult prop
;
3827 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
3828 return AttachDecision::NoAction
;
3831 holder
= &obj
->as
<NativeObject
>();
3833 if (!LookupPropertyPure(cx_
, obj
, key
, &holder
, &prop
)) {
3834 return AttachDecision::NoAction
;
3837 if (prop
.isNotFound()) {
3838 return AttachDecision::NoAction
;
3840 auto* nobj
= &obj
->as
<NativeObject
>();
3842 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
3843 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
, holder
));
3845 return AttachDecision::NoAction
;
3848 AttachDecision
HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId
,
3849 ValOperandId keyId
) {
3850 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3852 if (mode_
!= ICState::Mode::Megamorphic
) {
3853 return AttachDecision::NoAction
;
3856 writer
.megamorphicHasPropResult(objId
, keyId
, hasOwn
);
3857 writer
.returnFromIC();
3858 trackAttached("HasProp.Megamorphic");
3859 return AttachDecision::Attach
;
3862 AttachDecision
HasPropIRGenerator::tryAttachNative(NativeObject
* obj
,
3863 ObjOperandId objId
, jsid key
,
3865 PropertyResult prop
,
3866 NativeObject
* holder
) {
3867 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
3869 if (!prop
.isNativeProperty()) {
3870 return AttachDecision::NoAction
;
3873 emitIdGuard(keyId
, idVal_
, key
);
3874 EmitReadSlotGuard(writer
, obj
, holder
, objId
);
3875 writer
.loadBooleanResult(true);
3876 writer
.returnFromIC();
3878 trackAttached("HasProp.Native");
3879 return AttachDecision::Attach
;
3882 AttachDecision
HasPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
3884 ValOperandId keyId
) {
3885 // TODO: Support resizable typed arrays. (bug 1842999)
3886 if (!obj
->is
<FixedLengthTypedArrayObject
>()) {
3887 return AttachDecision::NoAction
;
3891 if (!ValueIsInt64Index(idVal_
, &index
)) {
3892 return AttachDecision::NoAction
;
3895 writer
.guardIsFixedLengthTypedArray(objId
);
3896 IntPtrOperandId intPtrIndexId
=
3897 guardToIntPtrIndex(idVal_
, keyId
, /* supportOOB = */ true);
3898 writer
.loadTypedArrayElementExistsResult(objId
, intPtrIndexId
);
3899 writer
.returnFromIC();
3901 trackAttached("HasProp.TypedArrayObject");
3902 return AttachDecision::Attach
;
3905 AttachDecision
HasPropIRGenerator::tryAttachSlotDoesNotExist(
3906 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
3907 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3909 emitIdGuard(keyId
, idVal_
, key
);
3911 TestMatchingNativeReceiver(writer
, obj
, objId
);
3913 EmitMissingPropGuard(writer
, obj
, objId
);
3915 writer
.loadBooleanResult(false);
3916 writer
.returnFromIC();
3918 trackAttached("HasProp.DoesNotExist");
3919 return AttachDecision::Attach
;
3922 AttachDecision
HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj
,
3925 ValOperandId keyId
) {
3926 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3928 // Check that property doesn't exist on |obj| or it's prototype chain. These
3929 // checks allow NativeObjects with a NativeObject prototype chain. They return
3930 // NoAction if unknown such as resolve hooks or proxies.
3932 if (!CheckHasNoSuchOwnProperty(cx_
, obj
, key
)) {
3933 return AttachDecision::NoAction
;
3936 if (!CheckHasNoSuchProperty(cx_
, obj
, key
)) {
3937 return AttachDecision::NoAction
;
3940 auto* nobj
= &obj
->as
<NativeObject
>();
3942 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
3943 TRY_ATTACH(tryAttachSlotDoesNotExist(nobj
, objId
, key
, keyId
));
3945 return AttachDecision::NoAction
;
3948 AttachDecision
HasPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3950 ValOperandId keyId
) {
3951 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3953 if (!obj
->is
<ProxyObject
>()) {
3954 return AttachDecision::NoAction
;
3957 writer
.guardIsProxy(objId
);
3958 writer
.proxyHasPropResult(objId
, keyId
, hasOwn
);
3959 writer
.returnFromIC();
3961 trackAttached("HasProp.ProxyElement");
3962 return AttachDecision::Attach
;
3965 AttachDecision
HasPropIRGenerator::tryAttachStub() {
3966 MOZ_ASSERT(cacheKind_
== CacheKind::In
|| cacheKind_
== CacheKind::HasOwn
);
3968 AutoAssertNoPendingException
aanpe(cx_
);
3970 // NOTE: Argument order is PROPERTY, OBJECT
3971 ValOperandId
keyId(writer
.setInputOperandId(0));
3972 ValOperandId
valId(writer
.setInputOperandId(1));
3974 if (!val_
.isObject()) {
3975 trackAttached(IRGenerator::NotAttached
);
3976 return AttachDecision::NoAction
;
3978 RootedObject
obj(cx_
, &val_
.toObject());
3979 ObjOperandId objId
= writer
.guardToObject(valId
);
3982 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, keyId
));
3986 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
3987 cx_
->clearPendingException();
3988 return AttachDecision::NoAction
;
3992 TRY_ATTACH(tryAttachNamedProp(obj
, objId
, id
, keyId
));
3993 TRY_ATTACH(tryAttachDoesNotExist(obj
, objId
, id
, keyId
));
3995 trackAttached(IRGenerator::NotAttached
);
3996 return AttachDecision::NoAction
;
3999 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, keyId
));
4002 Int32OperandId indexId
;
4003 if (maybeGuardInt32Index(idVal_
, keyId
, &index
, &indexId
)) {
4004 TRY_ATTACH(tryAttachDense(obj
, objId
, index
, indexId
));
4005 TRY_ATTACH(tryAttachDenseHole(obj
, objId
, index
, indexId
));
4006 TRY_ATTACH(tryAttachSparse(obj
, objId
, indexId
));
4007 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, indexId
));
4009 trackAttached(IRGenerator::NotAttached
);
4010 return AttachDecision::NoAction
;
4013 trackAttached(IRGenerator::NotAttached
);
4014 return AttachDecision::NoAction
;
4017 void HasPropIRGenerator::trackAttached(const char* name
) {
4018 stubName_
= name
? name
: "NotAttached";
4019 #ifdef JS_CACHEIR_SPEW
4020 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4021 sp
.valueProperty("base", val_
);
4022 sp
.valueProperty("property", idVal_
);
4027 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
4028 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
4029 CacheKind cacheKind
, HandleValue idVal
, HandleValue val
)
4030 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {
4031 MOZ_ASSERT(idVal
.isSymbol() && idVal
.toSymbol()->isPrivateName());
4034 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachStub() {
4035 AutoAssertNoPendingException
aanpe(cx_
);
4037 ValOperandId
valId(writer
.setInputOperandId(0));
4038 ValOperandId
keyId(writer
.setInputOperandId(1));
4040 if (!val_
.isObject()) {
4041 trackAttached(IRGenerator::NotAttached
);
4042 return AttachDecision::NoAction
;
4044 JSObject
* obj
= &val_
.toObject();
4045 ObjOperandId objId
= writer
.guardToObject(valId
);
4046 PropertyKey key
= PropertyKey::Symbol(idVal_
.toSymbol());
4048 ThrowCondition condition
;
4049 ThrowMsgKind msgKind
;
4050 GetCheckPrivateFieldOperands(pc_
, &condition
, &msgKind
);
4052 PropertyResult prop
;
4053 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
4054 return AttachDecision::NoAction
;
4057 if (CheckPrivateFieldWillThrow(condition
, prop
.isFound())) {
4058 // Don't attach a stub if the operation will throw.
4059 return AttachDecision::NoAction
;
4062 auto* nobj
= &obj
->as
<NativeObject
>();
4064 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
));
4066 return AttachDecision::NoAction
;
4069 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachNative(
4070 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
,
4071 PropertyResult prop
) {
4072 MOZ_ASSERT(prop
.isNativeProperty() || prop
.isNotFound());
4074 emitIdGuard(keyId
, idVal_
, key
);
4075 TestMatchingNativeReceiver(writer
, obj
, objId
);
4076 writer
.loadBooleanResult(prop
.isFound());
4077 writer
.returnFromIC();
4079 trackAttached("CheckPrivateField.Native");
4080 return AttachDecision::Attach
;
4083 void CheckPrivateFieldIRGenerator::trackAttached(const char* name
) {
4084 stubName_
= name
? name
: "NotAttached";
4085 #ifdef JS_CACHEIR_SPEW
4086 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4087 sp
.valueProperty("base", val_
);
4088 sp
.valueProperty("property", idVal_
);
4093 bool IRGenerator::maybeGuardInt32Index(const Value
& index
, ValOperandId indexId
,
4094 uint32_t* int32Index
,
4095 Int32OperandId
* int32IndexId
) {
4096 if (index
.isNumber()) {
4097 int32_t indexSigned
;
4098 if (index
.isInt32()) {
4099 indexSigned
= index
.toInt32();
4101 // We allow negative zero here.
4102 if (!mozilla::NumberEqualsInt32(index
.toDouble(), &indexSigned
)) {
4107 if (indexSigned
< 0) {
4111 *int32Index
= uint32_t(indexSigned
);
4112 *int32IndexId
= writer
.guardToInt32Index(indexId
);
4116 if (index
.isString()) {
4117 int32_t indexSigned
= GetIndexFromString(index
.toString());
4118 if (indexSigned
< 0) {
4122 StringOperandId strId
= writer
.guardToString(indexId
);
4123 *int32Index
= uint32_t(indexSigned
);
4124 *int32IndexId
= writer
.guardStringToIndex(strId
);
4131 SetPropIRGenerator::SetPropIRGenerator(JSContext
* cx
, HandleScript script
,
4132 jsbytecode
* pc
, CacheKind cacheKind
,
4133 ICState state
, HandleValue lhsVal
,
4134 HandleValue idVal
, HandleValue rhsVal
)
4135 : IRGenerator(cx
, script
, pc
, cacheKind
, state
),
4140 AttachDecision
SetPropIRGenerator::tryAttachStub() {
4141 AutoAssertNoPendingException
aanpe(cx_
);
4143 ValOperandId
objValId(writer
.setInputOperandId(0));
4144 ValOperandId rhsValId
;
4145 if (cacheKind_
== CacheKind::SetProp
) {
4146 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
4148 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4149 MOZ_ASSERT(setElemKeyValueId().id() == 1);
4150 writer
.setInputOperandId(1);
4151 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
4156 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4157 cx_
->clearPendingException();
4158 return AttachDecision::NoAction
;
4161 if (lhsVal_
.isObject()) {
4162 RootedObject
obj(cx_
, &lhsVal_
.toObject());
4164 ObjOperandId objId
= writer
.guardToObject(objValId
);
4165 if (IsPropertySetOp(JSOp(*pc_
))) {
4166 TRY_ATTACH(tryAttachMegamorphicSetElement(obj
, objId
, rhsValId
));
4169 TRY_ATTACH(tryAttachNativeSetSlot(obj
, objId
, id
, rhsValId
));
4170 if (IsPropertySetOp(JSOp(*pc_
))) {
4171 TRY_ATTACH(tryAttachSetArrayLength(obj
, objId
, id
, rhsValId
));
4172 TRY_ATTACH(tryAttachSetter(obj
, objId
, id
, rhsValId
));
4173 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
, rhsValId
));
4174 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, rhsValId
));
4175 TRY_ATTACH(tryAttachMegamorphicSetSlot(obj
, objId
, id
, rhsValId
));
4177 if (canAttachAddSlotStub(obj
, id
)) {
4178 deferType_
= DeferType::AddSlot
;
4179 return AttachDecision::Deferred
;
4181 return AttachDecision::NoAction
;
4184 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4186 if (IsPropertySetOp(JSOp(*pc_
))) {
4187 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, rhsValId
));
4190 TRY_ATTACH(tryAttachSetTypedArrayElement(obj
, objId
, rhsValId
));
4193 Int32OperandId indexId
;
4194 if (maybeGuardInt32Index(idVal_
, setElemKeyValueId(), &index
, &indexId
)) {
4196 tryAttachSetDenseElement(obj
, objId
, index
, indexId
, rhsValId
));
4198 tryAttachSetDenseElementHole(obj
, objId
, index
, indexId
, rhsValId
));
4199 TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj
, objId
, index
, indexId
,
4201 return AttachDecision::NoAction
;
4204 return AttachDecision::NoAction
;
4207 static void EmitStoreSlotAndReturn(CacheIRWriter
& writer
, ObjOperandId objId
,
4208 NativeObject
* nobj
, PropertyInfo prop
,
4209 ValOperandId rhsId
) {
4210 if (nobj
->isFixedSlot(prop
.slot())) {
4211 size_t offset
= NativeObject::getFixedSlotOffset(prop
.slot());
4212 writer
.storeFixedSlot(objId
, offset
, rhsId
);
4214 size_t offset
= nobj
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
4215 writer
.storeDynamicSlot(objId
, offset
, rhsId
);
4217 writer
.returnFromIC();
4220 static Maybe
<PropertyInfo
> LookupShapeForSetSlot(JSOp op
, NativeObject
* obj
,
4222 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(id
);
4223 if (prop
.isNothing() || !prop
->isDataProperty() || !prop
->writable()) {
4224 return mozilla::Nothing();
4227 // If this is a property init operation, the property's attributes may have to
4228 // be changed too, so make sure the current flags match.
4229 if (IsPropertyInitOp(op
)) {
4230 // Don't support locked init operations.
4231 if (IsLockedInitOp(op
)) {
4232 return mozilla::Nothing();
4235 // Can't redefine a non-configurable property.
4236 if (!prop
->configurable()) {
4237 return mozilla::Nothing();
4240 // Make sure the enumerable flag matches the init operation.
4241 if (IsHiddenInitOp(op
) == prop
->enumerable()) {
4242 return mozilla::Nothing();
4249 static bool CanAttachNativeSetSlot(JSOp op
, JSObject
* obj
, PropertyKey id
,
4250 Maybe
<PropertyInfo
>* prop
) {
4251 if (!obj
->is
<NativeObject
>()) {
4255 if (Watchtower::watchesPropertyModification(&obj
->as
<NativeObject
>())) {
4259 *prop
= LookupShapeForSetSlot(op
, &obj
->as
<NativeObject
>(), id
);
4260 return prop
->isSome();
4263 // There is no need to guard on the shape. Global lexical bindings are
4264 // non-configurable and can not be shadowed.
4265 static bool IsGlobalLexicalSetGName(JSOp op
, NativeObject
* obj
,
4266 PropertyInfo prop
) {
4267 // Ensure that the env can't change.
4268 if (op
!= JSOp::SetGName
&& op
!= JSOp::StrictSetGName
) {
4272 if (!obj
->is
<GlobalLexicalEnvironmentObject
>()) {
4276 // Uninitialized let bindings use a RuntimeLexicalErrorObject.
4277 MOZ_ASSERT(!obj
->getSlot(prop
.slot()).isMagic());
4278 MOZ_ASSERT(prop
.writable());
4279 MOZ_ASSERT(!prop
.configurable());
4283 AttachDecision
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj
,
4286 ValOperandId rhsId
) {
4287 Maybe
<PropertyInfo
> prop
;
4288 if (!CanAttachNativeSetSlot(JSOp(*pc_
), obj
, id
, &prop
)) {
4289 return AttachDecision::NoAction
;
4292 if (mode_
== ICState::Mode::Megamorphic
&& cacheKind_
== CacheKind::SetProp
&&
4293 IsPropertySetOp(JSOp(*pc_
))) {
4294 return AttachDecision::NoAction
;
4297 maybeEmitIdGuard(id
);
4299 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4300 if (!IsGlobalLexicalSetGName(JSOp(*pc_
), nobj
, *prop
)) {
4301 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4303 EmitStoreSlotAndReturn(writer
, objId
, nobj
, *prop
, rhsId
);
4305 trackAttached("SetProp.NativeSlot");
4306 return AttachDecision::Attach
;
4309 static bool ValueCanConvertToNumeric(Scalar::Type type
, const Value
& val
) {
4310 if (Scalar::isBigIntType(type
)) {
4311 return val
.isBigInt();
4313 return val
.isNumber() || val
.isNullOrUndefined() || val
.isBoolean() ||
4317 OperandId
IRGenerator::emitNumericGuard(ValOperandId valId
, const Value
& v
,
4318 Scalar::Type type
) {
4319 MOZ_ASSERT(ValueCanConvertToNumeric(type
, v
));
4324 case Scalar::Uint16
:
4326 case Scalar::Uint32
: {
4328 return writer
.guardToInt32ModUint32(valId
);
4330 if (v
.isNullOrUndefined()) {
4331 writer
.guardIsNullOrUndefined(valId
);
4332 return writer
.loadInt32Constant(0);
4334 if (v
.isBoolean()) {
4335 return writer
.guardBooleanToInt32(valId
);
4337 MOZ_ASSERT(v
.isString());
4338 StringOperandId strId
= writer
.guardToString(valId
);
4339 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4340 return writer
.truncateDoubleToUInt32(numId
);
4343 case Scalar::Float32
:
4344 case Scalar::Float64
: {
4346 return writer
.guardIsNumber(valId
);
4349 writer
.guardIsNull(valId
);
4350 return writer
.loadDoubleConstant(0.0);
4352 if (v
.isUndefined()) {
4353 writer
.guardIsUndefined(valId
);
4354 return writer
.loadDoubleConstant(JS::GenericNaN());
4356 if (v
.isBoolean()) {
4357 BooleanOperandId boolId
= writer
.guardToBoolean(valId
);
4358 return writer
.booleanToNumber(boolId
);
4360 MOZ_ASSERT(v
.isString());
4361 StringOperandId strId
= writer
.guardToString(valId
);
4362 return writer
.guardStringToNumber(strId
);
4365 case Scalar::Uint8Clamped
: {
4367 return writer
.guardToUint8Clamped(valId
);
4369 if (v
.isNullOrUndefined()) {
4370 writer
.guardIsNullOrUndefined(valId
);
4371 return writer
.loadInt32Constant(0);
4373 if (v
.isBoolean()) {
4374 return writer
.guardBooleanToInt32(valId
);
4376 MOZ_ASSERT(v
.isString());
4377 StringOperandId strId
= writer
.guardToString(valId
);
4378 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4379 return writer
.doubleToUint8Clamped(numId
);
4382 case Scalar::BigInt64
:
4383 case Scalar::BigUint64
:
4384 MOZ_ASSERT(v
.isBigInt());
4385 return writer
.guardToBigInt(valId
);
4387 case Scalar::MaxTypedArrayViewType
:
4389 case Scalar::Simd128
:
4392 MOZ_CRASH("Unsupported TypedArray type");
4395 void SetPropIRGenerator::trackAttached(const char* name
) {
4396 stubName_
= name
? name
: "NotAttached";
4397 #ifdef JS_CACHEIR_SPEW
4398 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4399 sp
.opcodeProperty("op", JSOp(*pc_
));
4400 sp
.valueProperty("base", lhsVal_
);
4401 sp
.valueProperty("property", idVal_
);
4402 sp
.valueProperty("value", rhsVal_
);
4407 static bool IsCacheableSetPropCallNative(NativeObject
* obj
,
4408 NativeObject
* holder
,
4409 PropertyInfo prop
) {
4410 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4412 if (!prop
.isAccessorProperty()) {
4416 JSObject
* setterObject
= holder
->getSetter(prop
);
4417 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4421 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4422 if (!setter
.isNativeWithoutJitEntry()) {
4426 if (setter
.isClassConstructor()) {
4433 static bool IsCacheableSetPropCallScripted(NativeObject
* obj
,
4434 NativeObject
* holder
,
4435 PropertyInfo prop
) {
4436 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4438 if (!prop
.isAccessorProperty()) {
4442 JSObject
* setterObject
= holder
->getSetter(prop
);
4443 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4447 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4448 if (setter
.isClassConstructor()) {
4452 // Scripted functions and natives with JIT entry can use the scripted path.
4453 return setter
.hasJitEntry();
4456 static bool CanAttachSetter(JSContext
* cx
, jsbytecode
* pc
, JSObject
* obj
,
4457 PropertyKey id
, NativeObject
** holder
,
4458 Maybe
<PropertyInfo
>* propInfo
) {
4459 // Don't attach a setter stub for ops like JSOp::InitElem.
4460 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
4462 PropertyResult prop
;
4463 if (!LookupPropertyPure(cx
, obj
, id
, holder
, &prop
)) {
4466 auto* nobj
= &obj
->as
<NativeObject
>();
4468 if (!prop
.isNativeProperty()) {
4472 if (!IsCacheableSetPropCallScripted(nobj
, *holder
, prop
.propertyInfo()) &&
4473 !IsCacheableSetPropCallNative(nobj
, *holder
, prop
.propertyInfo())) {
4477 *propInfo
= mozilla::Some(prop
.propertyInfo());
4481 static void EmitCallSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4482 NativeObject
* obj
, NativeObject
* holder
,
4483 PropertyInfo prop
, ObjOperandId receiverId
,
4484 ValOperandId rhsId
) {
4485 JSFunction
* target
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4486 bool sameRealm
= cx
->realm() == target
->realm();
4488 if (target
->isNativeWithoutJitEntry()) {
4489 MOZ_ASSERT(IsCacheableSetPropCallNative(obj
, holder
, prop
));
4490 writer
.callNativeSetter(receiverId
, target
, rhsId
, sameRealm
);
4491 writer
.returnFromIC();
4495 MOZ_ASSERT(IsCacheableSetPropCallScripted(obj
, holder
, prop
));
4496 writer
.callScriptedSetter(receiverId
, target
, rhsId
, sameRealm
);
4497 writer
.returnFromIC();
4500 static void EmitCallDOMSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4501 NativeObject
* holder
, PropertyInfo prop
,
4502 ObjOperandId objId
, ValOperandId rhsId
) {
4503 JSFunction
* setter
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4504 MOZ_ASSERT(cx
->realm() == setter
->realm());
4506 writer
.callDOMSetter(objId
, setter
->jitInfo(), rhsId
);
4507 writer
.returnFromIC();
4510 AttachDecision
SetPropIRGenerator::tryAttachSetter(HandleObject obj
,
4513 ValOperandId rhsId
) {
4514 // Don't attach a setter stub for ops like JSOp::InitElem.
4515 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4517 NativeObject
* holder
= nullptr;
4518 Maybe
<PropertyInfo
> prop
;
4519 if (!CanAttachSetter(cx_
, pc_
, obj
, id
, &holder
, &prop
)) {
4520 return AttachDecision::NoAction
;
4522 auto* nobj
= &obj
->as
<NativeObject
>();
4524 bool needsWindowProxy
=
4525 IsWindow(nobj
) && SetterNeedsWindowProxyThis(holder
, *prop
);
4527 maybeEmitIdGuard(id
);
4529 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
4530 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
4531 // require outerizing).
4532 if (mode_
== ICState::Mode::Specialized
|| IsWindow(nobj
)) {
4533 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4535 if (nobj
!= holder
) {
4536 GeneratePrototypeGuards(writer
, nobj
, holder
, objId
);
4538 // Guard on the holder's shape.
4539 ObjOperandId holderId
= writer
.loadObject(holder
);
4540 TestMatchingHolder(writer
, holder
, holderId
);
4542 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4543 /* holderIsConstant = */ true);
4545 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, objId
);
4548 GetterSetter
* gs
= holder
->getGetterSetter(*prop
);
4549 writer
.guardHasGetterSetter(objId
, id
, gs
);
4552 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Setter
, nobj
, holder
, *prop
,
4554 MOZ_ASSERT(!needsWindowProxy
);
4555 EmitCallDOMSetterNoGuards(cx_
, writer
, holder
, *prop
, objId
, rhsId
);
4557 trackAttached("SetProp.DOMSetter");
4558 return AttachDecision::Attach
;
4561 ObjOperandId receiverId
;
4562 if (needsWindowProxy
) {
4563 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
4564 receiverId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
4568 EmitCallSetterNoGuards(cx_
, writer
, nobj
, holder
, *prop
, receiverId
, rhsId
);
4570 trackAttached("SetProp.Setter");
4571 return AttachDecision::Attach
;
4574 AttachDecision
SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj
,
4577 ValOperandId rhsId
) {
4578 // Don't attach an array length stub for ops like JSOp::InitElem.
4579 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4581 if (!obj
->is
<ArrayObject
>() || !id
.isAtom(cx_
->names().length
) ||
4582 !obj
->as
<ArrayObject
>().lengthIsWritable()) {
4583 return AttachDecision::NoAction
;
4586 maybeEmitIdGuard(id
);
4587 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
4588 writer
.callSetArrayLength(objId
, IsStrictSetPC(pc_
), rhsId
);
4589 writer
.returnFromIC();
4591 trackAttached("SetProp.ArrayLength");
4592 return AttachDecision::Attach
;
4595 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElement(
4596 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4597 Int32OperandId indexId
, ValOperandId rhsId
) {
4598 if (!obj
->is
<NativeObject
>()) {
4599 return AttachDecision::NoAction
;
4602 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4603 if (!nobj
->containsDenseElement(index
) || nobj
->denseElementsAreFrozen()) {
4604 return AttachDecision::NoAction
;
4607 // Setting holes requires extra code for marking the elements non-packed.
4608 MOZ_ASSERT(!rhsVal_
.isMagic(JS_ELEMENTS_HOLE
));
4610 JSOp op
= JSOp(*pc_
);
4612 // We don't currently emit locked init for any indexed properties.
4613 MOZ_ASSERT(!IsLockedInitOp(op
));
4615 // We don't currently emit hidden init for any existing indexed properties.
4616 MOZ_ASSERT(!IsHiddenInitOp(op
));
4618 // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
4619 // the elements are sealed, we have to throw an exception. Note that we have
4620 // to check !isExtensible instead of denseElementsAreSealed because sealing
4621 // a (non-extensible) object does not necessarily trigger a Shape change.
4622 if (IsPropertyInitOp(op
) && !nobj
->isExtensible()) {
4623 return AttachDecision::NoAction
;
4626 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4628 writer
.storeDenseElement(objId
, indexId
, rhsId
);
4629 writer
.returnFromIC();
4631 trackAttached("SetProp.DenseElement");
4632 return AttachDecision::Attach
;
4635 static bool CanAttachAddElement(NativeObject
* obj
, bool isInit
,
4636 AllowIndexedReceiver allowIndexedReceiver
) {
4637 // Make sure the receiver doesn't have any indexed properties and that such
4638 // properties can't appear without a shape change.
4639 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
4644 // This check is also relevant for the receiver object.
4645 const JSClass
* clasp
= obj
->getClass();
4646 if (clasp
!= &ArrayObject::class_
&&
4647 (clasp
->getAddProperty() || clasp
->getResolve() ||
4648 clasp
->getOpsLookupProperty() || clasp
->getOpsSetProperty())) {
4652 // If we're initializing a property instead of setting one, the objects
4653 // on the prototype are not relevant.
4658 JSObject
* proto
= obj
->staticPrototype();
4663 if (!proto
->is
<NativeObject
>()) {
4667 NativeObject
* nproto
= &proto
->as
<NativeObject
>();
4668 if (nproto
->isIndexed()) {
4672 // We have to make sure the proto has no non-writable (frozen) elements
4673 // because we're not allowed to shadow them.
4674 if (nproto
->denseElementsAreFrozen() &&
4675 nproto
->getDenseInitializedLength() > 0) {
4685 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElementHole(
4686 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4687 Int32OperandId indexId
, ValOperandId rhsId
) {
4688 if (!obj
->is
<NativeObject
>()) {
4689 return AttachDecision::NoAction
;
4692 // Setting holes requires extra code for marking the elements non-packed.
4693 if (rhsVal_
.isMagic(JS_ELEMENTS_HOLE
)) {
4694 return AttachDecision::NoAction
;
4697 JSOp op
= JSOp(*pc_
);
4698 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4700 // We don't currently emit locked init for any indexed properties.
4701 MOZ_ASSERT(!IsLockedInitOp(op
));
4703 // Hidden init can be emitted for absent indexed properties.
4704 if (IsHiddenInitOp(op
)) {
4705 MOZ_ASSERT(op
== JSOp::InitHiddenElem
);
4706 return AttachDecision::NoAction
;
4709 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4710 if (!nobj
->isExtensible()) {
4711 return AttachDecision::NoAction
;
4714 MOZ_ASSERT(!nobj
->denseElementsAreFrozen(),
4715 "Extensible objects should not have frozen elements");
4717 uint32_t initLength
= nobj
->getDenseInitializedLength();
4719 // Optimize if we're adding an element at initLength or writing to a hole.
4721 // In the case where index > initLength, we need noteHasDenseAdd to be called
4722 // to ensure Ion is aware that writes have occurred to-out-of-bound indexes
4725 // TODO(post-Warp): noteHasDenseAdd (nee: noteArrayWriteHole) no longer exists
4726 bool isAdd
= index
== initLength
;
4727 bool isHoleInBounds
=
4728 index
< initLength
&& !nobj
->containsDenseElement(index
);
4729 if (!isAdd
&& !isHoleInBounds
) {
4730 return AttachDecision::NoAction
;
4733 // Can't add new elements to arrays with non-writable length.
4734 if (isAdd
&& nobj
->is
<ArrayObject
>() &&
4735 !nobj
->as
<ArrayObject
>().lengthIsWritable()) {
4736 return AttachDecision::NoAction
;
4739 // Typed arrays don't have dense elements.
4740 if (nobj
->is
<TypedArrayObject
>()) {
4741 return AttachDecision::NoAction
;
4744 // Check for other indexed properties or class hooks.
4745 if (!CanAttachAddElement(nobj
, IsPropertyInitOp(op
),
4746 AllowIndexedReceiver::No
)) {
4747 return AttachDecision::NoAction
;
4750 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4752 // Also shape guard the proto chain, unless this is an InitElem.
4753 if (IsPropertySetOp(op
)) {
4754 ShapeGuardProtoChain(writer
, nobj
, objId
);
4757 writer
.storeDenseElementHole(objId
, indexId
, rhsId
, isAdd
);
4758 writer
.returnFromIC();
4760 trackAttached(isAdd
? "AddDenseElement" : "StoreDenseElementHole");
4761 return AttachDecision::Attach
;
4764 // Add an IC for adding or updating a sparse element.
4765 AttachDecision
SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
4766 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4767 Int32OperandId indexId
, ValOperandId rhsId
) {
4768 JSOp op
= JSOp(*pc_
);
4769 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4771 if (op
!= JSOp::SetElem
&& op
!= JSOp::StrictSetElem
) {
4772 return AttachDecision::NoAction
;
4775 if (!obj
->is
<NativeObject
>()) {
4776 return AttachDecision::NoAction
;
4778 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4780 // We cannot attach a stub to a non-extensible object
4781 if (!nobj
->isExtensible()) {
4782 return AttachDecision::NoAction
;
4785 // Stub doesn't handle negative indices.
4786 if (index
> INT32_MAX
) {
4787 return AttachDecision::NoAction
;
4790 // The index must not be for a dense element.
4791 if (nobj
->containsDenseElement(index
)) {
4792 return AttachDecision::NoAction
;
4795 // Only handle ArrayObject and PlainObject in this stub.
4796 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
4797 return AttachDecision::NoAction
;
4800 // Don't attach if we're adding to an array with non-writable length.
4801 if (nobj
->is
<ArrayObject
>()) {
4802 ArrayObject
* aobj
= &nobj
->as
<ArrayObject
>();
4803 bool isAdd
= (index
>= aobj
->length());
4804 if (isAdd
&& !aobj
->lengthIsWritable()) {
4805 return AttachDecision::NoAction
;
4809 // Check for class hooks or indexed properties on the prototype chain that
4810 // we're not allowed to shadow.
4811 if (!CanAttachAddElement(nobj
, /* isInit = */ false,
4812 AllowIndexedReceiver::Yes
)) {
4813 return AttachDecision::NoAction
;
4816 // Ensure that obj is an ArrayObject or PlainObject.
4817 if (nobj
->is
<ArrayObject
>()) {
4818 writer
.guardClass(objId
, GuardClassKind::Array
);
4820 MOZ_ASSERT(nobj
->is
<PlainObject
>());
4821 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
4824 // The helper we are going to call only applies to non-dense elements.
4825 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
4827 // Guard extensible: We may be trying to add a new element, and so we'd best
4828 // be able to do so safely.
4829 writer
.guardIsExtensible(objId
);
4831 // Ensures we are able to efficiently able to map to an integral jsid.
4832 writer
.guardInt32IsNonNegative(indexId
);
4834 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
4835 // Guard the prototype of the receiver explicitly, because the receiver's
4836 // shape is not being guarded as a proxy for that.
4837 GuardReceiverProto(writer
, nobj
, objId
);
4839 // Dense elements may appear on the prototype chain (and prototypes may
4840 // have a different notion of which elements are dense), but they can
4841 // only be data properties, so our specialized Set handler is ok to bind
4843 if (IsPropertySetOp(op
)) {
4844 ShapeGuardProtoChain(writer
, nobj
, objId
);
4847 // Ensure that if we're adding an element to the object, the object's
4848 // length is writable.
4849 if (nobj
->is
<ArrayObject
>()) {
4850 writer
.guardIndexIsValidUpdateOrAdd(objId
, indexId
);
4853 writer
.callAddOrUpdateSparseElementHelper(
4854 objId
, indexId
, rhsId
,
4855 /* strict = */ op
== JSOp::StrictSetElem
);
4856 writer
.returnFromIC();
4858 trackAttached("SetProp.AddOrUpdateSparseElement");
4859 return AttachDecision::Attach
;
4862 AttachDecision
SetPropIRGenerator::tryAttachSetTypedArrayElement(
4863 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
4864 // TODO: Support resizable typed arrays. (bug 1842999)
4865 if (!obj
->is
<FixedLengthTypedArrayObject
>()) {
4866 return AttachDecision::NoAction
;
4868 if (!idVal_
.isNumber()) {
4869 return AttachDecision::NoAction
;
4872 auto* tarr
= &obj
->as
<FixedLengthTypedArrayObject
>();
4873 Scalar::Type elementType
= tarr
->type();
4875 // Don't attach if the input type doesn't match the guard added below.
4876 if (!ValueCanConvertToNumeric(elementType
, rhsVal_
)) {
4877 return AttachDecision::NoAction
;
4880 bool handleOOB
= false;
4882 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
4883 uint64_t(indexInt64
) >= tarr
->length()) {
4887 JSOp op
= JSOp(*pc_
);
4889 // The only expected property init operation is InitElem.
4890 MOZ_ASSERT_IF(IsPropertyInitOp(op
), op
== JSOp::InitElem
);
4892 // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
4893 if (handleOOB
&& IsPropertyInitOp(op
)) {
4894 return AttachDecision::NoAction
;
4897 writer
.guardShapeForClass(objId
, tarr
->shape());
4899 OperandId rhsValId
= emitNumericGuard(rhsId
, rhsVal_
, elementType
);
4901 ValOperandId keyId
= setElemKeyValueId();
4902 IntPtrOperandId indexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
4904 writer
.storeTypedArrayElement(objId
, elementType
, indexId
, rhsValId
,
4906 writer
.returnFromIC();
4908 trackAttached(handleOOB
? "SetTypedElementOOB" : "SetTypedElement");
4909 return AttachDecision::Attach
;
4912 AttachDecision
SetPropIRGenerator::tryAttachGenericProxy(
4913 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4914 ValOperandId rhsId
, bool handleDOMProxies
) {
4915 // Don't attach a proxy stub for ops like JSOp::InitElem.
4916 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4918 writer
.guardIsProxy(objId
);
4920 if (!handleDOMProxies
) {
4921 // Ensure that the incoming object is not a DOM proxy, so that we can
4922 // get to the specialized stubs. If handleDOMProxies is true, we were
4923 // unable to attach a specialized DOM stub, so we just handle all
4925 writer
.guardIsNotDOMProxy(objId
);
4928 if (cacheKind_
== CacheKind::SetProp
|| mode_
== ICState::Mode::Specialized
) {
4929 maybeEmitIdGuard(id
);
4930 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
4932 // Attach a stub that handles every id.
4933 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4934 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
4935 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
,
4936 IsStrictSetPC(pc_
));
4939 writer
.returnFromIC();
4941 trackAttached("SetProp.GenericProxy");
4942 return AttachDecision::Attach
;
4945 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyShadowed(
4946 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4947 ValOperandId rhsId
) {
4948 // Don't attach a proxy stub for ops like JSOp::InitElem.
4949 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4951 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
4953 maybeEmitIdGuard(id
);
4954 TestMatchingProxyReceiver(writer
, obj
, objId
);
4955 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
4956 writer
.returnFromIC();
4958 trackAttached("SetProp.DOMProxyShadowed");
4959 return AttachDecision::Attach
;
4962 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
4963 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
4964 ValOperandId rhsId
) {
4965 // Don't attach a proxy stub for ops like JSOp::InitElem.
4966 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4968 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
4970 JSObject
* proto
= obj
->staticPrototype();
4972 return AttachDecision::NoAction
;
4975 NativeObject
* holder
= nullptr;
4976 Maybe
<PropertyInfo
> prop
;
4977 if (!CanAttachSetter(cx_
, pc_
, proto
, id
, &holder
, &prop
)) {
4978 return AttachDecision::NoAction
;
4980 auto* nproto
= &proto
->as
<NativeObject
>();
4982 maybeEmitIdGuard(id
);
4984 // Guard that our proxy (expando) object hasn't started shadowing this
4986 TestMatchingProxyReceiver(writer
, obj
, objId
);
4987 bool canOptimizeMissing
= false;
4988 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
4990 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
4992 // Guard on the holder of the property.
4993 ObjOperandId holderId
= writer
.loadObject(holder
);
4994 TestMatchingHolder(writer
, holder
, holderId
);
4996 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4997 /* holderIsConstant = */ true);
4999 // EmitCallSetterNoGuards expects |obj| to be the object the property is
5000 // on to do some checks. Since we actually looked at proto, and no extra
5001 // guards will be generated, we can just pass that instead.
5002 EmitCallSetterNoGuards(cx_
, writer
, nproto
, holder
, *prop
, objId
, rhsId
);
5004 trackAttached("SetProp.DOMProxyUnshadowed");
5005 return AttachDecision::Attach
;
5008 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyExpando(
5009 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5010 ValOperandId rhsId
) {
5011 // Don't attach a proxy stub for ops like JSOp::InitElem.
5012 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5014 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5016 Value expandoVal
= GetProxyPrivate(obj
);
5017 JSObject
* expandoObj
;
5018 if (expandoVal
.isObject()) {
5019 expandoObj
= &expandoVal
.toObject();
5021 MOZ_ASSERT(!expandoVal
.isUndefined(),
5022 "How did a missing expando manage to shadow things?");
5023 auto expandoAndGeneration
=
5024 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
5025 MOZ_ASSERT(expandoAndGeneration
);
5026 expandoObj
= &expandoAndGeneration
->expando
.toObject();
5029 Maybe
<PropertyInfo
> prop
;
5030 if (CanAttachNativeSetSlot(JSOp(*pc_
), expandoObj
, id
, &prop
)) {
5031 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5033 maybeEmitIdGuard(id
);
5034 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5035 obj
, objId
, expandoVal
, nativeExpandoObj
);
5037 EmitStoreSlotAndReturn(writer
, expandoObjId
, nativeExpandoObj
, *prop
,
5039 trackAttached("SetProp.DOMProxyExpandoSlot");
5040 return AttachDecision::Attach
;
5043 NativeObject
* holder
= nullptr;
5044 if (CanAttachSetter(cx_
, pc_
, expandoObj
, id
, &holder
, &prop
)) {
5045 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5047 // Call the setter. Note that we pass objId, the DOM proxy, as |this|
5048 // and not the expando object.
5049 maybeEmitIdGuard(id
);
5050 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5051 obj
, objId
, expandoVal
, nativeExpandoObj
);
5053 MOZ_ASSERT(holder
== nativeExpandoObj
);
5054 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
5055 EmitCallSetterNoGuards(cx_
, writer
, nativeExpandoObj
, nativeExpandoObj
,
5056 *prop
, objId
, rhsId
);
5057 trackAttached("SetProp.DOMProxyExpandoSetter");
5058 return AttachDecision::Attach
;
5061 return AttachDecision::NoAction
;
5064 AttachDecision
SetPropIRGenerator::tryAttachProxy(HandleObject obj
,
5067 ValOperandId rhsId
) {
5068 // Don't attach a proxy stub for ops like JSOp::InitElem.
5069 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5071 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
5072 if (type
== ProxyStubType::None
) {
5073 return AttachDecision::NoAction
;
5075 auto proxy
= obj
.as
<ProxyObject
>();
5077 if (mode_
== ICState::Mode::Megamorphic
) {
5078 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5079 /* handleDOMProxies = */ true);
5083 case ProxyStubType::None
:
5085 case ProxyStubType::DOMExpando
:
5086 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, rhsId
));
5087 [[fallthrough
]]; // Fall through to the generic shadowed case.
5088 case ProxyStubType::DOMShadowed
:
5089 return tryAttachDOMProxyShadowed(proxy
, objId
, id
, rhsId
);
5090 case ProxyStubType::DOMUnshadowed
:
5091 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, rhsId
));
5092 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5093 /* handleDOMProxies = */ true);
5094 case ProxyStubType::Generic
:
5095 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5096 /* handleDOMProxies = */ false);
5099 MOZ_CRASH("Unexpected ProxyStubType");
5102 AttachDecision
SetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
5104 ValOperandId rhsId
) {
5105 // Don't attach a proxy stub for ops like JSOp::InitElem.
5106 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5108 if (!obj
->is
<ProxyObject
>()) {
5109 return AttachDecision::NoAction
;
5112 writer
.guardIsProxy(objId
);
5114 // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
5115 // proxies here as we don't have specialized DOM stubs for this.
5116 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5117 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
, IsStrictSetPC(pc_
));
5118 writer
.returnFromIC();
5120 trackAttached("SetProp.ProxyElement");
5121 return AttachDecision::Attach
;
5124 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetElement(
5125 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5126 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5128 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetElem
) {
5129 return AttachDecision::NoAction
;
5132 // The generic proxy stubs are faster.
5133 if (obj
->is
<ProxyObject
>()) {
5134 return AttachDecision::NoAction
;
5137 writer
.megamorphicSetElement(objId
, setElemKeyValueId(), rhsId
,
5138 IsStrictSetPC(pc_
));
5139 writer
.returnFromIC();
5141 trackAttached("SetProp.MegamorphicSetElement");
5142 return AttachDecision::Attach
;
5145 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetSlot(
5146 HandleObject obj
, ObjOperandId objId
, HandleId id
, ValOperandId rhsId
) {
5147 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetProp
) {
5148 return AttachDecision::NoAction
;
5151 writer
.megamorphicStoreSlot(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5152 writer
.returnFromIC();
5153 trackAttached("SetProp.MegamorphicNativeSlot");
5154 return AttachDecision::Attach
;
5157 AttachDecision
SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
5160 ValOperandId rhsId
) {
5161 // Don't attach a window proxy stub for ops like JSOp::InitElem.
5162 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5164 // Attach a stub when the receiver is a WindowProxy and we can do the set
5165 // on the Window (the global object).
5167 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
5168 return AttachDecision::NoAction
;
5171 // If we're megamorphic prefer a generic proxy stub that handles a lot more
5173 if (mode_
== ICState::Mode::Megamorphic
) {
5174 return AttachDecision::NoAction
;
5177 // Now try to do the set on the Window (the current global).
5178 GlobalObject
* windowObj
= cx_
->global();
5180 Maybe
<PropertyInfo
> prop
;
5181 if (!CanAttachNativeSetSlot(JSOp(*pc_
), windowObj
, id
, &prop
)) {
5182 return AttachDecision::NoAction
;
5185 maybeEmitIdGuard(id
);
5187 ObjOperandId windowObjId
=
5188 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
5189 writer
.guardShape(windowObjId
, windowObj
->shape());
5191 EmitStoreSlotAndReturn(writer
, windowObjId
, windowObj
, *prop
, rhsId
);
5193 trackAttached("SetProp.WindowProxySlot");
5194 return AttachDecision::Attach
;
5197 // Detect if |id| refers to the 'prototype' property of a function object. This
5198 // property is special-cased in canAttachAddSlotStub().
5199 static bool IsFunctionPrototype(const JSAtomState
& names
, JSObject
* obj
,
5201 return obj
->is
<JSFunction
>() && id
.isAtom(names
.prototype
);
5204 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj
, HandleId id
) {
5205 if (!obj
->is
<NativeObject
>()) {
5208 auto* nobj
= &obj
->as
<NativeObject
>();
5210 // Special-case JSFunction resolve hook to allow redefining the 'prototype'
5211 // property without triggering lazy expansion of property and object
5213 if (IsFunctionPrototype(cx_
->names(), nobj
, id
)) {
5214 MOZ_ASSERT(ClassMayResolveId(cx_
->names(), nobj
->getClass(), id
, nobj
));
5216 // We're only interested in functions that have a builtin .prototype
5217 // property (needsPrototypeProperty). The stub will guard on this because
5218 // the builtin .prototype property is non-configurable/non-enumerable and it
5219 // would be wrong to add a property with those attributes to a function that
5220 // doesn't have a builtin .prototype.
5222 // Inlining needsPrototypeProperty in JIT code is complicated so we use
5223 // isNonBuiltinConstructor as a stronger condition that's easier to check
5225 JSFunction
* fun
= &nobj
->as
<JSFunction
>();
5226 if (!fun
->isNonBuiltinConstructor()) {
5229 MOZ_ASSERT(fun
->needsPrototypeProperty());
5231 // If property exists this isn't an "add".
5232 if (fun
->lookupPure(id
)) {
5236 // Normal Case: If property exists this isn't an "add"
5237 PropertyResult prop
;
5238 if (!LookupOwnPropertyPure(cx_
, nobj
, id
, &prop
)) {
5241 if (prop
.isFound()) {
5246 // For now we don't optimize Watchtower-monitored objects.
5247 if (Watchtower::watchesPropertyAdd(nobj
)) {
5251 // Object must be extensible, or we must be initializing a private
5253 bool canAddNewProperty
= nobj
->isExtensible() || id
.isPrivateName();
5254 if (!canAddNewProperty
) {
5258 JSOp op
= JSOp(*pc_
);
5259 if (IsPropertyInitOp(op
)) {
5263 MOZ_ASSERT(IsPropertySetOp(op
));
5265 // Walk up the object prototype chain and ensure that all prototypes are
5266 // native, and that all prototypes have no setter defined on the property.
5267 for (JSObject
* proto
= nobj
->staticPrototype(); proto
;
5268 proto
= proto
->staticPrototype()) {
5269 if (!proto
->is
<NativeObject
>()) {
5273 // If prototype defines this property in a non-plain way, don't optimize.
5274 Maybe
<PropertyInfo
> protoProp
= proto
->as
<NativeObject
>().lookup(cx_
, id
);
5275 if (protoProp
.isSome() && !protoProp
->isDataProperty()) {
5279 // Otherwise, if there's no such property, watch out for a resolve hook
5280 // that would need to be invoked and thus prevent inlining of property
5281 // addition. Allow the JSFunction resolve hook as it only defines plain
5282 // data properties and we don't need to invoke it for objects on the
5284 if (ClassMayResolveId(cx_
->names(), proto
->getClass(), id
, proto
) &&
5285 !proto
->is
<JSFunction
>()) {
5293 static PropertyFlags
SetPropertyFlags(JSOp op
, bool isFunctionPrototype
) {
5294 // Locked properties are non-writable, non-enumerable, and non-configurable.
5295 if (IsLockedInitOp(op
)) {
5299 // Hidden properties are writable, non-enumerable, and configurable.
5300 if (IsHiddenInitOp(op
)) {
5302 PropertyFlag::Writable
,
5303 PropertyFlag::Configurable
,
5307 // This is a special case to overwrite an unresolved function.prototype
5308 // property. The initial property flags of this property are writable,
5309 // non-enumerable, and non-configurable. See canAttachAddSlotStub.
5310 if (isFunctionPrototype
) {
5312 PropertyFlag::Writable
,
5316 // Other properties are writable, enumerable, and configurable.
5317 return PropertyFlags::defaultDataPropFlags
;
5320 AttachDecision
SetPropIRGenerator::tryAttachAddSlotStub(
5321 Handle
<Shape
*> oldShape
) {
5322 ValOperandId
objValId(writer
.setInputOperandId(0));
5323 ValOperandId rhsValId
;
5324 if (cacheKind_
== CacheKind::SetProp
) {
5325 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
5327 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5328 MOZ_ASSERT(setElemKeyValueId().id() == 1);
5329 writer
.setInputOperandId(1);
5330 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
5335 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
5336 cx_
->clearPendingException();
5337 return AttachDecision::NoAction
;
5340 if (!lhsVal_
.isObject() || !nameOrSymbol
) {
5341 return AttachDecision::NoAction
;
5344 JSObject
* obj
= &lhsVal_
.toObject();
5346 PropertyResult prop
;
5347 if (!LookupOwnPropertyPure(cx_
, obj
, id
, &prop
)) {
5348 return AttachDecision::NoAction
;
5350 if (prop
.isNotFound()) {
5351 return AttachDecision::NoAction
;
5354 if (!obj
->is
<NativeObject
>()) {
5355 return AttachDecision::NoAction
;
5357 auto* nobj
= &obj
->as
<NativeObject
>();
5359 PropertyInfo propInfo
= prop
.propertyInfo();
5360 NativeObject
* holder
= nobj
;
5362 if (holder
->inDictionaryMode()) {
5363 return AttachDecision::NoAction
;
5366 SharedShape
* oldSharedShape
= &oldShape
->asShared();
5368 // The property must be the last added property of the object.
5369 SharedShape
* newShape
= holder
->sharedShape();
5370 MOZ_RELEASE_ASSERT(newShape
->lastProperty() == propInfo
);
5373 // Verify exactly one property was added by comparing the property map
5375 if (oldSharedShape
->propMapLength() == PropMap::Capacity
) {
5376 MOZ_ASSERT(newShape
->propMapLength() == 1);
5378 MOZ_ASSERT(newShape
->propMapLength() ==
5379 oldSharedShape
->propMapLength() + 1);
5383 bool isFunctionPrototype
= IsFunctionPrototype(cx_
->names(), nobj
, id
);
5385 JSOp op
= JSOp(*pc_
);
5386 PropertyFlags flags
= SetPropertyFlags(op
, isFunctionPrototype
);
5388 // Basic property checks.
5389 if (!propInfo
.isDataProperty() || propInfo
.flags() != flags
) {
5390 return AttachDecision::NoAction
;
5393 ObjOperandId objId
= writer
.guardToObject(objValId
);
5394 maybeEmitIdGuard(id
);
5396 // Shape guard the object.
5397 writer
.guardShape(objId
, oldShape
);
5399 // If this is the special function.prototype case, we need to guard the
5400 // function is a non-builtin constructor. See canAttachAddSlotStub.
5401 if (isFunctionPrototype
) {
5402 MOZ_ASSERT(nobj
->as
<JSFunction
>().isNonBuiltinConstructor());
5403 writer
.guardFunctionIsNonBuiltinCtor(objId
);
5406 // Also shape guard the proto chain, unless this is an InitElem.
5407 if (IsPropertySetOp(op
)) {
5408 ShapeGuardProtoChain(writer
, nobj
, objId
);
5411 // If the JSClass has an addProperty hook, we need to call a VM function to
5412 // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
5413 // anything for non-index properties.
5414 DebugOnly
<uint32_t> index
;
5415 MOZ_ASSERT_IF(obj
->is
<ArrayObject
>(), !IdIsIndex(id
, &index
));
5416 bool mustCallAddPropertyHook
=
5417 obj
->getClass()->getAddProperty() && !obj
->is
<ArrayObject
>();
5419 if (mustCallAddPropertyHook
) {
5420 writer
.addSlotAndCallAddPropHook(objId
, rhsValId
, newShape
);
5421 trackAttached("SetProp.AddSlotWithAddPropertyHook");
5422 } else if (holder
->isFixedSlot(propInfo
.slot())) {
5423 size_t offset
= NativeObject::getFixedSlotOffset(propInfo
.slot());
5424 writer
.addAndStoreFixedSlot(objId
, offset
, rhsValId
, newShape
);
5425 trackAttached("SetProp.AddSlotFixed");
5427 size_t offset
= holder
->dynamicSlotIndex(propInfo
.slot()) * sizeof(Value
);
5428 uint32_t numOldSlots
= NativeObject::calculateDynamicSlots(oldSharedShape
);
5429 uint32_t numNewSlots
= holder
->numDynamicSlots();
5430 if (numOldSlots
== numNewSlots
) {
5431 writer
.addAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
);
5432 trackAttached("SetProp.AddSlotDynamic");
5434 MOZ_ASSERT(numNewSlots
> numOldSlots
);
5435 writer
.allocateAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
,
5437 trackAttached("SetProp.AllocateSlot");
5440 writer
.returnFromIC();
5442 return AttachDecision::Attach
;
5445 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext
* cx
, HandleScript script
,
5446 jsbytecode
* pc
, ICState state
,
5447 HandleValue lhs
, HandleObject rhs
)
5448 : IRGenerator(cx
, script
, pc
, CacheKind::InstanceOf
, state
),
5452 AttachDecision
InstanceOfIRGenerator::tryAttachStub() {
5453 MOZ_ASSERT(cacheKind_
== CacheKind::InstanceOf
);
5454 AutoAssertNoPendingException
aanpe(cx_
);
5456 // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
5458 if (!rhsObj_
->is
<JSFunction
>()) {
5459 trackAttached(IRGenerator::NotAttached
);
5460 return AttachDecision::NoAction
;
5463 HandleFunction fun
= rhsObj_
.as
<JSFunction
>();
5465 // Look up the @@hasInstance property, and check that Function.__proto__ is
5466 // the property holder, and that no object further down the prototype chain
5467 // (including this function) has shadowed it; together with the fact that
5468 // Function.__proto__[@@hasInstance] is immutable, this ensures that the
5469 // hasInstance hook will not change without the need to guard on the actual
5471 PropertyResult hasInstanceProp
;
5472 NativeObject
* hasInstanceHolder
= nullptr;
5473 jsid hasInstanceID
= PropertyKey::Symbol(cx_
->wellKnownSymbols().hasInstance
);
5474 if (!LookupPropertyPure(cx_
, fun
, hasInstanceID
, &hasInstanceHolder
,
5475 &hasInstanceProp
) ||
5476 !hasInstanceProp
.isNativeProperty()) {
5477 trackAttached(IRGenerator::NotAttached
);
5478 return AttachDecision::NoAction
;
5481 JSObject
& funProto
= cx_
->global()->getPrototype(JSProto_Function
);
5482 if (hasInstanceHolder
!= &funProto
) {
5483 trackAttached(IRGenerator::NotAttached
);
5484 return AttachDecision::NoAction
;
5487 // If the above succeeded, then these should be true about @@hasInstance,
5488 // because the property on Function.__proto__ is an immutable data property:
5489 MOZ_ASSERT(hasInstanceProp
.propertyInfo().isDataProperty());
5490 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().configurable());
5491 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().writable());
5493 MOZ_ASSERT(IsCacheableProtoChain(fun
, hasInstanceHolder
));
5495 // Ensure that the function's prototype slot is the same.
5496 Maybe
<PropertyInfo
> prop
= fun
->lookupPure(cx_
->names().prototype
);
5497 if (prop
.isNothing() || !prop
->isDataProperty()) {
5498 trackAttached(IRGenerator::NotAttached
);
5499 return AttachDecision::NoAction
;
5502 uint32_t slot
= prop
->slot();
5503 MOZ_ASSERT(slot
>= fun
->numFixedSlots(), "Stub code relies on this");
5504 if (!fun
->getSlot(slot
).isObject()) {
5505 trackAttached(IRGenerator::NotAttached
);
5506 return AttachDecision::NoAction
;
5510 ValOperandId
lhs(writer
.setInputOperandId(0));
5511 ValOperandId
rhs(writer
.setInputOperandId(1));
5513 ObjOperandId rhsId
= writer
.guardToObject(rhs
);
5514 writer
.guardShape(rhsId
, fun
->shape());
5516 // Ensure that the shapes up the prototype chain for the RHS remain the same
5517 // so that @@hasInstance is not shadowed by some intermediate prototype
5519 if (hasInstanceHolder
!= fun
) {
5520 GeneratePrototypeGuards(writer
, fun
, hasInstanceHolder
, rhsId
);
5521 ObjOperandId holderId
= writer
.loadObject(hasInstanceHolder
);
5522 TestMatchingHolder(writer
, hasInstanceHolder
, holderId
);
5525 // Load the .prototype value and ensure it's an object.
5526 ValOperandId protoValId
=
5527 writer
.loadDynamicSlot(rhsId
, slot
- fun
->numFixedSlots());
5528 ObjOperandId protoId
= writer
.guardToObject(protoValId
);
5530 // Needn't guard LHS is object, because the actual stub can handle that
5531 // and correctly return false.
5532 writer
.loadInstanceOfObjectResult(lhs
, protoId
);
5533 writer
.returnFromIC();
5534 trackAttached("InstanceOf");
5535 return AttachDecision::Attach
;
5538 void InstanceOfIRGenerator::trackAttached(const char* name
) {
5539 stubName_
= name
? name
: "NotAttached";
5540 #ifdef JS_CACHEIR_SPEW
5541 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5542 sp
.valueProperty("lhs", lhsVal_
);
5543 sp
.valueProperty("rhs", ObjectValue(*rhsObj_
));
5546 // Silence Clang -Wunused-private-field warning.
5551 TypeOfIRGenerator::TypeOfIRGenerator(JSContext
* cx
, HandleScript script
,
5552 jsbytecode
* pc
, ICState state
,
5554 : IRGenerator(cx
, script
, pc
, CacheKind::TypeOf
, state
), val_(value
) {}
5556 void TypeOfIRGenerator::trackAttached(const char* name
) {
5557 stubName_
= name
? name
: "NotAttached";
5558 #ifdef JS_CACHEIR_SPEW
5559 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5560 sp
.valueProperty("val", val_
);
5565 AttachDecision
TypeOfIRGenerator::tryAttachStub() {
5566 MOZ_ASSERT(cacheKind_
== CacheKind::TypeOf
);
5568 AutoAssertNoPendingException
aanpe(cx_
);
5570 ValOperandId
valId(writer
.setInputOperandId(0));
5572 TRY_ATTACH(tryAttachPrimitive(valId
));
5573 TRY_ATTACH(tryAttachObject(valId
));
5575 MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
5576 return AttachDecision::NoAction
;
5579 AttachDecision
TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId
) {
5580 if (!val_
.isPrimitive()) {
5581 return AttachDecision::NoAction
;
5584 // Note: we don't use GuardIsNumber for int32 values because it's less
5585 // efficient in Warp (unboxing to double instead of int32).
5586 if (val_
.isDouble()) {
5587 writer
.guardIsNumber(valId
);
5589 writer
.guardNonDoubleType(valId
, val_
.type());
5592 writer
.loadConstantStringResult(
5593 TypeName(js::TypeOfValue(val_
), cx_
->names()));
5594 writer
.returnFromIC();
5595 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5596 trackAttached("TypeOf.Primitive");
5597 return AttachDecision::Attach
;
5600 AttachDecision
TypeOfIRGenerator::tryAttachObject(ValOperandId valId
) {
5601 if (!val_
.isObject()) {
5602 return AttachDecision::NoAction
;
5605 ObjOperandId objId
= writer
.guardToObject(valId
);
5606 writer
.loadTypeOfObjectResult(objId
);
5607 writer
.returnFromIC();
5608 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5609 trackAttached("TypeOf.Object");
5610 return AttachDecision::Attach
;
5613 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext
* cx
,
5614 HandleScript script
,
5615 jsbytecode
* pc
, ICState state
,
5617 : IRGenerator(cx
, script
, pc
, CacheKind::GetIterator
, state
), val_(value
) {}
5619 AttachDecision
GetIteratorIRGenerator::tryAttachStub() {
5620 MOZ_ASSERT(cacheKind_
== CacheKind::GetIterator
);
5622 AutoAssertNoPendingException
aanpe(cx_
);
5624 ValOperandId
valId(writer
.setInputOperandId(0));
5626 TRY_ATTACH(tryAttachObject(valId
));
5627 TRY_ATTACH(tryAttachNullOrUndefined(valId
));
5628 TRY_ATTACH(tryAttachGeneric(valId
));
5630 trackAttached(IRGenerator::NotAttached
);
5631 return AttachDecision::NoAction
;
5634 AttachDecision
GetIteratorIRGenerator::tryAttachObject(ValOperandId valId
) {
5635 if (!val_
.isObject()) {
5636 return AttachDecision::NoAction
;
5639 MOZ_ASSERT(val_
.toObject().compartment() == cx_
->compartment());
5641 ObjOperandId objId
= writer
.guardToObject(valId
);
5642 writer
.objectToIteratorResult(objId
, cx_
->compartment()->enumeratorsAddr());
5643 writer
.returnFromIC();
5645 trackAttached("GetIterator.Object");
5646 return AttachDecision::Attach
;
5649 AttachDecision
GetIteratorIRGenerator::tryAttachNullOrUndefined(
5650 ValOperandId valId
) {
5651 MOZ_ASSERT(JSOp(*pc_
) == JSOp::Iter
);
5653 // For null/undefined we can simply return the empty iterator singleton. This
5654 // works because this iterator is unlinked and immutable.
5656 if (!val_
.isNullOrUndefined()) {
5657 return AttachDecision::NoAction
;
5660 PropertyIteratorObject
* emptyIter
=
5661 GlobalObject::getOrCreateEmptyIterator(cx_
);
5663 cx_
->recoverFromOutOfMemory();
5664 return AttachDecision::NoAction
;
5667 writer
.guardIsNullOrUndefined(valId
);
5669 ObjOperandId iterId
= writer
.loadObject(emptyIter
);
5670 writer
.loadObjectResult(iterId
);
5671 writer
.returnFromIC();
5673 trackAttached("GetIterator.NullOrUndefined");
5674 return AttachDecision::Attach
;
5677 AttachDecision
GetIteratorIRGenerator::tryAttachGeneric(ValOperandId valId
) {
5678 writer
.valueToIteratorResult(valId
);
5679 writer
.returnFromIC();
5681 trackAttached("GetIterator.Generic");
5682 return AttachDecision::Attach
;
5685 void GetIteratorIRGenerator::trackAttached(const char* name
) {
5686 stubName_
= name
? name
: "NotAttached";
5687 #ifdef JS_CACHEIR_SPEW
5688 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5689 sp
.valueProperty("val", val_
);
5694 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
5695 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
5697 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeSpreadCall
, state
),
5700 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachStub() {
5701 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeSpreadCall
);
5703 AutoAssertNoPendingException
aanpe(cx_
);
5705 TRY_ATTACH(tryAttachArray());
5706 TRY_ATTACH(tryAttachArguments());
5707 TRY_ATTACH(tryAttachNotOptimizable());
5709 trackAttached(IRGenerator::NotAttached
);
5710 return AttachDecision::NoAction
;
5713 static bool IsArrayInstanceOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5714 MutableHandle
<NativeObject
*> arrProto
) {
5715 // Prototype must be Array.prototype.
5716 auto* proto
= cx
->global()->maybeGetArrayPrototype();
5717 if (!proto
|| arr
->staticPrototype() != proto
) {
5720 arrProto
.set(proto
);
5722 // The object must not have an own @@iterator property.
5723 PropertyKey iteratorKey
=
5724 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5725 return !arr
->lookupPure(iteratorKey
);
5728 static bool IsArrayPrototypeOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5729 Handle
<NativeObject
*> arrProto
,
5731 MutableHandle
<JSFunction
*> iterFun
) {
5732 PropertyKey iteratorKey
=
5733 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5734 // Ensure that Array.prototype's @@iterator slot is unchanged.
5735 Maybe
<PropertyInfo
> prop
= arrProto
->lookupPure(iteratorKey
);
5736 if (prop
.isNothing() || !prop
->isDataProperty()) {
5740 *slot
= prop
->slot();
5741 MOZ_ASSERT(arrProto
->numFixedSlots() == 0, "Stub code relies on this");
5743 const Value
& iterVal
= arrProto
->getSlot(*slot
);
5744 if (!iterVal
.isObject() || !iterVal
.toObject().is
<JSFunction
>()) {
5748 iterFun
.set(&iterVal
.toObject().as
<JSFunction
>());
5749 return IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
);
5752 enum class AllowIteratorReturn
: bool {
5756 static bool IsArrayIteratorPrototypeOptimizable(
5757 JSContext
* cx
, AllowIteratorReturn allowReturn
,
5758 MutableHandle
<NativeObject
*> arrIterProto
, uint32_t* slot
,
5759 MutableHandle
<JSFunction
*> nextFun
) {
5760 NativeObject
* proto
= nullptr;
5762 AutoEnterOOMUnsafeRegion oom
;
5763 proto
= GlobalObject::getOrCreateArrayIteratorPrototype(cx
, cx
->global());
5765 oom
.crash("failed to allocate Array iterator prototype");
5768 arrIterProto
.set(proto
);
5770 // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
5771 Maybe
<PropertyInfo
> prop
= proto
->lookupPure(cx
->names().next
);
5772 if (prop
.isNothing() || !prop
->isDataProperty()) {
5776 *slot
= prop
->slot();
5777 MOZ_ASSERT(proto
->numFixedSlots() == 0, "Stub code relies on this");
5779 const Value
& nextVal
= proto
->getSlot(*slot
);
5780 if (!nextVal
.isObject() || !nextVal
.toObject().is
<JSFunction
>()) {
5784 nextFun
.set(&nextVal
.toObject().as
<JSFunction
>());
5785 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
5789 if (allowReturn
== AllowIteratorReturn::No
) {
5790 // Ensure that %ArrayIteratorPrototype% doesn't define "return".
5791 if (!CheckHasNoSuchProperty(cx
, proto
, NameToId(cx
->names().return_
))) {
5799 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArray() {
5800 if (!isFirstStub_
) {
5801 return AttachDecision::NoAction
;
5804 // The value must be a packed array.
5805 if (!val_
.isObject()) {
5806 return AttachDecision::NoAction
;
5808 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
5809 if (!IsPackedArray(obj
)) {
5810 return AttachDecision::NoAction
;
5813 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
5815 Rooted
<NativeObject
*> arrProto(cx_
);
5816 uint32_t arrProtoIterSlot
;
5817 Rooted
<JSFunction
*> iterFun(cx_
);
5818 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
5819 return AttachDecision::NoAction
;
5822 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
5823 &arrProtoIterSlot
, &iterFun
)) {
5824 return AttachDecision::NoAction
;
5827 // %ArrayIteratorPrototype%.next must not be modified.
5828 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
5829 uint32_t iterNextSlot
;
5830 Rooted
<JSFunction
*> nextFun(cx_
);
5831 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
5832 &arrayIteratorProto
, &iterNextSlot
,
5834 return AttachDecision::NoAction
;
5837 ValOperandId
valId(writer
.setInputOperandId(0));
5838 ObjOperandId objId
= writer
.guardToObject(valId
);
5840 // Guard the object is a packed array with Array.prototype as proto.
5841 MOZ_ASSERT(obj
->is
<ArrayObject
>());
5842 writer
.guardShape(objId
, obj
->shape());
5843 writer
.guardArrayIsPacked(objId
);
5845 // Guard on Array.prototype[@@iterator].
5846 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
5847 ObjOperandId iterId
= writer
.loadObject(iterFun
);
5848 writer
.guardShape(arrProtoId
, arrProto
->shape());
5849 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
, arrProtoIterSlot
);
5851 // Guard on %ArrayIteratorPrototype%.next.
5852 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
5853 ObjOperandId nextId
= writer
.loadObject(nextFun
);
5854 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
5855 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, iterNextSlot
);
5857 writer
.loadObjectResult(objId
);
5858 writer
.returnFromIC();
5860 trackAttached("OptimizeSpreadCall.Array");
5861 return AttachDecision::Attach
;
5864 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArguments() {
5865 // The value must be an arguments object.
5866 if (!val_
.isObject()) {
5867 return AttachDecision::NoAction
;
5869 RootedObject
obj(cx_
, &val_
.toObject());
5870 if (!obj
->is
<ArgumentsObject
>()) {
5871 return AttachDecision::NoAction
;
5873 auto args
= obj
.as
<ArgumentsObject
>();
5875 // Ensure neither elements, nor the length, nor the iterator has been
5876 // overridden. Also ensure no args are forwarded to allow reading them
5877 // directly from the frame.
5878 if (args
->hasOverriddenElement() || args
->hasOverriddenLength() ||
5879 args
->hasOverriddenIterator() || args
->anyArgIsForwarded()) {
5880 return AttachDecision::NoAction
;
5883 Rooted
<Shape
*> shape(cx_
, GlobalObject::getArrayShapeWithDefaultProto(cx_
));
5885 cx_
->clearPendingException();
5886 return AttachDecision::NoAction
;
5889 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
5891 Rooted
<JSFunction
*> nextFun(cx_
);
5892 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
5893 &arrayIteratorProto
, &slot
,
5895 return AttachDecision::NoAction
;
5898 ValOperandId
valId(writer
.setInputOperandId(0));
5899 ObjOperandId objId
= writer
.guardToObject(valId
);
5901 if (args
->is
<MappedArgumentsObject
>()) {
5902 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
5904 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
5905 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
5907 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
5908 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
5909 ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
|
5910 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
5911 writer
.guardArgumentsObjectFlags(objId
, flags
);
5913 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
5914 ObjOperandId nextId
= writer
.loadObject(nextFun
);
5916 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
5918 // Ensure that proto[slot] == nextFun.
5919 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
5921 writer
.arrayFromArgumentsObjectResult(objId
, shape
);
5922 writer
.returnFromIC();
5924 trackAttached("OptimizeSpreadCall.Arguments");
5925 return AttachDecision::Attach
;
5928 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
5929 ValOperandId
valId(writer
.setInputOperandId(0));
5931 writer
.loadUndefinedResult();
5932 writer
.returnFromIC();
5934 trackAttached("OptimizeSpreadCall.NotOptimizable");
5935 return AttachDecision::Attach
;
5938 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name
) {
5939 stubName_
= name
? name
: "NotAttached";
5940 #ifdef JS_CACHEIR_SPEW
5941 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5942 sp
.valueProperty("val", val_
);
5947 CallIRGenerator::CallIRGenerator(JSContext
* cx
, HandleScript script
,
5948 jsbytecode
* pc
, JSOp op
, ICState state
,
5949 uint32_t argc
, HandleValue callee
,
5950 HandleValue thisval
, HandleValue newTarget
,
5951 HandleValueArray args
)
5952 : IRGenerator(cx
, script
, pc
, CacheKind::Call
, state
),
5957 newTarget_(newTarget
),
5960 void InlinableNativeIRGenerator::emitNativeCalleeGuard() {
5961 // Note: we rely on GuardSpecificFunction to also guard against the same
5962 // native from a different realm.
5963 MOZ_ASSERT(callee_
->isNativeWithoutJitEntry());
5965 ObjOperandId calleeObjId
;
5966 if (flags_
.getArgFormat() == CallFlags::Standard
) {
5967 ValOperandId calleeValId
=
5968 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
5969 calleeObjId
= writer
.guardToObject(calleeValId
);
5970 } else if (flags_
.getArgFormat() == CallFlags::Spread
) {
5971 ValOperandId calleeValId
=
5972 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
5973 calleeObjId
= writer
.guardToObject(calleeValId
);
5974 } else if (flags_
.getArgFormat() == CallFlags::FunCall
) {
5975 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
5977 Int32OperandId
argcId(0);
5978 calleeObjId
= generator_
.emitFunCallOrApplyGuard(argcId
);
5980 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
5981 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
5983 Int32OperandId
argcId(0);
5984 calleeObjId
= generator_
.emitFunApplyGuard(argcId
);
5987 writer
.guardSpecificFunction(calleeObjId
, callee_
);
5989 // If we're constructing we also need to guard newTarget == callee.
5990 if (flags_
.isConstructing()) {
5991 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
5992 MOZ_ASSERT(&newTarget_
.toObject() == callee_
);
5994 ValOperandId newTargetValId
=
5995 writer
.loadArgumentFixedSlot(ArgumentKind::NewTarget
, argc_
, flags_
);
5996 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
5997 writer
.guardSpecificFunction(newTargetObjId
, callee_
);
6001 ObjOperandId
InlinableNativeIRGenerator::emitLoadArgsArray() {
6002 if (flags_
.getArgFormat() == CallFlags::Spread
) {
6003 return writer
.loadSpreadArgs();
6006 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
6007 return generator_
.emitFunApplyArgsGuard(flags_
.getArgFormat()).ref();
6010 void IRGenerator::emitCalleeGuard(ObjOperandId calleeId
, JSFunction
* callee
) {
6011 // Guarding on the callee JSFunction* is most efficient, but doesn't work well
6012 // for lambda clones (multiple functions with the same BaseScript). We guard
6013 // on the function's BaseScript if the callee is scripted and this isn't the
6016 // Self-hosted functions are more complicated: top-level functions can be
6017 // relazified using SelfHostedLazyScript and this means they don't have a
6018 // stable BaseScript pointer. These functions are never lambda clones, though,
6019 // so we can just always guard on the JSFunction*. Self-hosted lambdas are
6020 // never relazified so there we use the normal heuristics.
6021 if (isFirstStub_
|| !callee
->hasBaseScript() ||
6022 (callee
->isSelfHostedBuiltin() && !callee
->isLambda())) {
6023 writer
.guardSpecificFunction(calleeId
, callee
);
6025 MOZ_ASSERT_IF(callee
->isSelfHostedBuiltin(),
6026 !callee
->baseScript()->allowRelazify());
6027 writer
.guardClass(calleeId
, GuardClassKind::JSFunction
);
6028 writer
.guardFunctionScript(calleeId
, callee
->baseScript());
6032 ObjOperandId
CallIRGenerator::emitFunCallOrApplyGuard(Int32OperandId argcId
) {
6033 JSFunction
* callee
= &callee_
.toObject().as
<JSFunction
>();
6034 MOZ_ASSERT(callee
->native() == fun_call
|| callee
->native() == fun_apply
);
6036 // Guard that callee is the |fun_call| or |fun_apply| native function.
6037 ValOperandId calleeValId
=
6038 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
);
6039 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
6040 writer
.guardSpecificFunction(calleeObjId
, callee
);
6042 // Guard that |this| is an object.
6043 ValOperandId thisValId
=
6044 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
);
6045 return writer
.guardToObject(thisValId
);
6048 ObjOperandId
CallIRGenerator::emitFunCallGuard(Int32OperandId argcId
) {
6049 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_call
);
6051 return emitFunCallOrApplyGuard(argcId
);
6054 ObjOperandId
CallIRGenerator::emitFunApplyGuard(Int32OperandId argcId
) {
6055 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_apply
);
6057 return emitFunCallOrApplyGuard(argcId
);
6060 Maybe
<ObjOperandId
> CallIRGenerator::emitFunApplyArgsGuard(
6061 CallFlags::ArgFormat format
) {
6062 MOZ_ASSERT(argc_
== 2);
6064 ValOperandId argValId
=
6065 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6067 if (format
== CallFlags::FunApplyArgsObj
) {
6068 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6069 if (args_
[1].toObject().is
<MappedArgumentsObject
>()) {
6070 writer
.guardClass(argObjId
, GuardClassKind::MappedArguments
);
6072 MOZ_ASSERT(args_
[1].toObject().is
<UnmappedArgumentsObject
>());
6073 writer
.guardClass(argObjId
, GuardClassKind::UnmappedArguments
);
6075 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6076 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6077 writer
.guardArgumentsObjectFlags(argObjId
, flags
);
6078 return mozilla::Some(argObjId
);
6081 if (format
== CallFlags::FunApplyArray
) {
6082 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6083 emitOptimisticClassGuard(argObjId
, &args_
[1].toObject(),
6084 GuardClassKind::Array
);
6085 writer
.guardArrayIsPacked(argObjId
);
6086 return mozilla::Some(argObjId
);
6089 MOZ_ASSERT(format
== CallFlags::FunApplyNullUndefined
);
6090 writer
.guardIsNullOrUndefined(argValId
);
6091 return mozilla::Nothing();
6094 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPush() {
6095 // Only optimize on obj.push(val);
6096 if (argc_
!= 1 || !thisval_
.isObject()) {
6097 return AttachDecision::NoAction
;
6100 // Where |obj| is a native array.
6101 JSObject
* thisobj
= &thisval_
.toObject();
6102 if (!thisobj
->is
<ArrayObject
>()) {
6103 return AttachDecision::NoAction
;
6106 auto* thisarray
= &thisobj
->as
<ArrayObject
>();
6108 // Check for other indexed properties or class hooks.
6109 if (!CanAttachAddElement(thisarray
, /* isInit = */ false,
6110 AllowIndexedReceiver::No
)) {
6111 return AttachDecision::NoAction
;
6114 // Can't add new elements to arrays with non-writable length.
6115 if (!thisarray
->lengthIsWritable()) {
6116 return AttachDecision::NoAction
;
6119 // Check that array is extensible.
6120 if (!thisarray
->isExtensible()) {
6121 return AttachDecision::NoAction
;
6124 // Check that the array is completely initialized (no holes).
6125 if (thisarray
->getDenseInitializedLength() != thisarray
->length()) {
6126 return AttachDecision::NoAction
;
6129 MOZ_ASSERT(!thisarray
->denseElementsAreFrozen(),
6130 "Extensible arrays should not have frozen elements");
6132 // After this point, we can generate code fine.
6134 // Initialize the input operand.
6135 initializeInputOperand();
6137 // Guard callee is the 'push' native function.
6138 emitNativeCalleeGuard();
6140 // Guard this is an array object.
6141 ValOperandId thisValId
=
6142 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6143 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6145 // Guard that the shape matches.
6146 TestMatchingNativeReceiver(writer
, thisarray
, thisObjId
);
6148 // Guard proto chain shapes.
6149 ShapeGuardProtoChain(writer
, thisarray
, thisObjId
);
6151 // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
6152 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6153 writer
.arrayPush(thisObjId
, argId
);
6155 writer
.returnFromIC();
6157 trackAttached("ArrayPush");
6158 return AttachDecision::Attach
;
6161 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPopShift(
6162 InlinableNative native
) {
6163 // Expecting no arguments.
6165 return AttachDecision::NoAction
;
6168 // Only optimize if |this| is a packed array.
6169 if (!thisval_
.isObject() || !IsPackedArray(&thisval_
.toObject())) {
6170 return AttachDecision::NoAction
;
6173 // Other conditions:
6175 // * The array length needs to be writable because we're changing it.
6176 // * The array must be extensible. Non-extensible arrays require preserving
6177 // the |initializedLength == capacity| invariant on ObjectElements.
6178 // See NativeObject::shrinkCapacityToInitializedLength.
6179 // This also ensures the elements aren't sealed/frozen.
6180 // * There must not be a for-in iterator for the elements because the IC stub
6181 // does not suppress deleted properties.
6182 ArrayObject
* arr
= &thisval_
.toObject().as
<ArrayObject
>();
6183 if (!arr
->lengthIsWritable() || !arr
->isExtensible() ||
6184 arr
->denseElementsHaveMaybeInIterationFlag()) {
6185 return AttachDecision::NoAction
;
6188 // Initialize the input operand.
6189 initializeInputOperand();
6191 // Guard callee is the 'pop' or 'shift' native function.
6192 emitNativeCalleeGuard();
6194 ValOperandId thisValId
=
6195 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6196 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6197 emitOptimisticClassGuard(objId
, arr
, GuardClassKind::Array
);
6199 if (native
== InlinableNative::ArrayPop
) {
6200 writer
.packedArrayPopResult(objId
);
6202 MOZ_ASSERT(native
== InlinableNative::ArrayShift
);
6203 writer
.packedArrayShiftResult(objId
);
6206 writer
.returnFromIC();
6208 trackAttached("ArrayPopShift");
6209 return AttachDecision::Attach
;
6212 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayJoin() {
6213 // Only handle argc <= 1.
6215 return AttachDecision::NoAction
;
6218 // Only optimize if |this| is an array.
6219 if (!thisval_
.isObject() || !thisval_
.toObject().is
<ArrayObject
>()) {
6220 return AttachDecision::NoAction
;
6223 // The separator argument must be a string, if present.
6224 if (argc_
> 0 && !args_
[0].isString()) {
6225 return AttachDecision::NoAction
;
6228 // IC stub code can handle non-packed array.
6230 // Initialize the input operand.
6231 initializeInputOperand();
6233 // Guard callee is the 'join' native function.
6234 emitNativeCalleeGuard();
6236 // Guard this is an array object.
6237 ValOperandId thisValId
=
6238 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6239 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6240 emitOptimisticClassGuard(thisObjId
, &thisval_
.toObject(),
6241 GuardClassKind::Array
);
6243 StringOperandId sepId
;
6245 // If argcount is 1, guard that the argument is a string.
6246 ValOperandId argValId
=
6247 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6248 sepId
= writer
.guardToString(argValId
);
6250 sepId
= writer
.loadConstantString(cx_
->names().comma_
);
6254 writer
.arrayJoinResult(thisObjId
, sepId
);
6256 writer
.returnFromIC();
6258 trackAttached("ArrayJoin");
6259 return AttachDecision::Attach
;
6262 AttachDecision
InlinableNativeIRGenerator::tryAttachArraySlice() {
6263 // Only handle argc <= 2.
6265 return AttachDecision::NoAction
;
6268 // Only optimize if |this| is a packed array or an arguments object.
6269 if (!thisval_
.isObject()) {
6270 return AttachDecision::NoAction
;
6273 bool isPackedArray
= IsPackedArray(&thisval_
.toObject());
6274 if (!isPackedArray
) {
6275 if (!thisval_
.toObject().is
<ArgumentsObject
>()) {
6276 return AttachDecision::NoAction
;
6278 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6280 // No elements must have been overridden or deleted.
6281 if (args
->hasOverriddenElement()) {
6282 return AttachDecision::NoAction
;
6285 // The length property mustn't be overridden.
6286 if (args
->hasOverriddenLength()) {
6287 return AttachDecision::NoAction
;
6290 // And finally also check that no argument is forwarded.
6291 if (args
->anyArgIsForwarded()) {
6292 return AttachDecision::NoAction
;
6296 // Arguments for the sliced region must be integers.
6297 if (argc_
> 0 && !args_
[0].isInt32()) {
6298 return AttachDecision::NoAction
;
6300 if (argc_
> 1 && !args_
[1].isInt32()) {
6301 return AttachDecision::NoAction
;
6304 JSObject
* templateObj
= NewDenseFullyAllocatedArray(cx_
, 0, TenuredObject
);
6306 cx_
->recoverFromOutOfMemory();
6307 return AttachDecision::NoAction
;
6310 // Initialize the input operand.
6311 initializeInputOperand();
6313 // Guard callee is the 'slice' native function.
6314 emitNativeCalleeGuard();
6316 ValOperandId thisValId
=
6317 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6318 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6320 if (isPackedArray
) {
6321 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6322 GuardClassKind::Array
);
6324 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6326 if (args
->is
<MappedArgumentsObject
>()) {
6327 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6329 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6330 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6333 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6334 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6335 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6336 writer
.guardArgumentsObjectFlags(objId
, flags
);
6339 Int32OperandId int32BeginId
;
6341 ValOperandId beginId
=
6342 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6343 int32BeginId
= writer
.guardToInt32(beginId
);
6345 int32BeginId
= writer
.loadInt32Constant(0);
6348 Int32OperandId int32EndId
;
6350 ValOperandId endId
=
6351 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6352 int32EndId
= writer
.guardToInt32(endId
);
6353 } else if (isPackedArray
) {
6354 int32EndId
= writer
.loadInt32ArrayLength(objId
);
6356 int32EndId
= writer
.loadArgumentsObjectLength(objId
);
6359 if (isPackedArray
) {
6360 writer
.packedArraySliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6362 writer
.argumentsSliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6364 writer
.returnFromIC();
6366 trackAttached(isPackedArray
? "ArraySlice" : "ArgumentsSlice");
6367 return AttachDecision::Attach
;
6370 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayIsArray() {
6371 // Need a single argument.
6373 return AttachDecision::NoAction
;
6376 // Initialize the input operand.
6377 initializeInputOperand();
6379 // Guard callee is the 'isArray' native function.
6380 emitNativeCalleeGuard();
6382 // Check if the argument is an Array and return result.
6383 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6384 writer
.isArrayResult(argId
);
6385 writer
.returnFromIC();
6387 trackAttached("ArrayIsArray");
6388 return AttachDecision::Attach
;
6391 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewGet(
6392 Scalar::Type type
) {
6393 // Ensure |this| is a DataViewObject.
6394 // TODO: Support resizable dataviews. (bug 1842999)
6395 if (!thisval_
.isObject() ||
6396 !thisval_
.toObject().is
<FixedLengthDataViewObject
>()) {
6397 return AttachDecision::NoAction
;
6400 // Expected arguments: offset (number), optional littleEndian (boolean).
6401 if (argc_
< 1 || argc_
> 2) {
6402 return AttachDecision::NoAction
;
6404 int64_t offsetInt64
;
6405 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6406 return AttachDecision::NoAction
;
6408 if (argc_
> 1 && !args_
[1].isBoolean()) {
6409 return AttachDecision::NoAction
;
6412 auto* dv
= &thisval_
.toObject().as
<FixedLengthDataViewObject
>();
6414 // Bounds check the offset.
6415 if (offsetInt64
< 0 ||
6416 !dv
->offsetIsInBounds(Scalar::byteSize(type
), offsetInt64
)) {
6417 return AttachDecision::NoAction
;
6420 // For getUint32 we let the stub return an Int32 if we have not seen a
6421 // double, to allow better codegen in Warp while avoiding bailout loops.
6422 bool forceDoubleForUint32
= false;
6423 if (type
== Scalar::Uint32
) {
6424 bool isLittleEndian
= argc_
> 1 && args_
[1].toBoolean();
6425 uint32_t res
= dv
->read
<uint32_t>(offsetInt64
, isLittleEndian
);
6426 forceDoubleForUint32
= res
>= INT32_MAX
;
6429 // Initialize the input operand.
6430 initializeInputOperand();
6432 // Guard callee is this DataView native function.
6433 emitNativeCalleeGuard();
6435 // Guard |this| is a DataViewObject.
6436 ValOperandId thisValId
=
6437 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6438 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6439 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6440 GuardClassKind::FixedLengthDataView
);
6442 // Convert offset to intPtr.
6443 ValOperandId offsetId
=
6444 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6445 IntPtrOperandId intPtrOffsetId
=
6446 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6448 BooleanOperandId boolLittleEndianId
;
6450 ValOperandId littleEndianId
=
6451 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6452 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6454 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6457 writer
.loadDataViewValueResult(objId
, intPtrOffsetId
, boolLittleEndianId
,
6458 type
, forceDoubleForUint32
);
6459 writer
.returnFromIC();
6461 trackAttached("DataViewGet");
6462 return AttachDecision::Attach
;
6465 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewSet(
6466 Scalar::Type type
) {
6467 // Ensure |this| is a DataViewObject.
6468 // TODO: Support resizable dataviews. (bug 1842999)
6469 if (!thisval_
.isObject() ||
6470 !thisval_
.toObject().is
<FixedLengthDataViewObject
>()) {
6471 return AttachDecision::NoAction
;
6474 // Expected arguments: offset (number), value, optional littleEndian (boolean)
6475 if (argc_
< 2 || argc_
> 3) {
6476 return AttachDecision::NoAction
;
6478 int64_t offsetInt64
;
6479 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6480 return AttachDecision::NoAction
;
6482 if (!ValueCanConvertToNumeric(type
, args_
[1])) {
6483 return AttachDecision::NoAction
;
6485 if (argc_
> 2 && !args_
[2].isBoolean()) {
6486 return AttachDecision::NoAction
;
6489 auto* dv
= &thisval_
.toObject().as
<FixedLengthDataViewObject
>();
6491 // Bounds check the offset.
6492 if (offsetInt64
< 0 ||
6493 !dv
->offsetIsInBounds(Scalar::byteSize(type
), offsetInt64
)) {
6494 return AttachDecision::NoAction
;
6497 // Initialize the input operand.
6498 initializeInputOperand();
6500 // Guard callee is this DataView native function.
6501 emitNativeCalleeGuard();
6503 // Guard |this| is a DataViewObject.
6504 ValOperandId thisValId
=
6505 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6506 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6507 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6508 GuardClassKind::FixedLengthDataView
);
6510 // Convert offset to intPtr.
6511 ValOperandId offsetId
=
6512 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6513 IntPtrOperandId intPtrOffsetId
=
6514 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6516 // Convert value to number or BigInt.
6517 ValOperandId valueId
=
6518 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6519 OperandId numericValueId
= emitNumericGuard(valueId
, args_
[1], type
);
6521 BooleanOperandId boolLittleEndianId
;
6523 ValOperandId littleEndianId
=
6524 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6525 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6527 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6530 writer
.storeDataViewValueResult(objId
, intPtrOffsetId
, numericValueId
,
6531 boolLittleEndianId
, type
);
6532 writer
.returnFromIC();
6534 trackAttached("DataViewSet");
6535 return AttachDecision::Attach
;
6538 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeGetReservedSlot(
6539 InlinableNative native
) {
6540 // Self-hosted code calls this with (object, int32) arguments.
6541 MOZ_ASSERT(argc_
== 2);
6542 MOZ_ASSERT(args_
[0].isObject());
6543 MOZ_ASSERT(args_
[1].isInt32());
6544 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6546 uint32_t slot
= uint32_t(args_
[1].toInt32());
6547 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6548 return AttachDecision::NoAction
;
6550 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6552 // Initialize the input operand.
6553 initializeInputOperand();
6555 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6557 // Guard that the first argument is an object.
6558 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6559 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6561 // BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot ensures that the
6562 // slot argument is constant. (At least for direct calls)
6565 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
6566 writer
.loadFixedSlotResult(objId
, offset
);
6568 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
6569 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Object
);
6571 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
6572 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Int32
);
6574 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
6575 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::String
);
6578 MOZ_CRASH("unexpected native");
6581 writer
.returnFromIC();
6583 trackAttached("UnsafeGetReservedSlot");
6584 return AttachDecision::Attach
;
6587 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeSetReservedSlot() {
6588 // Self-hosted code calls this with (object, int32, value) arguments.
6589 MOZ_ASSERT(argc_
== 3);
6590 MOZ_ASSERT(args_
[0].isObject());
6591 MOZ_ASSERT(args_
[1].isInt32());
6592 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6594 uint32_t slot
= uint32_t(args_
[1].toInt32());
6595 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6596 return AttachDecision::NoAction
;
6598 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6600 // Initialize the input operand.
6601 initializeInputOperand();
6603 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6605 // Guard that the first argument is an object.
6606 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6607 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6609 // BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot ensures that the
6610 // slot argument is constant. (At least for direct calls)
6612 // Get the value to set.
6613 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6615 // Set the fixed slot and return undefined.
6616 writer
.storeFixedSlotUndefinedResult(objId
, offset
, valId
);
6618 // This stub always returns undefined.
6619 writer
.returnFromIC();
6621 trackAttached("UnsafeSetReservedSlot");
6622 return AttachDecision::Attach
;
6625 AttachDecision
InlinableNativeIRGenerator::tryAttachIsSuspendedGenerator() {
6626 // The IsSuspendedGenerator intrinsic is only called in
6627 // self-hosted code, so it's safe to assume we have a single
6628 // argument and the callee is our intrinsic.
6630 MOZ_ASSERT(argc_
== 1);
6632 initializeInputOperand();
6634 // Stack layout here is (bottom to top):
6637 // 0: Arg <-- Top of stack.
6638 // We only care about the argument.
6639 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6641 // Check whether the argument is a suspended generator.
6642 // We don't need guards, because IsSuspendedGenerator returns
6643 // false for values that are not generator objects.
6644 writer
.callIsSuspendedGeneratorResult(valId
);
6645 writer
.returnFromIC();
6647 trackAttached("IsSuspendedGenerator");
6648 return AttachDecision::Attach
;
6651 AttachDecision
InlinableNativeIRGenerator::tryAttachToObject() {
6652 // Self-hosted code calls this with a single argument.
6653 MOZ_ASSERT(argc_
== 1);
6655 // Need a single object argument.
6656 // TODO(Warp): Support all or more conversions to object.
6657 if (!args_
[0].isObject()) {
6658 return AttachDecision::NoAction
;
6661 // Initialize the input operand.
6662 initializeInputOperand();
6664 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6666 // Guard that the argument is an object.
6667 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6668 ObjOperandId objId
= writer
.guardToObject(argId
);
6670 // Return the object.
6671 writer
.loadObjectResult(objId
);
6672 writer
.returnFromIC();
6674 trackAttached("ToObject");
6675 return AttachDecision::Attach
;
6678 AttachDecision
InlinableNativeIRGenerator::tryAttachToInteger() {
6679 // Self-hosted code calls this with a single argument.
6680 MOZ_ASSERT(argc_
== 1);
6682 // Need a single int32 argument.
6683 // TODO(Warp): Support all or more conversions to integer.
6684 // Make sure to update this code correctly if we ever start
6685 // returning non-int32 integers.
6686 if (!args_
[0].isInt32()) {
6687 return AttachDecision::NoAction
;
6690 // Initialize the input operand.
6691 initializeInputOperand();
6693 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6695 // Guard that the argument is an int32.
6696 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6697 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
6699 // Return the int32.
6700 writer
.loadInt32Result(int32Id
);
6701 writer
.returnFromIC();
6703 trackAttached("ToInteger");
6704 return AttachDecision::Attach
;
6707 AttachDecision
InlinableNativeIRGenerator::tryAttachToLength() {
6708 // Self-hosted code calls this with a single argument.
6709 MOZ_ASSERT(argc_
== 1);
6711 // Need a single int32 argument.
6712 if (!args_
[0].isInt32()) {
6713 return AttachDecision::NoAction
;
6716 // Initialize the input operand.
6717 initializeInputOperand();
6719 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6721 // ToLength(int32) is equivalent to max(int32, 0).
6722 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6723 Int32OperandId int32ArgId
= writer
.guardToInt32(argId
);
6724 Int32OperandId zeroId
= writer
.loadInt32Constant(0);
6726 Int32OperandId maxId
= writer
.int32MinMax(isMax
, int32ArgId
, zeroId
);
6727 writer
.loadInt32Result(maxId
);
6728 writer
.returnFromIC();
6730 trackAttached("ToLength");
6731 return AttachDecision::Attach
;
6734 AttachDecision
InlinableNativeIRGenerator::tryAttachIsObject() {
6735 // Self-hosted code calls this with a single argument.
6736 MOZ_ASSERT(argc_
== 1);
6738 // Initialize the input operand.
6739 initializeInputOperand();
6741 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6743 // Type check the argument and return result.
6744 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6745 writer
.isObjectResult(argId
);
6746 writer
.returnFromIC();
6748 trackAttached("IsObject");
6749 return AttachDecision::Attach
;
6752 AttachDecision
InlinableNativeIRGenerator::tryAttachIsPackedArray() {
6753 // Self-hosted code calls this with a single object argument.
6754 MOZ_ASSERT(argc_
== 1);
6755 MOZ_ASSERT(args_
[0].isObject());
6757 // Initialize the input operand.
6758 initializeInputOperand();
6760 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6762 // Check if the argument is packed and return result.
6763 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6764 ObjOperandId objArgId
= writer
.guardToObject(argId
);
6765 writer
.isPackedArrayResult(objArgId
);
6766 writer
.returnFromIC();
6768 trackAttached("IsPackedArray");
6769 return AttachDecision::Attach
;
6772 AttachDecision
InlinableNativeIRGenerator::tryAttachIsCallable() {
6773 // Self-hosted code calls this with a single argument.
6774 MOZ_ASSERT(argc_
== 1);
6776 // Initialize the input operand.
6777 initializeInputOperand();
6779 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6781 // Check if the argument is callable and return result.
6782 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6783 writer
.isCallableResult(argId
);
6784 writer
.returnFromIC();
6786 trackAttached("IsCallable");
6787 return AttachDecision::Attach
;
6790 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructor() {
6791 // Self-hosted code calls this with a single argument.
6792 MOZ_ASSERT(argc_
== 1);
6794 // Need a single object argument.
6795 if (!args_
[0].isObject()) {
6796 return AttachDecision::NoAction
;
6799 // Initialize the input operand.
6800 initializeInputOperand();
6802 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6804 // Guard that the argument is an object.
6805 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6806 ObjOperandId objId
= writer
.guardToObject(argId
);
6808 // Check if the argument is a constructor and return result.
6809 writer
.isConstructorResult(objId
);
6810 writer
.returnFromIC();
6812 trackAttached("IsConstructor");
6813 return AttachDecision::Attach
;
6817 InlinableNativeIRGenerator::tryAttachIsCrossRealmArrayConstructor() {
6818 // Self-hosted code calls this with an object argument.
6819 MOZ_ASSERT(argc_
== 1);
6820 MOZ_ASSERT(args_
[0].isObject());
6822 if (args_
[0].toObject().is
<ProxyObject
>()) {
6823 return AttachDecision::NoAction
;
6826 // Initialize the input operand.
6827 initializeInputOperand();
6829 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6831 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6832 ObjOperandId objId
= writer
.guardToObject(argId
);
6833 writer
.guardIsNotProxy(objId
);
6834 writer
.isCrossRealmArrayConstructorResult(objId
);
6835 writer
.returnFromIC();
6837 trackAttached("IsCrossRealmArrayConstructor");
6838 return AttachDecision::Attach
;
6841 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
6842 InlinableNative native
) {
6843 // Self-hosted code calls this with an object argument.
6844 MOZ_ASSERT(argc_
== 1);
6845 MOZ_ASSERT(args_
[0].isObject());
6847 // Class must match.
6848 const JSClass
* clasp
= InlinableNativeGuardToClass(native
);
6849 if (args_
[0].toObject().getClass() != clasp
) {
6850 return AttachDecision::NoAction
;
6853 // Initialize the input operand.
6854 initializeInputOperand();
6856 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6858 // Guard that the argument is an object.
6859 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6860 ObjOperandId objId
= writer
.guardToObject(argId
);
6862 // Guard that the object has the correct class.
6863 writer
.guardAnyClass(objId
, clasp
);
6865 // Return the object.
6866 writer
.loadObjectResult(objId
);
6867 writer
.returnFromIC();
6869 trackAttached("GuardToClass");
6870 return AttachDecision::Attach
;
6873 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() {
6874 // TODO: Support resizable ArrayBuffers (bug 1842999), for now simply
6875 // pass through to tryAttachGuardToClass which guards on
6876 // FixedLengthArrayBufferObject.
6877 return tryAttachGuardToClass(InlinableNative::IntrinsicGuardToArrayBuffer
);
6880 AttachDecision
InlinableNativeIRGenerator::tryAttachHasClass(
6881 const JSClass
* clasp
, bool isPossiblyWrapped
) {
6882 // Self-hosted code calls this with an object argument.
6883 MOZ_ASSERT(argc_
== 1);
6884 MOZ_ASSERT(args_
[0].isObject());
6886 // Only optimize when the object isn't a proxy.
6887 if (isPossiblyWrapped
&& args_
[0].toObject().is
<ProxyObject
>()) {
6888 return AttachDecision::NoAction
;
6891 // Initialize the input operand.
6892 initializeInputOperand();
6894 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6896 // Perform the Class check.
6897 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6898 ObjOperandId objId
= writer
.guardToObject(argId
);
6900 if (isPossiblyWrapped
) {
6901 writer
.guardIsNotProxy(objId
);
6904 writer
.hasClassResult(objId
, clasp
);
6905 writer
.returnFromIC();
6907 trackAttached("HasClass");
6908 return AttachDecision::Attach
;
6911 // Returns whether the .lastIndex property is a non-negative int32 value and is
6913 static bool HasOptimizableLastIndexSlot(RegExpObject
* regexp
, JSContext
* cx
) {
6914 auto lastIndexProp
= regexp
->lookupPure(cx
->names().lastIndex
);
6915 MOZ_ASSERT(lastIndexProp
->isDataProperty());
6916 if (!lastIndexProp
->writable()) {
6919 Value lastIndex
= regexp
->getLastIndex();
6920 if (!lastIndex
.isInt32() || lastIndex
.toInt32() < 0) {
6926 // Returns the RegExp stub used by the optimized code path for this intrinsic.
6927 // We store a pointer to this in the IC stub to ensure GC doesn't discard it.
6928 static JitCode
* GetOrCreateRegExpStub(JSContext
* cx
, InlinableNative native
) {
6929 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
6932 // The stubs assume the global has non-null RegExpStatics and match result
6934 if (!GlobalObject::getRegExpStatics(cx
, cx
->global()) ||
6935 !cx
->global()->regExpRealm().getOrCreateMatchResultShape(cx
)) {
6936 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
6937 cx
->clearPendingException();
6942 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
6943 case InlinableNative::IntrinsicRegExpExecForTest
:
6944 code
= cx
->zone()->jitZone()->ensureRegExpExecTestStubExists(cx
);
6946 case InlinableNative::IntrinsicRegExpBuiltinExec
:
6947 case InlinableNative::IntrinsicRegExpExec
:
6948 code
= cx
->zone()->jitZone()->ensureRegExpExecMatchStubExists(cx
);
6950 case InlinableNative::RegExpMatcher
:
6951 code
= cx
->zone()->jitZone()->ensureRegExpMatcherStubExists(cx
);
6953 case InlinableNative::RegExpSearcher
:
6954 code
= cx
->zone()->jitZone()->ensureRegExpSearcherStubExists(cx
);
6957 MOZ_CRASH("Unexpected native");
6960 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
6961 cx
->clearPendingException();
6968 static void EmitGuardLastIndexIsNonNegativeInt32(CacheIRWriter
& writer
,
6969 ObjOperandId regExpId
) {
6971 NativeObject::getFixedSlotOffset(RegExpObject::lastIndexSlot());
6972 ValOperandId lastIndexValId
= writer
.loadFixedSlot(regExpId
, offset
);
6973 Int32OperandId lastIndexId
= writer
.guardToInt32(lastIndexValId
);
6974 writer
.guardInt32IsNonNegative(lastIndexId
);
6977 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpBuiltinExec(
6978 InlinableNative native
) {
6979 // Self-hosted code calls this with (regexp, string) arguments.
6980 MOZ_ASSERT(argc_
== 2);
6981 MOZ_ASSERT(args_
[0].isObject());
6982 MOZ_ASSERT(args_
[1].isString());
6984 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
6986 return AttachDecision::NoAction
;
6989 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
6990 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
6991 return AttachDecision::NoAction
;
6994 // Initialize the input operand.
6995 initializeInputOperand();
6997 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6999 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7000 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7001 writer
.guardShape(regExpId
, re
->shape());
7002 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7004 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7005 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7007 if (native
== InlinableNative::IntrinsicRegExpBuiltinExecForTest
) {
7008 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7010 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7012 writer
.returnFromIC();
7014 trackAttached("IntrinsicRegExpBuiltinExec");
7015 return AttachDecision::Attach
;
7018 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpExec(
7019 InlinableNative native
) {
7020 // Self-hosted code calls this with (object, string) arguments.
7021 MOZ_ASSERT(argc_
== 2);
7022 MOZ_ASSERT(args_
[0].isObject());
7023 MOZ_ASSERT(args_
[1].isString());
7025 if (!args_
[0].toObject().is
<RegExpObject
>()) {
7026 return AttachDecision::NoAction
;
7029 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7031 return AttachDecision::NoAction
;
7034 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
7035 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
7036 return AttachDecision::NoAction
;
7039 // Ensure regexp.exec is the original RegExp.prototype.exec function on the
7041 if (re
->containsPure(cx_
->names().exec
)) {
7042 return AttachDecision::NoAction
;
7044 MOZ_ASSERT(cx_
->global()->maybeGetRegExpPrototype());
7046 &cx_
->global()->maybeGetRegExpPrototype()->as
<NativeObject
>();
7047 if (re
->staticPrototype() != regExpProto
) {
7048 return AttachDecision::NoAction
;
7050 auto execProp
= regExpProto
->as
<NativeObject
>().lookupPure(cx_
->names().exec
);
7051 if (!execProp
|| !execProp
->isDataProperty()) {
7052 return AttachDecision::NoAction
;
7054 // It should be stored in a dynamic slot. We assert this in
7055 // FinishRegExpClassInit.
7056 if (regExpProto
->isFixedSlot(execProp
->slot())) {
7057 return AttachDecision::NoAction
;
7059 Value execVal
= regExpProto
->getSlot(execProp
->slot());
7060 PropertyName
* execName
= cx_
->names().RegExp_prototype_Exec
;
7061 if (!IsSelfHostedFunctionWithName(execVal
, execName
)) {
7062 return AttachDecision::NoAction
;
7064 JSFunction
* execFunction
= &execVal
.toObject().as
<JSFunction
>();
7066 // Initialize the input operand.
7067 initializeInputOperand();
7069 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7071 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7072 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7073 writer
.guardShape(regExpId
, re
->shape());
7074 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7076 // Emit guards for the RegExp.prototype.exec property.
7077 ObjOperandId regExpProtoId
= writer
.loadObject(regExpProto
);
7078 writer
.guardShape(regExpProtoId
, regExpProto
->shape());
7080 regExpProto
->dynamicSlotIndex(execProp
->slot()) * sizeof(Value
);
7081 writer
.guardDynamicSlotValue(regExpProtoId
, offset
,
7082 ObjectValue(*execFunction
));
7084 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7085 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7087 if (native
== InlinableNative::IntrinsicRegExpExecForTest
) {
7088 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7090 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7092 writer
.returnFromIC();
7094 trackAttached("IntrinsicRegExpExec");
7095 return AttachDecision::Attach
;
7098 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpMatcherSearcher(
7099 InlinableNative native
) {
7100 // Self-hosted code calls this with (object, string, number) arguments.
7101 MOZ_ASSERT(argc_
== 3);
7102 MOZ_ASSERT(args_
[0].isObject());
7103 MOZ_ASSERT(args_
[1].isString());
7104 MOZ_ASSERT(args_
[2].isNumber());
7106 // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
7107 if (!args_
[2].isInt32()) {
7108 return AttachDecision::NoAction
;
7111 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7113 return AttachDecision::NoAction
;
7116 // Initialize the input operand.
7117 initializeInputOperand();
7119 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7121 // Guard argument types.
7122 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7123 ObjOperandId reId
= writer
.guardToObject(arg0Id
);
7125 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7126 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7128 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7129 Int32OperandId lastIndexId
= writer
.guardToInt32(arg2Id
);
7132 case InlinableNative::RegExpMatcher
:
7133 writer
.callRegExpMatcherResult(reId
, inputId
, lastIndexId
, stub
);
7134 writer
.returnFromIC();
7135 trackAttached("RegExpMatcher");
7138 case InlinableNative::RegExpSearcher
:
7139 writer
.callRegExpSearcherResult(reId
, inputId
, lastIndexId
, stub
);
7140 writer
.returnFromIC();
7141 trackAttached("RegExpSearcher");
7145 MOZ_CRASH("Unexpected native");
7148 return AttachDecision::Attach
;
7151 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
7152 // Self-hosted code calls this with a string argument that's only used for an
7154 MOZ_ASSERT(argc_
== 1);
7155 MOZ_ASSERT(args_
[0].isString());
7157 // Initialize the input operand.
7158 initializeInputOperand();
7160 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7162 writer
.regExpSearcherLastLimitResult();
7163 writer
.returnFromIC();
7165 trackAttached("RegExpSearcherLastLimit");
7166 return AttachDecision::Attach
;
7169 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
7170 // Self-hosted code calls this with object and string arguments.
7171 MOZ_ASSERT(argc_
== 2);
7172 MOZ_ASSERT(args_
[0].toObject().is
<RegExpObject
>());
7173 MOZ_ASSERT(args_
[1].isString());
7175 // Initialize the input operand.
7176 initializeInputOperand();
7178 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7180 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7181 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7183 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7184 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7186 writer
.regExpHasCaptureGroupsResult(objId
, inputId
);
7187 writer
.returnFromIC();
7189 trackAttached("RegExpHasCaptureGroups");
7190 return AttachDecision::Attach
;
7194 InlinableNativeIRGenerator::tryAttachRegExpPrototypeOptimizable() {
7195 // Self-hosted code calls this with a single object argument.
7196 MOZ_ASSERT(argc_
== 1);
7197 MOZ_ASSERT(args_
[0].isObject());
7199 // Initialize the input operand.
7200 initializeInputOperand();
7202 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7204 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7205 ObjOperandId protoId
= writer
.guardToObject(arg0Id
);
7207 writer
.regExpPrototypeOptimizableResult(protoId
);
7208 writer
.returnFromIC();
7210 trackAttached("RegExpPrototypeOptimizable");
7211 return AttachDecision::Attach
;
7215 InlinableNativeIRGenerator::tryAttachRegExpInstanceOptimizable() {
7216 // Self-hosted code calls this with two object arguments.
7217 MOZ_ASSERT(argc_
== 2);
7218 MOZ_ASSERT(args_
[0].isObject());
7219 MOZ_ASSERT(args_
[1].isObject());
7221 // Initialize the input operand.
7222 initializeInputOperand();
7224 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7226 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7227 ObjOperandId regexpId
= writer
.guardToObject(arg0Id
);
7229 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7230 ObjOperandId protoId
= writer
.guardToObject(arg1Id
);
7232 writer
.regExpInstanceOptimizableResult(regexpId
, protoId
);
7233 writer
.returnFromIC();
7235 trackAttached("RegExpInstanceOptimizable");
7236 return AttachDecision::Attach
;
7239 AttachDecision
InlinableNativeIRGenerator::tryAttachGetFirstDollarIndex() {
7240 // Self-hosted code calls this with a single string argument.
7241 MOZ_ASSERT(argc_
== 1);
7242 MOZ_ASSERT(args_
[0].isString());
7244 // Initialize the input operand.
7245 initializeInputOperand();
7247 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7249 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7250 StringOperandId strId
= writer
.guardToString(arg0Id
);
7252 writer
.getFirstDollarIndexResult(strId
);
7253 writer
.returnFromIC();
7255 trackAttached("GetFirstDollarIndex");
7256 return AttachDecision::Attach
;
7259 AttachDecision
InlinableNativeIRGenerator::tryAttachSubstringKernel() {
7260 // Self-hosted code calls this with (string, int32, int32) arguments.
7261 MOZ_ASSERT(argc_
== 3);
7262 MOZ_ASSERT(args_
[0].isString());
7263 MOZ_ASSERT(args_
[1].isInt32());
7264 MOZ_ASSERT(args_
[2].isInt32());
7266 // Initialize the input operand.
7267 initializeInputOperand();
7269 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7271 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7272 StringOperandId strId
= writer
.guardToString(arg0Id
);
7274 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7275 Int32OperandId beginId
= writer
.guardToInt32(arg1Id
);
7277 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7278 Int32OperandId lengthId
= writer
.guardToInt32(arg2Id
);
7280 writer
.callSubstringKernelResult(strId
, beginId
, lengthId
);
7281 writer
.returnFromIC();
7283 trackAttached("SubstringKernel");
7284 return AttachDecision::Attach
;
7287 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectHasPrototype() {
7288 // Self-hosted code calls this with (object, object) arguments.
7289 MOZ_ASSERT(argc_
== 2);
7290 MOZ_ASSERT(args_
[0].isObject());
7291 MOZ_ASSERT(args_
[1].isObject());
7293 auto* obj
= &args_
[0].toObject().as
<NativeObject
>();
7294 auto* proto
= &args_
[1].toObject().as
<NativeObject
>();
7296 // Only attach when obj.__proto__ is proto.
7297 if (obj
->staticPrototype() != proto
) {
7298 return AttachDecision::NoAction
;
7301 // Initialize the input operand.
7302 initializeInputOperand();
7304 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7306 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7307 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7309 writer
.guardProto(objId
, proto
);
7310 writer
.loadBooleanResult(true);
7311 writer
.returnFromIC();
7313 trackAttached("ObjectHasPrototype");
7314 return AttachDecision::Attach
;
7317 static bool CanConvertToString(const Value
& v
) {
7318 return v
.isString() || v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
7321 AttachDecision
InlinableNativeIRGenerator::tryAttachString() {
7322 // Need a single argument that is or can be converted to a string.
7323 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7324 return AttachDecision::NoAction
;
7327 // Initialize the input operand.
7328 initializeInputOperand();
7330 // Guard callee is the 'String' function.
7331 emitNativeCalleeGuard();
7333 // Guard that the argument is a string or can be converted to one.
7334 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7335 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7337 // Return the string.
7338 writer
.loadStringResult(strId
);
7339 writer
.returnFromIC();
7341 trackAttached("String");
7342 return AttachDecision::Attach
;
7345 AttachDecision
InlinableNativeIRGenerator::tryAttachStringConstructor() {
7346 // Need a single argument that is or can be converted to a string.
7347 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7348 return AttachDecision::NoAction
;
7351 RootedString
emptyString(cx_
, cx_
->runtime()->emptyString
);
7352 JSObject
* templateObj
= StringObject::create(
7353 cx_
, emptyString
, /* proto = */ nullptr, TenuredObject
);
7355 cx_
->recoverFromOutOfMemory();
7356 return AttachDecision::NoAction
;
7359 // Initialize the input operand.
7360 initializeInputOperand();
7362 // Guard callee is the 'String' function.
7363 emitNativeCalleeGuard();
7365 // Guard on number and convert to string.
7366 ValOperandId argId
=
7367 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
7368 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7370 writer
.newStringObjectResult(templateObj
, strId
);
7371 writer
.returnFromIC();
7373 trackAttached("StringConstructor");
7374 return AttachDecision::Attach
;
7377 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToStringValueOf() {
7378 // Expecting no arguments.
7380 return AttachDecision::NoAction
;
7383 // Ensure |this| is a primitive string value.
7384 if (!thisval_
.isString()) {
7385 return AttachDecision::NoAction
;
7388 // Initialize the input operand.
7389 initializeInputOperand();
7391 // Guard callee is the 'toString' OR 'valueOf' native function.
7392 emitNativeCalleeGuard();
7394 // Guard |this| is a string.
7395 ValOperandId thisValId
=
7396 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7397 StringOperandId strId
= writer
.guardToString(thisValId
);
7399 // Return the string
7400 writer
.loadStringResult(strId
);
7401 writer
.returnFromIC();
7403 trackAttached("StringToStringValueOf");
7404 return AttachDecision::Attach
;
7407 AttachDecision
InlinableNativeIRGenerator::tryAttachStringReplaceString() {
7408 // Self-hosted code calls this with (string, string, string) arguments.
7409 MOZ_ASSERT(argc_
== 3);
7410 MOZ_ASSERT(args_
[0].isString());
7411 MOZ_ASSERT(args_
[1].isString());
7412 MOZ_ASSERT(args_
[2].isString());
7414 // Initialize the input operand.
7415 initializeInputOperand();
7417 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7419 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7420 StringOperandId strId
= writer
.guardToString(arg0Id
);
7422 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7423 StringOperandId patternId
= writer
.guardToString(arg1Id
);
7425 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7426 StringOperandId replacementId
= writer
.guardToString(arg2Id
);
7428 writer
.stringReplaceStringResult(strId
, patternId
, replacementId
);
7429 writer
.returnFromIC();
7431 trackAttached("StringReplaceString");
7432 return AttachDecision::Attach
;
7435 AttachDecision
InlinableNativeIRGenerator::tryAttachStringSplitString() {
7436 // Self-hosted code calls this with (string, string) arguments.
7437 MOZ_ASSERT(argc_
== 2);
7438 MOZ_ASSERT(args_
[0].isString());
7439 MOZ_ASSERT(args_
[1].isString());
7441 // Initialize the input operand.
7442 initializeInputOperand();
7444 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7446 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7447 StringOperandId strId
= writer
.guardToString(arg0Id
);
7449 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7450 StringOperandId separatorId
= writer
.guardToString(arg1Id
);
7452 writer
.stringSplitStringResult(strId
, separatorId
);
7453 writer
.returnFromIC();
7455 trackAttached("StringSplitString");
7456 return AttachDecision::Attach
;
7459 AttachDecision
InlinableNativeIRGenerator::tryAttachStringChar(
7461 // Need one argument.
7463 return AttachDecision::NoAction
;
7466 auto attach
= CanAttachStringChar(thisval_
, args_
[0], kind
);
7467 if (attach
== AttachStringChar::No
) {
7468 return AttachDecision::NoAction
;
7471 bool handleOOB
= attach
== AttachStringChar::OutOfBounds
;
7473 // Initialize the input operand.
7474 initializeInputOperand();
7476 // Guard callee is the 'charCodeAt', 'codePointAt', 'charAt', or 'at' native
7478 emitNativeCalleeGuard();
7480 // Guard this is a string.
7481 ValOperandId thisValId
=
7482 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7483 StringOperandId strId
= writer
.guardToString(thisValId
);
7485 // Guard int32 index.
7486 ValOperandId indexId
=
7487 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7488 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
7490 // Handle relative string indices, if necessary.
7491 if (kind
== StringChar::At
) {
7492 int32IndexId
= writer
.toRelativeStringIndex(int32IndexId
, strId
);
7495 // Linearize the string.
7497 // AttachStringChar doesn't have a separate state when OOB access happens on
7498 // a string which needs to be linearized, so just linearize unconditionally
7499 // for out-of-bounds accesses.
7500 if (attach
== AttachStringChar::Linearize
||
7501 attach
== AttachStringChar::OutOfBounds
) {
7503 case StringChar::CharCodeAt
:
7504 case StringChar::CharAt
:
7505 case StringChar::At
:
7506 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
7508 case StringChar::CodePointAt
:
7509 strId
= writer
.linearizeForCodePointAccess(strId
, int32IndexId
);
7514 // Load string char or code.
7516 case StringChar::CharCodeAt
:
7517 writer
.loadStringCharCodeResult(strId
, int32IndexId
, handleOOB
);
7519 case StringChar::CodePointAt
:
7520 writer
.loadStringCodePointResult(strId
, int32IndexId
, handleOOB
);
7522 case StringChar::CharAt
:
7523 writer
.loadStringCharResult(strId
, int32IndexId
, handleOOB
);
7525 case StringChar::At
:
7526 writer
.loadStringAtResult(strId
, int32IndexId
, handleOOB
);
7530 writer
.returnFromIC();
7533 case StringChar::CharCodeAt
:
7534 trackAttached("StringCharCodeAt");
7536 case StringChar::CodePointAt
:
7537 trackAttached("StringCodePointAt");
7539 case StringChar::CharAt
:
7540 trackAttached("StringCharAt");
7542 case StringChar::At
:
7543 trackAttached("StringAt");
7547 return AttachDecision::Attach
;
7550 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharCodeAt() {
7551 return tryAttachStringChar(StringChar::CharCodeAt
);
7554 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCodePointAt() {
7555 return tryAttachStringChar(StringChar::CodePointAt
);
7558 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharAt() {
7559 return tryAttachStringChar(StringChar::CharAt
);
7562 AttachDecision
InlinableNativeIRGenerator::tryAttachStringAt() {
7563 return tryAttachStringChar(StringChar::At
);
7566 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCharCode() {
7567 // Need one number argument.
7568 if (argc_
!= 1 || !args_
[0].isNumber()) {
7569 return AttachDecision::NoAction
;
7572 // Initialize the input operand.
7573 initializeInputOperand();
7575 // Guard callee is the 'fromCharCode' native function.
7576 emitNativeCalleeGuard();
7578 // Guard int32 argument.
7579 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7580 Int32OperandId codeId
;
7581 if (args_
[0].isInt32()) {
7582 codeId
= writer
.guardToInt32(argId
);
7584 // 'fromCharCode' performs ToUint16 on its input. We can use Uint32
7585 // semantics, because ToUint16(ToUint32(v)) == ToUint16(v).
7586 codeId
= writer
.guardToInt32ModUint32(argId
);
7589 // Return string created from code.
7590 writer
.stringFromCharCodeResult(codeId
);
7591 writer
.returnFromIC();
7593 trackAttached("StringFromCharCode");
7594 return AttachDecision::Attach
;
7597 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCodePoint() {
7598 // Need one int32 argument.
7599 if (argc_
!= 1 || !args_
[0].isInt32()) {
7600 return AttachDecision::NoAction
;
7603 // String.fromCodePoint throws for invalid code points.
7604 int32_t codePoint
= args_
[0].toInt32();
7605 if (codePoint
< 0 || codePoint
> int32_t(unicode::NonBMPMax
)) {
7606 return AttachDecision::NoAction
;
7609 // Initialize the input operand.
7610 initializeInputOperand();
7612 // Guard callee is the 'fromCodePoint' native function.
7613 emitNativeCalleeGuard();
7615 // Guard int32 argument.
7616 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7617 Int32OperandId codeId
= writer
.guardToInt32(argId
);
7619 // Return string created from code point.
7620 writer
.stringFromCodePointResult(codeId
);
7621 writer
.returnFromIC();
7623 trackAttached("StringFromCodePoint");
7624 return AttachDecision::Attach
;
7627 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIncludes() {
7628 // Need one string argument.
7629 if (argc_
!= 1 || !args_
[0].isString()) {
7630 return AttachDecision::NoAction
;
7633 // Ensure |this| is a primitive string value.
7634 if (!thisval_
.isString()) {
7635 return AttachDecision::NoAction
;
7638 // Initialize the input operand.
7639 initializeInputOperand();
7641 // Guard callee is the 'includes' native function.
7642 emitNativeCalleeGuard();
7644 // Guard this is a string.
7645 ValOperandId thisValId
=
7646 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7647 StringOperandId strId
= writer
.guardToString(thisValId
);
7649 // Guard string argument.
7650 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7651 StringOperandId searchStrId
= writer
.guardToString(argId
);
7653 writer
.stringIncludesResult(strId
, searchStrId
);
7654 writer
.returnFromIC();
7656 trackAttached("StringIncludes");
7657 return AttachDecision::Attach
;
7660 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIndexOf() {
7661 // Need one string argument.
7662 if (argc_
!= 1 || !args_
[0].isString()) {
7663 return AttachDecision::NoAction
;
7666 // Ensure |this| is a primitive string value.
7667 if (!thisval_
.isString()) {
7668 return AttachDecision::NoAction
;
7671 // Initialize the input operand.
7672 initializeInputOperand();
7674 // Guard callee is the 'indexOf' native function.
7675 emitNativeCalleeGuard();
7677 // Guard this is a string.
7678 ValOperandId thisValId
=
7679 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7680 StringOperandId strId
= writer
.guardToString(thisValId
);
7682 // Guard string argument.
7683 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7684 StringOperandId searchStrId
= writer
.guardToString(argId
);
7686 writer
.stringIndexOfResult(strId
, searchStrId
);
7687 writer
.returnFromIC();
7689 trackAttached("StringIndexOf");
7690 return AttachDecision::Attach
;
7693 AttachDecision
InlinableNativeIRGenerator::tryAttachStringLastIndexOf() {
7694 // Need one string argument.
7695 if (argc_
!= 1 || !args_
[0].isString()) {
7696 return AttachDecision::NoAction
;
7699 // Ensure |this| is a primitive string value.
7700 if (!thisval_
.isString()) {
7701 return AttachDecision::NoAction
;
7704 // Initialize the input operand.
7705 initializeInputOperand();
7707 // Guard callee is the 'lastIndexOf' native function.
7708 emitNativeCalleeGuard();
7710 // Guard this is a string.
7711 ValOperandId thisValId
=
7712 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7713 StringOperandId strId
= writer
.guardToString(thisValId
);
7715 // Guard string argument.
7716 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7717 StringOperandId searchStrId
= writer
.guardToString(argId
);
7719 writer
.stringLastIndexOfResult(strId
, searchStrId
);
7720 writer
.returnFromIC();
7722 trackAttached("StringLastIndexOf");
7723 return AttachDecision::Attach
;
7726 AttachDecision
InlinableNativeIRGenerator::tryAttachStringStartsWith() {
7727 // Need one string argument.
7728 if (argc_
!= 1 || !args_
[0].isString()) {
7729 return AttachDecision::NoAction
;
7732 // Ensure |this| is a primitive string value.
7733 if (!thisval_
.isString()) {
7734 return AttachDecision::NoAction
;
7737 // Initialize the input operand.
7738 initializeInputOperand();
7740 // Guard callee is the 'startsWith' native function.
7741 emitNativeCalleeGuard();
7743 // Guard this is a string.
7744 ValOperandId thisValId
=
7745 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7746 StringOperandId strId
= writer
.guardToString(thisValId
);
7748 // Guard string argument.
7749 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7750 StringOperandId searchStrId
= writer
.guardToString(argId
);
7752 writer
.stringStartsWithResult(strId
, searchStrId
);
7753 writer
.returnFromIC();
7755 trackAttached("StringStartsWith");
7756 return AttachDecision::Attach
;
7759 AttachDecision
InlinableNativeIRGenerator::tryAttachStringEndsWith() {
7760 // Need one string argument.
7761 if (argc_
!= 1 || !args_
[0].isString()) {
7762 return AttachDecision::NoAction
;
7765 // Ensure |this| is a primitive string value.
7766 if (!thisval_
.isString()) {
7767 return AttachDecision::NoAction
;
7770 // Initialize the input operand.
7771 initializeInputOperand();
7773 // Guard callee is the 'endsWith' native function.
7774 emitNativeCalleeGuard();
7776 // Guard this is a string.
7777 ValOperandId thisValId
=
7778 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7779 StringOperandId strId
= writer
.guardToString(thisValId
);
7781 // Guard string argument.
7782 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7783 StringOperandId searchStrId
= writer
.guardToString(argId
);
7785 writer
.stringEndsWithResult(strId
, searchStrId
);
7786 writer
.returnFromIC();
7788 trackAttached("StringEndsWith");
7789 return AttachDecision::Attach
;
7792 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToLowerCase() {
7793 // Expecting no arguments.
7795 return AttachDecision::NoAction
;
7798 // Ensure |this| is a primitive string value.
7799 if (!thisval_
.isString()) {
7800 return AttachDecision::NoAction
;
7803 // Initialize the input operand.
7804 initializeInputOperand();
7806 // Guard callee is the 'toLowerCase' native function.
7807 emitNativeCalleeGuard();
7809 // Guard this is a string.
7810 ValOperandId thisValId
=
7811 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7812 StringOperandId strId
= writer
.guardToString(thisValId
);
7814 // Return string converted to lower-case.
7815 writer
.stringToLowerCaseResult(strId
);
7816 writer
.returnFromIC();
7818 trackAttached("StringToLowerCase");
7819 return AttachDecision::Attach
;
7822 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToUpperCase() {
7823 // Expecting no arguments.
7825 return AttachDecision::NoAction
;
7828 // Ensure |this| is a primitive string value.
7829 if (!thisval_
.isString()) {
7830 return AttachDecision::NoAction
;
7833 // Initialize the input operand.
7834 initializeInputOperand();
7836 // Guard callee is the 'toUpperCase' native function.
7837 emitNativeCalleeGuard();
7839 // Guard this is a string.
7840 ValOperandId thisValId
=
7841 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7842 StringOperandId strId
= writer
.guardToString(thisValId
);
7844 // Return string converted to upper-case.
7845 writer
.stringToUpperCaseResult(strId
);
7846 writer
.returnFromIC();
7848 trackAttached("StringToUpperCase");
7849 return AttachDecision::Attach
;
7852 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrim() {
7853 // Expecting no arguments.
7855 return AttachDecision::NoAction
;
7858 // Ensure |this| is a primitive string value.
7859 if (!thisval_
.isString()) {
7860 return AttachDecision::NoAction
;
7863 // Initialize the input operand.
7864 initializeInputOperand();
7866 // Guard callee is the 'trim' native function.
7867 emitNativeCalleeGuard();
7869 // Guard this is a string.
7870 ValOperandId thisValId
=
7871 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7872 StringOperandId strId
= writer
.guardToString(thisValId
);
7874 writer
.stringTrimResult(strId
);
7875 writer
.returnFromIC();
7877 trackAttached("StringTrim");
7878 return AttachDecision::Attach
;
7881 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimStart() {
7882 // Expecting no arguments.
7884 return AttachDecision::NoAction
;
7887 // Ensure |this| is a primitive string value.
7888 if (!thisval_
.isString()) {
7889 return AttachDecision::NoAction
;
7892 // Initialize the input operand.
7893 initializeInputOperand();
7895 // Guard callee is the 'trimStart' native function.
7896 emitNativeCalleeGuard();
7898 // Guard this is a string.
7899 ValOperandId thisValId
=
7900 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7901 StringOperandId strId
= writer
.guardToString(thisValId
);
7903 writer
.stringTrimStartResult(strId
);
7904 writer
.returnFromIC();
7906 trackAttached("StringTrimStart");
7907 return AttachDecision::Attach
;
7910 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimEnd() {
7911 // Expecting no arguments.
7913 return AttachDecision::NoAction
;
7916 // Ensure |this| is a primitive string value.
7917 if (!thisval_
.isString()) {
7918 return AttachDecision::NoAction
;
7921 // Initialize the input operand.
7922 initializeInputOperand();
7924 // Guard callee is the 'trimEnd' native function.
7925 emitNativeCalleeGuard();
7927 // Guard this is a string.
7928 ValOperandId thisValId
=
7929 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7930 StringOperandId strId
= writer
.guardToString(thisValId
);
7932 writer
.stringTrimEndResult(strId
);
7933 writer
.returnFromIC();
7935 trackAttached("StringTrimEnd");
7936 return AttachDecision::Attach
;
7939 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRandom() {
7940 // Expecting no arguments.
7942 return AttachDecision::NoAction
;
7945 MOZ_ASSERT(cx_
->realm() == callee_
->realm(),
7946 "Shouldn't inline cross-realm Math.random because per-realm RNG");
7948 // Initialize the input operand.
7949 initializeInputOperand();
7951 // Guard callee is the 'random' native function.
7952 emitNativeCalleeGuard();
7954 mozilla::non_crypto::XorShift128PlusRNG
* rng
=
7955 &cx_
->realm()->getOrCreateRandomNumberGenerator();
7956 writer
.mathRandomResult(rng
);
7958 writer
.returnFromIC();
7960 trackAttached("MathRandom");
7961 return AttachDecision::Attach
;
7964 AttachDecision
InlinableNativeIRGenerator::tryAttachMathAbs() {
7965 // Need one argument.
7967 return AttachDecision::NoAction
;
7970 if (!args_
[0].isNumber()) {
7971 return AttachDecision::NoAction
;
7974 // Initialize the input operand.
7975 initializeInputOperand();
7977 // Guard callee is the 'abs' native function.
7978 emitNativeCalleeGuard();
7980 ValOperandId argumentId
=
7981 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7983 // abs(INT_MIN) is a double.
7984 if (args_
[0].isInt32() && args_
[0].toInt32() != INT_MIN
) {
7985 Int32OperandId int32Id
= writer
.guardToInt32(argumentId
);
7986 writer
.mathAbsInt32Result(int32Id
);
7988 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
7989 writer
.mathAbsNumberResult(numberId
);
7992 writer
.returnFromIC();
7994 trackAttached("MathAbs");
7995 return AttachDecision::Attach
;
7998 AttachDecision
InlinableNativeIRGenerator::tryAttachMathClz32() {
7999 // Need one (number) argument.
8000 if (argc_
!= 1 || !args_
[0].isNumber()) {
8001 return AttachDecision::NoAction
;
8004 // Initialize the input operand.
8005 initializeInputOperand();
8007 // Guard callee is the 'clz32' native function.
8008 emitNativeCalleeGuard();
8010 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8012 Int32OperandId int32Id
;
8013 if (args_
[0].isInt32()) {
8014 int32Id
= writer
.guardToInt32(argId
);
8016 MOZ_ASSERT(args_
[0].isDouble());
8017 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8018 int32Id
= writer
.truncateDoubleToUInt32(numId
);
8020 writer
.mathClz32Result(int32Id
);
8021 writer
.returnFromIC();
8023 trackAttached("MathClz32");
8024 return AttachDecision::Attach
;
8027 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSign() {
8028 // Need one (number) argument.
8029 if (argc_
!= 1 || !args_
[0].isNumber()) {
8030 return AttachDecision::NoAction
;
8033 // Initialize the input operand.
8034 initializeInputOperand();
8036 // Guard callee is the 'sign' native function.
8037 emitNativeCalleeGuard();
8039 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8041 if (args_
[0].isInt32()) {
8042 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
8043 writer
.mathSignInt32Result(int32Id
);
8045 // Math.sign returns a double only if the input is -0 or NaN so try to
8046 // optimize the common Number => Int32 case.
8047 double d
= math_sign_impl(args_
[0].toDouble());
8049 bool resultIsInt32
= mozilla::NumberIsInt32(d
, &unused
);
8051 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8052 if (resultIsInt32
) {
8053 writer
.mathSignNumberToInt32Result(numId
);
8055 writer
.mathSignNumberResult(numId
);
8059 writer
.returnFromIC();
8061 trackAttached("MathSign");
8062 return AttachDecision::Attach
;
8065 AttachDecision
InlinableNativeIRGenerator::tryAttachMathImul() {
8066 // Need two (number) arguments.
8067 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8068 return AttachDecision::NoAction
;
8071 // Initialize the input operand.
8072 initializeInputOperand();
8074 // Guard callee is the 'imul' native function.
8075 emitNativeCalleeGuard();
8077 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8078 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8080 Int32OperandId int32Arg0Id
, int32Arg1Id
;
8081 if (args_
[0].isInt32() && args_
[1].isInt32()) {
8082 int32Arg0Id
= writer
.guardToInt32(arg0Id
);
8083 int32Arg1Id
= writer
.guardToInt32(arg1Id
);
8085 // Treat both arguments as numbers if at least one of them is non-int32.
8086 NumberOperandId numArg0Id
= writer
.guardIsNumber(arg0Id
);
8087 NumberOperandId numArg1Id
= writer
.guardIsNumber(arg1Id
);
8088 int32Arg0Id
= writer
.truncateDoubleToUInt32(numArg0Id
);
8089 int32Arg1Id
= writer
.truncateDoubleToUInt32(numArg1Id
);
8091 writer
.mathImulResult(int32Arg0Id
, int32Arg1Id
);
8092 writer
.returnFromIC();
8094 trackAttached("MathImul");
8095 return AttachDecision::Attach
;
8098 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFloor() {
8099 // Need one (number) argument.
8100 if (argc_
!= 1 || !args_
[0].isNumber()) {
8101 return AttachDecision::NoAction
;
8104 // Check if the result fits in int32.
8105 double res
= math_floor_impl(args_
[0].toNumber());
8107 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8109 // Initialize the input operand.
8110 initializeInputOperand();
8112 // Guard callee is the 'floor' native function.
8113 emitNativeCalleeGuard();
8115 ValOperandId argumentId
=
8116 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8118 if (args_
[0].isInt32()) {
8119 MOZ_ASSERT(resultIsInt32
);
8121 // Use an indirect truncation to inform the optimizer it needs to preserve
8122 // a bailout when the input can't be represented as an int32, even if the
8123 // final result is fully truncated.
8124 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8125 writer
.indirectTruncateInt32Result(intId
);
8127 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8129 if (resultIsInt32
) {
8130 writer
.mathFloorToInt32Result(numberId
);
8132 writer
.mathFloorNumberResult(numberId
);
8136 writer
.returnFromIC();
8138 trackAttached("MathFloor");
8139 return AttachDecision::Attach
;
8142 AttachDecision
InlinableNativeIRGenerator::tryAttachMathCeil() {
8143 // Need one (number) argument.
8144 if (argc_
!= 1 || !args_
[0].isNumber()) {
8145 return AttachDecision::NoAction
;
8148 // Check if the result fits in int32.
8149 double res
= math_ceil_impl(args_
[0].toNumber());
8151 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8153 // Initialize the input operand.
8154 initializeInputOperand();
8156 // Guard callee is the 'ceil' native function.
8157 emitNativeCalleeGuard();
8159 ValOperandId argumentId
=
8160 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8162 if (args_
[0].isInt32()) {
8163 MOZ_ASSERT(resultIsInt32
);
8165 // Use an indirect truncation to inform the optimizer it needs to preserve
8166 // a bailout when the input can't be represented as an int32, even if the
8167 // final result is fully truncated.
8168 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8169 writer
.indirectTruncateInt32Result(intId
);
8171 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8173 if (resultIsInt32
) {
8174 writer
.mathCeilToInt32Result(numberId
);
8176 writer
.mathCeilNumberResult(numberId
);
8180 writer
.returnFromIC();
8182 trackAttached("MathCeil");
8183 return AttachDecision::Attach
;
8186 AttachDecision
InlinableNativeIRGenerator::tryAttachMathTrunc() {
8187 // Need one (number) argument.
8188 if (argc_
!= 1 || !args_
[0].isNumber()) {
8189 return AttachDecision::NoAction
;
8192 // Check if the result fits in int32.
8193 double res
= math_trunc_impl(args_
[0].toNumber());
8195 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8197 // Initialize the input operand.
8198 initializeInputOperand();
8200 // Guard callee is the 'trunc' native function.
8201 emitNativeCalleeGuard();
8203 ValOperandId argumentId
=
8204 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8206 if (args_
[0].isInt32()) {
8207 MOZ_ASSERT(resultIsInt32
);
8209 // We don't need an indirect truncation barrier here, because Math.trunc
8210 // always truncates, but never rounds its input away from zero.
8211 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8212 writer
.loadInt32Result(intId
);
8214 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8216 if (resultIsInt32
) {
8217 writer
.mathTruncToInt32Result(numberId
);
8219 writer
.mathTruncNumberResult(numberId
);
8223 writer
.returnFromIC();
8225 trackAttached("MathTrunc");
8226 return AttachDecision::Attach
;
8229 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRound() {
8230 // Need one (number) argument.
8231 if (argc_
!= 1 || !args_
[0].isNumber()) {
8232 return AttachDecision::NoAction
;
8235 // Check if the result fits in int32.
8236 double res
= math_round_impl(args_
[0].toNumber());
8238 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8240 // Initialize the input operand.
8241 initializeInputOperand();
8243 // Guard callee is the 'round' native function.
8244 emitNativeCalleeGuard();
8246 ValOperandId argumentId
=
8247 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8249 if (args_
[0].isInt32()) {
8250 MOZ_ASSERT(resultIsInt32
);
8252 // Use an indirect truncation to inform the optimizer it needs to preserve
8253 // a bailout when the input can't be represented as an int32, even if the
8254 // final result is fully truncated.
8255 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8256 writer
.indirectTruncateInt32Result(intId
);
8258 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8260 if (resultIsInt32
) {
8261 writer
.mathRoundToInt32Result(numberId
);
8263 writer
.mathFunctionNumberResult(numberId
, UnaryMathFunction::Round
);
8267 writer
.returnFromIC();
8269 trackAttached("MathRound");
8270 return AttachDecision::Attach
;
8273 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSqrt() {
8274 // Need one (number) argument.
8275 if (argc_
!= 1 || !args_
[0].isNumber()) {
8276 return AttachDecision::NoAction
;
8279 // Initialize the input operand.
8280 initializeInputOperand();
8282 // Guard callee is the 'sqrt' native function.
8283 emitNativeCalleeGuard();
8285 ValOperandId argumentId
=
8286 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8287 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8288 writer
.mathSqrtNumberResult(numberId
);
8289 writer
.returnFromIC();
8291 trackAttached("MathSqrt");
8292 return AttachDecision::Attach
;
8295 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFRound() {
8296 // Need one (number) argument.
8297 if (argc_
!= 1 || !args_
[0].isNumber()) {
8298 return AttachDecision::NoAction
;
8301 // Initialize the input operand.
8302 initializeInputOperand();
8304 // Guard callee is the 'fround' native function.
8305 emitNativeCalleeGuard();
8307 ValOperandId argumentId
=
8308 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8309 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8310 writer
.mathFRoundNumberResult(numberId
);
8311 writer
.returnFromIC();
8313 trackAttached("MathFRound");
8314 return AttachDecision::Attach
;
8317 static bool CanAttachInt32Pow(const Value
& baseVal
, const Value
& powerVal
) {
8318 auto valToInt32
= [](const Value
& v
) {
8322 if (v
.isBoolean()) {
8323 return int32_t(v
.toBoolean());
8325 MOZ_ASSERT(v
.isNull());
8328 int32_t base
= valToInt32(baseVal
);
8329 int32_t power
= valToInt32(powerVal
);
8331 // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
8332 // gets large enough. It's hard to determine when exactly y is "large enough",
8333 // so we don't use Int32PowResult when x != 1 and y < 0.
8334 // Note: it's important for this condition to match the code generated by
8335 // MacroAssembler::pow32 to prevent failure loops.
8340 double res
= powi(base
, power
);
8342 return mozilla::NumberIsInt32(res
, &unused
);
8345 AttachDecision
InlinableNativeIRGenerator::tryAttachMathPow() {
8346 // Need two number arguments.
8347 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8348 return AttachDecision::NoAction
;
8351 // Initialize the input operand.
8352 initializeInputOperand();
8354 // Guard callee is the 'pow' function.
8355 emitNativeCalleeGuard();
8357 ValOperandId baseId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8358 ValOperandId exponentId
=
8359 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8361 if (args_
[0].isInt32() && args_
[1].isInt32() &&
8362 CanAttachInt32Pow(args_
[0], args_
[1])) {
8363 Int32OperandId baseInt32Id
= writer
.guardToInt32(baseId
);
8364 Int32OperandId exponentInt32Id
= writer
.guardToInt32(exponentId
);
8365 writer
.int32PowResult(baseInt32Id
, exponentInt32Id
);
8367 NumberOperandId baseNumberId
= writer
.guardIsNumber(baseId
);
8368 NumberOperandId exponentNumberId
= writer
.guardIsNumber(exponentId
);
8369 writer
.doublePowResult(baseNumberId
, exponentNumberId
);
8372 writer
.returnFromIC();
8374 trackAttached("MathPow");
8375 return AttachDecision::Attach
;
8378 AttachDecision
InlinableNativeIRGenerator::tryAttachMathHypot() {
8379 // Only optimize if there are 2-4 arguments.
8380 if (argc_
< 2 || argc_
> 4) {
8381 return AttachDecision::NoAction
;
8384 for (size_t i
= 0; i
< argc_
; i
++) {
8385 if (!args_
[i
].isNumber()) {
8386 return AttachDecision::NoAction
;
8390 // Initialize the input operand.
8391 initializeInputOperand();
8393 // Guard callee is the 'hypot' native function.
8394 emitNativeCalleeGuard();
8396 ValOperandId firstId
=
8397 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8398 ValOperandId secondId
=
8399 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8401 NumberOperandId firstNumId
= writer
.guardIsNumber(firstId
);
8402 NumberOperandId secondNumId
= writer
.guardIsNumber(secondId
);
8404 ValOperandId thirdId
;
8405 ValOperandId fourthId
;
8406 NumberOperandId thirdNumId
;
8407 NumberOperandId fourthNumId
;
8411 writer
.mathHypot2NumberResult(firstNumId
, secondNumId
);
8414 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8415 thirdNumId
= writer
.guardIsNumber(thirdId
);
8416 writer
.mathHypot3NumberResult(firstNumId
, secondNumId
, thirdNumId
);
8419 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8420 fourthId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8421 thirdNumId
= writer
.guardIsNumber(thirdId
);
8422 fourthNumId
= writer
.guardIsNumber(fourthId
);
8423 writer
.mathHypot4NumberResult(firstNumId
, secondNumId
, thirdNumId
,
8427 MOZ_CRASH("Unexpected number of arguments to hypot function.");
8430 writer
.returnFromIC();
8432 trackAttached("MathHypot");
8433 return AttachDecision::Attach
;
8436 AttachDecision
InlinableNativeIRGenerator::tryAttachMathATan2() {
8437 // Requires two numbers as arguments.
8438 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8439 return AttachDecision::NoAction
;
8442 // Initialize the input operand.
8443 initializeInputOperand();
8445 // Guard callee is the 'atan2' native function.
8446 emitNativeCalleeGuard();
8448 ValOperandId yId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8449 ValOperandId xId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8451 NumberOperandId yNumberId
= writer
.guardIsNumber(yId
);
8452 NumberOperandId xNumberId
= writer
.guardIsNumber(xId
);
8454 writer
.mathAtan2NumberResult(yNumberId
, xNumberId
);
8455 writer
.returnFromIC();
8457 trackAttached("MathAtan2");
8458 return AttachDecision::Attach
;
8461 AttachDecision
InlinableNativeIRGenerator::tryAttachMathMinMax(bool isMax
) {
8462 // For now only optimize if there are 1-4 arguments.
8463 if (argc_
< 1 || argc_
> 4) {
8464 return AttachDecision::NoAction
;
8467 // Ensure all arguments are numbers.
8468 bool allInt32
= true;
8469 for (size_t i
= 0; i
< argc_
; i
++) {
8470 if (!args_
[i
].isNumber()) {
8471 return AttachDecision::NoAction
;
8473 if (!args_
[i
].isInt32()) {
8478 // Initialize the input operand.
8479 initializeInputOperand();
8481 // Guard callee is this Math function.
8482 emitNativeCalleeGuard();
8485 ValOperandId valId
=
8486 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8487 Int32OperandId resId
= writer
.guardToInt32(valId
);
8488 for (size_t i
= 1; i
< argc_
; i
++) {
8489 ValOperandId argId
=
8490 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8491 Int32OperandId argInt32Id
= writer
.guardToInt32(argId
);
8492 resId
= writer
.int32MinMax(isMax
, resId
, argInt32Id
);
8494 writer
.loadInt32Result(resId
);
8496 ValOperandId valId
=
8497 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8498 NumberOperandId resId
= writer
.guardIsNumber(valId
);
8499 for (size_t i
= 1; i
< argc_
; i
++) {
8500 ValOperandId argId
=
8501 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8502 NumberOperandId argNumId
= writer
.guardIsNumber(argId
);
8503 resId
= writer
.numberMinMax(isMax
, resId
, argNumId
);
8505 writer
.loadDoubleResult(resId
);
8508 writer
.returnFromIC();
8510 trackAttached(isMax
? "MathMax" : "MathMin");
8511 return AttachDecision::Attach
;
8514 AttachDecision
InlinableNativeIRGenerator::tryAttachSpreadMathMinMax(
8516 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Spread
||
8517 flags_
.getArgFormat() == CallFlags::FunApplyArray
);
8519 // The result will be an int32 if there is at least one argument,
8520 // and all the arguments are int32.
8521 bool int32Result
= args_
.length() > 0;
8522 for (size_t i
= 0; i
< args_
.length(); i
++) {
8523 if (!args_
[i
].isNumber()) {
8524 return AttachDecision::NoAction
;
8526 if (!args_
[i
].isInt32()) {
8527 int32Result
= false;
8531 // Initialize the input operand.
8532 initializeInputOperand();
8534 // Guard callee is this Math function.
8535 emitNativeCalleeGuard();
8537 // Load the argument array.
8538 ObjOperandId argsId
= emitLoadArgsArray();
8541 writer
.int32MinMaxArrayResult(argsId
, isMax
);
8543 writer
.numberMinMaxArrayResult(argsId
, isMax
);
8546 writer
.returnFromIC();
8548 trackAttached(isMax
? "MathMaxArray" : "MathMinArray");
8549 return AttachDecision::Attach
;
8552 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFunction(
8553 UnaryMathFunction fun
) {
8554 // Need one argument.
8556 return AttachDecision::NoAction
;
8559 if (!args_
[0].isNumber()) {
8560 return AttachDecision::NoAction
;
8563 if (math_use_fdlibm_for_sin_cos_tan() ||
8564 callee_
->realm()->creationOptions().alwaysUseFdlibm()) {
8566 case UnaryMathFunction::SinNative
:
8567 fun
= UnaryMathFunction::SinFdlibm
;
8569 case UnaryMathFunction::CosNative
:
8570 fun
= UnaryMathFunction::CosFdlibm
;
8572 case UnaryMathFunction::TanNative
:
8573 fun
= UnaryMathFunction::TanFdlibm
;
8580 // Initialize the input operand.
8581 initializeInputOperand();
8583 // Guard callee is this Math function.
8584 emitNativeCalleeGuard();
8586 ValOperandId argumentId
=
8587 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8588 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8589 writer
.mathFunctionNumberResult(numberId
, fun
);
8590 writer
.returnFromIC();
8592 trackAttached("MathFunction");
8593 return AttachDecision::Attach
;
8596 AttachDecision
InlinableNativeIRGenerator::tryAttachNumber() {
8597 // Expect a single string argument.
8598 if (argc_
!= 1 || !args_
[0].isString()) {
8599 return AttachDecision::NoAction
;
8603 if (!StringToNumber(cx_
, args_
[0].toString(), &num
)) {
8604 cx_
->recoverFromOutOfMemory();
8605 return AttachDecision::NoAction
;
8608 // Initialize the input operand.
8609 initializeInputOperand();
8611 // Guard callee is the `Number` function.
8612 emitNativeCalleeGuard();
8614 // Guard that the argument is a string.
8615 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8616 StringOperandId strId
= writer
.guardToString(argId
);
8618 // Return either an Int32 or Double result.
8620 if (mozilla::NumberIsInt32(num
, &unused
)) {
8621 Int32OperandId resultId
= writer
.guardStringToInt32(strId
);
8622 writer
.loadInt32Result(resultId
);
8624 NumberOperandId resultId
= writer
.guardStringToNumber(strId
);
8625 writer
.loadDoubleResult(resultId
);
8627 writer
.returnFromIC();
8629 trackAttached("Number");
8630 return AttachDecision::Attach
;
8633 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberParseInt() {
8634 // Expected arguments: input (string or number), optional radix (int32).
8635 if (argc_
< 1 || argc_
> 2) {
8636 return AttachDecision::NoAction
;
8638 if (!args_
[0].isString() && !args_
[0].isNumber()) {
8639 return AttachDecision::NoAction
;
8641 if (args_
[0].isDouble()) {
8642 double d
= args_
[0].toDouble();
8644 // See num_parseInt for why we have to reject numbers smaller than 1.0e-6.
8645 // Negative numbers in the exclusive range (-1, -0) return -0.
8646 bool canTruncateToInt32
=
8647 (DOUBLE_DECIMAL_IN_SHORTEST_LOW
<= d
&& d
<= double(INT32_MAX
)) ||
8648 (double(INT32_MIN
) <= d
&& d
<= -1.0) || (d
== 0.0);
8649 if (!canTruncateToInt32
) {
8650 return AttachDecision::NoAction
;
8653 if (argc_
> 1 && !args_
[1].isInt32(10)) {
8654 return AttachDecision::NoAction
;
8657 // Initialize the input operand.
8658 initializeInputOperand();
8660 // Guard callee is the 'parseInt' native function.
8661 emitNativeCalleeGuard();
8663 auto guardRadix
= [&]() {
8664 ValOperandId radixId
=
8665 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8666 Int32OperandId intRadixId
= writer
.guardToInt32(radixId
);
8667 writer
.guardSpecificInt32(intRadixId
, 10);
8671 ValOperandId inputId
=
8672 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8674 if (args_
[0].isString()) {
8675 StringOperandId strId
= writer
.guardToString(inputId
);
8677 Int32OperandId intRadixId
;
8679 intRadixId
= guardRadix();
8681 intRadixId
= writer
.loadInt32Constant(0);
8684 writer
.numberParseIntResult(strId
, intRadixId
);
8685 } else if (args_
[0].isInt32()) {
8686 Int32OperandId intId
= writer
.guardToInt32(inputId
);
8690 writer
.loadInt32Result(intId
);
8692 MOZ_ASSERT(args_
[0].isDouble());
8694 NumberOperandId numId
= writer
.guardIsNumber(inputId
);
8698 writer
.doubleParseIntResult(numId
);
8701 writer
.returnFromIC();
8703 trackAttached("NumberParseInt");
8704 return AttachDecision::Attach
;
8707 StringOperandId
IRGenerator::emitToStringGuard(ValOperandId id
,
8709 MOZ_ASSERT(CanConvertToString(v
));
8711 return writer
.guardToString(id
);
8713 if (v
.isBoolean()) {
8714 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
8715 return writer
.booleanToString(boolId
);
8718 writer
.guardIsNull(id
);
8719 return writer
.loadConstantString(cx_
->names().null
);
8721 if (v
.isUndefined()) {
8722 writer
.guardIsUndefined(id
);
8723 return writer
.loadConstantString(cx_
->names().undefined
);
8726 Int32OperandId intId
= writer
.guardToInt32(id
);
8727 return writer
.callInt32ToString(intId
);
8729 // At this point we are creating an IC that will handle
8730 // both Int32 and Double cases.
8731 MOZ_ASSERT(v
.isNumber());
8732 NumberOperandId numId
= writer
.guardIsNumber(id
);
8733 return writer
.callNumberToString(numId
);
8736 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberToString() {
8737 // Expecting no arguments or a single int32 argument.
8739 return AttachDecision::NoAction
;
8741 if (argc_
== 1 && !args_
[0].isInt32()) {
8742 return AttachDecision::NoAction
;
8745 // Ensure |this| is a primitive number value.
8746 if (!thisval_
.isNumber()) {
8747 return AttachDecision::NoAction
;
8750 // No arguments means base 10.
8753 base
= args_
[0].toInt32();
8754 if (base
< 2 || base
> 36) {
8755 return AttachDecision::NoAction
;
8758 // Non-decimal bases currently only support int32 inputs.
8759 if (base
!= 10 && !thisval_
.isInt32()) {
8760 return AttachDecision::NoAction
;
8763 MOZ_ASSERT(2 <= base
&& base
<= 36);
8765 // Initialize the input operand.
8766 initializeInputOperand();
8768 // Guard callee is the 'toString' native function.
8769 emitNativeCalleeGuard();
8771 // Initialize the |this| operand.
8772 ValOperandId thisValId
=
8773 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8775 // Guard on number and convert to string.
8777 // If an explicit base was passed, guard its value.
8779 // Guard the `base` argument is an int32.
8780 ValOperandId baseId
=
8781 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8782 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
8784 // Guard `base` is 10 for decimal toString representation.
8785 writer
.guardSpecificInt32(intBaseId
, 10);
8788 StringOperandId strId
= emitToStringGuard(thisValId
, thisval_
);
8790 // Return the string.
8791 writer
.loadStringResult(strId
);
8793 MOZ_ASSERT(argc_
> 0);
8795 // Guard the |this| value is an int32.
8796 Int32OperandId thisIntId
= writer
.guardToInt32(thisValId
);
8798 // Guard the `base` argument is an int32.
8799 ValOperandId baseId
=
8800 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8801 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
8803 // Return the string.
8804 writer
.int32ToStringWithBaseResult(thisIntId
, intBaseId
);
8807 writer
.returnFromIC();
8809 trackAttached("NumberToString");
8810 return AttachDecision::Attach
;
8813 AttachDecision
InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
8814 // Need one argument.
8816 return AttachDecision::NoAction
;
8819 if (!args_
[0].isObject()) {
8820 return AttachDecision::NoAction
;
8823 // Initialize the input operand.
8824 initializeInputOperand();
8826 // Guard callee is the 'getPrototypeOf' native function.
8827 emitNativeCalleeGuard();
8829 ValOperandId argumentId
=
8830 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8831 ObjOperandId objId
= writer
.guardToObject(argumentId
);
8833 writer
.reflectGetPrototypeOfResult(objId
);
8834 writer
.returnFromIC();
8836 trackAttached("ReflectGetPrototypeOf");
8837 return AttachDecision::Attach
;
8840 static bool AtomicsMeetsPreconditions(FixedLengthTypedArrayObject
* typedArray
,
8841 const Value
& index
) {
8842 switch (typedArray
->type()) {
8846 case Scalar::Uint16
:
8848 case Scalar::Uint32
:
8849 case Scalar::BigInt64
:
8850 case Scalar::BigUint64
:
8853 case Scalar::Float32
:
8854 case Scalar::Float64
:
8855 case Scalar::Uint8Clamped
:
8856 // Exclude floating types and Uint8Clamped.
8859 case Scalar::MaxTypedArrayViewType
:
8861 case Scalar::Simd128
:
8862 MOZ_CRASH("Unsupported TypedArray type");
8865 // Bounds check the index argument.
8867 if (!ValueIsInt64Index(index
, &indexInt64
)) {
8870 if (indexInt64
< 0 || uint64_t(indexInt64
) >= typedArray
->length()) {
8877 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
8878 if (!JitSupportsAtomics()) {
8879 return AttachDecision::NoAction
;
8882 // Need four arguments.
8884 return AttachDecision::NoAction
;
8887 // TODO: Support resizable typed arrays. (bug 1842999)
8888 // Arguments: typedArray, index (number), expected, replacement.
8889 if (!args_
[0].isObject() ||
8890 !args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
8891 return AttachDecision::NoAction
;
8893 if (!args_
[1].isNumber()) {
8894 return AttachDecision::NoAction
;
8897 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
8898 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
8899 return AttachDecision::NoAction
;
8902 Scalar::Type elementType
= typedArray
->type();
8903 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
8904 return AttachDecision::NoAction
;
8906 if (!ValueCanConvertToNumeric(elementType
, args_
[3])) {
8907 return AttachDecision::NoAction
;
8910 // Initialize the input operand.
8911 initializeInputOperand();
8913 // Guard callee is the `compareExchange` native function.
8914 emitNativeCalleeGuard();
8916 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8917 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
8918 writer
.guardShapeForClass(objId
, typedArray
->shape());
8920 // Convert index to intPtr.
8921 ValOperandId indexId
=
8922 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8923 IntPtrOperandId intPtrIndexId
=
8924 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
8926 // Convert expected value to int32/BigInt.
8927 ValOperandId expectedId
=
8928 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8929 OperandId numericExpectedId
=
8930 emitNumericGuard(expectedId
, args_
[2], elementType
);
8932 // Convert replacement value to int32/BigInt.
8933 ValOperandId replacementId
=
8934 writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8935 OperandId numericReplacementId
=
8936 emitNumericGuard(replacementId
, args_
[3], elementType
);
8938 writer
.atomicsCompareExchangeResult(objId
, intPtrIndexId
, numericExpectedId
,
8939 numericReplacementId
, typedArray
->type());
8940 writer
.returnFromIC();
8942 trackAttached("AtomicsCompareExchange");
8943 return AttachDecision::Attach
;
8946 bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
8947 if (!JitSupportsAtomics()) {
8951 // Need three arguments.
8956 // TODO: Support resizable typed arrays. (bug 1842999)
8957 // Arguments: typedArray, index (number), value.
8958 if (!args_
[0].isObject() ||
8959 !args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
8962 if (!args_
[1].isNumber()) {
8966 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
8967 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
8970 if (!ValueCanConvertToNumeric(typedArray
->type(), args_
[2])) {
8976 InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands
8977 InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() {
8978 MOZ_ASSERT(canAttachAtomicsReadWriteModify());
8980 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
8982 // Initialize the input operand.
8983 initializeInputOperand();
8985 // Guard callee is this Atomics function.
8986 emitNativeCalleeGuard();
8988 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8989 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
8990 writer
.guardShapeForClass(objId
, typedArray
->shape());
8992 // Convert index to intPtr.
8993 ValOperandId indexId
=
8994 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8995 IntPtrOperandId intPtrIndexId
=
8996 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
8998 // Convert value to int32/BigInt.
8999 ValOperandId valueId
=
9000 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9001 OperandId numericValueId
=
9002 emitNumericGuard(valueId
, args_
[2], typedArray
->type());
9004 return {objId
, intPtrIndexId
, numericValueId
};
9007 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsExchange() {
9008 if (!canAttachAtomicsReadWriteModify()) {
9009 return AttachDecision::NoAction
;
9012 auto [objId
, intPtrIndexId
, numericValueId
] =
9013 emitAtomicsReadWriteModifyOperands();
9015 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9017 writer
.atomicsExchangeResult(objId
, intPtrIndexId
, numericValueId
,
9018 typedArray
->type());
9019 writer
.returnFromIC();
9021 trackAttached("AtomicsExchange");
9022 return AttachDecision::Attach
;
9025 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAdd() {
9026 if (!canAttachAtomicsReadWriteModify()) {
9027 return AttachDecision::NoAction
;
9030 auto [objId
, intPtrIndexId
, numericValueId
] =
9031 emitAtomicsReadWriteModifyOperands();
9033 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9034 bool forEffect
= ignoresResult();
9036 writer
.atomicsAddResult(objId
, intPtrIndexId
, numericValueId
,
9037 typedArray
->type(), forEffect
);
9038 writer
.returnFromIC();
9040 trackAttached("AtomicsAdd");
9041 return AttachDecision::Attach
;
9044 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsSub() {
9045 if (!canAttachAtomicsReadWriteModify()) {
9046 return AttachDecision::NoAction
;
9049 auto [objId
, intPtrIndexId
, numericValueId
] =
9050 emitAtomicsReadWriteModifyOperands();
9052 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9053 bool forEffect
= ignoresResult();
9055 writer
.atomicsSubResult(objId
, intPtrIndexId
, numericValueId
,
9056 typedArray
->type(), forEffect
);
9057 writer
.returnFromIC();
9059 trackAttached("AtomicsSub");
9060 return AttachDecision::Attach
;
9063 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAnd() {
9064 if (!canAttachAtomicsReadWriteModify()) {
9065 return AttachDecision::NoAction
;
9068 auto [objId
, intPtrIndexId
, numericValueId
] =
9069 emitAtomicsReadWriteModifyOperands();
9071 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9072 bool forEffect
= ignoresResult();
9074 writer
.atomicsAndResult(objId
, intPtrIndexId
, numericValueId
,
9075 typedArray
->type(), forEffect
);
9076 writer
.returnFromIC();
9078 trackAttached("AtomicsAnd");
9079 return AttachDecision::Attach
;
9082 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsOr() {
9083 if (!canAttachAtomicsReadWriteModify()) {
9084 return AttachDecision::NoAction
;
9087 auto [objId
, intPtrIndexId
, numericValueId
] =
9088 emitAtomicsReadWriteModifyOperands();
9090 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9091 bool forEffect
= ignoresResult();
9093 writer
.atomicsOrResult(objId
, intPtrIndexId
, numericValueId
,
9094 typedArray
->type(), forEffect
);
9095 writer
.returnFromIC();
9097 trackAttached("AtomicsOr");
9098 return AttachDecision::Attach
;
9101 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsXor() {
9102 if (!canAttachAtomicsReadWriteModify()) {
9103 return AttachDecision::NoAction
;
9106 auto [objId
, intPtrIndexId
, numericValueId
] =
9107 emitAtomicsReadWriteModifyOperands();
9109 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9110 bool forEffect
= ignoresResult();
9112 writer
.atomicsXorResult(objId
, intPtrIndexId
, numericValueId
,
9113 typedArray
->type(), forEffect
);
9114 writer
.returnFromIC();
9116 trackAttached("AtomicsXor");
9117 return AttachDecision::Attach
;
9120 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
9121 if (!JitSupportsAtomics()) {
9122 return AttachDecision::NoAction
;
9125 // Need two arguments.
9127 return AttachDecision::NoAction
;
9130 // TODO: Support resizable typed arrays. (bug 1842999)
9131 // Arguments: typedArray, index (number).
9132 if (!args_
[0].isObject() ||
9133 !args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
9134 return AttachDecision::NoAction
;
9136 if (!args_
[1].isNumber()) {
9137 return AttachDecision::NoAction
;
9140 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9141 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9142 return AttachDecision::NoAction
;
9145 // Initialize the input operand.
9146 initializeInputOperand();
9148 // Guard callee is the `load` native function.
9149 emitNativeCalleeGuard();
9151 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9152 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9153 writer
.guardShapeForClass(objId
, typedArray
->shape());
9155 // Convert index to intPtr.
9156 ValOperandId indexId
=
9157 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9158 IntPtrOperandId intPtrIndexId
=
9159 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9161 writer
.atomicsLoadResult(objId
, intPtrIndexId
, typedArray
->type());
9162 writer
.returnFromIC();
9164 trackAttached("AtomicsLoad");
9165 return AttachDecision::Attach
;
9168 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsStore() {
9169 if (!JitSupportsAtomics()) {
9170 return AttachDecision::NoAction
;
9173 // Need three arguments.
9175 return AttachDecision::NoAction
;
9178 // Atomics.store() is annoying because it returns the result of converting the
9179 // value by ToInteger(), not the input value, nor the result of converting the
9180 // value by ToInt32(). It is especially annoying because almost nobody uses
9181 // the result value.
9183 // As an expedient compromise, therefore, we inline only if the result is
9184 // obviously unused or if the argument is already Int32 and thus requires no
9187 // TODO: Support resizable typed arrays. (bug 1842999)
9188 // Arguments: typedArray, index (number), value.
9189 if (!args_
[0].isObject() ||
9190 !args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
9191 return AttachDecision::NoAction
;
9193 if (!args_
[1].isNumber()) {
9194 return AttachDecision::NoAction
;
9197 auto* typedArray
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
9198 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9199 return AttachDecision::NoAction
;
9202 Scalar::Type elementType
= typedArray
->type();
9203 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
9204 return AttachDecision::NoAction
;
9207 bool guardIsInt32
= !Scalar::isBigIntType(elementType
) && !ignoresResult();
9209 if (guardIsInt32
&& !args_
[2].isInt32()) {
9210 return AttachDecision::NoAction
;
9213 // Initialize the input operand.
9214 initializeInputOperand();
9216 // Guard callee is the `store` native function.
9217 emitNativeCalleeGuard();
9219 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9220 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9221 writer
.guardShapeForClass(objId
, typedArray
->shape());
9223 // Convert index to intPtr.
9224 ValOperandId indexId
=
9225 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9226 IntPtrOperandId intPtrIndexId
=
9227 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9229 // Ensure value is int32 or BigInt.
9230 ValOperandId valueId
=
9231 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9232 OperandId numericValueId
;
9234 numericValueId
= writer
.guardToInt32(valueId
);
9236 numericValueId
= emitNumericGuard(valueId
, args_
[2], elementType
);
9239 writer
.atomicsStoreResult(objId
, intPtrIndexId
, numericValueId
,
9240 typedArray
->type());
9241 writer
.returnFromIC();
9243 trackAttached("AtomicsStore");
9244 return AttachDecision::Attach
;
9247 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsIsLockFree() {
9248 // Need one argument.
9250 return AttachDecision::NoAction
;
9253 if (!args_
[0].isInt32()) {
9254 return AttachDecision::NoAction
;
9257 // Initialize the input operand.
9258 initializeInputOperand();
9260 // Guard callee is the `isLockFree` native function.
9261 emitNativeCalleeGuard();
9263 // Ensure value is int32.
9264 ValOperandId valueId
=
9265 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9266 Int32OperandId int32ValueId
= writer
.guardToInt32(valueId
);
9268 writer
.atomicsIsLockFreeResult(int32ValueId
);
9269 writer
.returnFromIC();
9271 trackAttached("AtomicsIsLockFree");
9272 return AttachDecision::Attach
;
9275 AttachDecision
InlinableNativeIRGenerator::tryAttachBoolean() {
9276 // Need zero or one argument.
9278 return AttachDecision::NoAction
;
9281 // Initialize the input operand.
9282 initializeInputOperand();
9284 // Guard callee is the 'Boolean' native function.
9285 emitNativeCalleeGuard();
9288 writer
.loadBooleanResult(false);
9290 ValOperandId valId
=
9291 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9293 writer
.loadValueTruthyResult(valId
);
9296 writer
.returnFromIC();
9298 trackAttached("Boolean");
9299 return AttachDecision::Attach
;
9302 AttachDecision
InlinableNativeIRGenerator::tryAttachBailout() {
9303 // Expecting no arguments.
9305 return AttachDecision::NoAction
;
9308 // Initialize the input operand.
9309 initializeInputOperand();
9311 // Guard callee is the 'bailout' native function.
9312 emitNativeCalleeGuard();
9315 writer
.loadUndefinedResult();
9316 writer
.returnFromIC();
9318 trackAttached("Bailout");
9319 return AttachDecision::Attach
;
9322 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertFloat32() {
9323 // Expecting two arguments.
9325 return AttachDecision::NoAction
;
9328 // Initialize the input operand.
9329 initializeInputOperand();
9331 // Guard callee is the 'assertFloat32' native function.
9332 emitNativeCalleeGuard();
9334 // TODO: Warp doesn't yet optimize Float32 (bug 1655773).
9336 // NOP when not in IonMonkey.
9337 writer
.loadUndefinedResult();
9338 writer
.returnFromIC();
9340 trackAttached("AssertFloat32");
9341 return AttachDecision::Attach
;
9344 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertRecoveredOnBailout() {
9345 // Expecting two arguments.
9347 return AttachDecision::NoAction
;
9350 // (Fuzzing unsafe) testing function which must be called with a constant
9351 // boolean as its second argument.
9352 bool mustBeRecovered
= args_
[1].toBoolean();
9354 // Initialize the input operand.
9355 initializeInputOperand();
9357 // Guard callee is the 'assertRecoveredOnBailout' native function.
9358 emitNativeCalleeGuard();
9360 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9362 writer
.assertRecoveredOnBailoutResult(valId
, mustBeRecovered
);
9363 writer
.returnFromIC();
9365 trackAttached("AssertRecoveredOnBailout");
9366 return AttachDecision::Attach
;
9369 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIs() {
9370 // Need two arguments.
9372 return AttachDecision::NoAction
;
9375 // Initialize the input operand.
9376 initializeInputOperand();
9378 // Guard callee is the `is` native function.
9379 emitNativeCalleeGuard();
9381 ValOperandId lhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9382 ValOperandId rhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9384 HandleValue lhs
= args_
[0];
9385 HandleValue rhs
= args_
[1];
9387 if (!isFirstStub()) {
9388 writer
.sameValueResult(lhsId
, rhsId
);
9389 } else if (lhs
.isNumber() && rhs
.isNumber() &&
9390 !(lhs
.isInt32() && rhs
.isInt32())) {
9391 NumberOperandId lhsNumId
= writer
.guardIsNumber(lhsId
);
9392 NumberOperandId rhsNumId
= writer
.guardIsNumber(rhsId
);
9393 writer
.compareDoubleSameValueResult(lhsNumId
, rhsNumId
);
9394 } else if (!SameType(lhs
, rhs
)) {
9395 // Compare tags for strictly different types.
9396 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
9397 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
9398 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
9399 writer
.loadBooleanResult(false);
9401 MOZ_ASSERT(lhs
.type() == rhs
.type());
9402 MOZ_ASSERT(lhs
.type() != JS::ValueType::Double
);
9404 switch (lhs
.type()) {
9405 case JS::ValueType::Int32
: {
9406 Int32OperandId lhsIntId
= writer
.guardToInt32(lhsId
);
9407 Int32OperandId rhsIntId
= writer
.guardToInt32(rhsId
);
9408 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9411 case JS::ValueType::Boolean
: {
9412 Int32OperandId lhsIntId
= writer
.guardBooleanToInt32(lhsId
);
9413 Int32OperandId rhsIntId
= writer
.guardBooleanToInt32(rhsId
);
9414 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9417 case JS::ValueType::Undefined
: {
9418 writer
.guardIsUndefined(lhsId
);
9419 writer
.guardIsUndefined(rhsId
);
9420 writer
.loadBooleanResult(true);
9423 case JS::ValueType::Null
: {
9424 writer
.guardIsNull(lhsId
);
9425 writer
.guardIsNull(rhsId
);
9426 writer
.loadBooleanResult(true);
9429 case JS::ValueType::String
: {
9430 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
9431 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
9432 writer
.compareStringResult(JSOp::StrictEq
, lhsStrId
, rhsStrId
);
9435 case JS::ValueType::Symbol
: {
9436 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
9437 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
9438 writer
.compareSymbolResult(JSOp::StrictEq
, lhsSymId
, rhsSymId
);
9441 case JS::ValueType::BigInt
: {
9442 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
9443 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
9444 writer
.compareBigIntResult(JSOp::StrictEq
, lhsBigIntId
, rhsBigIntId
);
9447 case JS::ValueType::Object
: {
9448 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
9449 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
9450 writer
.compareObjectResult(JSOp::StrictEq
, lhsObjId
, rhsObjId
);
9454 #ifdef ENABLE_RECORD_TUPLE
9455 case ValueType::ExtendedPrimitive
:
9457 case JS::ValueType::Double
:
9458 case JS::ValueType::Magic
:
9459 case JS::ValueType::PrivateGCThing
:
9460 MOZ_CRASH("Unexpected type");
9464 writer
.returnFromIC();
9466 trackAttached("ObjectIs");
9467 return AttachDecision::Attach
;
9470 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIsPrototypeOf() {
9471 // Ensure |this| is an object.
9472 if (!thisval_
.isObject()) {
9473 return AttachDecision::NoAction
;
9476 // Need a single argument.
9478 return AttachDecision::NoAction
;
9481 // Initialize the input operand.
9482 initializeInputOperand();
9484 // Guard callee is the `isPrototypeOf` native function.
9485 emitNativeCalleeGuard();
9487 // Guard that |this| is an object.
9488 ValOperandId thisValId
=
9489 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9490 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9492 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9494 writer
.loadInstanceOfObjectResult(argId
, thisObjId
);
9495 writer
.returnFromIC();
9497 trackAttached("ObjectIsPrototypeOf");
9498 return AttachDecision::Attach
;
9501 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectKeys() {
9502 // Only handle argc <= 1.
9504 return AttachDecision::NoAction
;
9507 // Do not attach any IC if the argument is not an object.
9508 if (!args_
[0].isObject()) {
9509 return AttachDecision::NoAction
;
9511 // Do not attach any IC if the argument is a Proxy. While implementation could
9512 // work with proxies the goal of this implementation is to provide an
9513 // optimization for calls of `Object.keys(obj)` where there is no side-effect,
9514 // and where the computation of the array of property name can be moved.
9515 const JSClass
* clasp
= args_
[0].toObject().getClass();
9516 if (clasp
->isProxyObject()) {
9517 return AttachDecision::NoAction
;
9520 // Generate cache IR code to attach a new inline cache which will delegate the
9521 // call to Object.keys to the native function.
9522 initializeInputOperand();
9524 // Guard callee is the 'keys' native function.
9525 emitNativeCalleeGuard();
9527 // Implicit: Note `Object.keys` is a property of the `Object` global. The fact
9528 // that we are in this function implies that we already identify the function
9529 // as being the proper one. Thus there should not be any need to validate that
9530 // this is the proper function. (test: ion/object-keys-05)
9532 // Guard `arg0` is an object.
9533 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9534 ObjOperandId argObjId
= writer
.guardToObject(argId
);
9536 // Guard against proxies.
9537 writer
.guardIsNotProxy(argObjId
);
9539 // Compute the keys array.
9540 writer
.objectKeysResult(argObjId
);
9542 writer
.returnFromIC();
9544 trackAttached("ObjectKeys");
9545 return AttachDecision::Attach
;
9548 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectToString() {
9549 // Expecting no arguments.
9551 return AttachDecision::NoAction
;
9554 // Ensure |this| is an object.
9555 if (!thisval_
.isObject()) {
9556 return AttachDecision::NoAction
;
9559 // Don't attach if the object has @@toStringTag or is a proxy.
9560 if (!ObjectClassToString(cx_
, &thisval_
.toObject())) {
9561 return AttachDecision::NoAction
;
9564 // Initialize the input operand.
9565 initializeInputOperand();
9567 // Guard callee is the 'toString' native function.
9568 emitNativeCalleeGuard();
9570 // Guard that |this| is an object.
9571 ValOperandId thisValId
=
9572 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9573 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9575 writer
.objectToStringResult(thisObjId
);
9576 writer
.returnFromIC();
9578 trackAttached("ObjectToString");
9579 return AttachDecision::Attach
;
9582 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsIntN() {
9583 // Need two arguments (Int32, BigInt).
9584 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9585 return AttachDecision::NoAction
;
9588 // Negative bits throws an error.
9589 if (args_
[0].toInt32() < 0) {
9590 return AttachDecision::NoAction
;
9593 // Initialize the input operand.
9594 initializeInputOperand();
9596 // Guard callee is the 'BigInt.asIntN' native function.
9597 emitNativeCalleeGuard();
9599 // Convert bits to int32.
9600 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9601 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9603 // Number of bits mustn't be negative.
9604 writer
.guardInt32IsNonNegative(int32BitsId
);
9606 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9607 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9609 writer
.bigIntAsIntNResult(int32BitsId
, bigIntId
);
9610 writer
.returnFromIC();
9612 trackAttached("BigIntAsIntN");
9613 return AttachDecision::Attach
;
9616 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsUintN() {
9617 // Need two arguments (Int32, BigInt).
9618 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9619 return AttachDecision::NoAction
;
9622 // Negative bits throws an error.
9623 if (args_
[0].toInt32() < 0) {
9624 return AttachDecision::NoAction
;
9627 // Initialize the input operand.
9628 initializeInputOperand();
9630 // Guard callee is the 'BigInt.asUintN' native function.
9631 emitNativeCalleeGuard();
9633 // Convert bits to int32.
9634 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9635 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9637 // Number of bits mustn't be negative.
9638 writer
.guardInt32IsNonNegative(int32BitsId
);
9640 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9641 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9643 writer
.bigIntAsUintNResult(int32BitsId
, bigIntId
);
9644 writer
.returnFromIC();
9646 trackAttached("BigIntAsUintN");
9647 return AttachDecision::Attach
;
9650 AttachDecision
InlinableNativeIRGenerator::tryAttachSetHas() {
9651 // Ensure |this| is a SetObject.
9652 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
9653 return AttachDecision::NoAction
;
9656 // Need a single argument.
9658 return AttachDecision::NoAction
;
9661 // Initialize the input operand.
9662 initializeInputOperand();
9664 // Guard callee is the 'has' native function.
9665 emitNativeCalleeGuard();
9667 // Guard |this| is a SetObject.
9668 ValOperandId thisValId
=
9669 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9670 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9671 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Set
);
9673 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9675 #ifndef JS_CODEGEN_X86
9676 // Assume the hash key will likely always have the same type when attaching
9677 // the first stub. If the call is polymorphic on the hash key, attach a stub
9678 // which handles any value.
9679 if (isFirstStub()) {
9680 switch (args_
[0].type()) {
9681 case ValueType::Double
:
9682 case ValueType::Int32
:
9683 case ValueType::Boolean
:
9684 case ValueType::Undefined
:
9685 case ValueType::Null
: {
9686 writer
.guardToNonGCThing(argId
);
9687 writer
.setHasNonGCThingResult(objId
, argId
);
9690 case ValueType::String
: {
9691 StringOperandId strId
= writer
.guardToString(argId
);
9692 writer
.setHasStringResult(objId
, strId
);
9695 case ValueType::Symbol
: {
9696 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9697 writer
.setHasSymbolResult(objId
, symId
);
9700 case ValueType::BigInt
: {
9701 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9702 writer
.setHasBigIntResult(objId
, bigIntId
);
9705 case ValueType::Object
: {
9706 // Currently only supported on 64-bit platforms.
9708 ObjOperandId valId
= writer
.guardToObject(argId
);
9709 writer
.setHasObjectResult(objId
, valId
);
9711 writer
.setHasResult(objId
, argId
);
9716 # ifdef ENABLE_RECORD_TUPLE
9717 case ValueType::ExtendedPrimitive
:
9719 case ValueType::Magic
:
9720 case ValueType::PrivateGCThing
:
9721 MOZ_CRASH("Unexpected type");
9724 writer
.setHasResult(objId
, argId
);
9727 // The optimized versions require too many registers on x86.
9728 writer
.setHasResult(objId
, argId
);
9731 writer
.returnFromIC();
9733 trackAttached("SetHas");
9734 return AttachDecision::Attach
;
9737 AttachDecision
InlinableNativeIRGenerator::tryAttachSetSize() {
9738 // Ensure |this| is a SetObject.
9739 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
9740 return AttachDecision::NoAction
;
9743 // Expecting no arguments.
9745 return AttachDecision::NoAction
;
9748 // Initialize the input operand.
9749 initializeInputOperand();
9751 // Guard callee is the 'size' native function.
9752 emitNativeCalleeGuard();
9754 // Guard |this| is a SetObject.
9755 ValOperandId thisValId
=
9756 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9757 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9758 writer
.guardClass(objId
, GuardClassKind::Set
);
9760 writer
.setSizeResult(objId
);
9761 writer
.returnFromIC();
9763 trackAttached("SetSize");
9764 return AttachDecision::Attach
;
9767 AttachDecision
InlinableNativeIRGenerator::tryAttachMapHas() {
9768 // Ensure |this| is a MapObject.
9769 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
9770 return AttachDecision::NoAction
;
9773 // Need a single argument.
9775 return AttachDecision::NoAction
;
9778 // Initialize the input operand.
9779 initializeInputOperand();
9781 // Guard callee is the 'has' native function.
9782 emitNativeCalleeGuard();
9784 // Guard |this| is a MapObject.
9785 ValOperandId thisValId
=
9786 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9787 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9788 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
9790 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9792 #ifndef JS_CODEGEN_X86
9793 // Assume the hash key will likely always have the same type when attaching
9794 // the first stub. If the call is polymorphic on the hash key, attach a stub
9795 // which handles any value.
9796 if (isFirstStub()) {
9797 switch (args_
[0].type()) {
9798 case ValueType::Double
:
9799 case ValueType::Int32
:
9800 case ValueType::Boolean
:
9801 case ValueType::Undefined
:
9802 case ValueType::Null
: {
9803 writer
.guardToNonGCThing(argId
);
9804 writer
.mapHasNonGCThingResult(objId
, argId
);
9807 case ValueType::String
: {
9808 StringOperandId strId
= writer
.guardToString(argId
);
9809 writer
.mapHasStringResult(objId
, strId
);
9812 case ValueType::Symbol
: {
9813 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9814 writer
.mapHasSymbolResult(objId
, symId
);
9817 case ValueType::BigInt
: {
9818 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9819 writer
.mapHasBigIntResult(objId
, bigIntId
);
9822 case ValueType::Object
: {
9823 // Currently only supported on 64-bit platforms.
9825 ObjOperandId valId
= writer
.guardToObject(argId
);
9826 writer
.mapHasObjectResult(objId
, valId
);
9828 writer
.mapHasResult(objId
, argId
);
9833 # ifdef ENABLE_RECORD_TUPLE
9834 case ValueType::ExtendedPrimitive
:
9836 case ValueType::Magic
:
9837 case ValueType::PrivateGCThing
:
9838 MOZ_CRASH("Unexpected type");
9841 writer
.mapHasResult(objId
, argId
);
9844 // The optimized versions require too many registers on x86.
9845 writer
.mapHasResult(objId
, argId
);
9848 writer
.returnFromIC();
9850 trackAttached("MapHas");
9851 return AttachDecision::Attach
;
9854 AttachDecision
InlinableNativeIRGenerator::tryAttachMapGet() {
9855 // Ensure |this| is a MapObject.
9856 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
9857 return AttachDecision::NoAction
;
9860 // Need a single argument.
9862 return AttachDecision::NoAction
;
9865 // Initialize the input operand.
9866 initializeInputOperand();
9868 // Guard callee is the 'get' native function.
9869 emitNativeCalleeGuard();
9871 // Guard |this| is a MapObject.
9872 ValOperandId thisValId
=
9873 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9874 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9875 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
9877 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9879 #ifndef JS_CODEGEN_X86
9880 // Assume the hash key will likely always have the same type when attaching
9881 // the first stub. If the call is polymorphic on the hash key, attach a stub
9882 // which handles any value.
9883 if (isFirstStub()) {
9884 switch (args_
[0].type()) {
9885 case ValueType::Double
:
9886 case ValueType::Int32
:
9887 case ValueType::Boolean
:
9888 case ValueType::Undefined
:
9889 case ValueType::Null
: {
9890 writer
.guardToNonGCThing(argId
);
9891 writer
.mapGetNonGCThingResult(objId
, argId
);
9894 case ValueType::String
: {
9895 StringOperandId strId
= writer
.guardToString(argId
);
9896 writer
.mapGetStringResult(objId
, strId
);
9899 case ValueType::Symbol
: {
9900 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9901 writer
.mapGetSymbolResult(objId
, symId
);
9904 case ValueType::BigInt
: {
9905 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9906 writer
.mapGetBigIntResult(objId
, bigIntId
);
9909 case ValueType::Object
: {
9910 // Currently only supported on 64-bit platforms.
9912 ObjOperandId valId
= writer
.guardToObject(argId
);
9913 writer
.mapGetObjectResult(objId
, valId
);
9915 writer
.mapGetResult(objId
, argId
);
9920 # ifdef ENABLE_RECORD_TUPLE
9921 case ValueType::ExtendedPrimitive
:
9923 case ValueType::Magic
:
9924 case ValueType::PrivateGCThing
:
9925 MOZ_CRASH("Unexpected type");
9928 writer
.mapGetResult(objId
, argId
);
9931 // The optimized versions require too many registers on x86.
9932 writer
.mapGetResult(objId
, argId
);
9935 writer
.returnFromIC();
9937 trackAttached("MapGet");
9938 return AttachDecision::Attach
;
9941 AttachDecision
CallIRGenerator::tryAttachFunCall(HandleFunction callee
) {
9942 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
9944 if (callee
->native() != fun_call
) {
9945 return AttachDecision::NoAction
;
9948 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
9949 return AttachDecision::NoAction
;
9951 RootedFunction
target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
9953 bool isScripted
= target
->hasJitEntry();
9954 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
9956 if (target
->isClassConstructor()) {
9957 return AttachDecision::NoAction
;
9959 Int32OperandId
argcId(writer
.setInputOperandId(0));
9961 CallFlags
targetFlags(CallFlags::FunCall
);
9962 if (mode_
== ICState::Mode::Specialized
) {
9963 if (cx_
->realm() == target
->realm()) {
9964 targetFlags
.setIsSameRealm();
9968 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&& argc_
> 0) {
9969 // The stack layout is already in the correct form for calls with at least
9974 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
9975 // Callee <-- argc+1
9976 // ThisValue <-- argc
9977 // Args: | Arg0 | <-- argc-1
9978 // | Arg1 | <-- argc-2
9982 // When passing |argc-1| as the number of arguments, we get:
9984 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
9985 // Callee <-- (argc-1)+1 = argc = ThisValue
9986 // ThisValue <-- (argc-1) = argc-1 = Arg0
9987 // Args: | Arg0 | <-- (argc-1)-1 = argc-2 = Arg1
9988 // | Arg1 | <-- (argc-1)-2 = argc-3 = Arg2
9993 // This allows to call |loadArgumentFixedSlot(ArgumentKind::Arg0)| and we
9994 // still load the correct argument index from |ArgumentKind::Arg1|.
9996 // When no arguments are passed, i.e. |argc==0|, we have to replace
9997 // |ArgumentKind::Arg0| with the undefined value. But we don't yet support
9999 HandleValue newTarget
= NullHandleValue
;
10000 HandleValue thisValue
= args_
[0];
10001 HandleValueArray args
=
10002 HandleValueArray::subarray(args_
, 1, args_
.length() - 1);
10004 // Check for specific native-function optimizations.
10005 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10006 args
, targetFlags
);
10007 TRY_ATTACH(nativeGen
.tryAttachStub());
10010 ObjOperandId thisObjId
= emitFunCallGuard(argcId
);
10012 if (mode_
== ICState::Mode::Specialized
) {
10013 // Ensure that |this| is the expected target function.
10014 emitCalleeGuard(thisObjId
, target
);
10017 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10018 ClampFixedArgc(argc_
));
10020 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
10021 ClampFixedArgc(argc_
));
10024 // Guard that |this| is a function.
10025 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
10027 // Guard that function is not a class constructor.
10028 writer
.guardNotClassConstructor(thisObjId
);
10031 writer
.guardFunctionHasJitEntry(thisObjId
, /*isConstructing =*/false);
10032 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10033 ClampFixedArgc(argc_
));
10035 writer
.guardFunctionHasNoJitEntry(thisObjId
);
10036 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
,
10037 ClampFixedArgc(argc_
));
10041 writer
.returnFromIC();
10044 trackAttached("Scripted fun_call");
10046 trackAttached("Native fun_call");
10049 return AttachDecision::Attach
;
10052 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArray(
10053 bool isPossiblyWrapped
) {
10054 // Self-hosted code calls this with a single object argument.
10055 MOZ_ASSERT(argc_
== 1);
10056 MOZ_ASSERT(args_
[0].isObject());
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
.isTypedArrayResult(objArgId
, isPossiblyWrapped
);
10066 writer
.returnFromIC();
10068 trackAttached(isPossiblyWrapped
? "IsPossiblyWrappedTypedArray"
10070 return AttachDecision::Attach
;
10073 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArrayConstructor() {
10074 // Self-hosted code calls this with a single object argument.
10075 MOZ_ASSERT(argc_
== 1);
10076 MOZ_ASSERT(args_
[0].isObject());
10078 // Initialize the input operand.
10079 initializeInputOperand();
10081 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10083 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10084 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10085 writer
.isTypedArrayConstructorResult(objArgId
);
10086 writer
.returnFromIC();
10088 trackAttached("IsTypedArrayConstructor");
10089 return AttachDecision::Attach
;
10092 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() {
10093 // Self-hosted code calls this with a single TypedArrayObject argument.
10094 MOZ_ASSERT(argc_
== 1);
10095 MOZ_ASSERT(args_
[0].isObject());
10096 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10098 // TODO: Support resizable typed arrays. (bug 1842999)
10099 if (!args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
10100 return AttachDecision::NoAction
;
10103 auto* tarr
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
10105 // Initialize the input operand.
10106 initializeInputOperand();
10108 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10110 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10111 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10112 writer
.guardIsFixedLengthTypedArray(objArgId
);
10113 if (tarr
->byteOffset() <= INT32_MAX
) {
10114 writer
.arrayBufferViewByteOffsetInt32Result(objArgId
);
10116 writer
.arrayBufferViewByteOffsetDoubleResult(objArgId
);
10118 writer
.returnFromIC();
10120 trackAttached("IntrinsicTypedArrayByteOffset");
10121 return AttachDecision::Attach
;
10124 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayElementSize() {
10125 // Self-hosted code calls this with a single TypedArrayObject argument.
10126 MOZ_ASSERT(argc_
== 1);
10127 MOZ_ASSERT(args_
[0].isObject());
10128 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10130 // Initialize the input operand.
10131 initializeInputOperand();
10133 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10135 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10136 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10137 writer
.typedArrayElementSizeResult(objArgId
);
10138 writer
.returnFromIC();
10140 trackAttached("TypedArrayElementSize");
10141 return AttachDecision::Attach
;
10144 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayLength(
10145 bool isPossiblyWrapped
) {
10146 // Self-hosted code calls this with a single, possibly wrapped,
10147 // TypedArrayObject argument.
10148 MOZ_ASSERT(argc_
== 1);
10149 MOZ_ASSERT(args_
[0].isObject());
10151 // Only optimize when the object isn't a wrapper.
10152 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10153 return AttachDecision::NoAction
;
10156 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10158 // TODO: Support resizable typed arrays. (bug 1842999)
10159 if (!args_
[0].toObject().is
<FixedLengthTypedArrayObject
>()) {
10160 return AttachDecision::NoAction
;
10163 auto* tarr
= &args_
[0].toObject().as
<FixedLengthTypedArrayObject
>();
10165 // Initialize the input operand.
10166 initializeInputOperand();
10168 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10170 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10171 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10173 if (isPossiblyWrapped
) {
10174 writer
.guardIsNotProxy(objArgId
);
10177 writer
.guardIsFixedLengthTypedArray(objArgId
);
10178 if (tarr
->length() <= INT32_MAX
) {
10179 writer
.loadArrayBufferViewLengthInt32Result(objArgId
);
10181 writer
.loadArrayBufferViewLengthDoubleResult(objArgId
);
10183 writer
.returnFromIC();
10185 trackAttached("IntrinsicTypedArrayLength");
10186 return AttachDecision::Attach
;
10189 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayBufferByteLength(
10190 bool isPossiblyWrapped
) {
10191 // Self-hosted code calls this with a single, possibly wrapped,
10192 // ArrayBufferObject argument.
10193 MOZ_ASSERT(argc_
== 1);
10194 MOZ_ASSERT(args_
[0].isObject());
10196 // Only optimize when the object isn't a wrapper.
10197 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10198 return AttachDecision::NoAction
;
10201 MOZ_ASSERT(args_
[0].toObject().is
<ArrayBufferObject
>());
10203 auto* buffer
= &args_
[0].toObject().as
<ArrayBufferObject
>();
10205 // TODO: Support resizable buffers. (bug 1842999)
10206 if (buffer
->isResizable()) {
10207 return AttachDecision::NoAction
;
10210 // Initialize the input operand.
10211 initializeInputOperand();
10213 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10215 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10216 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10218 if (isPossiblyWrapped
) {
10219 writer
.guardIsNotProxy(objArgId
);
10222 if (buffer
->byteLength() <= INT32_MAX
) {
10223 writer
.loadArrayBufferByteLengthInt32Result(objArgId
);
10225 writer
.loadArrayBufferByteLengthDoubleResult(objArgId
);
10227 writer
.returnFromIC();
10229 trackAttached("ArrayBufferByteLength");
10230 return AttachDecision::Attach
;
10233 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructing() {
10234 // Self-hosted code calls this with no arguments in function scripts.
10235 MOZ_ASSERT(argc_
== 0);
10236 MOZ_ASSERT(script()->isFunction());
10238 // Initialize the input operand.
10239 initializeInputOperand();
10241 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10243 writer
.frameIsConstructingResult();
10244 writer
.returnFromIC();
10246 trackAttached("IsConstructing");
10247 return AttachDecision::Attach
;
10251 InlinableNativeIRGenerator::tryAttachGetNextMapSetEntryForIterator(bool isMap
) {
10252 // Self-hosted code calls this with two objects.
10253 MOZ_ASSERT(argc_
== 2);
10255 MOZ_ASSERT(args_
[0].toObject().is
<MapIteratorObject
>());
10257 MOZ_ASSERT(args_
[0].toObject().is
<SetIteratorObject
>());
10259 MOZ_ASSERT(args_
[1].toObject().is
<ArrayObject
>());
10261 // Initialize the input operand.
10262 initializeInputOperand();
10264 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10266 ValOperandId iterId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10267 ObjOperandId objIterId
= writer
.guardToObject(iterId
);
10269 ValOperandId resultArrId
=
10270 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10271 ObjOperandId objResultArrId
= writer
.guardToObject(resultArrId
);
10273 writer
.getNextMapSetEntryForIteratorResult(objIterId
, objResultArrId
, isMap
);
10274 writer
.returnFromIC();
10276 trackAttached("GetNextMapSetEntryForIterator");
10277 return AttachDecision::Attach
;
10280 AttachDecision
InlinableNativeIRGenerator::tryAttachNewArrayIterator() {
10281 // Self-hosted code calls this without any arguments
10282 MOZ_ASSERT(argc_
== 0);
10284 JSObject
* templateObj
= NewArrayIteratorTemplate(cx_
);
10285 if (!templateObj
) {
10286 cx_
->recoverFromOutOfMemory();
10287 return AttachDecision::NoAction
;
10290 // Initialize the input operand.
10291 initializeInputOperand();
10293 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10295 writer
.newArrayIteratorResult(templateObj
);
10296 writer
.returnFromIC();
10298 trackAttached("NewArrayIterator");
10299 return AttachDecision::Attach
;
10302 AttachDecision
InlinableNativeIRGenerator::tryAttachNewStringIterator() {
10303 // Self-hosted code calls this without any arguments
10304 MOZ_ASSERT(argc_
== 0);
10306 JSObject
* templateObj
= NewStringIteratorTemplate(cx_
);
10307 if (!templateObj
) {
10308 cx_
->recoverFromOutOfMemory();
10309 return AttachDecision::NoAction
;
10312 // Initialize the input operand.
10313 initializeInputOperand();
10315 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10317 writer
.newStringIteratorResult(templateObj
);
10318 writer
.returnFromIC();
10320 trackAttached("NewStringIterator");
10321 return AttachDecision::Attach
;
10324 AttachDecision
InlinableNativeIRGenerator::tryAttachNewRegExpStringIterator() {
10325 // Self-hosted code calls this without any arguments
10326 MOZ_ASSERT(argc_
== 0);
10328 JSObject
* templateObj
= NewRegExpStringIteratorTemplate(cx_
);
10329 if (!templateObj
) {
10330 cx_
->recoverFromOutOfMemory();
10331 return AttachDecision::NoAction
;
10334 // Initialize the input operand.
10335 initializeInputOperand();
10337 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10339 writer
.newRegExpStringIteratorResult(templateObj
);
10340 writer
.returnFromIC();
10342 trackAttached("NewRegExpStringIterator");
10343 return AttachDecision::Attach
;
10347 InlinableNativeIRGenerator::tryAttachArrayIteratorPrototypeOptimizable() {
10348 // Self-hosted code calls this without any arguments
10349 MOZ_ASSERT(argc_
== 0);
10351 if (!isFirstStub()) {
10352 // Attach only once to prevent slowdowns for polymorphic calls.
10353 return AttachDecision::NoAction
;
10356 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
10358 Rooted
<JSFunction
*> nextFun(cx_
);
10359 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
10360 &arrayIteratorProto
, &slot
,
10362 return AttachDecision::NoAction
;
10365 // Initialize the input operand.
10366 initializeInputOperand();
10368 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10370 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
10371 ObjOperandId nextId
= writer
.loadObject(nextFun
);
10373 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
10375 // Ensure that proto[slot] == nextFun.
10376 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
10377 writer
.loadBooleanResult(true);
10378 writer
.returnFromIC();
10380 trackAttached("ArrayIteratorPrototypeOptimizable");
10381 return AttachDecision::Attach
;
10384 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectCreate() {
10385 // Need a single object-or-null argument.
10386 if (argc_
!= 1 || !args_
[0].isObjectOrNull()) {
10387 return AttachDecision::NoAction
;
10390 if (!isFirstStub()) {
10391 // Attach only once to prevent slowdowns for polymorphic calls.
10392 return AttachDecision::NoAction
;
10395 RootedObject
proto(cx_
, args_
[0].toObjectOrNull());
10396 JSObject
* templateObj
= ObjectCreateImpl(cx_
, proto
, TenuredObject
);
10397 if (!templateObj
) {
10398 cx_
->recoverFromOutOfMemory();
10399 return AttachDecision::NoAction
;
10402 // Initialize the input operand.
10403 initializeInputOperand();
10405 // Guard callee is the 'create' native function.
10406 emitNativeCalleeGuard();
10408 // Guard on the proto argument.
10409 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10411 ObjOperandId protoId
= writer
.guardToObject(argId
);
10412 writer
.guardSpecificObject(protoId
, proto
);
10414 writer
.guardIsNull(argId
);
10417 writer
.objectCreateResult(templateObj
);
10418 writer
.returnFromIC();
10420 trackAttached("ObjectCreate");
10421 return AttachDecision::Attach
;
10424 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectConstructor() {
10425 // Expecting no arguments or a single object argument.
10426 // TODO(Warp): Support all or more conversions to object.
10428 return AttachDecision::NoAction
;
10430 if (argc_
== 1 && !args_
[0].isObject()) {
10431 return AttachDecision::NoAction
;
10434 PlainObject
* templateObj
= nullptr;
10436 // Stub doesn't support metadata builder
10437 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
10438 return AttachDecision::NoAction
;
10441 // Create a temporary object to act as the template object.
10442 templateObj
= NewPlainObjectWithAllocKind(cx_
, NewObjectGCKind());
10443 if (!templateObj
) {
10444 cx_
->recoverFromOutOfMemory();
10445 return AttachDecision::NoAction
;
10449 // Initialize the input operand.
10450 initializeInputOperand();
10452 // Guard callee and newTarget (if constructing) are this Object constructor
10454 emitNativeCalleeGuard();
10457 // TODO: Support pre-tenuring.
10458 gc::AllocSite
* site
=
10459 script()->zone()->unknownAllocSite(JS::TraceKind::Object
);
10462 uint32_t numFixedSlots
= templateObj
->numUsedFixedSlots();
10463 uint32_t numDynamicSlots
= templateObj
->numDynamicSlots();
10464 gc::AllocKind allocKind
= templateObj
->allocKindForTenure();
10465 Shape
* shape
= templateObj
->shape();
10467 writer
.guardNoAllocationMetadataBuilder(
10468 cx_
->realm()->addressOfMetadataBuilder());
10469 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
,
10472 // Use standard call flags when this is an inline Function.prototype.call(),
10473 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10474 CallFlags flags
= flags_
;
10475 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10476 flags
= CallFlags(CallFlags::Standard
);
10479 // Guard that the argument is an object.
10480 ValOperandId argId
=
10481 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10482 ObjOperandId objId
= writer
.guardToObject(argId
);
10484 // Return the object.
10485 writer
.loadObjectResult(objId
);
10488 writer
.returnFromIC();
10490 trackAttached("ObjectConstructor");
10491 return AttachDecision::Attach
;
10494 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayConstructor() {
10495 // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
10496 // for now. Note that self-hosted code calls this without |new| via std_Array.
10498 return AttachDecision::NoAction
;
10500 if (argc_
== 1 && !args_
[0].isInt32()) {
10501 return AttachDecision::NoAction
;
10504 int32_t length
= (argc_
== 1) ? args_
[0].toInt32() : 0;
10505 if (length
< 0 || uint32_t(length
) > ArrayObject::EagerAllocationMaxLength
) {
10506 return AttachDecision::NoAction
;
10509 // We allow inlining this function across realms so make sure the template
10510 // object is allocated in that realm. See CanInlineNativeCrossRealm.
10511 JSObject
* templateObj
;
10513 AutoRealm
ar(cx_
, callee_
);
10514 templateObj
= NewDenseFullyAllocatedArray(cx_
, length
, TenuredObject
);
10515 if (!templateObj
) {
10516 cx_
->clearPendingException();
10517 return AttachDecision::NoAction
;
10521 // Initialize the input operand.
10522 initializeInputOperand();
10524 // Guard callee and newTarget (if constructing) are this Array constructor
10526 emitNativeCalleeGuard();
10528 Int32OperandId lengthId
;
10530 // Use standard call flags when this is an inline Function.prototype.call(),
10531 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10532 CallFlags flags
= flags_
;
10533 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10534 flags
= CallFlags(CallFlags::Standard
);
10537 ValOperandId arg0Id
=
10538 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10539 lengthId
= writer
.guardToInt32(arg0Id
);
10541 MOZ_ASSERT(argc_
== 0);
10542 lengthId
= writer
.loadInt32Constant(0);
10545 writer
.newArrayFromLengthResult(templateObj
, lengthId
);
10546 writer
.returnFromIC();
10548 trackAttached("ArrayConstructor");
10549 return AttachDecision::Attach
;
10552 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() {
10553 MOZ_ASSERT(flags_
.isConstructing());
10555 if (argc_
== 0 || argc_
> 3) {
10556 return AttachDecision::NoAction
;
10559 if (!isFirstStub()) {
10560 // Attach only once to prevent slowdowns for polymorphic calls.
10561 return AttachDecision::NoAction
;
10564 // The first argument must be int32 or a non-proxy object.
10565 if (!args_
[0].isInt32() && !args_
[0].isObject()) {
10566 return AttachDecision::NoAction
;
10568 if (args_
[0].isObject() && args_
[0].toObject().is
<ProxyObject
>()) {
10569 return AttachDecision::NoAction
;
10571 if (args_
[0].isObject() &&
10572 args_
[0].toObject().is
<ResizableArrayBufferObject
>()) {
10573 return AttachDecision::NoAction
;
10576 #ifdef JS_CODEGEN_X86
10577 // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
10578 // we can easily support on 32-bit x86 for now.
10579 if (args_
[0].isObject() &&
10580 args_
[0].toObject().is
<ArrayBufferObjectMaybeShared
>()) {
10581 return AttachDecision::NoAction
;
10585 RootedObject
templateObj(cx_
);
10586 if (!TypedArrayObject::GetTemplateObjectForNative(cx_
, callee_
->native(),
10587 args_
, &templateObj
)) {
10588 cx_
->recoverFromOutOfMemory();
10589 return AttachDecision::NoAction
;
10592 if (!templateObj
) {
10593 // This can happen for large length values.
10594 MOZ_ASSERT(args_
[0].isInt32());
10595 return AttachDecision::NoAction
;
10598 // Initialize the input operand.
10599 initializeInputOperand();
10601 // Guard callee and newTarget are this TypedArray constructor function.
10602 emitNativeCalleeGuard();
10604 ValOperandId arg0Id
=
10605 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
10607 if (args_
[0].isInt32()) {
10609 Int32OperandId lengthId
= writer
.guardToInt32(arg0Id
);
10610 writer
.newTypedArrayFromLengthResult(templateObj
, lengthId
);
10612 JSObject
* obj
= &args_
[0].toObject();
10613 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
10615 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
10616 // From ArrayBuffer.
10617 if (obj
->is
<FixedLengthArrayBufferObject
>()) {
10618 writer
.guardClass(objId
, GuardClassKind::FixedLengthArrayBuffer
);
10620 MOZ_ASSERT(obj
->is
<SharedArrayBufferObject
>());
10621 writer
.guardClass(objId
, GuardClassKind::SharedArrayBuffer
);
10623 ValOperandId byteOffsetId
;
10626 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
, flags_
);
10628 byteOffsetId
= writer
.loadUndefined();
10630 ValOperandId lengthId
;
10633 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
, flags_
);
10635 lengthId
= writer
.loadUndefined();
10637 writer
.newTypedArrayFromArrayBufferResult(templateObj
, objId
,
10638 byteOffsetId
, lengthId
);
10640 // From Array-like.
10641 writer
.guardIsNotArrayBufferMaybeShared(objId
);
10642 writer
.guardIsNotProxy(objId
);
10643 writer
.newTypedArrayFromArrayResult(templateObj
, objId
);
10647 writer
.returnFromIC();
10649 trackAttached("TypedArrayConstructor");
10650 return AttachDecision::Attach
;
10653 AttachDecision
InlinableNativeIRGenerator::tryAttachSpecializedFunctionBind(
10654 Handle
<JSObject
*> target
, Handle
<BoundFunctionObject
*> templateObj
) {
10655 // Try to attach a faster stub that's more specialized than what we emit in
10656 // tryAttachFunctionBind. This lets us allocate and initialize a bound
10657 // function object in Ion without calling into C++.
10659 // We can do this if:
10661 // * The target's prototype is Function.prototype, because that's the proto we
10662 // use for the template object.
10663 // * All bound arguments can be stored inline.
10664 // * The `.name`, `.length`, and `IsConstructor` values match `target`.
10666 // We initialize the template object with the bound function's name, length,
10667 // and flags. At runtime we then only have to clone the template object and
10668 // initialize the slots for the target, the bound `this` and the bound
10671 if (!isFirstStub()) {
10672 return AttachDecision::NoAction
;
10674 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
10675 return AttachDecision::NoAction
;
10677 if (target
->staticPrototype() != &cx_
->global()->getFunctionPrototype()) {
10678 return AttachDecision::NoAction
;
10680 size_t numBoundArgs
= argc_
> 0 ? argc_
- 1 : 0;
10681 if (numBoundArgs
> BoundFunctionObject::MaxInlineBoundArgs
) {
10682 return AttachDecision::NoAction
;
10685 const bool targetIsConstructor
= target
->isConstructor();
10686 Rooted
<JSAtom
*> targetName(cx_
);
10687 uint32_t targetLength
= 0;
10689 if (target
->is
<JSFunction
>()) {
10690 Rooted
<JSFunction
*> fun(cx_
, &target
->as
<JSFunction
>());
10691 if (fun
->isNativeFun()) {
10692 return AttachDecision::NoAction
;
10694 if (fun
->hasResolvedLength() || fun
->hasResolvedName()) {
10695 return AttachDecision::NoAction
;
10698 if (!JSFunction::getUnresolvedLength(cx_
, fun
, &len
)) {
10699 cx_
->clearPendingException();
10700 return AttachDecision::NoAction
;
10702 targetName
= fun
->getUnresolvedName(cx_
);
10704 cx_
->clearPendingException();
10705 return AttachDecision::NoAction
;
10708 targetLength
= len
;
10710 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
10711 if (!targetIsConstructor
) {
10712 // Only support constructors for now. This lets us use
10713 // GuardBoundFunctionIsConstructor.
10714 return AttachDecision::NoAction
;
10716 Shape
* initialShape
=
10717 cx_
->global()->maybeBoundFunctionShapeWithDefaultProto();
10718 if (bound
->shape() != initialShape
) {
10719 return AttachDecision::NoAction
;
10721 Value lenVal
= bound
->getLengthForInitialShape();
10722 Value nameVal
= bound
->getNameForInitialShape();
10723 if (!lenVal
.isInt32() || lenVal
.toInt32() < 0 || !nameVal
.isString() ||
10724 !nameVal
.toString()->isAtom()) {
10725 return AttachDecision::NoAction
;
10727 targetName
= &nameVal
.toString()->asAtom();
10728 targetLength
= uint32_t(lenVal
.toInt32());
10731 if (!templateObj
->initTemplateSlotsForSpecializedBind(
10732 cx_
, numBoundArgs
, targetIsConstructor
, targetLength
, targetName
)) {
10733 cx_
->recoverFromOutOfMemory();
10734 return AttachDecision::NoAction
;
10737 initializeInputOperand();
10738 emitNativeCalleeGuard();
10740 ValOperandId thisValId
=
10741 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10742 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
10744 // Ensure the JSClass and proto match, and that the `length` and `name`
10745 // properties haven't been redefined.
10746 writer
.guardShape(targetId
, target
->shape());
10748 // Emit guards for the `IsConstructor`, `.length`, and `.name` values.
10749 if (target
->is
<JSFunction
>()) {
10751 // * The BaseScript (because that's what JSFunction uses for the `length`).
10752 // Because MGuardFunctionScript doesn't support self-hosted functions yet,
10753 // we use GuardSpecificFunction instead in this case.
10754 // See assertion in MGuardFunctionScript::getAliasSet.
10755 // * The flags slot (for the CONSTRUCTOR, RESOLVED_NAME, RESOLVED_LENGTH,
10756 // HAS_INFERRED_NAME, and HAS_GUESSED_ATOM flags).
10757 // * The atom slot.
10758 JSFunction
* fun
= &target
->as
<JSFunction
>();
10759 if (fun
->isSelfHostedBuiltin()) {
10760 writer
.guardSpecificFunction(targetId
, fun
);
10762 writer
.guardFunctionScript(targetId
, fun
->baseScript());
10764 writer
.guardFixedSlotValue(
10765 targetId
, JSFunction::offsetOfFlagsAndArgCount(),
10766 fun
->getReservedSlot(JSFunction::FlagsAndArgCountSlot
));
10767 writer
.guardFixedSlotValue(targetId
, JSFunction::offsetOfAtom(),
10768 fun
->getReservedSlot(JSFunction::AtomSlot
));
10770 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
10771 writer
.guardBoundFunctionIsConstructor(targetId
);
10772 writer
.guardFixedSlotValue(targetId
,
10773 BoundFunctionObject::offsetOfLengthSlot(),
10774 bound
->getLengthForInitialShape());
10775 writer
.guardFixedSlotValue(targetId
,
10776 BoundFunctionObject::offsetOfNameSlot(),
10777 bound
->getNameForInitialShape());
10780 writer
.specializedBindFunctionResult(targetId
, argc_
, templateObj
);
10781 writer
.returnFromIC();
10783 trackAttached("SpecializedFunctionBind");
10784 return AttachDecision::Attach
;
10787 AttachDecision
InlinableNativeIRGenerator::tryAttachFunctionBind() {
10788 // Ensure |this| (the target) is a function object or a bound function object.
10789 // We could support other callables too, but note that we rely on the target
10790 // having a static prototype in BoundFunctionObject::functionBindImpl.
10791 if (!thisval_
.isObject()) {
10792 return AttachDecision::NoAction
;
10794 Rooted
<JSObject
*> target(cx_
, &thisval_
.toObject());
10795 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
10796 return AttachDecision::NoAction
;
10799 // Only support standard, non-spread calls.
10800 if (flags_
.getArgFormat() != CallFlags::Standard
) {
10801 return AttachDecision::NoAction
;
10804 // Only optimize if the number of arguments is small. This ensures we don't
10805 // compile a lot of different stubs (because we bake in argc) and that we
10806 // don't get anywhere near ARGS_LENGTH_MAX.
10807 static constexpr size_t MaxArguments
= 6;
10808 if (argc_
> MaxArguments
) {
10809 return AttachDecision::NoAction
;
10812 Rooted
<BoundFunctionObject
*> templateObj(
10813 cx_
, BoundFunctionObject::createTemplateObject(cx_
));
10814 if (!templateObj
) {
10815 cx_
->recoverFromOutOfMemory();
10816 return AttachDecision::NoAction
;
10819 TRY_ATTACH(tryAttachSpecializedFunctionBind(target
, templateObj
));
10821 initializeInputOperand();
10823 emitNativeCalleeGuard();
10825 // Guard |this| is a function object or a bound function object.
10826 ValOperandId thisValId
=
10827 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10828 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
10829 if (target
->is
<JSFunction
>()) {
10830 writer
.guardClass(targetId
, GuardClassKind::JSFunction
);
10832 MOZ_ASSERT(target
->is
<BoundFunctionObject
>());
10833 writer
.guardClass(targetId
, GuardClassKind::BoundFunction
);
10836 writer
.bindFunctionResult(targetId
, argc_
, templateObj
);
10837 writer
.returnFromIC();
10839 trackAttached("FunctionBind");
10840 return AttachDecision::Attach
;
10843 AttachDecision
CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc
) {
10844 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
10846 if (calleeFunc
->native() != fun_apply
) {
10847 return AttachDecision::NoAction
;
10851 return AttachDecision::NoAction
;
10854 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
10855 return AttachDecision::NoAction
;
10857 Rooted
<JSFunction
*> target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
10859 bool isScripted
= target
->hasJitEntry();
10860 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
10862 if (target
->isClassConstructor()) {
10863 return AttachDecision::NoAction
;
10866 CallFlags::ArgFormat format
= CallFlags::Standard
;
10868 // |fun.apply()| and |fun.apply(thisValue)| are equivalent to |fun.call()|
10869 // resp. |fun.call(thisValue)|.
10870 format
= CallFlags::FunCall
;
10871 } else if (args_
[1].isNullOrUndefined()) {
10872 // |fun.apply(thisValue, null)| and |fun.apply(thisValue, undefined)| are
10873 // also equivalent to |fun.call(thisValue)|, but we can't use FunCall
10874 // because we have to discard the second argument.
10875 format
= CallFlags::FunApplyNullUndefined
;
10876 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArgumentsObject
>()) {
10877 auto* argsObj
= &args_
[1].toObject().as
<ArgumentsObject
>();
10878 if (argsObj
->hasOverriddenElement() || argsObj
->anyArgIsForwarded() ||
10879 argsObj
->hasOverriddenLength() ||
10880 argsObj
->initialLength() > JIT_ARGS_LENGTH_MAX
) {
10881 return AttachDecision::NoAction
;
10883 format
= CallFlags::FunApplyArgsObj
;
10884 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArrayObject
>() &&
10885 args_
[1].toObject().as
<ArrayObject
>().length() <=
10886 JIT_ARGS_LENGTH_MAX
&&
10887 IsPackedArray(&args_
[1].toObject())) {
10888 format
= CallFlags::FunApplyArray
;
10890 return AttachDecision::NoAction
;
10893 Int32OperandId
argcId(writer
.setInputOperandId(0));
10895 CallFlags
targetFlags(format
);
10896 if (mode_
== ICState::Mode::Specialized
) {
10897 if (cx_
->realm() == target
->realm()) {
10898 targetFlags
.setIsSameRealm();
10902 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
10903 format
== CallFlags::FunApplyArray
) {
10904 HandleValue newTarget
= NullHandleValue
;
10905 HandleValue thisValue
= args_
[0];
10906 Rooted
<ArrayObject
*> aobj(cx_
, &args_
[1].toObject().as
<ArrayObject
>());
10907 HandleValueArray args
= HandleValueArray::fromMarkedLocation(
10908 aobj
->length(), aobj
->getDenseElements());
10910 // Check for specific native-function optimizations.
10911 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10912 args
, targetFlags
);
10913 TRY_ATTACH(nativeGen
.tryAttachStub());
10916 // Don't inline when no arguments are passed, cf. |tryAttachFunCall()|.
10917 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
10918 format
== CallFlags::FunCall
&& argc_
> 0) {
10919 MOZ_ASSERT(argc_
== 1);
10921 HandleValue newTarget
= NullHandleValue
;
10922 HandleValue thisValue
= args_
[0];
10923 HandleValueArray args
= HandleValueArray::empty();
10925 // Check for specific native-function optimizations.
10926 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10927 args
, targetFlags
);
10928 TRY_ATTACH(nativeGen
.tryAttachStub());
10931 ObjOperandId thisObjId
= emitFunApplyGuard(argcId
);
10933 uint32_t fixedArgc
;
10934 if (format
== CallFlags::FunApplyArray
||
10935 format
== CallFlags::FunApplyArgsObj
||
10936 format
== CallFlags::FunApplyNullUndefined
) {
10937 emitFunApplyArgsGuard(format
);
10939 // We always use MaxUnrolledArgCopy here because the fixed argc is
10940 // meaningless in a FunApply case.
10941 fixedArgc
= MaxUnrolledArgCopy
;
10943 MOZ_ASSERT(format
== CallFlags::FunCall
);
10945 // Whereas for the FunCall case we need to use the actual fixed argc value.
10946 fixedArgc
= ClampFixedArgc(argc_
);
10949 if (mode_
== ICState::Mode::Specialized
) {
10950 // Ensure that |this| is the expected target function.
10951 emitCalleeGuard(thisObjId
, target
);
10954 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10956 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
10960 // Guard that |this| is a function.
10961 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
10963 // Guard that function is not a class constructor.
10964 writer
.guardNotClassConstructor(thisObjId
);
10967 // Guard that function is scripted.
10968 writer
.guardFunctionHasJitEntry(thisObjId
, /*constructing =*/false);
10969 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10971 // Guard that function is native.
10972 writer
.guardFunctionHasNoJitEntry(thisObjId
);
10973 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
10977 writer
.returnFromIC();
10980 trackAttached("Call.ScriptedFunApply");
10982 trackAttached("Call.NativeFunApply");
10985 return AttachDecision::Attach
;
10988 AttachDecision
CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc
) {
10989 // Try to optimize calls into Wasm code by emitting the CallWasmFunction
10990 // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
10991 // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
10994 // Note: some code refers to these optimized Wasm calls as "inlined" calls.
10996 MOZ_ASSERT(calleeFunc
->isWasmWithJitEntry());
10998 if (!JitOptions
.enableWasmIonFastCalls
) {
10999 return AttachDecision::NoAction
;
11001 if (!isFirstStub_
) {
11002 return AttachDecision::NoAction
;
11004 JSOp op
= JSOp(*pc_
);
11005 if (op
!= JSOp::Call
&& op
!= JSOp::CallContent
&&
11006 op
!= JSOp::CallIgnoresRv
) {
11007 return AttachDecision::NoAction
;
11009 if (cx_
->realm() != calleeFunc
->realm()) {
11010 return AttachDecision::NoAction
;
11013 wasm::Instance
& inst
= wasm::ExportedFunctionToInstance(calleeFunc
);
11014 uint32_t funcIndex
= inst
.code().getFuncIndex(calleeFunc
);
11016 auto bestTier
= inst
.code().bestTier();
11017 const wasm::FuncExport
& funcExport
=
11018 inst
.metadata(bestTier
).lookupFuncExport(funcIndex
);
11019 const wasm::FuncType
& sig
= inst
.metadata().getFuncExportType(funcExport
);
11021 MOZ_ASSERT(!IsInsideNursery(inst
.object()));
11022 MOZ_ASSERT(sig
.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
11024 // If there are too many arguments, don't optimize (we won't be able to store
11025 // the arguments in the LIR node).
11026 static_assert(wasm::MaxArgsForJitInlineCall
<= ArgumentKindArgIndexLimit
);
11027 if (sig
.args().length() > wasm::MaxArgsForJitInlineCall
||
11028 argc_
> ArgumentKindArgIndexLimit
) {
11029 return AttachDecision::NoAction
;
11032 // If there are too many results, don't optimize as Warp currently doesn't
11033 // have code to handle this.
11034 if (sig
.results().length() > wasm::MaxResultsForJitInlineCall
) {
11035 return AttachDecision::NoAction
;
11038 // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
11039 // because it is more difficult (because it requires multiple LIR arguments
11042 // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
11043 // spilled to the stack because it causes problems with register allocation.
11045 constexpr bool optimizeWithI64
= true;
11047 constexpr bool optimizeWithI64
= false;
11049 ABIArgGenerator abi
;
11050 for (const auto& valType
: sig
.args()) {
11051 MIRType mirType
= valType
.toMIRType();
11052 ABIArg abiArg
= abi
.next(mirType
);
11053 if (mirType
!= MIRType::Int64
) {
11056 if (!optimizeWithI64
|| abiArg
.kind() == ABIArg::Stack
) {
11057 return AttachDecision::NoAction
;
11061 // Check that all arguments can be converted to the Wasm type in Warp code
11062 // without bailing out.
11063 for (size_t i
= 0; i
< sig
.args().length(); i
++) {
11064 Value argVal
= i
< argc_
? args_
[i
] : UndefinedValue();
11065 switch (sig
.args()[i
].kind()) {
11066 case wasm::ValType::I32
:
11067 case wasm::ValType::F32
:
11068 case wasm::ValType::F64
:
11069 if (!argVal
.isNumber() && !argVal
.isBoolean() &&
11070 !argVal
.isUndefined()) {
11071 return AttachDecision::NoAction
;
11074 case wasm::ValType::I64
:
11075 if (!argVal
.isBigInt() && !argVal
.isBoolean() && !argVal
.isString()) {
11076 return AttachDecision::NoAction
;
11079 case wasm::ValType::V128
:
11080 MOZ_CRASH("Function should not have a Wasm JitEntry");
11081 case wasm::ValType::Ref
:
11082 // canHaveJitEntry restricts args to externref, where all JS values are
11083 // valid and can be boxed.
11084 MOZ_ASSERT(sig
.args()[i
].refType().isExtern(),
11085 "Unexpected type for Wasm JitEntry");
11090 CallFlags
flags(/* isConstructing = */ false, /* isSpread = */ false,
11091 /* isSameRealm = */ true);
11094 Int32OperandId
argcId(writer
.setInputOperandId(0));
11096 // Load the callee and ensure it is an object
11097 ValOperandId calleeValId
=
11098 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags
);
11099 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11101 // Ensure the callee is this Wasm function.
11102 emitCalleeGuard(calleeObjId
, calleeFunc
);
11104 // Guard the argument types.
11105 uint32_t guardedArgs
= std::min
<uint32_t>(sig
.args().length(), argc_
);
11106 for (uint32_t i
= 0; i
< guardedArgs
; i
++) {
11107 ArgumentKind argKind
= ArgumentKindForArgIndex(i
);
11108 ValOperandId argId
= writer
.loadArgumentFixedSlot(argKind
, argc_
, flags
);
11109 writer
.guardWasmArg(argId
, sig
.args()[i
].kind());
11112 writer
.callWasmFunction(calleeObjId
, argcId
, flags
, ClampFixedArgc(argc_
),
11113 &funcExport
, inst
.object());
11114 writer
.returnFromIC();
11116 trackAttached("Call.WasmCall");
11118 return AttachDecision::Attach
;
11121 AttachDecision
CallIRGenerator::tryAttachInlinableNative(HandleFunction callee
,
11123 MOZ_ASSERT(mode_
== ICState::Mode::Specialized
);
11124 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
11125 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
||
11126 flags
.getArgFormat() == CallFlags::Spread
);
11128 // Special case functions are only optimized for normal calls.
11129 if (op_
!= JSOp::Call
&& op_
!= JSOp::CallContent
&& op_
!= JSOp::New
&&
11130 op_
!= JSOp::NewContent
&& op_
!= JSOp::CallIgnoresRv
&&
11131 op_
!= JSOp::SpreadCall
) {
11132 return AttachDecision::NoAction
;
11135 InlinableNativeIRGenerator
nativeGen(*this, callee
, newTarget_
, thisval_
,
11137 return nativeGen
.tryAttachStub();
11140 #ifdef FUZZING_JS_FUZZILLI
11141 AttachDecision
InlinableNativeIRGenerator::tryAttachFuzzilliHash() {
11143 return AttachDecision::NoAction
;
11146 // Initialize the input operand.
11147 initializeInputOperand();
11149 // Guard callee is the 'fuzzilli_hash' native function.
11150 emitNativeCalleeGuard();
11152 ValOperandId argValId
=
11153 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
11155 writer
.fuzzilliHashResult(argValId
);
11156 writer
.returnFromIC();
11158 trackAttached("FuzzilliHash");
11159 return AttachDecision::Attach
;
11163 AttachDecision
InlinableNativeIRGenerator::tryAttachStub() {
11164 if (!callee_
->hasJitInfo() ||
11165 callee_
->jitInfo()->type() != JSJitInfo::InlinableNative
) {
11166 return AttachDecision::NoAction
;
11169 InlinableNative native
= callee_
->jitInfo()->inlinableNative
;
11171 // Not all natives can be inlined cross-realm.
11172 if (cx_
->realm() != callee_
->realm() && !CanInlineNativeCrossRealm(native
)) {
11173 return AttachDecision::NoAction
;
11176 // Check for special-cased native constructors.
11177 if (flags_
.isConstructing()) {
11178 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
11180 // newTarget must match the callee. CacheIR for this is emitted in
11181 // emitNativeCalleeGuard.
11182 if (ObjectValue(*callee_
) != newTarget_
) {
11183 return AttachDecision::NoAction
;
11186 case InlinableNative::Array
:
11187 return tryAttachArrayConstructor();
11188 case InlinableNative::TypedArrayConstructor
:
11189 return tryAttachTypedArrayConstructor();
11190 case InlinableNative::String
:
11191 return tryAttachStringConstructor();
11192 case InlinableNative::Object
:
11193 return tryAttachObjectConstructor();
11197 return AttachDecision::NoAction
;
11200 // Check for special-cased native spread calls.
11201 if (flags_
.getArgFormat() == CallFlags::Spread
||
11202 flags_
.getArgFormat() == CallFlags::FunApplyArray
) {
11204 case InlinableNative::MathMin
:
11205 return tryAttachSpreadMathMinMax(/*isMax = */ false);
11206 case InlinableNative::MathMax
:
11207 return tryAttachSpreadMathMinMax(/*isMax = */ true);
11211 return AttachDecision::NoAction
;
11214 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
||
11215 flags_
.getArgFormat() == CallFlags::FunCall
);
11217 // Check for special-cased native functions.
11220 case InlinableNative::Array
:
11221 return tryAttachArrayConstructor();
11222 case InlinableNative::ArrayPush
:
11223 return tryAttachArrayPush();
11224 case InlinableNative::ArrayPop
:
11225 case InlinableNative::ArrayShift
:
11226 return tryAttachArrayPopShift(native
);
11227 case InlinableNative::ArrayJoin
:
11228 return tryAttachArrayJoin();
11229 case InlinableNative::ArraySlice
:
11230 return tryAttachArraySlice();
11231 case InlinableNative::ArrayIsArray
:
11232 return tryAttachArrayIsArray();
11234 // DataView natives.
11235 case InlinableNative::DataViewGetInt8
:
11236 return tryAttachDataViewGet(Scalar::Int8
);
11237 case InlinableNative::DataViewGetUint8
:
11238 return tryAttachDataViewGet(Scalar::Uint8
);
11239 case InlinableNative::DataViewGetInt16
:
11240 return tryAttachDataViewGet(Scalar::Int16
);
11241 case InlinableNative::DataViewGetUint16
:
11242 return tryAttachDataViewGet(Scalar::Uint16
);
11243 case InlinableNative::DataViewGetInt32
:
11244 return tryAttachDataViewGet(Scalar::Int32
);
11245 case InlinableNative::DataViewGetUint32
:
11246 return tryAttachDataViewGet(Scalar::Uint32
);
11247 case InlinableNative::DataViewGetFloat32
:
11248 return tryAttachDataViewGet(Scalar::Float32
);
11249 case InlinableNative::DataViewGetFloat64
:
11250 return tryAttachDataViewGet(Scalar::Float64
);
11251 case InlinableNative::DataViewGetBigInt64
:
11252 return tryAttachDataViewGet(Scalar::BigInt64
);
11253 case InlinableNative::DataViewGetBigUint64
:
11254 return tryAttachDataViewGet(Scalar::BigUint64
);
11255 case InlinableNative::DataViewSetInt8
:
11256 return tryAttachDataViewSet(Scalar::Int8
);
11257 case InlinableNative::DataViewSetUint8
:
11258 return tryAttachDataViewSet(Scalar::Uint8
);
11259 case InlinableNative::DataViewSetInt16
:
11260 return tryAttachDataViewSet(Scalar::Int16
);
11261 case InlinableNative::DataViewSetUint16
:
11262 return tryAttachDataViewSet(Scalar::Uint16
);
11263 case InlinableNative::DataViewSetInt32
:
11264 return tryAttachDataViewSet(Scalar::Int32
);
11265 case InlinableNative::DataViewSetUint32
:
11266 return tryAttachDataViewSet(Scalar::Uint32
);
11267 case InlinableNative::DataViewSetFloat32
:
11268 return tryAttachDataViewSet(Scalar::Float32
);
11269 case InlinableNative::DataViewSetFloat64
:
11270 return tryAttachDataViewSet(Scalar::Float64
);
11271 case InlinableNative::DataViewSetBigInt64
:
11272 return tryAttachDataViewSet(Scalar::BigInt64
);
11273 case InlinableNative::DataViewSetBigUint64
:
11274 return tryAttachDataViewSet(Scalar::BigUint64
);
11276 // Function natives.
11277 case InlinableNative::FunctionBind
:
11278 return tryAttachFunctionBind();
11281 case InlinableNative::IntlGuardToCollator
:
11282 case InlinableNative::IntlGuardToDateTimeFormat
:
11283 case InlinableNative::IntlGuardToDisplayNames
:
11284 case InlinableNative::IntlGuardToListFormat
:
11285 case InlinableNative::IntlGuardToNumberFormat
:
11286 case InlinableNative::IntlGuardToPluralRules
:
11287 case InlinableNative::IntlGuardToRelativeTimeFormat
:
11288 case InlinableNative::IntlGuardToSegmenter
:
11289 case InlinableNative::IntlGuardToSegments
:
11290 case InlinableNative::IntlGuardToSegmentIterator
:
11291 return tryAttachGuardToClass(native
);
11293 // Slot intrinsics.
11294 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
11295 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
11296 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
11297 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
11298 return tryAttachUnsafeGetReservedSlot(native
);
11299 case InlinableNative::IntrinsicUnsafeSetReservedSlot
:
11300 return tryAttachUnsafeSetReservedSlot();
11303 case InlinableNative::IntrinsicIsSuspendedGenerator
:
11304 return tryAttachIsSuspendedGenerator();
11305 case InlinableNative::IntrinsicToObject
:
11306 return tryAttachToObject();
11307 case InlinableNative::IntrinsicToInteger
:
11308 return tryAttachToInteger();
11309 case InlinableNative::IntrinsicToLength
:
11310 return tryAttachToLength();
11311 case InlinableNative::IntrinsicIsObject
:
11312 return tryAttachIsObject();
11313 case InlinableNative::IntrinsicIsPackedArray
:
11314 return tryAttachIsPackedArray();
11315 case InlinableNative::IntrinsicIsCallable
:
11316 return tryAttachIsCallable();
11317 case InlinableNative::IntrinsicIsConstructor
:
11318 return tryAttachIsConstructor();
11319 case InlinableNative::IntrinsicIsCrossRealmArrayConstructor
:
11320 return tryAttachIsCrossRealmArrayConstructor();
11321 case InlinableNative::IntrinsicGuardToArrayIterator
:
11322 case InlinableNative::IntrinsicGuardToMapIterator
:
11323 case InlinableNative::IntrinsicGuardToSetIterator
:
11324 case InlinableNative::IntrinsicGuardToStringIterator
:
11325 case InlinableNative::IntrinsicGuardToRegExpStringIterator
:
11326 case InlinableNative::IntrinsicGuardToWrapForValidIterator
:
11327 case InlinableNative::IntrinsicGuardToIteratorHelper
:
11328 case InlinableNative::IntrinsicGuardToAsyncIteratorHelper
:
11329 return tryAttachGuardToClass(native
);
11330 case InlinableNative::IntrinsicSubstringKernel
:
11331 return tryAttachSubstringKernel();
11332 case InlinableNative::IntrinsicIsConstructing
:
11333 return tryAttachIsConstructing();
11334 case InlinableNative::IntrinsicNewArrayIterator
:
11335 return tryAttachNewArrayIterator();
11336 case InlinableNative::IntrinsicNewStringIterator
:
11337 return tryAttachNewStringIterator();
11338 case InlinableNative::IntrinsicNewRegExpStringIterator
:
11339 return tryAttachNewRegExpStringIterator();
11340 case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable
:
11341 return tryAttachArrayIteratorPrototypeOptimizable();
11342 case InlinableNative::IntrinsicObjectHasPrototype
:
11343 return tryAttachObjectHasPrototype();
11346 case InlinableNative::IsRegExpObject
:
11347 return tryAttachHasClass(&RegExpObject::class_
,
11348 /* isPossiblyWrapped = */ false);
11349 case InlinableNative::IsPossiblyWrappedRegExpObject
:
11350 return tryAttachHasClass(&RegExpObject::class_
,
11351 /* isPossiblyWrapped = */ true);
11352 case InlinableNative::RegExpMatcher
:
11353 case InlinableNative::RegExpSearcher
:
11354 return tryAttachRegExpMatcherSearcher(native
);
11355 case InlinableNative::RegExpSearcherLastLimit
:
11356 return tryAttachRegExpSearcherLastLimit();
11357 case InlinableNative::RegExpHasCaptureGroups
:
11358 return tryAttachRegExpHasCaptureGroups();
11359 case InlinableNative::RegExpPrototypeOptimizable
:
11360 return tryAttachRegExpPrototypeOptimizable();
11361 case InlinableNative::RegExpInstanceOptimizable
:
11362 return tryAttachRegExpInstanceOptimizable();
11363 case InlinableNative::GetFirstDollarIndex
:
11364 return tryAttachGetFirstDollarIndex();
11365 case InlinableNative::IntrinsicRegExpBuiltinExec
:
11366 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
11367 return tryAttachIntrinsicRegExpBuiltinExec(native
);
11368 case InlinableNative::IntrinsicRegExpExec
:
11369 case InlinableNative::IntrinsicRegExpExecForTest
:
11370 return tryAttachIntrinsicRegExpExec(native
);
11373 case InlinableNative::String
:
11374 return tryAttachString();
11375 case InlinableNative::StringToString
:
11376 case InlinableNative::StringValueOf
:
11377 return tryAttachStringToStringValueOf();
11378 case InlinableNative::StringCharCodeAt
:
11379 return tryAttachStringCharCodeAt();
11380 case InlinableNative::StringCodePointAt
:
11381 return tryAttachStringCodePointAt();
11382 case InlinableNative::StringCharAt
:
11383 return tryAttachStringCharAt();
11384 case InlinableNative::StringAt
:
11385 return tryAttachStringAt();
11386 case InlinableNative::StringFromCharCode
:
11387 return tryAttachStringFromCharCode();
11388 case InlinableNative::StringFromCodePoint
:
11389 return tryAttachStringFromCodePoint();
11390 case InlinableNative::StringIncludes
:
11391 return tryAttachStringIncludes();
11392 case InlinableNative::StringIndexOf
:
11393 return tryAttachStringIndexOf();
11394 case InlinableNative::StringLastIndexOf
:
11395 return tryAttachStringLastIndexOf();
11396 case InlinableNative::StringStartsWith
:
11397 return tryAttachStringStartsWith();
11398 case InlinableNative::StringEndsWith
:
11399 return tryAttachStringEndsWith();
11400 case InlinableNative::StringToLowerCase
:
11401 return tryAttachStringToLowerCase();
11402 case InlinableNative::StringToUpperCase
:
11403 return tryAttachStringToUpperCase();
11404 case InlinableNative::StringTrim
:
11405 return tryAttachStringTrim();
11406 case InlinableNative::StringTrimStart
:
11407 return tryAttachStringTrimStart();
11408 case InlinableNative::StringTrimEnd
:
11409 return tryAttachStringTrimEnd();
11410 case InlinableNative::IntrinsicStringReplaceString
:
11411 return tryAttachStringReplaceString();
11412 case InlinableNative::IntrinsicStringSplitString
:
11413 return tryAttachStringSplitString();
11416 case InlinableNative::MathRandom
:
11417 return tryAttachMathRandom();
11418 case InlinableNative::MathAbs
:
11419 return tryAttachMathAbs();
11420 case InlinableNative::MathClz32
:
11421 return tryAttachMathClz32();
11422 case InlinableNative::MathSign
:
11423 return tryAttachMathSign();
11424 case InlinableNative::MathImul
:
11425 return tryAttachMathImul();
11426 case InlinableNative::MathFloor
:
11427 return tryAttachMathFloor();
11428 case InlinableNative::MathCeil
:
11429 return tryAttachMathCeil();
11430 case InlinableNative::MathTrunc
:
11431 return tryAttachMathTrunc();
11432 case InlinableNative::MathRound
:
11433 return tryAttachMathRound();
11434 case InlinableNative::MathSqrt
:
11435 return tryAttachMathSqrt();
11436 case InlinableNative::MathFRound
:
11437 return tryAttachMathFRound();
11438 case InlinableNative::MathHypot
:
11439 return tryAttachMathHypot();
11440 case InlinableNative::MathATan2
:
11441 return tryAttachMathATan2();
11442 case InlinableNative::MathSin
:
11443 return tryAttachMathFunction(UnaryMathFunction::SinNative
);
11444 case InlinableNative::MathTan
:
11445 return tryAttachMathFunction(UnaryMathFunction::TanNative
);
11446 case InlinableNative::MathCos
:
11447 return tryAttachMathFunction(UnaryMathFunction::CosNative
);
11448 case InlinableNative::MathExp
:
11449 return tryAttachMathFunction(UnaryMathFunction::Exp
);
11450 case InlinableNative::MathLog
:
11451 return tryAttachMathFunction(UnaryMathFunction::Log
);
11452 case InlinableNative::MathASin
:
11453 return tryAttachMathFunction(UnaryMathFunction::ASin
);
11454 case InlinableNative::MathATan
:
11455 return tryAttachMathFunction(UnaryMathFunction::ATan
);
11456 case InlinableNative::MathACos
:
11457 return tryAttachMathFunction(UnaryMathFunction::ACos
);
11458 case InlinableNative::MathLog10
:
11459 return tryAttachMathFunction(UnaryMathFunction::Log10
);
11460 case InlinableNative::MathLog2
:
11461 return tryAttachMathFunction(UnaryMathFunction::Log2
);
11462 case InlinableNative::MathLog1P
:
11463 return tryAttachMathFunction(UnaryMathFunction::Log1P
);
11464 case InlinableNative::MathExpM1
:
11465 return tryAttachMathFunction(UnaryMathFunction::ExpM1
);
11466 case InlinableNative::MathCosH
:
11467 return tryAttachMathFunction(UnaryMathFunction::CosH
);
11468 case InlinableNative::MathSinH
:
11469 return tryAttachMathFunction(UnaryMathFunction::SinH
);
11470 case InlinableNative::MathTanH
:
11471 return tryAttachMathFunction(UnaryMathFunction::TanH
);
11472 case InlinableNative::MathACosH
:
11473 return tryAttachMathFunction(UnaryMathFunction::ACosH
);
11474 case InlinableNative::MathASinH
:
11475 return tryAttachMathFunction(UnaryMathFunction::ASinH
);
11476 case InlinableNative::MathATanH
:
11477 return tryAttachMathFunction(UnaryMathFunction::ATanH
);
11478 case InlinableNative::MathCbrt
:
11479 return tryAttachMathFunction(UnaryMathFunction::Cbrt
);
11480 case InlinableNative::MathPow
:
11481 return tryAttachMathPow();
11482 case InlinableNative::MathMin
:
11483 return tryAttachMathMinMax(/* isMax = */ false);
11484 case InlinableNative::MathMax
:
11485 return tryAttachMathMinMax(/* isMax = */ true);
11488 case InlinableNative::IntrinsicGuardToMapObject
:
11489 return tryAttachGuardToClass(native
);
11490 case InlinableNative::IntrinsicGetNextMapEntryForIterator
:
11491 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true);
11494 case InlinableNative::Number
:
11495 return tryAttachNumber();
11496 case InlinableNative::NumberParseInt
:
11497 return tryAttachNumberParseInt();
11498 case InlinableNative::NumberToString
:
11499 return tryAttachNumberToString();
11502 case InlinableNative::Object
:
11503 return tryAttachObjectConstructor();
11504 case InlinableNative::ObjectCreate
:
11505 return tryAttachObjectCreate();
11506 case InlinableNative::ObjectIs
:
11507 return tryAttachObjectIs();
11508 case InlinableNative::ObjectIsPrototypeOf
:
11509 return tryAttachObjectIsPrototypeOf();
11510 case InlinableNative::ObjectKeys
:
11511 return tryAttachObjectKeys();
11512 case InlinableNative::ObjectToString
:
11513 return tryAttachObjectToString();
11516 case InlinableNative::IntrinsicGuardToSetObject
:
11517 return tryAttachGuardToClass(native
);
11518 case InlinableNative::IntrinsicGetNextSetEntryForIterator
:
11519 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false);
11521 // ArrayBuffer intrinsics.
11522 case InlinableNative::IntrinsicGuardToArrayBuffer
:
11523 return tryAttachGuardToArrayBuffer();
11524 case InlinableNative::IntrinsicArrayBufferByteLength
:
11525 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false);
11526 case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength
:
11527 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true);
11529 // SharedArrayBuffer intrinsics.
11530 case InlinableNative::IntrinsicGuardToSharedArrayBuffer
:
11531 return tryAttachGuardToClass(native
);
11533 // TypedArray intrinsics.
11534 case InlinableNative::TypedArrayConstructor
:
11535 return AttachDecision::NoAction
; // Not callable.
11536 case InlinableNative::IntrinsicIsTypedArray
:
11537 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ false);
11538 case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray
:
11539 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ true);
11540 case InlinableNative::IntrinsicIsTypedArrayConstructor
:
11541 return tryAttachIsTypedArrayConstructor();
11542 case InlinableNative::IntrinsicTypedArrayByteOffset
:
11543 return tryAttachTypedArrayByteOffset();
11544 case InlinableNative::IntrinsicTypedArrayElementSize
:
11545 return tryAttachTypedArrayElementSize();
11546 case InlinableNative::IntrinsicTypedArrayLength
:
11547 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false);
11548 case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength
:
11549 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true);
11551 // Reflect natives.
11552 case InlinableNative::ReflectGetPrototypeOf
:
11553 return tryAttachReflectGetPrototypeOf();
11555 // Atomics intrinsics:
11556 case InlinableNative::AtomicsCompareExchange
:
11557 return tryAttachAtomicsCompareExchange();
11558 case InlinableNative::AtomicsExchange
:
11559 return tryAttachAtomicsExchange();
11560 case InlinableNative::AtomicsAdd
:
11561 return tryAttachAtomicsAdd();
11562 case InlinableNative::AtomicsSub
:
11563 return tryAttachAtomicsSub();
11564 case InlinableNative::AtomicsAnd
:
11565 return tryAttachAtomicsAnd();
11566 case InlinableNative::AtomicsOr
:
11567 return tryAttachAtomicsOr();
11568 case InlinableNative::AtomicsXor
:
11569 return tryAttachAtomicsXor();
11570 case InlinableNative::AtomicsLoad
:
11571 return tryAttachAtomicsLoad();
11572 case InlinableNative::AtomicsStore
:
11573 return tryAttachAtomicsStore();
11574 case InlinableNative::AtomicsIsLockFree
:
11575 return tryAttachAtomicsIsLockFree();
11578 case InlinableNative::BigIntAsIntN
:
11579 return tryAttachBigIntAsIntN();
11580 case InlinableNative::BigIntAsUintN
:
11581 return tryAttachBigIntAsUintN();
11583 // Boolean natives.
11584 case InlinableNative::Boolean
:
11585 return tryAttachBoolean();
11588 case InlinableNative::SetHas
:
11589 return tryAttachSetHas();
11590 case InlinableNative::SetSize
:
11591 return tryAttachSetSize();
11594 case InlinableNative::MapHas
:
11595 return tryAttachMapHas();
11596 case InlinableNative::MapGet
:
11597 return tryAttachMapGet();
11599 // Testing functions.
11600 case InlinableNative::TestBailout
:
11601 if (js::SupportDifferentialTesting()) {
11602 return AttachDecision::NoAction
;
11604 return tryAttachBailout();
11605 case InlinableNative::TestAssertFloat32
:
11606 return tryAttachAssertFloat32();
11607 case InlinableNative::TestAssertRecoveredOnBailout
:
11608 if (js::SupportDifferentialTesting()) {
11609 return AttachDecision::NoAction
;
11611 return tryAttachAssertRecoveredOnBailout();
11613 #ifdef FUZZING_JS_FUZZILLI
11614 // Fuzzilli function
11615 case InlinableNative::FuzzilliHash
:
11616 return tryAttachFuzzilliHash();
11619 case InlinableNative::Limit
:
11623 MOZ_CRASH("Shouldn't get here");
11626 // Remember the shape of the this object for any script being called as a
11627 // constructor, for later use during Ion compilation.
11628 ScriptedThisResult
CallIRGenerator::getThisShapeForScripted(
11629 HandleFunction calleeFunc
, Handle
<JSObject
*> newTarget
,
11630 MutableHandle
<Shape
*> result
) {
11631 // Some constructors allocate their own |this| object.
11632 if (calleeFunc
->constructorNeedsUninitializedThis()) {
11633 return ScriptedThisResult::UninitializedThis
;
11636 // Only attach a stub if the newTarget is a function with a
11637 // nonconfigurable prototype.
11638 if (!newTarget
->is
<JSFunction
>() ||
11639 !newTarget
->as
<JSFunction
>().hasNonConfigurablePrototypeDataProperty()) {
11640 return ScriptedThisResult::NoAction
;
11643 AutoRealm
ar(cx_
, calleeFunc
);
11644 Shape
* thisShape
= ThisShapeForFunction(cx_
, calleeFunc
, newTarget
);
11646 cx_
->clearPendingException();
11647 return ScriptedThisResult::NoAction
;
11650 MOZ_ASSERT(thisShape
->realm() == calleeFunc
->realm());
11651 result
.set(thisShape
);
11652 return ScriptedThisResult::PlainObjectShape
;
11655 static bool CanOptimizeScriptedCall(JSFunction
* callee
, bool isConstructing
) {
11656 if (!callee
->hasJitEntry()) {
11660 // If callee is not an interpreted constructor, we have to throw.
11661 if (isConstructing
&& !callee
->isConstructor()) {
11665 // Likewise, if the callee is a class constructor, we have to throw.
11666 if (!isConstructing
&& callee
->isClassConstructor()) {
11673 void CallIRGenerator::emitCallScriptedGuards(ObjOperandId calleeObjId
,
11674 JSFunction
* calleeFunc
,
11675 Int32OperandId argcId
,
11676 CallFlags flags
, Shape
* thisShape
,
11677 bool isBoundFunction
) {
11678 bool isConstructing
= flags
.isConstructing();
11680 if (mode_
== ICState::Mode::Specialized
) {
11681 MOZ_ASSERT_IF(isConstructing
, thisShape
|| flags
.needsUninitializedThis());
11683 // Ensure callee matches this stub's callee
11684 emitCalleeGuard(calleeObjId
, calleeFunc
);
11686 // Emit guards to ensure the newTarget's .prototype property is what we
11687 // expect. Note that getThisForScripted checked newTarget is a function
11688 // with a non-configurable .prototype data property.
11690 JSFunction
* newTarget
;
11691 ObjOperandId newTargetObjId
;
11692 if (isBoundFunction
) {
11693 newTarget
= calleeFunc
;
11694 newTargetObjId
= calleeObjId
;
11696 newTarget
= &newTarget_
.toObject().as
<JSFunction
>();
11697 ValOperandId newTargetValId
= writer
.loadArgumentDynamicSlot(
11698 ArgumentKind::NewTarget
, argcId
, flags
);
11699 newTargetObjId
= writer
.guardToObject(newTargetValId
);
11702 Maybe
<PropertyInfo
> prop
= newTarget
->lookupPure(cx_
->names().prototype
);
11703 MOZ_ASSERT(prop
.isSome());
11704 uint32_t slot
= prop
->slot();
11705 MOZ_ASSERT(slot
>= newTarget
->numFixedSlots(),
11706 "Stub code relies on this");
11708 writer
.guardShape(newTargetObjId
, newTarget
->shape());
11710 const Value
& value
= newTarget
->getSlot(slot
);
11711 if (value
.isObject()) {
11712 JSObject
* prototypeObject
= &value
.toObject();
11714 ObjOperandId protoId
= writer
.loadObject(prototypeObject
);
11715 writer
.guardDynamicSlotIsSpecificObject(
11716 newTargetObjId
, protoId
, slot
- newTarget
->numFixedSlots());
11718 writer
.guardDynamicSlotIsNotObject(newTargetObjId
,
11719 slot
- newTarget
->numFixedSlots());
11722 // Call metaScriptedThisShape before emitting the call, so that Warp can
11723 // use the shape to create the |this| object before transpiling the call.
11724 writer
.metaScriptedThisShape(thisShape
);
11727 // Guard that object is a scripted function
11728 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
11729 writer
.guardFunctionHasJitEntry(calleeObjId
, isConstructing
);
11731 if (isConstructing
) {
11732 // If callee is not a constructor, we have to throw.
11733 writer
.guardFunctionIsConstructor(calleeObjId
);
11735 // If callee is a class constructor, we have to throw.
11736 writer
.guardNotClassConstructor(calleeObjId
);
11741 AttachDecision
CallIRGenerator::tryAttachCallScripted(
11742 HandleFunction calleeFunc
) {
11743 MOZ_ASSERT(calleeFunc
->hasJitEntry());
11745 if (calleeFunc
->isWasmWithJitEntry()) {
11746 TRY_ATTACH(tryAttachWasmCall(calleeFunc
));
11749 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
11751 bool isConstructing
= IsConstructPC(pc_
);
11752 bool isSpread
= IsSpreadPC(pc_
);
11753 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
11754 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
11756 if (!CanOptimizeScriptedCall(calleeFunc
, isConstructing
)) {
11757 return AttachDecision::NoAction
;
11760 if (isConstructing
&& !calleeFunc
->hasJitScript()) {
11761 // If we're constructing, require the callee to have a JitScript. This isn't
11762 // required for correctness but avoids allocating a template object below
11763 // for constructors that aren't hot. See bug 1419758.
11764 return AttachDecision::TemporarilyUnoptimizable
;
11767 // Verify that spread calls have a reasonable number of arguments.
11768 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
11769 return AttachDecision::NoAction
;
11772 Rooted
<Shape
*> thisShape(cx_
);
11773 if (isConstructing
&& isSpecialized
) {
11774 Rooted
<JSObject
*> newTarget(cx_
, &newTarget_
.toObject());
11775 switch (getThisShapeForScripted(calleeFunc
, newTarget
, &thisShape
)) {
11776 case ScriptedThisResult::PlainObjectShape
:
11778 case ScriptedThisResult::UninitializedThis
:
11779 flags
.setNeedsUninitializedThis();
11781 case ScriptedThisResult::NoAction
:
11782 return AttachDecision::NoAction
;
11787 Int32OperandId
argcId(writer
.setInputOperandId(0));
11789 // Load the callee and ensure it is an object
11790 ValOperandId calleeValId
=
11791 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11792 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11794 emitCallScriptedGuards(calleeObjId
, calleeFunc
, argcId
, flags
, thisShape
,
11795 /* isBoundFunction = */ false);
11797 writer
.callScriptedFunction(calleeObjId
, argcId
, flags
,
11798 ClampFixedArgc(argc_
));
11799 writer
.returnFromIC();
11801 if (isSpecialized
) {
11802 trackAttached("Call.CallScripted");
11804 trackAttached("Call.CallAnyScripted");
11807 return AttachDecision::Attach
;
11810 AttachDecision
CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc
) {
11811 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
11813 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
11815 bool isSpread
= IsSpreadPC(pc_
);
11816 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
11817 bool isConstructing
= IsConstructPC(pc_
);
11818 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
11820 if (isConstructing
&& !calleeFunc
->isConstructor()) {
11821 return AttachDecision::NoAction
;
11824 // Verify that spread calls have a reasonable number of arguments.
11825 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
11826 return AttachDecision::NoAction
;
11829 // Check for specific native-function optimizations.
11830 if (isSpecialized
) {
11831 TRY_ATTACH(tryAttachInlinableNative(calleeFunc
, flags
));
11835 Int32OperandId
argcId(writer
.setInputOperandId(0));
11837 // Load the callee and ensure it is an object
11838 ValOperandId calleeValId
=
11839 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11840 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11842 // DOM calls need an additional guard so only try optimizing the first stub.
11843 // Can only optimize normal (non-spread) calls.
11844 if (isFirstStub_
&& !isSpread
&& thisval_
.isObject() &&
11845 CanAttachDOMCall(cx_
, JSJitInfo::Method
, &thisval_
.toObject(), calleeFunc
,
11847 MOZ_ASSERT(!isConstructing
, "DOM functions are not constructors");
11849 // Guard that |this| is an object.
11850 ValOperandId thisValId
=
11851 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
, flags
);
11852 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
11854 // Guard on the |this| shape to make sure it's the right instance. This also
11855 // ensures DOM_OBJECT_SLOT is stored in a fixed slot. See CanAttachDOMCall.
11856 writer
.guardShape(thisObjId
, thisval_
.toObject().shape());
11858 // Ensure callee matches this stub's callee
11859 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
11860 writer
.callDOMFunction(calleeObjId
, argcId
, thisObjId
, calleeFunc
, flags
,
11861 ClampFixedArgc(argc_
));
11863 trackAttached("Call.CallDOM");
11864 } else if (isSpecialized
) {
11865 // Ensure callee matches this stub's callee
11866 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
11867 writer
.callNativeFunction(calleeObjId
, argcId
, op_
, calleeFunc
, flags
,
11868 ClampFixedArgc(argc_
));
11870 trackAttached("Call.CallNative");
11872 // Guard that object is a native function
11873 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
11874 writer
.guardFunctionHasNoJitEntry(calleeObjId
);
11876 if (isConstructing
) {
11877 // If callee is not a constructor, we have to throw.
11878 writer
.guardFunctionIsConstructor(calleeObjId
);
11880 // If callee is a class constructor, we have to throw.
11881 writer
.guardNotClassConstructor(calleeObjId
);
11883 writer
.callAnyNativeFunction(calleeObjId
, argcId
, flags
,
11884 ClampFixedArgc(argc_
));
11886 trackAttached("Call.CallAnyNative");
11889 writer
.returnFromIC();
11891 return AttachDecision::Attach
;
11894 AttachDecision
CallIRGenerator::tryAttachCallHook(HandleObject calleeObj
) {
11895 if (mode_
!= ICState::Mode::Specialized
) {
11896 // We do not have megamorphic call hook stubs.
11897 // TODO: Should we attach specialized call hook stubs in
11898 // megamorphic mode to avoid going generic?
11899 return AttachDecision::NoAction
;
11902 bool isSpread
= IsSpreadPC(pc_
);
11903 bool isConstructing
= IsConstructPC(pc_
);
11904 CallFlags
flags(isConstructing
, isSpread
);
11906 isConstructing
? calleeObj
->constructHook() : calleeObj
->callHook();
11908 return AttachDecision::NoAction
;
11911 // Bound functions have a JSClass construct hook but are not always
11913 if (isConstructing
&& !calleeObj
->isConstructor()) {
11914 return AttachDecision::NoAction
;
11917 // We don't support spread calls in the transpiler yet.
11919 return AttachDecision::NoAction
;
11923 Int32OperandId
argcId(writer
.setInputOperandId(0));
11925 // Load the callee and ensure it is an object
11926 ValOperandId calleeValId
=
11927 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
11928 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11930 // Ensure the callee's class matches the one in this stub.
11931 writer
.guardAnyClass(calleeObjId
, calleeObj
->getClass());
11933 if (isConstructing
&& calleeObj
->is
<BoundFunctionObject
>()) {
11934 writer
.guardBoundFunctionIsConstructor(calleeObjId
);
11937 writer
.callClassHook(calleeObjId
, argcId
, hook
, flags
, ClampFixedArgc(argc_
));
11938 writer
.returnFromIC();
11940 trackAttached("Call.CallHook");
11942 return AttachDecision::Attach
;
11945 AttachDecision
CallIRGenerator::tryAttachBoundFunction(
11946 Handle
<BoundFunctionObject
*> calleeObj
) {
11947 // The target must be a JSFunction with a JitEntry.
11948 if (!calleeObj
->getTarget()->is
<JSFunction
>()) {
11949 return AttachDecision::NoAction
;
11952 bool isSpread
= IsSpreadPC(pc_
);
11953 bool isConstructing
= IsConstructPC(pc_
);
11955 // Spread calls are not supported yet.
11957 return AttachDecision::NoAction
;
11960 Rooted
<JSFunction
*> target(cx_
, &calleeObj
->getTarget()->as
<JSFunction
>());
11961 if (!CanOptimizeScriptedCall(target
, isConstructing
)) {
11962 return AttachDecision::NoAction
;
11965 // Limit the number of bound arguments to prevent us from compiling many
11966 // different stubs (we bake in numBoundArgs and it's usually very small).
11967 static constexpr size_t MaxBoundArgs
= 10;
11968 size_t numBoundArgs
= calleeObj
->numBoundArgs();
11969 if (numBoundArgs
> MaxBoundArgs
) {
11970 return AttachDecision::NoAction
;
11973 // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
11974 if (numBoundArgs
+ argc_
> JIT_ARGS_LENGTH_MAX
) {
11975 return AttachDecision::NoAction
;
11978 CallFlags
flags(isConstructing
, isSpread
);
11980 if (mode_
== ICState::Mode::Specialized
) {
11981 if (cx_
->realm() == target
->realm()) {
11982 flags
.setIsSameRealm();
11986 Rooted
<Shape
*> thisShape(cx_
);
11987 if (isConstructing
) {
11988 // Only optimize if newTarget == callee. This is the common case and ensures
11989 // we can always pass the bound function's target as newTarget.
11990 if (newTarget_
!= ObjectValue(*calleeObj
)) {
11991 return AttachDecision::NoAction
;
11994 if (mode_
== ICState::Mode::Specialized
) {
11995 Handle
<JSFunction
*> newTarget
= target
;
11996 switch (getThisShapeForScripted(target
, newTarget
, &thisShape
)) {
11997 case ScriptedThisResult::PlainObjectShape
:
11999 case ScriptedThisResult::UninitializedThis
:
12000 flags
.setNeedsUninitializedThis();
12002 case ScriptedThisResult::NoAction
:
12003 return AttachDecision::NoAction
;
12009 Int32OperandId
argcId(writer
.setInputOperandId(0));
12011 // Load the callee and ensure it's a bound function.
12012 ValOperandId calleeValId
=
12013 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12014 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12015 writer
.guardClass(calleeObjId
, GuardClassKind::BoundFunction
);
12017 // Ensure numBoundArgs matches.
12018 Int32OperandId numBoundArgsId
= writer
.loadBoundFunctionNumArgs(calleeObjId
);
12019 writer
.guardSpecificInt32(numBoundArgsId
, numBoundArgs
);
12021 if (isConstructing
) {
12022 // Guard newTarget == callee. We depend on this in CallBoundScriptedFunction
12023 // and in emitCallScriptedGuards by using boundTarget as newTarget.
12024 ValOperandId newTargetValId
=
12025 writer
.loadArgumentDynamicSlot(ArgumentKind::NewTarget
, argcId
, flags
);
12026 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
12027 writer
.guardObjectIdentity(newTargetObjId
, calleeObjId
);
12030 ObjOperandId targetId
= writer
.loadBoundFunctionTarget(calleeObjId
);
12032 emitCallScriptedGuards(targetId
, target
, argcId
, flags
, thisShape
,
12033 /* isBoundFunction = */ true);
12035 writer
.callBoundScriptedFunction(calleeObjId
, targetId
, argcId
, flags
,
12037 writer
.returnFromIC();
12039 trackAttached("Call.BoundFunction");
12040 return AttachDecision::Attach
;
12043 AttachDecision
CallIRGenerator::tryAttachStub() {
12044 AutoAssertNoPendingException
aanpe(cx_
);
12046 // Some opcodes are not yet supported.
12049 case JSOp::CallContent
:
12050 case JSOp::CallIgnoresRv
:
12051 case JSOp::CallIter
:
12052 case JSOp::CallContentIter
:
12053 case JSOp::SpreadCall
:
12055 case JSOp::NewContent
:
12056 case JSOp::SpreadNew
:
12057 case JSOp::SuperCall
:
12058 case JSOp::SpreadSuperCall
:
12061 return AttachDecision::NoAction
;
12064 MOZ_ASSERT(mode_
!= ICState::Mode::Generic
);
12066 // Ensure callee is a function.
12067 if (!callee_
.isObject()) {
12068 return AttachDecision::NoAction
;
12071 RootedObject
calleeObj(cx_
, &callee_
.toObject());
12072 if (calleeObj
->is
<BoundFunctionObject
>()) {
12073 TRY_ATTACH(tryAttachBoundFunction(calleeObj
.as
<BoundFunctionObject
>()));
12075 if (!calleeObj
->is
<JSFunction
>()) {
12076 return tryAttachCallHook(calleeObj
);
12079 HandleFunction calleeFunc
= calleeObj
.as
<JSFunction
>();
12081 // Check for scripted optimizations.
12082 if (calleeFunc
->hasJitEntry()) {
12083 return tryAttachCallScripted(calleeFunc
);
12086 // Check for native-function optimizations.
12087 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12089 // Try inlining Function.prototype.{call,apply}. We don't use the
12090 // InlinableNative mechanism for this because we want to optimize these more
12091 // aggressively than other natives.
12092 if (op_
== JSOp::Call
|| op_
== JSOp::CallContent
||
12093 op_
== JSOp::CallIgnoresRv
) {
12094 TRY_ATTACH(tryAttachFunCall(calleeFunc
));
12095 TRY_ATTACH(tryAttachFunApply(calleeFunc
));
12098 return tryAttachCallNative(calleeFunc
);
12101 void CallIRGenerator::trackAttached(const char* name
) {
12102 stubName_
= name
? name
: "NotAttached";
12103 #ifdef JS_CACHEIR_SPEW
12104 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12105 sp
.valueProperty("callee", callee_
);
12106 sp
.valueProperty("thisval", thisval_
);
12107 sp
.valueProperty("argc", Int32Value(argc_
));
12109 // Try to log the first two arguments.
12110 if (args_
.length() >= 1) {
12111 sp
.valueProperty("arg0", args_
[0]);
12113 if (args_
.length() >= 2) {
12114 sp
.valueProperty("arg1", args_
[1]);
12120 // Class which holds a shape pointer for use when caches might reference data in
12122 static const JSClass shapeContainerClass
= {"ShapeContainer",
12123 JSCLASS_HAS_RESERVED_SLOTS(1)};
12125 static const size_t SHAPE_CONTAINER_SLOT
= 0;
12127 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
12128 Handle
<NativeObject
*> obj
) {
12129 MOZ_ASSERT(cx
->compartment() != obj
->compartment());
12131 RootedObject
wrapper(cx
);
12133 AutoRealm
ar(cx
, obj
);
12134 wrapper
= NewBuiltinClassInstance(cx
, &shapeContainerClass
);
12138 wrapper
->as
<NativeObject
>().setReservedSlot(
12139 SHAPE_CONTAINER_SLOT
, PrivateGCThingValue(obj
->shape()));
12141 if (!JS_WrapObject(cx
, &wrapper
)) {
12144 MOZ_ASSERT(IsWrapper(wrapper
));
12148 void jit::LoadShapeWrapperContents(MacroAssembler
& masm
, Register obj
,
12149 Register dst
, Label
* failure
) {
12150 masm
.loadPtr(Address(obj
, ProxyObject::offsetOfReservedSlots()), dst
);
12151 Address
privateAddr(dst
,
12152 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
12153 masm
.fallibleUnboxObject(privateAddr
, dst
, failure
);
12154 masm
.unboxNonDouble(
12155 Address(dst
, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT
)), dst
,
12156 JSVAL_TYPE_PRIVATE_GCTHING
);
12159 static bool CanConvertToInt32ForToNumber(const Value
& v
) {
12160 return v
.isInt32() || v
.isBoolean() || v
.isNull();
12163 static Int32OperandId
EmitGuardToInt32ForToNumber(CacheIRWriter
& writer
,
12167 return writer
.guardToInt32(id
);
12170 writer
.guardIsNull(id
);
12171 return writer
.loadInt32Constant(0);
12173 MOZ_ASSERT(v
.isBoolean());
12174 return writer
.guardBooleanToInt32(id
);
12177 static bool CanConvertToDoubleForToNumber(const Value
& v
) {
12178 return v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
12181 static NumberOperandId
EmitGuardToDoubleForToNumber(CacheIRWriter
& writer
,
12184 if (v
.isNumber()) {
12185 return writer
.guardIsNumber(id
);
12187 if (v
.isBoolean()) {
12188 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
12189 return writer
.booleanToNumber(boolId
);
12192 writer
.guardIsNull(id
);
12193 return writer
.loadDoubleConstant(0.0);
12195 MOZ_ASSERT(v
.isUndefined());
12196 writer
.guardIsUndefined(id
);
12197 return writer
.loadDoubleConstant(JS::GenericNaN());
12200 CompareIRGenerator::CompareIRGenerator(JSContext
* cx
, HandleScript script
,
12201 jsbytecode
* pc
, ICState state
, JSOp op
,
12202 HandleValue lhsVal
, HandleValue rhsVal
)
12203 : IRGenerator(cx
, script
, pc
, CacheKind::Compare
, state
),
12208 AttachDecision
CompareIRGenerator::tryAttachString(ValOperandId lhsId
,
12209 ValOperandId rhsId
) {
12210 if (!lhsVal_
.isString() || !rhsVal_
.isString()) {
12211 return AttachDecision::NoAction
;
12214 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
12215 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
12216 writer
.compareStringResult(op_
, lhsStrId
, rhsStrId
);
12217 writer
.returnFromIC();
12219 trackAttached("Compare.String");
12220 return AttachDecision::Attach
;
12223 AttachDecision
CompareIRGenerator::tryAttachObject(ValOperandId lhsId
,
12224 ValOperandId rhsId
) {
12225 MOZ_ASSERT(IsEqualityOp(op_
));
12227 if (!lhsVal_
.isObject() || !rhsVal_
.isObject()) {
12228 return AttachDecision::NoAction
;
12231 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
12232 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
12233 writer
.compareObjectResult(op_
, lhsObjId
, rhsObjId
);
12234 writer
.returnFromIC();
12236 trackAttached("Compare.Object");
12237 return AttachDecision::Attach
;
12240 AttachDecision
CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId
,
12241 ValOperandId rhsId
) {
12242 MOZ_ASSERT(IsEqualityOp(op_
));
12244 if (!lhsVal_
.isSymbol() || !rhsVal_
.isSymbol()) {
12245 return AttachDecision::NoAction
;
12248 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
12249 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
12250 writer
.compareSymbolResult(op_
, lhsSymId
, rhsSymId
);
12251 writer
.returnFromIC();
12253 trackAttached("Compare.Symbol");
12254 return AttachDecision::Attach
;
12257 AttachDecision
CompareIRGenerator::tryAttachStrictDifferentTypes(
12258 ValOperandId lhsId
, ValOperandId rhsId
) {
12259 MOZ_ASSERT(IsEqualityOp(op_
));
12261 if (op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
) {
12262 return AttachDecision::NoAction
;
12265 // Probably can't hit some of these.
12266 if (SameType(lhsVal_
, rhsVal_
) ||
12267 (lhsVal_
.isNumber() && rhsVal_
.isNumber())) {
12268 return AttachDecision::NoAction
;
12272 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
12273 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
12274 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
12276 // Now that we've passed the guard, we know differing types, so return the
12278 writer
.loadBooleanResult(op_
== JSOp::StrictNe
? true : false);
12279 writer
.returnFromIC();
12281 trackAttached("Compare.StrictDifferentTypes");
12282 return AttachDecision::Attach
;
12285 AttachDecision
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId
,
12286 ValOperandId rhsId
) {
12287 if (!CanConvertToInt32ForToNumber(lhsVal_
) ||
12288 !CanConvertToInt32ForToNumber(rhsVal_
)) {
12289 return AttachDecision::NoAction
;
12292 // Strictly different types should have been handed by
12293 // tryAttachStrictDifferentTypes.
12294 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12295 lhsVal_
.type() == rhsVal_
.type());
12297 // Should have been handled by tryAttachAnyNullUndefined.
12298 MOZ_ASSERT_IF(lhsVal_
.isNull() || rhsVal_
.isNull(), !IsEqualityOp(op_
));
12300 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12301 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12303 writer
.compareInt32Result(op_
, lhsIntId
, rhsIntId
);
12304 writer
.returnFromIC();
12306 trackAttached("Compare.Int32");
12307 return AttachDecision::Attach
;
12310 AttachDecision
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId
,
12311 ValOperandId rhsId
) {
12312 if (!CanConvertToDoubleForToNumber(lhsVal_
) ||
12313 !CanConvertToDoubleForToNumber(rhsVal_
)) {
12314 return AttachDecision::NoAction
;
12317 // Strictly different types should have been handed by
12318 // tryAttachStrictDifferentTypes.
12319 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12320 lhsVal_
.type() == rhsVal_
.type() ||
12321 (lhsVal_
.isNumber() && rhsVal_
.isNumber()));
12323 // Should have been handled by tryAttachAnyNullUndefined.
12324 MOZ_ASSERT_IF(lhsVal_
.isNullOrUndefined() || rhsVal_
.isNullOrUndefined(),
12325 !IsEqualityOp(op_
));
12327 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12328 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12329 writer
.compareDoubleResult(op_
, lhs
, rhs
);
12330 writer
.returnFromIC();
12332 trackAttached("Compare.Number");
12333 return AttachDecision::Attach
;
12336 AttachDecision
CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId
,
12337 ValOperandId rhsId
) {
12338 if (!lhsVal_
.isBigInt() || !rhsVal_
.isBigInt()) {
12339 return AttachDecision::NoAction
;
12342 BigIntOperandId lhs
= writer
.guardToBigInt(lhsId
);
12343 BigIntOperandId rhs
= writer
.guardToBigInt(rhsId
);
12345 writer
.compareBigIntResult(op_
, lhs
, rhs
);
12346 writer
.returnFromIC();
12348 trackAttached("Compare.BigInt");
12349 return AttachDecision::Attach
;
12352 AttachDecision
CompareIRGenerator::tryAttachAnyNullUndefined(
12353 ValOperandId lhsId
, ValOperandId rhsId
) {
12354 MOZ_ASSERT(IsEqualityOp(op_
));
12356 // Either RHS or LHS needs to be null/undefined.
12357 if (!lhsVal_
.isNullOrUndefined() && !rhsVal_
.isNullOrUndefined()) {
12358 return AttachDecision::NoAction
;
12361 // We assume that the side with null/undefined is usually constant, in
12362 // code like `if (x === undefined) { x = {}; }`.
12363 // That is why we don't attach when both sides are undefined/null,
12364 // because we would basically need to decide by chance which side is
12365 // the likely constant.
12366 // The actual generated code however handles null/undefined of course.
12367 if (lhsVal_
.isNullOrUndefined() && rhsVal_
.isNullOrUndefined()) {
12368 return AttachDecision::NoAction
;
12371 if (rhsVal_
.isNullOrUndefined()) {
12372 if (rhsVal_
.isNull()) {
12373 writer
.guardIsNull(rhsId
);
12374 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, lhsId
);
12375 trackAttached("Compare.AnyNull");
12377 writer
.guardIsUndefined(rhsId
);
12378 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, lhsId
);
12379 trackAttached("Compare.AnyUndefined");
12382 if (lhsVal_
.isNull()) {
12383 writer
.guardIsNull(lhsId
);
12384 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, rhsId
);
12385 trackAttached("Compare.NullAny");
12387 writer
.guardIsUndefined(lhsId
);
12388 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, rhsId
);
12389 trackAttached("Compare.UndefinedAny");
12393 writer
.returnFromIC();
12394 return AttachDecision::Attach
;
12397 // Handle {null/undefined} x {null,undefined} equality comparisons
12398 AttachDecision
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId
,
12399 ValOperandId rhsId
) {
12400 if (!lhsVal_
.isNullOrUndefined() || !rhsVal_
.isNullOrUndefined()) {
12401 return AttachDecision::NoAction
;
12404 if (op_
== JSOp::Eq
|| op_
== JSOp::Ne
) {
12405 writer
.guardIsNullOrUndefined(lhsId
);
12406 writer
.guardIsNullOrUndefined(rhsId
);
12407 // Sloppy equality means we actually only care about the op:
12408 writer
.loadBooleanResult(op_
== JSOp::Eq
);
12409 trackAttached("Compare.SloppyNullUndefined");
12411 // Strict equality only hits this branch, and only in the
12412 // undef {!,=}== undef and null {!,=}== null cases.
12413 // The other cases should have hit tryAttachStrictDifferentTypes.
12414 MOZ_ASSERT(lhsVal_
.isNull() == rhsVal_
.isNull());
12415 lhsVal_
.isNull() ? writer
.guardIsNull(lhsId
)
12416 : writer
.guardIsUndefined(lhsId
);
12417 rhsVal_
.isNull() ? writer
.guardIsNull(rhsId
)
12418 : writer
.guardIsUndefined(rhsId
);
12419 writer
.loadBooleanResult(op_
== JSOp::StrictEq
);
12420 trackAttached("Compare.StrictNullUndefinedEquality");
12423 writer
.returnFromIC();
12424 return AttachDecision::Attach
;
12427 AttachDecision
CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId
,
12428 ValOperandId rhsId
) {
12429 // Ensure String x {Number, Boolean, Null, Undefined}
12430 if (!(lhsVal_
.isString() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12431 !(rhsVal_
.isString() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12432 return AttachDecision::NoAction
;
12435 // Case should have been handled by tryAttachStrictDifferentTypes
12436 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12438 auto createGuards
= [&](const Value
& v
, ValOperandId vId
) {
12439 if (v
.isString()) {
12440 StringOperandId strId
= writer
.guardToString(vId
);
12441 return writer
.guardStringToNumber(strId
);
12443 return EmitGuardToDoubleForToNumber(writer
, vId
, v
);
12446 NumberOperandId lhsGuardedId
= createGuards(lhsVal_
, lhsId
);
12447 NumberOperandId rhsGuardedId
= createGuards(rhsVal_
, rhsId
);
12448 writer
.compareDoubleResult(op_
, lhsGuardedId
, rhsGuardedId
);
12449 writer
.returnFromIC();
12451 trackAttached("Compare.StringNumber");
12452 return AttachDecision::Attach
;
12455 AttachDecision
CompareIRGenerator::tryAttachPrimitiveSymbol(
12456 ValOperandId lhsId
, ValOperandId rhsId
) {
12457 MOZ_ASSERT(IsEqualityOp(op_
));
12459 // The set of primitive cases we want to handle here (excluding null,
12460 // undefined, and symbol)
12461 auto isPrimitive
= [](const Value
& x
) {
12462 return x
.isString() || x
.isBoolean() || x
.isNumber() || x
.isBigInt();
12465 // Ensure Symbol x {String, Bool, Number, BigInt}.
12466 if (!(lhsVal_
.isSymbol() && isPrimitive(rhsVal_
)) &&
12467 !(rhsVal_
.isSymbol() && isPrimitive(lhsVal_
))) {
12468 return AttachDecision::NoAction
;
12471 auto guardPrimitive
= [&](const Value
& v
, ValOperandId id
) {
12472 MOZ_ASSERT(isPrimitive(v
));
12473 if (v
.isNumber()) {
12474 writer
.guardIsNumber(id
);
12477 switch (v
.extractNonDoubleType()) {
12478 case JSVAL_TYPE_STRING
:
12479 writer
.guardToString(id
);
12481 case JSVAL_TYPE_BOOLEAN
:
12482 writer
.guardToBoolean(id
);
12484 case JSVAL_TYPE_BIGINT
:
12485 writer
.guardToBigInt(id
);
12488 MOZ_CRASH("unexpected type");
12493 if (lhsVal_
.isSymbol()) {
12494 writer
.guardToSymbol(lhsId
);
12495 guardPrimitive(rhsVal_
, rhsId
);
12497 guardPrimitive(lhsVal_
, lhsId
);
12498 writer
.guardToSymbol(rhsId
);
12501 // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
12502 // always be false for other compare ops.
12503 writer
.loadBooleanResult(op_
== JSOp::Ne
|| op_
== JSOp::StrictNe
);
12504 writer
.returnFromIC();
12506 trackAttached("Compare.PrimitiveSymbol");
12507 return AttachDecision::Attach
;
12510 AttachDecision
CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId
,
12511 ValOperandId rhsId
) {
12512 // Ensure BigInt x {Int32, Boolean, Null}.
12513 if (!(lhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(rhsVal_
)) &&
12514 !(rhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(lhsVal_
))) {
12515 return AttachDecision::NoAction
;
12518 // Case should have been handled by tryAttachStrictDifferentTypes
12519 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12521 if (lhsVal_
.isBigInt()) {
12522 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12523 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12525 writer
.compareBigIntInt32Result(op_
, bigIntId
, intId
);
12527 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12528 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12530 writer
.compareBigIntInt32Result(ReverseCompareOp(op_
), bigIntId
, intId
);
12532 writer
.returnFromIC();
12534 trackAttached("Compare.BigIntInt32");
12535 return AttachDecision::Attach
;
12538 AttachDecision
CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId
,
12539 ValOperandId rhsId
) {
12540 // Ensure BigInt x {Number, Undefined}.
12541 if (!(lhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12542 !(rhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12543 return AttachDecision::NoAction
;
12546 // Case should have been handled by tryAttachStrictDifferentTypes
12547 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12549 // Case should have been handled by tryAttachBigIntInt32.
12550 MOZ_ASSERT(!CanConvertToInt32ForToNumber(lhsVal_
));
12551 MOZ_ASSERT(!CanConvertToInt32ForToNumber(rhsVal_
));
12553 if (lhsVal_
.isBigInt()) {
12554 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12555 NumberOperandId numId
=
12556 EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12558 writer
.compareBigIntNumberResult(op_
, bigIntId
, numId
);
12560 NumberOperandId numId
=
12561 EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12562 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12564 writer
.compareBigIntNumberResult(ReverseCompareOp(op_
), bigIntId
, numId
);
12566 writer
.returnFromIC();
12568 trackAttached("Compare.BigIntNumber");
12569 return AttachDecision::Attach
;
12572 AttachDecision
CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId
,
12573 ValOperandId rhsId
) {
12574 // Ensure BigInt x String.
12575 if (!(lhsVal_
.isBigInt() && rhsVal_
.isString()) &&
12576 !(rhsVal_
.isBigInt() && lhsVal_
.isString())) {
12577 return AttachDecision::NoAction
;
12580 // Case should have been handled by tryAttachStrictDifferentTypes
12581 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12583 if (lhsVal_
.isBigInt()) {
12584 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12585 StringOperandId strId
= writer
.guardToString(rhsId
);
12587 writer
.compareBigIntStringResult(op_
, bigIntId
, strId
);
12589 StringOperandId strId
= writer
.guardToString(lhsId
);
12590 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12592 writer
.compareBigIntStringResult(ReverseCompareOp(op_
), bigIntId
, strId
);
12594 writer
.returnFromIC();
12596 trackAttached("Compare.BigIntString");
12597 return AttachDecision::Attach
;
12600 AttachDecision
CompareIRGenerator::tryAttachStub() {
12601 MOZ_ASSERT(cacheKind_
== CacheKind::Compare
);
12602 MOZ_ASSERT(IsEqualityOp(op_
) || IsRelationalOp(op_
));
12604 AutoAssertNoPendingException
aanpe(cx_
);
12606 constexpr uint8_t lhsIndex
= 0;
12607 constexpr uint8_t rhsIndex
= 1;
12609 ValOperandId
lhsId(writer
.setInputOperandId(lhsIndex
));
12610 ValOperandId
rhsId(writer
.setInputOperandId(rhsIndex
));
12612 // For sloppy equality ops, there are cases this IC does not handle:
12613 // - {Object} x {String, Symbol, Bool, Number, BigInt}.
12615 // For relational comparison ops, these cases aren't handled:
12616 // - Object x {String, Bool, Number, BigInt, Object, Null, Undefined}.
12617 // Note: |Symbol x any| always throws, so it doesn't need to be handled.
12619 // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
12620 // already present.)
12622 if (IsEqualityOp(op_
)) {
12623 TRY_ATTACH(tryAttachObject(lhsId
, rhsId
));
12624 TRY_ATTACH(tryAttachSymbol(lhsId
, rhsId
));
12626 // Handles any (non null or undefined) comparison with null/undefined.
12627 TRY_ATTACH(tryAttachAnyNullUndefined(lhsId
, rhsId
));
12629 // This covers -strict- equality/inequality using a type tag check, so
12630 // catches all different type pairs outside of Numbers, which cannot be
12631 // checked on tags alone.
12632 TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId
, rhsId
));
12634 TRY_ATTACH(tryAttachNullUndefined(lhsId
, rhsId
));
12636 TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId
, rhsId
));
12639 // We want these to be last, to allow us to bypass the
12640 // strictly-different-types cases in the below attachment code
12641 TRY_ATTACH(tryAttachInt32(lhsId
, rhsId
));
12642 TRY_ATTACH(tryAttachNumber(lhsId
, rhsId
));
12643 TRY_ATTACH(tryAttachBigInt(lhsId
, rhsId
));
12644 TRY_ATTACH(tryAttachString(lhsId
, rhsId
));
12646 TRY_ATTACH(tryAttachStringNumber(lhsId
, rhsId
));
12648 TRY_ATTACH(tryAttachBigIntInt32(lhsId
, rhsId
));
12649 TRY_ATTACH(tryAttachBigIntNumber(lhsId
, rhsId
));
12650 TRY_ATTACH(tryAttachBigIntString(lhsId
, rhsId
));
12652 // Strict equality is always supported.
12653 MOZ_ASSERT(!IsStrictEqualityOp(op_
));
12655 // Other operations are unsupported iff at least one operand is an object.
12656 MOZ_ASSERT(lhsVal_
.isObject() || rhsVal_
.isObject());
12658 trackAttached(IRGenerator::NotAttached
);
12659 return AttachDecision::NoAction
;
12662 void CompareIRGenerator::trackAttached(const char* name
) {
12663 stubName_
= name
? name
: "NotAttached";
12664 #ifdef JS_CACHEIR_SPEW
12665 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12666 sp
.valueProperty("lhs", lhsVal_
);
12667 sp
.valueProperty("rhs", rhsVal_
);
12668 sp
.opcodeProperty("op", op_
);
12673 ToBoolIRGenerator::ToBoolIRGenerator(JSContext
* cx
, HandleScript script
,
12674 jsbytecode
* pc
, ICState state
,
12676 : IRGenerator(cx
, script
, pc
, CacheKind::ToBool
, state
), val_(val
) {}
12678 void ToBoolIRGenerator::trackAttached(const char* name
) {
12679 stubName_
= name
? name
: "NotAttached";
12680 #ifdef JS_CACHEIR_SPEW
12681 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12682 sp
.valueProperty("val", val_
);
12687 AttachDecision
ToBoolIRGenerator::tryAttachStub() {
12688 AutoAssertNoPendingException
aanpe(cx_
);
12689 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
12691 TRY_ATTACH(tryAttachBool());
12692 TRY_ATTACH(tryAttachInt32());
12693 TRY_ATTACH(tryAttachNumber());
12694 TRY_ATTACH(tryAttachString());
12695 TRY_ATTACH(tryAttachNullOrUndefined());
12696 TRY_ATTACH(tryAttachObject());
12697 TRY_ATTACH(tryAttachSymbol());
12698 TRY_ATTACH(tryAttachBigInt());
12700 trackAttached(IRGenerator::NotAttached
);
12701 return AttachDecision::NoAction
;
12704 AttachDecision
ToBoolIRGenerator::tryAttachBool() {
12705 if (!val_
.isBoolean()) {
12706 return AttachDecision::NoAction
;
12709 ValOperandId
valId(writer
.setInputOperandId(0));
12710 writer
.guardNonDoubleType(valId
, ValueType::Boolean
);
12711 writer
.loadOperandResult(valId
);
12712 writer
.returnFromIC();
12713 trackAttached("ToBool.Bool");
12714 return AttachDecision::Attach
;
12717 AttachDecision
ToBoolIRGenerator::tryAttachInt32() {
12718 if (!val_
.isInt32()) {
12719 return AttachDecision::NoAction
;
12722 ValOperandId
valId(writer
.setInputOperandId(0));
12723 writer
.guardNonDoubleType(valId
, ValueType::Int32
);
12724 writer
.loadInt32TruthyResult(valId
);
12725 writer
.returnFromIC();
12726 trackAttached("ToBool.Int32");
12727 return AttachDecision::Attach
;
12730 AttachDecision
ToBoolIRGenerator::tryAttachNumber() {
12731 if (!val_
.isNumber()) {
12732 return AttachDecision::NoAction
;
12735 ValOperandId
valId(writer
.setInputOperandId(0));
12736 NumberOperandId numId
= writer
.guardIsNumber(valId
);
12737 writer
.loadDoubleTruthyResult(numId
);
12738 writer
.returnFromIC();
12739 trackAttached("ToBool.Number");
12740 return AttachDecision::Attach
;
12743 AttachDecision
ToBoolIRGenerator::tryAttachSymbol() {
12744 if (!val_
.isSymbol()) {
12745 return AttachDecision::NoAction
;
12748 ValOperandId
valId(writer
.setInputOperandId(0));
12749 writer
.guardNonDoubleType(valId
, ValueType::Symbol
);
12750 writer
.loadBooleanResult(true);
12751 writer
.returnFromIC();
12752 trackAttached("ToBool.Symbol");
12753 return AttachDecision::Attach
;
12756 AttachDecision
ToBoolIRGenerator::tryAttachString() {
12757 if (!val_
.isString()) {
12758 return AttachDecision::NoAction
;
12761 ValOperandId
valId(writer
.setInputOperandId(0));
12762 StringOperandId strId
= writer
.guardToString(valId
);
12763 writer
.loadStringTruthyResult(strId
);
12764 writer
.returnFromIC();
12765 trackAttached("ToBool.String");
12766 return AttachDecision::Attach
;
12769 AttachDecision
ToBoolIRGenerator::tryAttachNullOrUndefined() {
12770 if (!val_
.isNullOrUndefined()) {
12771 return AttachDecision::NoAction
;
12774 ValOperandId
valId(writer
.setInputOperandId(0));
12775 writer
.guardIsNullOrUndefined(valId
);
12776 writer
.loadBooleanResult(false);
12777 writer
.returnFromIC();
12778 trackAttached("ToBool.NullOrUndefined");
12779 return AttachDecision::Attach
;
12782 AttachDecision
ToBoolIRGenerator::tryAttachObject() {
12783 if (!val_
.isObject()) {
12784 return AttachDecision::NoAction
;
12787 ValOperandId
valId(writer
.setInputOperandId(0));
12788 ObjOperandId objId
= writer
.guardToObject(valId
);
12789 writer
.loadObjectTruthyResult(objId
);
12790 writer
.returnFromIC();
12791 trackAttached("ToBool.Object");
12792 return AttachDecision::Attach
;
12795 AttachDecision
ToBoolIRGenerator::tryAttachBigInt() {
12796 if (!val_
.isBigInt()) {
12797 return AttachDecision::NoAction
;
12800 ValOperandId
valId(writer
.setInputOperandId(0));
12801 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
12802 writer
.loadBigIntTruthyResult(bigIntId
);
12803 writer
.returnFromIC();
12804 trackAttached("ToBool.BigInt");
12805 return AttachDecision::Attach
;
12808 GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext
* cx
,
12809 HandleScript script
,
12810 jsbytecode
* pc
, ICState state
,
12812 : IRGenerator(cx
, script
, pc
, CacheKind::GetIntrinsic
, state
), val_(val
) {}
12814 void GetIntrinsicIRGenerator::trackAttached(const char* name
) {
12815 stubName_
= name
? name
: "NotAttached";
12816 #ifdef JS_CACHEIR_SPEW
12817 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12818 sp
.valueProperty("val", val_
);
12823 AttachDecision
GetIntrinsicIRGenerator::tryAttachStub() {
12824 AutoAssertNoPendingException
aanpe(cx_
);
12825 writer
.loadValueResult(val_
);
12826 writer
.returnFromIC();
12827 trackAttached("GetIntrinsic");
12828 return AttachDecision::Attach
;
12831 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext
* cx
, HandleScript script
,
12832 jsbytecode
* pc
, ICState state
,
12833 JSOp op
, HandleValue val
,
12835 : IRGenerator(cx
, script
, pc
, CacheKind::UnaryArith
, state
),
12840 void UnaryArithIRGenerator::trackAttached(const char* name
) {
12841 stubName_
= name
? name
: "NotAttached";
12842 #ifdef JS_CACHEIR_SPEW
12843 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12844 sp
.valueProperty("val", val_
);
12845 sp
.valueProperty("res", res_
);
12850 AttachDecision
UnaryArithIRGenerator::tryAttachStub() {
12851 AutoAssertNoPendingException
aanpe(cx_
);
12852 TRY_ATTACH(tryAttachInt32());
12853 TRY_ATTACH(tryAttachNumber());
12854 TRY_ATTACH(tryAttachBitwise());
12855 TRY_ATTACH(tryAttachBigInt());
12856 TRY_ATTACH(tryAttachStringInt32());
12857 TRY_ATTACH(tryAttachStringNumber());
12859 trackAttached(IRGenerator::NotAttached
);
12860 return AttachDecision::NoAction
;
12863 AttachDecision
UnaryArithIRGenerator::tryAttachInt32() {
12864 if (op_
== JSOp::BitNot
) {
12865 return AttachDecision::NoAction
;
12867 if (!CanConvertToInt32ForToNumber(val_
) || !res_
.isInt32()) {
12868 return AttachDecision::NoAction
;
12871 ValOperandId
valId(writer
.setInputOperandId(0));
12873 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, valId
, val_
);
12876 writer
.loadInt32Result(intId
);
12877 trackAttached("UnaryArith.Int32Pos");
12880 writer
.int32NegationResult(intId
);
12881 trackAttached("UnaryArith.Int32Neg");
12884 writer
.int32IncResult(intId
);
12885 trackAttached("UnaryArith.Int32Inc");
12888 writer
.int32DecResult(intId
);
12889 trackAttached("UnaryArith.Int32Dec");
12891 case JSOp::ToNumeric
:
12892 writer
.loadInt32Result(intId
);
12893 trackAttached("UnaryArith.Int32ToNumeric");
12896 MOZ_CRASH("unexpected OP");
12899 writer
.returnFromIC();
12900 return AttachDecision::Attach
;
12903 AttachDecision
UnaryArithIRGenerator::tryAttachNumber() {
12904 if (op_
== JSOp::BitNot
) {
12905 return AttachDecision::NoAction
;
12907 if (!CanConvertToDoubleForToNumber(val_
)) {
12908 return AttachDecision::NoAction
;
12910 MOZ_ASSERT(res_
.isNumber());
12912 ValOperandId
valId(writer
.setInputOperandId(0));
12913 NumberOperandId numId
= EmitGuardToDoubleForToNumber(writer
, valId
, val_
);
12917 writer
.loadDoubleResult(numId
);
12918 trackAttached("UnaryArith.DoublePos");
12921 writer
.doubleNegationResult(numId
);
12922 trackAttached("UnaryArith.DoubleNeg");
12925 writer
.doubleIncResult(numId
);
12926 trackAttached("UnaryArith.DoubleInc");
12929 writer
.doubleDecResult(numId
);
12930 trackAttached("UnaryArith.DoubleDec");
12932 case JSOp::ToNumeric
:
12933 writer
.loadDoubleResult(numId
);
12934 trackAttached("UnaryArith.DoubleToNumeric");
12937 MOZ_CRASH("Unexpected OP");
12940 writer
.returnFromIC();
12941 return AttachDecision::Attach
;
12944 static bool CanTruncateToInt32(const Value
& val
) {
12945 return val
.isNumber() || val
.isBoolean() || val
.isNullOrUndefined() ||
12949 // Convert type into int32 for the bitwise/shift operands.
12950 static Int32OperandId
EmitTruncateToInt32Guard(CacheIRWriter
& writer
,
12952 const Value
& val
) {
12953 MOZ_ASSERT(CanTruncateToInt32(val
));
12954 if (val
.isInt32()) {
12955 return writer
.guardToInt32(id
);
12957 if (val
.isBoolean()) {
12958 return writer
.guardBooleanToInt32(id
);
12960 if (val
.isNullOrUndefined()) {
12961 writer
.guardIsNullOrUndefined(id
);
12962 return writer
.loadInt32Constant(0);
12964 NumberOperandId numId
;
12965 if (val
.isString()) {
12966 StringOperandId strId
= writer
.guardToString(id
);
12967 numId
= writer
.guardStringToNumber(strId
);
12969 MOZ_ASSERT(val
.isDouble());
12970 numId
= writer
.guardIsNumber(id
);
12972 return writer
.truncateDoubleToUInt32(numId
);
12975 AttachDecision
UnaryArithIRGenerator::tryAttachBitwise() {
12976 // Only bitwise operators.
12977 if (op_
!= JSOp::BitNot
) {
12978 return AttachDecision::NoAction
;
12981 // Check guard conditions
12982 if (!CanTruncateToInt32(val_
)) {
12983 return AttachDecision::NoAction
;
12986 // Bitwise operators always produce Int32 values.
12987 MOZ_ASSERT(res_
.isInt32());
12989 ValOperandId
valId(writer
.setInputOperandId(0));
12990 Int32OperandId intId
= EmitTruncateToInt32Guard(writer
, valId
, val_
);
12991 writer
.int32NotResult(intId
);
12992 trackAttached("UnaryArith.BitwiseBitNot");
12994 writer
.returnFromIC();
12995 return AttachDecision::Attach
;
12998 AttachDecision
UnaryArithIRGenerator::tryAttachBigInt() {
12999 if (!val_
.isBigInt()) {
13000 return AttachDecision::NoAction
;
13002 MOZ_ASSERT(res_
.isBigInt());
13004 MOZ_ASSERT(op_
!= JSOp::Pos
,
13005 "Applying the unary + operator on BigInt values throws an error");
13007 ValOperandId
valId(writer
.setInputOperandId(0));
13008 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13011 writer
.bigIntNotResult(bigIntId
);
13012 trackAttached("UnaryArith.BigIntNot");
13015 writer
.bigIntNegationResult(bigIntId
);
13016 trackAttached("UnaryArith.BigIntNeg");
13019 writer
.bigIntIncResult(bigIntId
);
13020 trackAttached("UnaryArith.BigIntInc");
13023 writer
.bigIntDecResult(bigIntId
);
13024 trackAttached("UnaryArith.BigIntDec");
13026 case JSOp::ToNumeric
:
13027 writer
.loadBigIntResult(bigIntId
);
13028 trackAttached("UnaryArith.BigIntToNumeric");
13031 MOZ_CRASH("Unexpected OP");
13034 writer
.returnFromIC();
13035 return AttachDecision::Attach
;
13038 AttachDecision
UnaryArithIRGenerator::tryAttachStringInt32() {
13039 if (!val_
.isString()) {
13040 return AttachDecision::NoAction
;
13042 MOZ_ASSERT(res_
.isNumber());
13044 // Case should have been handled by tryAttachBitwise.
13045 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13047 if (!res_
.isInt32()) {
13048 return AttachDecision::NoAction
;
13051 ValOperandId
valId(writer
.setInputOperandId(0));
13052 StringOperandId stringId
= writer
.guardToString(valId
);
13053 Int32OperandId intId
= writer
.guardStringToInt32(stringId
);
13057 writer
.loadInt32Result(intId
);
13058 trackAttached("UnaryArith.StringInt32Pos");
13061 writer
.int32NegationResult(intId
);
13062 trackAttached("UnaryArith.StringInt32Neg");
13065 writer
.int32IncResult(intId
);
13066 trackAttached("UnaryArith.StringInt32Inc");
13069 writer
.int32DecResult(intId
);
13070 trackAttached("UnaryArith.StringInt32Dec");
13072 case JSOp::ToNumeric
:
13073 writer
.loadInt32Result(intId
);
13074 trackAttached("UnaryArith.StringInt32ToNumeric");
13077 MOZ_CRASH("Unexpected OP");
13080 writer
.returnFromIC();
13081 return AttachDecision::Attach
;
13084 AttachDecision
UnaryArithIRGenerator::tryAttachStringNumber() {
13085 if (!val_
.isString()) {
13086 return AttachDecision::NoAction
;
13088 MOZ_ASSERT(res_
.isNumber());
13090 // Case should have been handled by tryAttachBitwise.
13091 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13093 ValOperandId
valId(writer
.setInputOperandId(0));
13094 StringOperandId stringId
= writer
.guardToString(valId
);
13095 NumberOperandId numId
= writer
.guardStringToNumber(stringId
);
13097 Int32OperandId truncatedId
;
13100 writer
.loadDoubleResult(numId
);
13101 trackAttached("UnaryArith.StringNumberPos");
13104 writer
.doubleNegationResult(numId
);
13105 trackAttached("UnaryArith.StringNumberNeg");
13108 writer
.doubleIncResult(numId
);
13109 trackAttached("UnaryArith.StringNumberInc");
13112 writer
.doubleDecResult(numId
);
13113 trackAttached("UnaryArith.StringNumberDec");
13115 case JSOp::ToNumeric
:
13116 writer
.loadDoubleResult(numId
);
13117 trackAttached("UnaryArith.StringNumberToNumeric");
13120 MOZ_CRASH("Unexpected OP");
13123 writer
.returnFromIC();
13124 return AttachDecision::Attach
;
13127 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext
* cx
,
13128 HandleScript script
,
13132 : IRGenerator(cx
, script
, pc
, CacheKind::ToPropertyKey
, state
), val_(val
) {}
13134 void ToPropertyKeyIRGenerator::trackAttached(const char* name
) {
13135 stubName_
= name
? name
: "NotAttached";
13136 #ifdef JS_CACHEIR_SPEW
13137 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13138 sp
.valueProperty("val", val_
);
13143 AttachDecision
ToPropertyKeyIRGenerator::tryAttachStub() {
13144 AutoAssertNoPendingException
aanpe(cx_
);
13145 TRY_ATTACH(tryAttachInt32());
13146 TRY_ATTACH(tryAttachNumber());
13147 TRY_ATTACH(tryAttachString());
13148 TRY_ATTACH(tryAttachSymbol());
13150 trackAttached(IRGenerator::NotAttached
);
13151 return AttachDecision::NoAction
;
13154 AttachDecision
ToPropertyKeyIRGenerator::tryAttachInt32() {
13155 if (!val_
.isInt32()) {
13156 return AttachDecision::NoAction
;
13159 ValOperandId
valId(writer
.setInputOperandId(0));
13161 Int32OperandId intId
= writer
.guardToInt32(valId
);
13162 writer
.loadInt32Result(intId
);
13163 writer
.returnFromIC();
13165 trackAttached("ToPropertyKey.Int32");
13166 return AttachDecision::Attach
;
13169 AttachDecision
ToPropertyKeyIRGenerator::tryAttachNumber() {
13170 if (!val_
.isNumber()) {
13171 return AttachDecision::NoAction
;
13174 // We allow negative zero here because ToPropertyKey(-0.0) is 0.
13176 if (!mozilla::NumberEqualsInt32(val_
.toNumber(), &unused
)) {
13177 return AttachDecision::NoAction
;
13180 ValOperandId
valId(writer
.setInputOperandId(0));
13182 Int32OperandId intId
= writer
.guardToInt32Index(valId
);
13183 writer
.loadInt32Result(intId
);
13184 writer
.returnFromIC();
13186 trackAttached("ToPropertyKey.Number");
13187 return AttachDecision::Attach
;
13190 AttachDecision
ToPropertyKeyIRGenerator::tryAttachString() {
13191 if (!val_
.isString()) {
13192 return AttachDecision::NoAction
;
13195 ValOperandId
valId(writer
.setInputOperandId(0));
13197 StringOperandId strId
= writer
.guardToString(valId
);
13198 writer
.loadStringResult(strId
);
13199 writer
.returnFromIC();
13201 trackAttached("ToPropertyKey.String");
13202 return AttachDecision::Attach
;
13205 AttachDecision
ToPropertyKeyIRGenerator::tryAttachSymbol() {
13206 if (!val_
.isSymbol()) {
13207 return AttachDecision::NoAction
;
13210 ValOperandId
valId(writer
.setInputOperandId(0));
13212 SymbolOperandId strId
= writer
.guardToSymbol(valId
);
13213 writer
.loadSymbolResult(strId
);
13214 writer
.returnFromIC();
13216 trackAttached("ToPropertyKey.Symbol");
13217 return AttachDecision::Attach
;
13220 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext
* cx
,
13221 HandleScript script
,
13222 jsbytecode
* pc
, ICState state
,
13223 JSOp op
, HandleValue lhs
,
13224 HandleValue rhs
, HandleValue res
)
13225 : IRGenerator(cx
, script
, pc
, CacheKind::BinaryArith
, state
),
13231 void BinaryArithIRGenerator::trackAttached(const char* name
) {
13232 stubName_
= name
? name
: "NotAttached";
13233 #ifdef JS_CACHEIR_SPEW
13234 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13235 sp
.opcodeProperty("op", op_
);
13236 sp
.valueProperty("rhs", rhs_
);
13237 sp
.valueProperty("lhs", lhs_
);
13242 AttachDecision
BinaryArithIRGenerator::tryAttachStub() {
13243 AutoAssertNoPendingException
aanpe(cx_
);
13244 // Arithmetic operations with Int32 operands
13245 TRY_ATTACH(tryAttachInt32());
13247 // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
13249 TRY_ATTACH(tryAttachBitwise());
13251 // Arithmetic operations with Double operands. This needs to come after
13252 // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
13253 // more specialized Int32 IC if it is possible.
13254 TRY_ATTACH(tryAttachDouble());
13256 // String x {String,Number,Boolean,Null,Undefined}
13257 TRY_ATTACH(tryAttachStringConcat());
13260 TRY_ATTACH(tryAttachStringObjectConcat());
13262 // Arithmetic operations or bitwise operations with BigInt operands
13263 TRY_ATTACH(tryAttachBigInt());
13265 // Arithmetic operations (without addition) with String x Int32.
13266 TRY_ATTACH(tryAttachStringInt32Arith());
13268 // Arithmetic operations (without addition) with String x Number. This needs
13269 // to come after tryAttachStringInt32Arith, as the guards overlap, and we'd
13270 // prefer to attach the more specialized Int32 IC if it is possible.
13271 TRY_ATTACH(tryAttachStringNumberArith());
13273 trackAttached(IRGenerator::NotAttached
);
13274 return AttachDecision::NoAction
;
13277 AttachDecision
BinaryArithIRGenerator::tryAttachBitwise() {
13278 // Only bit-wise and shifts.
13279 if (op_
!= JSOp::BitOr
&& op_
!= JSOp::BitXor
&& op_
!= JSOp::BitAnd
&&
13280 op_
!= JSOp::Lsh
&& op_
!= JSOp::Rsh
&& op_
!= JSOp::Ursh
) {
13281 return AttachDecision::NoAction
;
13284 // Check guard conditions
13285 if (!CanTruncateToInt32(lhs_
) || !CanTruncateToInt32(rhs_
)) {
13286 return AttachDecision::NoAction
;
13289 // All ops, with the exception of Ursh, produce Int32 values.
13290 MOZ_ASSERT_IF(op_
!= JSOp::Ursh
, res_
.isInt32());
13292 ValOperandId
lhsId(writer
.setInputOperandId(0));
13293 ValOperandId
rhsId(writer
.setInputOperandId(1));
13295 Int32OperandId lhsIntId
= EmitTruncateToInt32Guard(writer
, lhsId
, lhs_
);
13296 Int32OperandId rhsIntId
= EmitTruncateToInt32Guard(writer
, rhsId
, rhs_
);
13300 writer
.int32BitOrResult(lhsIntId
, rhsIntId
);
13301 trackAttached("BinaryArith.BitwiseBitOr");
13304 writer
.int32BitXorResult(lhsIntId
, rhsIntId
);
13305 trackAttached("BinaryArith.BitwiseBitXor");
13308 writer
.int32BitAndResult(lhsIntId
, rhsIntId
);
13309 trackAttached("BinaryArith.BitwiseBitAnd");
13312 writer
.int32LeftShiftResult(lhsIntId
, rhsIntId
);
13313 trackAttached("BinaryArith.BitwiseLeftShift");
13316 writer
.int32RightShiftResult(lhsIntId
, rhsIntId
);
13317 trackAttached("BinaryArith.BitwiseRightShift");
13320 writer
.int32URightShiftResult(lhsIntId
, rhsIntId
, res_
.isDouble());
13321 trackAttached("BinaryArith.BitwiseUnsignedRightShift");
13324 MOZ_CRASH("Unhandled op in tryAttachBitwise");
13327 writer
.returnFromIC();
13328 return AttachDecision::Attach
;
13331 AttachDecision
BinaryArithIRGenerator::tryAttachDouble() {
13332 // Check valid opcodes
13333 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13334 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13335 return AttachDecision::NoAction
;
13338 // Check guard conditions.
13339 if (!CanConvertToDoubleForToNumber(lhs_
) ||
13340 !CanConvertToDoubleForToNumber(rhs_
)) {
13341 return AttachDecision::NoAction
;
13344 ValOperandId
lhsId(writer
.setInputOperandId(0));
13345 ValOperandId
rhsId(writer
.setInputOperandId(1));
13347 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhs_
);
13348 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhs_
);
13352 writer
.doubleAddResult(lhs
, rhs
);
13353 trackAttached("BinaryArith.DoubleAdd");
13356 writer
.doubleSubResult(lhs
, rhs
);
13357 trackAttached("BinaryArith.DoubleSub");
13360 writer
.doubleMulResult(lhs
, rhs
);
13361 trackAttached("BinaryArith.DoubleMul");
13364 writer
.doubleDivResult(lhs
, rhs
);
13365 trackAttached("BinaryArith.DoubleDiv");
13368 writer
.doubleModResult(lhs
, rhs
);
13369 trackAttached("BinaryArith.DoubleMod");
13372 writer
.doublePowResult(lhs
, rhs
);
13373 trackAttached("BinaryArith.DoublePow");
13376 MOZ_CRASH("Unhandled Op");
13378 writer
.returnFromIC();
13379 return AttachDecision::Attach
;
13382 AttachDecision
BinaryArithIRGenerator::tryAttachInt32() {
13383 // Check guard conditions.
13384 if (!CanConvertToInt32ForToNumber(lhs_
) ||
13385 !CanConvertToInt32ForToNumber(rhs_
)) {
13386 return AttachDecision::NoAction
;
13389 // These ICs will failure() if result can't be encoded in an Int32:
13390 // If sample result is not Int32, we should avoid IC.
13391 if (!res_
.isInt32()) {
13392 return AttachDecision::NoAction
;
13395 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13396 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13397 return AttachDecision::NoAction
;
13400 if (op_
== JSOp::Pow
&& !CanAttachInt32Pow(lhs_
, rhs_
)) {
13401 return AttachDecision::NoAction
;
13404 ValOperandId
lhsId(writer
.setInputOperandId(0));
13405 ValOperandId
rhsId(writer
.setInputOperandId(1));
13407 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhs_
);
13408 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhs_
);
13412 writer
.int32AddResult(lhsIntId
, rhsIntId
);
13413 trackAttached("BinaryArith.Int32Add");
13416 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13417 trackAttached("BinaryArith.Int32Sub");
13420 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13421 trackAttached("BinaryArith.Int32Mul");
13424 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13425 trackAttached("BinaryArith.Int32Div");
13428 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13429 trackAttached("BinaryArith.Int32Mod");
13432 writer
.int32PowResult(lhsIntId
, rhsIntId
);
13433 trackAttached("BinaryArith.Int32Pow");
13436 MOZ_CRASH("Unhandled op in tryAttachInt32");
13439 writer
.returnFromIC();
13440 return AttachDecision::Attach
;
13443 AttachDecision
BinaryArithIRGenerator::tryAttachStringConcat() {
13445 if (op_
!= JSOp::Add
) {
13446 return AttachDecision::NoAction
;
13449 // One side must be a string, the other side a primitive value we can easily
13450 // convert to a string.
13451 if (!(lhs_
.isString() && CanConvertToString(rhs_
)) &&
13452 !(CanConvertToString(lhs_
) && rhs_
.isString())) {
13453 return AttachDecision::NoAction
;
13456 ValOperandId
lhsId(writer
.setInputOperandId(0));
13457 ValOperandId
rhsId(writer
.setInputOperandId(1));
13459 StringOperandId lhsStrId
= emitToStringGuard(lhsId
, lhs_
);
13460 StringOperandId rhsStrId
= emitToStringGuard(rhsId
, rhs_
);
13462 writer
.callStringConcatResult(lhsStrId
, rhsStrId
);
13464 writer
.returnFromIC();
13465 trackAttached("BinaryArith.StringConcat");
13466 return AttachDecision::Attach
;
13469 AttachDecision
BinaryArithIRGenerator::tryAttachStringObjectConcat() {
13471 if (op_
!= JSOp::Add
) {
13472 return AttachDecision::NoAction
;
13476 if (!(lhs_
.isObject() && rhs_
.isString()) &&
13477 !(lhs_
.isString() && rhs_
.isObject()))
13478 return AttachDecision::NoAction
;
13480 ValOperandId
lhsId(writer
.setInputOperandId(0));
13481 ValOperandId
rhsId(writer
.setInputOperandId(1));
13483 // This guard is actually overly tight, as the runtime
13484 // helper can handle lhs or rhs being a string, so long
13485 // as the other is an object.
13486 if (lhs_
.isString()) {
13487 writer
.guardToString(lhsId
);
13488 writer
.guardToObject(rhsId
);
13490 writer
.guardToObject(lhsId
);
13491 writer
.guardToString(rhsId
);
13494 writer
.callStringObjectConcatResult(lhsId
, rhsId
);
13496 writer
.returnFromIC();
13497 trackAttached("BinaryArith.StringObjectConcat");
13498 return AttachDecision::Attach
;
13501 AttachDecision
BinaryArithIRGenerator::tryAttachBigInt() {
13503 if (!lhs_
.isBigInt() || !rhs_
.isBigInt()) {
13504 return AttachDecision::NoAction
;
13514 // Arithmetic operations.
13522 // Bitwise operations.
13526 return AttachDecision::NoAction
;
13529 ValOperandId
lhsId(writer
.setInputOperandId(0));
13530 ValOperandId
rhsId(writer
.setInputOperandId(1));
13532 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
13533 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
13537 writer
.bigIntAddResult(lhsBigIntId
, rhsBigIntId
);
13538 trackAttached("BinaryArith.BigIntAdd");
13541 writer
.bigIntSubResult(lhsBigIntId
, rhsBigIntId
);
13542 trackAttached("BinaryArith.BigIntSub");
13545 writer
.bigIntMulResult(lhsBigIntId
, rhsBigIntId
);
13546 trackAttached("BinaryArith.BigIntMul");
13549 writer
.bigIntDivResult(lhsBigIntId
, rhsBigIntId
);
13550 trackAttached("BinaryArith.BigIntDiv");
13553 writer
.bigIntModResult(lhsBigIntId
, rhsBigIntId
);
13554 trackAttached("BinaryArith.BigIntMod");
13557 writer
.bigIntPowResult(lhsBigIntId
, rhsBigIntId
);
13558 trackAttached("BinaryArith.BigIntPow");
13561 writer
.bigIntBitOrResult(lhsBigIntId
, rhsBigIntId
);
13562 trackAttached("BinaryArith.BigIntBitOr");
13565 writer
.bigIntBitXorResult(lhsBigIntId
, rhsBigIntId
);
13566 trackAttached("BinaryArith.BigIntBitXor");
13569 writer
.bigIntBitAndResult(lhsBigIntId
, rhsBigIntId
);
13570 trackAttached("BinaryArith.BigIntBitAnd");
13573 writer
.bigIntLeftShiftResult(lhsBigIntId
, rhsBigIntId
);
13574 trackAttached("BinaryArith.BigIntLeftShift");
13577 writer
.bigIntRightShiftResult(lhsBigIntId
, rhsBigIntId
);
13578 trackAttached("BinaryArith.BigIntRightShift");
13581 MOZ_CRASH("Unhandled op in tryAttachBigInt");
13584 writer
.returnFromIC();
13585 return AttachDecision::Attach
;
13588 AttachDecision
BinaryArithIRGenerator::tryAttachStringInt32Arith() {
13589 // Check for either int32 x string or string x int32.
13590 if (!(lhs_
.isInt32() && rhs_
.isString()) &&
13591 !(lhs_
.isString() && rhs_
.isInt32())) {
13592 return AttachDecision::NoAction
;
13595 // The created ICs will fail if the result can't be encoded as as int32.
13596 // Thus skip this IC, if the sample result is not an int32.
13597 if (!res_
.isInt32()) {
13598 return AttachDecision::NoAction
;
13601 // Must _not_ support Add, because it would be string concatenation instead.
13602 // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
13603 // reject that as well.
13604 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13605 op_
!= JSOp::Mod
) {
13606 return AttachDecision::NoAction
;
13609 // The string operand must be convertable to an int32 value.
13610 JSString
* str
= lhs_
.isString() ? lhs_
.toString() : rhs_
.toString();
13613 if (!StringToNumber(cx_
, str
, &num
)) {
13614 cx_
->recoverFromOutOfMemory();
13615 return AttachDecision::NoAction
;
13619 if (!mozilla::NumberIsInt32(num
, &unused
)) {
13620 return AttachDecision::NoAction
;
13623 ValOperandId
lhsId(writer
.setInputOperandId(0));
13624 ValOperandId
rhsId(writer
.setInputOperandId(1));
13626 auto guardToInt32
= [&](ValOperandId id
, const Value
& v
) {
13628 return writer
.guardToInt32(id
);
13631 MOZ_ASSERT(v
.isString());
13632 StringOperandId strId
= writer
.guardToString(id
);
13633 return writer
.guardStringToInt32(strId
);
13636 Int32OperandId lhsIntId
= guardToInt32(lhsId
, lhs_
);
13637 Int32OperandId rhsIntId
= guardToInt32(rhsId
, rhs_
);
13641 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13642 trackAttached("BinaryArith.StringInt32Sub");
13645 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13646 trackAttached("BinaryArith.StringInt32Mul");
13649 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13650 trackAttached("BinaryArith.StringInt32Div");
13653 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13654 trackAttached("BinaryArith.StringInt32Mod");
13657 MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
13660 writer
.returnFromIC();
13661 return AttachDecision::Attach
;
13664 AttachDecision
BinaryArithIRGenerator::tryAttachStringNumberArith() {
13665 // Check for either number x string or string x number.
13666 if (!(lhs_
.isNumber() && rhs_
.isString()) &&
13667 !(lhs_
.isString() && rhs_
.isNumber())) {
13668 return AttachDecision::NoAction
;
13671 // Must _not_ support Add, because it would be string concatenation instead.
13672 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13673 op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13674 return AttachDecision::NoAction
;
13677 ValOperandId
lhsId(writer
.setInputOperandId(0));
13678 ValOperandId
rhsId(writer
.setInputOperandId(1));
13680 auto guardToNumber
= [&](ValOperandId id
, const Value
& v
) {
13681 if (v
.isNumber()) {
13682 return writer
.guardIsNumber(id
);
13685 MOZ_ASSERT(v
.isString());
13686 StringOperandId strId
= writer
.guardToString(id
);
13687 return writer
.guardStringToNumber(strId
);
13690 NumberOperandId lhsIntId
= guardToNumber(lhsId
, lhs_
);
13691 NumberOperandId rhsIntId
= guardToNumber(rhsId
, rhs_
);
13695 writer
.doubleSubResult(lhsIntId
, rhsIntId
);
13696 trackAttached("BinaryArith.StringNumberSub");
13699 writer
.doubleMulResult(lhsIntId
, rhsIntId
);
13700 trackAttached("BinaryArith.StringNumberMul");
13703 writer
.doubleDivResult(lhsIntId
, rhsIntId
);
13704 trackAttached("BinaryArith.StringNumberDiv");
13707 writer
.doubleModResult(lhsIntId
, rhsIntId
);
13708 trackAttached("BinaryArith.StringNumberMod");
13711 writer
.doublePowResult(lhsIntId
, rhsIntId
);
13712 trackAttached("BinaryArith.StringNumberPow");
13715 MOZ_CRASH("Unhandled op in tryAttachStringNumberArith");
13718 writer
.returnFromIC();
13719 return AttachDecision::Attach
;
13722 NewArrayIRGenerator::NewArrayIRGenerator(JSContext
* cx
, HandleScript script
,
13723 jsbytecode
* pc
, ICState state
, JSOp op
,
13724 HandleObject templateObj
,
13725 BaselineFrame
* frame
)
13726 : IRGenerator(cx
, script
, pc
, CacheKind::NewArray
, state
),
13727 #ifdef JS_CACHEIR_SPEW
13730 templateObject_(templateObj
),
13732 MOZ_ASSERT(templateObject_
);
13735 void NewArrayIRGenerator::trackAttached(const char* name
) {
13736 stubName_
= name
? name
: "NotAttached";
13737 #ifdef JS_CACHEIR_SPEW
13738 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13739 sp
.opcodeProperty("op", op_
);
13744 // Allocation sites are usually created during baseline compilation, but we also
13745 // need to create them when an IC stub is added to a baseline compiled script
13746 // and when trial inlining.
13747 static gc::AllocSite
* MaybeCreateAllocSite(jsbytecode
* pc
,
13748 BaselineFrame
* frame
) {
13749 MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc
)));
13751 JSScript
* outerScript
= frame
->outerScript();
13752 bool hasBaselineScript
= outerScript
->hasBaselineScript();
13753 bool isInlined
= frame
->icScript()->isInlined();
13755 if (!hasBaselineScript
&& !isInlined
) {
13756 MOZ_ASSERT(frame
->runningInInterpreter());
13757 return outerScript
->zone()->unknownAllocSite(JS::TraceKind::Object
);
13760 uint32_t pcOffset
= frame
->script()->pcToOffset(pc
);
13761 return frame
->icScript()->getOrCreateAllocSite(outerScript
, pcOffset
);
13764 AttachDecision
NewArrayIRGenerator::tryAttachArrayObject() {
13765 ArrayObject
* arrayObj
= &templateObject_
->as
<ArrayObject
>();
13767 MOZ_ASSERT(arrayObj
->numUsedFixedSlots() == 0);
13768 MOZ_ASSERT(arrayObj
->numDynamicSlots() == 0);
13769 MOZ_ASSERT(!arrayObj
->isSharedMemory());
13771 // The macro assembler only supports creating arrays with fixed elements.
13772 if (arrayObj
->hasDynamicElements()) {
13773 return AttachDecision::NoAction
;
13776 // Stub doesn't support metadata builder
13777 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
13778 return AttachDecision::NoAction
;
13781 writer
.guardNoAllocationMetadataBuilder(
13782 cx_
->realm()->addressOfMetadataBuilder());
13784 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
13786 return AttachDecision::NoAction
;
13789 Shape
* shape
= arrayObj
->shape();
13790 uint32_t length
= arrayObj
->length();
13792 writer
.newArrayObjectResult(length
, shape
, site
);
13794 writer
.returnFromIC();
13796 trackAttached("NewArray.Object");
13797 return AttachDecision::Attach
;
13800 AttachDecision
NewArrayIRGenerator::tryAttachStub() {
13801 AutoAssertNoPendingException
aanpe(cx_
);
13803 TRY_ATTACH(tryAttachArrayObject());
13805 trackAttached(IRGenerator::NotAttached
);
13806 return AttachDecision::NoAction
;
13809 NewObjectIRGenerator::NewObjectIRGenerator(JSContext
* cx
, HandleScript script
,
13810 jsbytecode
* pc
, ICState state
,
13811 JSOp op
, HandleObject templateObj
,
13812 BaselineFrame
* frame
)
13813 : IRGenerator(cx
, script
, pc
, CacheKind::NewObject
, state
),
13814 #ifdef JS_CACHEIR_SPEW
13817 templateObject_(templateObj
),
13819 MOZ_ASSERT(templateObject_
);
13822 void NewObjectIRGenerator::trackAttached(const char* name
) {
13823 stubName_
= name
? name
: "NotAttached";
13824 #ifdef JS_CACHEIR_SPEW
13825 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13826 sp
.opcodeProperty("op", op_
);
13831 AttachDecision
NewObjectIRGenerator::tryAttachPlainObject() {
13832 // Don't optimize allocations with too many dynamic slots. We use an unrolled
13833 // loop when initializing slots and this avoids generating too much code.
13834 static const uint32_t MaxDynamicSlotsToOptimize
= 64;
13836 NativeObject
* nativeObj
= &templateObject_
->as
<NativeObject
>();
13837 MOZ_ASSERT(nativeObj
->is
<PlainObject
>());
13839 // Stub doesn't support metadata builder
13840 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
13841 return AttachDecision::NoAction
;
13844 if (nativeObj
->numDynamicSlots() > MaxDynamicSlotsToOptimize
) {
13845 return AttachDecision::NoAction
;
13848 MOZ_ASSERT(!nativeObj
->hasDynamicElements());
13849 MOZ_ASSERT(!nativeObj
->isSharedMemory());
13851 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
13853 return AttachDecision::NoAction
;
13856 uint32_t numFixedSlots
= nativeObj
->numUsedFixedSlots();
13857 uint32_t numDynamicSlots
= nativeObj
->numDynamicSlots();
13858 gc::AllocKind allocKind
= nativeObj
->allocKindForTenure();
13859 Shape
* shape
= nativeObj
->shape();
13861 writer
.guardNoAllocationMetadataBuilder(
13862 cx_
->realm()->addressOfMetadataBuilder());
13863 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
, shape
,
13866 writer
.returnFromIC();
13868 trackAttached("NewObject.PlainObject");
13869 return AttachDecision::Attach
;
13872 AttachDecision
NewObjectIRGenerator::tryAttachStub() {
13873 AutoAssertNoPendingException
aanpe(cx_
);
13875 TRY_ATTACH(tryAttachPlainObject());
13877 trackAttached(IRGenerator::NotAttached
);
13878 return AttachDecision::NoAction
;
13881 CloseIterIRGenerator::CloseIterIRGenerator(JSContext
* cx
, HandleScript script
,
13882 jsbytecode
* pc
, ICState state
,
13884 CompletionKind kind
)
13885 : IRGenerator(cx
, script
, pc
, CacheKind::CloseIter
, state
),
13889 void CloseIterIRGenerator::trackAttached(const char* name
) {
13890 #ifdef JS_CACHEIR_SPEW
13891 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13892 sp
.valueProperty("iter", ObjectValue(*iter_
));
13897 AttachDecision
CloseIterIRGenerator::tryAttachNoReturnMethod() {
13898 Maybe
<PropertyInfo
> prop
;
13899 NativeObject
* holder
= nullptr;
13901 // If we can guard that the iterator does not have a |return| method,
13902 // then this CloseIter is a no-op.
13903 NativeGetPropKind kind
= CanAttachNativeGetProp(
13904 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
13905 if (kind
!= NativeGetPropKind::Missing
) {
13906 return AttachDecision::NoAction
;
13908 MOZ_ASSERT(!holder
);
13910 ObjOperandId
objId(writer
.setInputOperandId(0));
13912 EmitMissingPropGuard(writer
, &iter_
->as
<NativeObject
>(), objId
);
13914 // There is no return method, so we don't have to do anything.
13915 writer
.returnFromIC();
13917 trackAttached("CloseIter.NoReturn");
13918 return AttachDecision::Attach
;
13921 AttachDecision
CloseIterIRGenerator::tryAttachScriptedReturn() {
13922 Maybe
<PropertyInfo
> prop
;
13923 NativeObject
* holder
= nullptr;
13925 NativeGetPropKind kind
= CanAttachNativeGetProp(
13926 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
13927 if (kind
!= NativeGetPropKind::Slot
) {
13928 return AttachDecision::NoAction
;
13930 MOZ_ASSERT(holder
);
13931 MOZ_ASSERT(prop
->isDataProperty());
13933 size_t slot
= prop
->slot();
13934 Value calleeVal
= holder
->getSlot(slot
);
13935 if (!calleeVal
.isObject() || !calleeVal
.toObject().is
<JSFunction
>()) {
13936 return AttachDecision::NoAction
;
13939 JSFunction
* callee
= &calleeVal
.toObject().as
<JSFunction
>();
13940 if (!callee
->hasJitEntry()) {
13941 return AttachDecision::NoAction
;
13943 if (callee
->isClassConstructor()) {
13944 return AttachDecision::NoAction
;
13947 // We don't support cross-realm |return|.
13948 if (cx_
->realm() != callee
->realm()) {
13949 return AttachDecision::NoAction
;
13952 ObjOperandId
objId(writer
.setInputOperandId(0));
13954 ObjOperandId holderId
=
13955 EmitReadSlotGuard(writer
, &iter_
->as
<NativeObject
>(), holder
, objId
);
13957 ValOperandId calleeValId
= EmitLoadSlot(writer
, holder
, holderId
, slot
);
13958 ObjOperandId calleeId
= writer
.guardToObject(calleeValId
);
13959 emitCalleeGuard(calleeId
, callee
);
13961 writer
.closeIterScriptedResult(objId
, calleeId
, kind_
, callee
->nargs());
13963 writer
.returnFromIC();
13964 trackAttached("CloseIter.ScriptedReturn");
13966 return AttachDecision::Attach
;
13969 AttachDecision
CloseIterIRGenerator::tryAttachStub() {
13970 AutoAssertNoPendingException
aanpe(cx_
);
13972 TRY_ATTACH(tryAttachNoReturnMethod());
13973 TRY_ATTACH(tryAttachScriptedReturn());
13975 trackAttached(IRGenerator::NotAttached
);
13976 return AttachDecision::NoAction
;
13979 OptimizeGetIteratorIRGenerator::OptimizeGetIteratorIRGenerator(
13980 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
13982 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeGetIterator
, state
),
13985 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachStub() {
13986 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeGetIterator
);
13988 AutoAssertNoPendingException
aanpe(cx_
);
13990 TRY_ATTACH(tryAttachArray());
13991 TRY_ATTACH(tryAttachNotOptimizable());
13993 MOZ_CRASH("Failed to attach unoptimizable case.");
13996 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachArray() {
13997 if (!isFirstStub_
) {
13998 return AttachDecision::NoAction
;
14001 // The value must be a packed array.
14002 if (!val_
.isObject()) {
14003 return AttachDecision::NoAction
;
14005 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
14006 if (!IsPackedArray(obj
)) {
14007 return AttachDecision::NoAction
;
14010 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
14012 Rooted
<NativeObject
*> arrProto(cx_
);
14013 uint32_t arrProtoIterSlot
;
14014 Rooted
<JSFunction
*> iterFun(cx_
);
14015 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
14016 return AttachDecision::NoAction
;
14019 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
14020 &arrProtoIterSlot
, &iterFun
)) {
14021 // Fuse should be popped.
14023 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14024 return AttachDecision::NoAction
;
14027 // %ArrayIteratorPrototype%.next must not be modified and
14028 // %ArrayIteratorPrototype%.return must not be present.
14029 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
14031 Rooted
<JSFunction
*> nextFun(cx_
);
14032 if (!IsArrayIteratorPrototypeOptimizable(
14033 cx_
, AllowIteratorReturn::No
, &arrayIteratorProto
, &slot
, &nextFun
)) {
14034 // Fuse should be popped.
14036 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14037 return AttachDecision::NoAction
;
14040 ValOperandId
valId(writer
.setInputOperandId(0));
14041 ObjOperandId objId
= writer
.guardToObject(valId
);
14043 // Guard the object is a packed array with Array.prototype as proto.
14044 MOZ_ASSERT(obj
->is
<ArrayObject
>());
14045 writer
.guardShape(objId
, obj
->shape());
14046 writer
.guardArrayIsPacked(objId
);
14047 bool intact
= obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact();
14049 // If the fuse isn't intact but we've still passed all these dynamic checks
14050 // then we can attach a version of the IC that dynamically checks to ensure
14051 // the required invariants still hold.
14053 // As an example of how this could be the case, consider an assignment
14055 // Array.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]
14057 // This assignment pops the fuse, however we can still use the dynamic check
14058 // version of this IC, as the actual -value- is still correct.
14059 bool useDynamicCheck
= !intact
|| !cx_
->options().enableDestructuringFuse();
14060 if (useDynamicCheck
) {
14061 // Guard on Array.prototype[@@iterator].
14062 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
14063 ObjOperandId iterId
= writer
.loadObject(iterFun
);
14064 writer
.guardShape(arrProtoId
, arrProto
->shape());
14065 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
,
14068 // Guard on %ArrayIteratorPrototype%.next.
14069 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
14070 ObjOperandId nextId
= writer
.loadObject(nextFun
);
14071 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
14072 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, slot
);
14074 // Guard on the prototype chain to ensure no "return" method is present.
14075 ShapeGuardProtoChain(writer
, arrayIteratorProto
, iterProtoId
);
14077 // Guard on Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next.
14078 // This fuse also ensures the prototype chain for Array Iterator is
14079 // maintained and that no return method is added.
14080 writer
.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse
);
14083 writer
.loadBooleanResult(true);
14084 writer
.returnFromIC();
14086 if (useDynamicCheck
) {
14087 trackAttached("OptimizeGetIterator.Array.Dynamic");
14089 trackAttached("OptimizeGetIterator.Array.Fuse");
14091 return AttachDecision::Attach
;
14094 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachNotOptimizable() {
14095 ValOperandId
valId(writer
.setInputOperandId(0));
14097 writer
.loadBooleanResult(false);
14098 writer
.returnFromIC();
14100 trackAttached("OptimizeGetIterator.NotOptimizable");
14101 return AttachDecision::Attach
;
14104 void OptimizeGetIteratorIRGenerator::trackAttached(const char* name
) {
14105 stubName_
= name
? name
: "NotAttached";
14107 #ifdef JS_CACHEIR_SPEW
14108 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14109 sp
.valueProperty("val", val_
);
14114 #ifdef JS_SIMULATOR
14115 bool js::jit::CallAnyNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
14116 CallArgs args
= CallArgsFromVp(argc
, vp
);
14117 JSObject
* calleeObj
= &args
.callee();
14119 MOZ_ASSERT(calleeObj
->is
<JSFunction
>());
14120 auto* calleeFunc
= &calleeObj
->as
<JSFunction
>();
14121 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
14123 JSNative native
= calleeFunc
->native();
14124 return native(cx
, args
.length(), args
.base());
14127 const void* js::jit::RedirectedCallAnyNative() {
14128 // The simulator requires native calls to be redirected to a
14129 // special swi instruction. If we are calling an arbitrary native
14130 // function, we can't wrap the real target ahead of time, so we
14131 // call a wrapper function (CallAnyNative) that calls the target
14132 // itself, and redirect that wrapper.
14133 JSNative target
= CallAnyNative
;
14134 void* rawPtr
= JS_FUNC_TO_DATA_PTR(void*, target
);
14135 void* redirected
= Simulator::RedirectNativeFunction(rawPtr
, Args_General3
);