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/Prefs.h" // JS::Prefs
35 #include "js/RegExpFlags.h" // JS::RegExpFlags
36 #include "js/ScalarType.h" // js::Scalar::Type
37 #include "js/Utility.h" // JS::AutoEnterOOMUnsafeRegion
38 #include "js/Wrapper.h"
39 #include "proxy/DOMProxy.h" // js::GetDOMProxyHandlerFamily
40 #include "proxy/ScriptedProxyHandler.h"
41 #include "util/DifferentialTesting.h"
42 #include "util/Unicode.h"
43 #include "vm/ArrayBufferObject.h"
44 #include "vm/BoundFunctionObject.h"
45 #include "vm/BytecodeUtil.h"
46 #include "vm/Compartment.h"
47 #include "vm/Iteration.h"
48 #include "vm/PlainObject.h" // js::PlainObject
49 #include "vm/ProxyObject.h"
50 #include "vm/RegExpObject.h"
51 #include "vm/SelfHosting.h"
52 #include "vm/ThrowMsgKind.h" // ThrowCondition
53 #include "vm/Watchtower.h"
54 #include "wasm/WasmInstance.h"
56 #include "jit/BaselineFrame-inl.h"
57 #include "jit/MacroAssembler-inl.h"
58 #include "vm/ArrayBufferObject-inl.h"
59 #include "vm/BytecodeUtil-inl.h"
60 #include "vm/EnvironmentObject-inl.h"
61 #include "vm/JSContext-inl.h"
62 #include "vm/JSFunction-inl.h"
63 #include "vm/JSObject-inl.h"
64 #include "vm/JSScript-inl.h"
65 #include "vm/List-inl.h"
66 #include "vm/NativeObject-inl.h"
67 #include "vm/PlainObject-inl.h"
68 #include "vm/StringObject-inl.h"
69 #include "wasm/WasmInstance-inl.h"
72 using namespace js::jit
;
74 using mozilla::DebugOnly
;
77 using JS::DOMProxyShadowsResult
;
78 using JS::ExpandoAndGeneration
;
80 const char* const js::jit::CacheKindNames
[] = {
81 #define DEFINE_KIND(kind) #kind,
82 CACHE_IR_KINDS(DEFINE_KIND
)
86 const char* const js::jit::CacheIROpNames
[] = {
87 #define OPNAME(op, ...) #op,
92 const CacheIROpInfo
js::jit::CacheIROpInfos
[] = {
93 #define OPINFO(op, len, transpile, ...) {len, transpile},
98 const uint32_t js::jit::CacheIROpHealth
[] = {
99 #define OPHEALTH(op, len, transpile, health) health,
100 CACHE_IR_OPS(OPHEALTH
)
104 size_t js::jit::NumInputsForCacheKind(CacheKind kind
) {
106 case CacheKind::NewArray
:
107 case CacheKind::NewObject
:
108 case CacheKind::GetIntrinsic
:
110 case CacheKind::GetProp
:
111 case CacheKind::TypeOf
:
112 case CacheKind::ToPropertyKey
:
113 case CacheKind::GetIterator
:
114 case CacheKind::ToBool
:
115 case CacheKind::UnaryArith
:
116 case CacheKind::GetName
:
117 case CacheKind::BindName
:
118 case CacheKind::Call
:
119 case CacheKind::OptimizeSpreadCall
:
120 case CacheKind::CloseIter
:
121 case CacheKind::OptimizeGetIterator
:
123 case CacheKind::Compare
:
124 case CacheKind::GetElem
:
125 case CacheKind::GetPropSuper
:
126 case CacheKind::SetProp
:
128 case CacheKind::HasOwn
:
129 case CacheKind::CheckPrivateField
:
130 case CacheKind::InstanceOf
:
131 case CacheKind::BinaryArith
:
133 case CacheKind::GetElemSuper
:
134 case CacheKind::SetElem
:
137 MOZ_CRASH("Invalid kind");
141 void CacheIRWriter::assertSameCompartment(JSObject
* obj
) {
142 MOZ_ASSERT(cx_
->compartment() == obj
->compartment());
144 void CacheIRWriter::assertSameZone(Shape
* shape
) {
145 MOZ_ASSERT(cx_
->zone() == shape
->zone());
149 StubField
CacheIRWriter::readStubField(uint32_t offset
,
150 StubField::Type type
) const {
152 size_t currentOffset
= 0;
154 // If we've seen an offset earlier than this before, we know we can start the
155 // search there at least, otherwise, we start the search from the beginning.
156 if (lastOffset_
< offset
) {
157 currentOffset
= lastOffset_
;
161 while (currentOffset
!= offset
) {
162 currentOffset
+= StubField::sizeInBytes(stubFields_
[index
].type());
164 MOZ_ASSERT(index
< stubFields_
.length());
167 MOZ_ASSERT(stubFields_
[index
].type() == type
);
169 lastOffset_
= currentOffset
;
172 return stubFields_
[index
];
175 CacheIRCloner::CacheIRCloner(ICCacheIRStub
* stub
)
176 : stubInfo_(stub
->stubInfo()), stubData_(stub
->stubDataStart()) {}
178 void CacheIRCloner::cloneOp(CacheOp op
, CacheIRReader
& reader
,
179 CacheIRWriter
& writer
) {
181 #define DEFINE_OP(op, ...) \
183 clone##op(reader, writer); \
185 CACHE_IR_OPS(DEFINE_OP
)
188 MOZ_CRASH("Invalid op");
192 uintptr_t CacheIRCloner::readStubWord(uint32_t offset
) {
193 return stubInfo_
->getStubRawWord(stubData_
, offset
);
195 int64_t CacheIRCloner::readStubInt64(uint32_t offset
) {
196 return stubInfo_
->getStubRawInt64(stubData_
, offset
);
199 Shape
* CacheIRCloner::getShapeField(uint32_t stubOffset
) {
200 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
202 Shape
* CacheIRCloner::getWeakShapeField(uint32_t stubOffset
) {
203 // No barrier is required to clone a weak pointer.
204 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
206 GetterSetter
* CacheIRCloner::getWeakGetterSetterField(uint32_t stubOffset
) {
207 // No barrier is required to clone a weak pointer.
208 return reinterpret_cast<GetterSetter
*>(readStubWord(stubOffset
));
210 JSObject
* CacheIRCloner::getObjectField(uint32_t stubOffset
) {
211 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
213 JSObject
* CacheIRCloner::getWeakObjectField(uint32_t stubOffset
) {
214 // No barrier is required to clone a weak pointer.
215 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
217 JSString
* CacheIRCloner::getStringField(uint32_t stubOffset
) {
218 return reinterpret_cast<JSString
*>(readStubWord(stubOffset
));
220 JSAtom
* CacheIRCloner::getAtomField(uint32_t stubOffset
) {
221 return reinterpret_cast<JSAtom
*>(readStubWord(stubOffset
));
223 JS::Symbol
* CacheIRCloner::getSymbolField(uint32_t stubOffset
) {
224 return reinterpret_cast<JS::Symbol
*>(readStubWord(stubOffset
));
226 BaseScript
* CacheIRCloner::getWeakBaseScriptField(uint32_t stubOffset
) {
227 // No barrier is required to clone a weak pointer.
228 return reinterpret_cast<BaseScript
*>(readStubWord(stubOffset
));
230 JitCode
* CacheIRCloner::getJitCodeField(uint32_t stubOffset
) {
231 return reinterpret_cast<JitCode
*>(readStubWord(stubOffset
));
233 uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset
) {
234 return uint32_t(reinterpret_cast<uintptr_t>(readStubWord(stubOffset
)));
236 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset
) {
237 return reinterpret_cast<const void*>(readStubWord(stubOffset
));
239 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset
) {
240 return static_cast<uint64_t>(readStubInt64(stubOffset
));
242 gc::AllocSite
* CacheIRCloner::getAllocSiteField(uint32_t stubOffset
) {
243 return reinterpret_cast<gc::AllocSite
*>(readStubWord(stubOffset
));
246 jsid
CacheIRCloner::getIdField(uint32_t stubOffset
) {
247 return jsid::fromRawBits(readStubWord(stubOffset
));
249 const Value
CacheIRCloner::getValueField(uint32_t stubOffset
) {
250 return Value::fromRawBits(uint64_t(readStubInt64(stubOffset
)));
252 double CacheIRCloner::getDoubleField(uint32_t stubOffset
) {
253 uint64_t bits
= uint64_t(readStubInt64(stubOffset
));
254 return mozilla::BitwiseCast
<double>(bits
);
257 IRGenerator::IRGenerator(JSContext
* cx
, HandleScript script
, jsbytecode
* pc
,
258 CacheKind cacheKind
, ICState state
)
263 cacheKind_(cacheKind
),
265 isFirstStub_(state
.newStubIsFirstStub()),
266 numOptimizedStubs_(state
.numOptimizedStubs()) {}
268 GetPropIRGenerator::GetPropIRGenerator(JSContext
* cx
, HandleScript script
,
269 jsbytecode
* pc
, ICState state
,
270 CacheKind cacheKind
, HandleValue val
,
272 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
274 static void EmitLoadSlotResult(CacheIRWriter
& writer
, ObjOperandId holderId
,
275 NativeObject
* holder
, PropertyInfo prop
) {
276 if (holder
->isFixedSlot(prop
.slot())) {
277 writer
.loadFixedSlotResult(holderId
,
278 NativeObject::getFixedSlotOffset(prop
.slot()));
280 size_t dynamicSlotOffset
=
281 holder
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
282 writer
.loadDynamicSlotResult(holderId
, dynamicSlotOffset
);
289 // DOM proxies are proxies that are used to implement various DOM objects like
290 // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
291 // object that stores extra properties added to the object. The following
292 // CacheIR instructions are only used with DOM proxies:
294 // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
295 // returns either an UndefinedValue (no expando), ObjectValue (the expando
296 // object), or PrivateValue(ExpandoAndGeneration*).
298 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
299 // slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
300 // generation, then returns expandoAndGeneration->expando. This Value is
301 // either an UndefinedValue or ObjectValue.
303 // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
304 // expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
305 // returns the expandoAndGeneration->expando Value.
307 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
308 // guards it's either UndefinedValue or an object with the expected shape.
310 enum class ProxyStubType
{
318 static bool IsCacheableDOMProxy(ProxyObject
* obj
) {
319 const BaseProxyHandler
* handler
= obj
->handler();
320 if (handler
->family() != GetDOMProxyHandlerFamily()) {
324 // Some DOM proxies have dynamic prototypes. We can't really cache those very
326 return obj
->hasStaticPrototype();
329 static ProxyStubType
GetProxyStubType(JSContext
* cx
, HandleObject obj
,
331 if (!obj
->is
<ProxyObject
>()) {
332 return ProxyStubType::None
;
334 auto proxy
= obj
.as
<ProxyObject
>();
336 if (!IsCacheableDOMProxy(proxy
)) {
337 return ProxyStubType::Generic
;
340 // Private fields are defined on a separate expando object.
341 if (id
.isPrivateName()) {
342 return ProxyStubType::Generic
;
345 DOMProxyShadowsResult shadows
= GetDOMProxyShadowsCheck()(cx
, proxy
, id
);
346 if (shadows
== DOMProxyShadowsResult::ShadowCheckFailed
) {
347 cx
->clearPendingException();
348 return ProxyStubType::None
;
351 if (DOMProxyIsShadowing(shadows
)) {
352 if (shadows
== DOMProxyShadowsResult::ShadowsViaDirectExpando
||
353 shadows
== DOMProxyShadowsResult::ShadowsViaIndirectExpando
) {
354 return ProxyStubType::DOMExpando
;
356 return ProxyStubType::DOMShadowed
;
359 MOZ_ASSERT(shadows
== DOMProxyShadowsResult::DoesntShadow
||
360 shadows
== DOMProxyShadowsResult::DoesntShadowUnique
);
361 return ProxyStubType::DOMUnshadowed
;
364 static bool ValueToNameOrSymbolId(JSContext
* cx
, HandleValue idVal
,
365 MutableHandleId id
, bool* nameOrSymbol
) {
366 *nameOrSymbol
= false;
368 if (!idVal
.isString() && !idVal
.isSymbol() && !idVal
.isUndefined() &&
373 if (!PrimitiveValueToId
<CanGC
>(cx
, idVal
, id
)) {
377 if (!id
.isAtom() && !id
.isSymbol()) {
378 id
.set(JS::PropertyKey::Void());
382 if (id
.isAtom() && id
.toAtom()->isIndex()) {
383 id
.set(JS::PropertyKey::Void());
387 *nameOrSymbol
= true;
391 AttachDecision
GetPropIRGenerator::tryAttachStub() {
392 AutoAssertNoPendingException
aanpe(cx_
);
394 ValOperandId
valId(writer
.setInputOperandId(0));
395 if (cacheKind_
!= CacheKind::GetProp
) {
396 MOZ_ASSERT_IF(cacheKind_
== CacheKind::GetPropSuper
,
397 getSuperReceiverValueId().id() == 1);
398 MOZ_ASSERT_IF(cacheKind_
!= CacheKind::GetPropSuper
,
399 getElemKeyValueId().id() == 1);
400 writer
.setInputOperandId(1);
402 if (cacheKind_
== CacheKind::GetElemSuper
) {
403 MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
404 writer
.setInputOperandId(2);
409 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
410 cx_
->clearPendingException();
411 return AttachDecision::NoAction
;
414 // |super.prop| getter calls use a |this| value that differs from lookup
416 ValOperandId receiverId
= isSuper() ? getSuperReceiverValueId() : valId
;
418 if (val_
.isObject()) {
419 RootedObject
obj(cx_
, &val_
.toObject());
420 ObjOperandId objId
= writer
.guardToObject(valId
);
422 TRY_ATTACH(tryAttachObjectLength(obj
, objId
, id
));
423 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, id
));
424 TRY_ATTACH(tryAttachDataView(obj
, objId
, id
));
425 TRY_ATTACH(tryAttachArrayBufferMaybeShared(obj
, objId
, id
));
426 TRY_ATTACH(tryAttachRegExp(obj
, objId
, id
));
427 TRY_ATTACH(tryAttachMap(obj
, objId
, id
));
428 TRY_ATTACH(tryAttachSet(obj
, objId
, id
));
429 TRY_ATTACH(tryAttachNative(obj
, objId
, id
, receiverId
));
430 TRY_ATTACH(tryAttachModuleNamespace(obj
, objId
, id
));
431 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
));
432 TRY_ATTACH(tryAttachCrossCompartmentWrapper(obj
, objId
, id
));
434 tryAttachXrayCrossCompartmentWrapper(obj
, objId
, id
, receiverId
));
435 TRY_ATTACH(tryAttachFunction(obj
, objId
, id
));
436 TRY_ATTACH(tryAttachArgumentsObjectIterator(obj
, objId
, id
));
437 TRY_ATTACH(tryAttachArgumentsObjectCallee(obj
, objId
, id
));
438 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, receiverId
));
440 trackAttached(IRGenerator::NotAttached
);
441 return AttachDecision::NoAction
;
444 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
445 cacheKind_
== CacheKind::GetElemSuper
);
447 TRY_ATTACH(tryAttachProxyElement(obj
, objId
));
448 TRY_ATTACH(tryAttachTypedArrayElement(obj
, objId
));
451 Int32OperandId indexId
;
452 if (maybeGuardInt32Index(idVal_
, getElemKeyValueId(), &index
, &indexId
)) {
453 TRY_ATTACH(tryAttachDenseElement(obj
, objId
, index
, indexId
));
454 TRY_ATTACH(tryAttachDenseElementHole(obj
, objId
, index
, indexId
));
455 TRY_ATTACH(tryAttachSparseElement(obj
, objId
, index
, indexId
));
456 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, index
, indexId
));
457 TRY_ATTACH(tryAttachArgumentsObjectArgHole(obj
, objId
, index
, indexId
));
459 tryAttachGenericElement(obj
, objId
, index
, indexId
, receiverId
));
461 trackAttached(IRGenerator::NotAttached
);
462 return AttachDecision::NoAction
;
465 trackAttached(IRGenerator::NotAttached
);
466 return AttachDecision::NoAction
;
470 TRY_ATTACH(tryAttachPrimitive(valId
, id
));
471 TRY_ATTACH(tryAttachStringLength(valId
, id
));
473 trackAttached(IRGenerator::NotAttached
);
474 return AttachDecision::NoAction
;
477 if (idVal_
.isInt32()) {
478 ValOperandId indexId
= getElemKeyValueId();
479 TRY_ATTACH(tryAttachStringChar(valId
, indexId
));
481 trackAttached(IRGenerator::NotAttached
);
482 return AttachDecision::NoAction
;
485 trackAttached(IRGenerator::NotAttached
);
486 return AttachDecision::NoAction
;
490 // Any property lookups performed when trying to attach ICs must be pure, i.e.
491 // must use LookupPropertyPure() or similar functions. Pure lookups are
492 // guaranteed to never modify the prototype chain. This ensures that the holder
493 // object can always be found on the prototype chain.
494 static bool IsCacheableProtoChain(NativeObject
* obj
, NativeObject
* holder
) {
495 while (obj
!= holder
) {
496 JSObject
* proto
= obj
->staticPrototype();
497 if (!proto
|| !proto
->is
<NativeObject
>()) {
500 obj
= &proto
->as
<NativeObject
>();
506 static bool IsCacheableGetPropSlot(NativeObject
* obj
, NativeObject
* holder
,
508 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
510 return prop
.isDataProperty();
513 enum class NativeGetPropKind
{
521 static NativeGetPropKind
IsCacheableGetPropCall(NativeObject
* obj
,
522 NativeObject
* holder
,
524 jsbytecode
* pc
= nullptr) {
525 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
527 if (pc
&& JSOp(*pc
) == JSOp::GetBoundName
) {
528 return NativeGetPropKind::None
;
531 if (!prop
.isAccessorProperty()) {
532 return NativeGetPropKind::None
;
535 JSObject
* getterObject
= holder
->getGetter(prop
);
536 if (!getterObject
|| !getterObject
->is
<JSFunction
>()) {
537 return NativeGetPropKind::None
;
540 JSFunction
& getter
= getterObject
->as
<JSFunction
>();
542 if (getter
.isClassConstructor()) {
543 return NativeGetPropKind::None
;
546 // Scripted functions and natives with JIT entry can use the scripted path.
547 if (getter
.hasJitEntry()) {
548 return NativeGetPropKind::ScriptedGetter
;
551 MOZ_ASSERT(getter
.isNativeWithoutJitEntry());
552 return NativeGetPropKind::NativeGetter
;
555 static bool CheckHasNoSuchOwnProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
556 if (!obj
->is
<NativeObject
>()) {
559 // Don't handle objects with resolve hooks.
560 if (ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
)) {
563 if (obj
->as
<NativeObject
>().contains(cx
, id
)) {
569 static bool CheckHasNoSuchProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
570 JSObject
* curObj
= obj
;
572 if (!CheckHasNoSuchOwnProperty(cx
, curObj
, id
)) {
576 curObj
= curObj
->staticPrototype();
582 static bool IsCacheableNoProperty(JSContext
* cx
, NativeObject
* obj
,
583 NativeObject
* holder
, jsid id
,
587 // If we're doing a name lookup, we have to throw a ReferenceError.
588 if (JSOp(*pc
) == JSOp::GetBoundName
) {
592 return CheckHasNoSuchProperty(cx
, obj
, id
);
595 static NativeGetPropKind
CanAttachNativeGetProp(JSContext
* cx
, JSObject
* obj
,
597 NativeObject
** holder
,
598 Maybe
<PropertyInfo
>* propInfo
,
600 MOZ_ASSERT(id
.isString() || id
.isSymbol());
601 MOZ_ASSERT(!*holder
);
603 // The lookup needs to be universally pure, otherwise we risk calling hooks
604 // out of turn. We don't mind doing this even when purity isn't required,
605 // because we only miss out on shape hashification, which is only a temporary
606 // perf cost. The limits were arbitrarily set, anyways.
607 NativeObject
* baseHolder
= nullptr;
609 if (!LookupPropertyPure(cx
, obj
, id
, &baseHolder
, &prop
)) {
610 return NativeGetPropKind::None
;
612 auto* nobj
= &obj
->as
<NativeObject
>();
614 if (prop
.isNativeProperty()) {
615 MOZ_ASSERT(baseHolder
);
616 *holder
= baseHolder
;
617 *propInfo
= mozilla::Some(prop
.propertyInfo());
619 if (IsCacheableGetPropSlot(nobj
, *holder
, propInfo
->ref())) {
620 return NativeGetPropKind::Slot
;
623 return IsCacheableGetPropCall(nobj
, *holder
, propInfo
->ref(), pc
);
626 if (!prop
.isFound()) {
627 if (IsCacheableNoProperty(cx
, nobj
, *holder
, id
, pc
)) {
628 return NativeGetPropKind::Missing
;
632 return NativeGetPropKind::None
;
635 static void GuardReceiverProto(CacheIRWriter
& writer
, NativeObject
* obj
,
636 ObjOperandId objId
) {
637 // Note: we guard on the actual prototype and not on the shape because this is
638 // used for sparse elements where we expect shape changes.
640 if (JSObject
* proto
= obj
->staticPrototype()) {
641 writer
.guardProto(objId
, proto
);
643 writer
.guardNullProto(objId
);
647 // Guard that a given object has same class and same OwnProperties (excluding
648 // dense elements and dynamic properties).
649 static void TestMatchingNativeReceiver(CacheIRWriter
& writer
, NativeObject
* obj
,
650 ObjOperandId objId
) {
651 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
654 // Similar to |TestMatchingNativeReceiver|, but specialized for ProxyObject.
655 static void TestMatchingProxyReceiver(CacheIRWriter
& writer
, ProxyObject
* obj
,
656 ObjOperandId objId
) {
657 writer
.guardShapeForClass(objId
, obj
->shape());
660 static void GeneratePrototypeGuards(CacheIRWriter
& writer
, JSObject
* obj
,
661 NativeObject
* holder
, ObjOperandId objId
) {
662 // Assuming target property is on |holder|, generate appropriate guards to
663 // ensure |holder| is still on the prototype chain of |obj| and we haven't
664 // introduced any shadowing definitions.
666 // For each item in the proto chain before holder, we must ensure that
667 // [[GetPrototypeOf]] still has the expected result, and that
668 // [[GetOwnProperty]] has no definition of the target property.
671 // [SMDOC] Shape Teleporting Optimization
672 // --------------------------------------
674 // Starting with the assumption (and guideline to developers) that mutating
675 // prototypes is an uncommon and fair-to-penalize operation we move cost
676 // from the access side to the mutation side.
678 // Consider the following proto chain, with B defining a property 'x':
680 // D -> C -> B{x: 3} -> A -> null
682 // When accessing |D.x| we refer to D as the "receiver", and B as the
683 // "holder". To optimize this access we need to ensure that neither D nor C
684 // has since defined a shadowing property 'x'. Since C is a prototype that
685 // we assume is rarely mutated we would like to avoid checking each time if
686 // new properties are added. To do this we require that whenever C starts
687 // shadowing a property on its proto chain, we invalidate (and opt out of) the
688 // teleporting optimization by setting the InvalidatedTeleporting flag on the
689 // object we're shadowing, triggering a shape change of that object. As a
690 // result, checking the shape of D and B is sufficient. Note that we do not
691 // care if the shape or properties of A change since the lookup of 'x' will
694 // The second condition we must verify is that the prototype chain was not
695 // mutated. The same mechanism as above is used. When the prototype link is
696 // changed, we generate a new shape for the object. If the object whose
697 // link we are mutating is itself a prototype, we regenerate shapes down
698 // the chain by setting the InvalidatedTeleporting flag on them. This means
699 // the same two shape checks as above are sufficient.
701 // Once the InvalidatedTeleporting flag is set, it means the shape will no
702 // longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
703 // In this case we can no longer apply the optimization.
706 // - ReshapeForProtoMutation
707 // - ReshapeForShadowedProp
710 MOZ_ASSERT(obj
!= holder
);
712 // Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
713 // unchanged so peel off the receiver.
714 JSObject
* pobj
= obj
->staticPrototype();
715 MOZ_ASSERT(pobj
->isUsedAsPrototype());
717 // If teleporting is supported for this holder, we are done.
718 if (!holder
->hasInvalidatedTeleporting()) {
722 // If already at the holder, no further proto checks are needed.
723 if (pobj
== holder
) {
727 // Synchronize pobj and protoId.
728 MOZ_ASSERT(pobj
== obj
->staticPrototype());
729 ObjOperandId protoId
= writer
.loadProto(objId
);
731 // Shape guard each prototype object between receiver and holder. This guards
732 // against both proto changes and shadowing properties.
733 while (pobj
!= holder
) {
734 writer
.guardShape(protoId
, pobj
->shape());
736 pobj
= pobj
->staticPrototype();
737 protoId
= writer
.loadProto(protoId
);
741 static void GeneratePrototypeHoleGuards(CacheIRWriter
& writer
,
742 NativeObject
* obj
, ObjOperandId objId
,
743 bool alwaysGuardFirstProto
) {
744 if (alwaysGuardFirstProto
) {
745 GuardReceiverProto(writer
, obj
, objId
);
748 JSObject
* pobj
= obj
->staticPrototype();
750 ObjOperandId protoId
= writer
.loadObject(pobj
);
752 // Make sure the shape matches, to ensure the proto is unchanged and to
753 // avoid non-dense elements or anything else that is being checked by
754 // CanAttachDenseElementHole.
755 MOZ_ASSERT(pobj
->is
<NativeObject
>());
756 writer
.guardShape(protoId
, pobj
->shape());
758 // Also make sure there are no dense elements.
759 writer
.guardNoDenseElements(protoId
);
761 pobj
= pobj
->staticPrototype();
765 // Similar to |TestMatchingReceiver|, but for the holder object (when it
766 // differs from the receiver). The holder may also be the expando of the
767 // receiver if it exists.
768 static void TestMatchingHolder(CacheIRWriter
& writer
, NativeObject
* obj
,
769 ObjOperandId objId
) {
770 // The GeneratePrototypeGuards + TestMatchingHolder checks only support
771 // prototype chains composed of NativeObject (excluding the receiver
773 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
776 enum class IsCrossCompartment
{ No
, Yes
};
778 // Emit a shape guard for all objects on the proto chain. This does NOT include
779 // the receiver; callers must ensure the receiver's proto is the first proto by
780 // either emitting a shape guard or a prototype guard for |objId|.
782 // Note: this relies on shape implying proto.
783 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
784 static void ShapeGuardProtoChain(CacheIRWriter
& writer
, NativeObject
* obj
,
785 ObjOperandId objId
) {
787 static const uint32_t MAX_CACHED_LOADS
= 4;
788 ObjOperandId receiverObjId
= objId
;
791 JSObject
* proto
= obj
->staticPrototype();
796 obj
= &proto
->as
<NativeObject
>();
798 // After guarding the shape of an object, we can safely bake that
799 // object's proto into the stub data. Compared to LoadProto, this
800 // takes one load instead of three (object -> shape -> baseshape
801 // -> proto). We cap the depth to avoid bloating the size of the
802 // stub data. To avoid compartment mismatch, we skip this optimization
803 // in the cross-compartment case.
804 if (depth
< MAX_CACHED_LOADS
&&
805 MaybeCrossCompartment
== IsCrossCompartment::No
) {
806 objId
= writer
.loadProtoObject(obj
, receiverObjId
);
808 objId
= writer
.loadProto(objId
);
812 writer
.guardShape(objId
, obj
->shape());
816 // For cross compartment guards we shape-guard the prototype chain to avoid
817 // referencing the holder object.
819 // This peels off the first layer because it's guarded against obj == holder.
821 // Returns the holder's OperandId.
822 static ObjOperandId
ShapeGuardProtoChainForCrossCompartmentHolder(
823 CacheIRWriter
& writer
, NativeObject
* obj
, ObjOperandId objId
,
824 NativeObject
* holder
) {
825 MOZ_ASSERT(obj
!= holder
);
828 MOZ_ASSERT(obj
->staticPrototype());
829 obj
= &obj
->staticPrototype()->as
<NativeObject
>();
831 objId
= writer
.loadProto(objId
);
833 TestMatchingHolder(writer
, obj
, objId
);
836 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
840 // Emit guards for reading a data property on |holder|. Returns the holder's
842 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
843 static ObjOperandId
EmitReadSlotGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
844 NativeObject
* holder
,
845 ObjOperandId objId
) {
847 TestMatchingNativeReceiver(writer
, obj
, objId
);
853 if (MaybeCrossCompartment
== IsCrossCompartment::Yes
) {
854 // Guard proto chain integrity.
855 // We use a variant of guards that avoid baking in any cross-compartment
857 return ShapeGuardProtoChainForCrossCompartmentHolder(writer
, obj
, objId
,
861 // Guard proto chain integrity.
862 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
864 // Guard on the holder's shape.
865 ObjOperandId holderId
= writer
.loadObject(holder
);
866 TestMatchingHolder(writer
, holder
, holderId
);
870 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
871 static void EmitMissingPropGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
872 ObjOperandId objId
) {
873 TestMatchingNativeReceiver(writer
, obj
, objId
);
875 // The property does not exist. Guard on everything in the prototype
876 // chain. This is guaranteed to see only Native objects because of
877 // CanAttachNativeGetProp().
878 ShapeGuardProtoChain
<MaybeCrossCompartment
>(writer
, obj
, objId
);
881 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
882 static void EmitReadSlotResult(CacheIRWriter
& writer
, NativeObject
* obj
,
883 NativeObject
* holder
, PropertyInfo prop
,
884 ObjOperandId objId
) {
887 ObjOperandId holderId
=
888 EmitReadSlotGuard
<MaybeCrossCompartment
>(writer
, obj
, holder
, objId
);
890 MOZ_ASSERT(holderId
.valid());
891 EmitLoadSlotResult(writer
, holderId
, holder
, prop
);
894 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
895 static void EmitMissingPropResult(CacheIRWriter
& writer
, NativeObject
* obj
,
896 ObjOperandId objId
) {
897 EmitMissingPropGuard
<MaybeCrossCompartment
>(writer
, obj
, objId
);
898 writer
.loadUndefinedResult();
901 static void EmitCallGetterResultNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
902 NativeGetPropKind kind
,
904 NativeObject
* holder
,
906 ValOperandId receiverId
) {
907 MOZ_ASSERT(IsCacheableGetPropCall(obj
, holder
, prop
) == kind
);
909 JSFunction
* target
= &holder
->getGetter(prop
)->as
<JSFunction
>();
910 bool sameRealm
= cx
->realm() == target
->realm();
913 case NativeGetPropKind::NativeGetter
: {
914 writer
.callNativeGetterResult(receiverId
, target
, sameRealm
);
915 writer
.returnFromIC();
918 case NativeGetPropKind::ScriptedGetter
: {
919 writer
.callScriptedGetterResult(receiverId
, target
, sameRealm
);
920 writer
.returnFromIC();
924 // CanAttachNativeGetProp guarantees that the getter is either a native or
925 // a scripted function.
926 MOZ_ASSERT_UNREACHABLE("Can't attach getter");
931 // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
933 static void EmitGuardGetterSetterSlot(CacheIRWriter
& writer
,
934 NativeObject
* holder
, PropertyInfo prop
,
935 ObjOperandId holderId
,
936 bool holderIsConstant
= false) {
937 // If the holder is guaranteed to be the same object, and it never had a
938 // slot holding a GetterSetter mutated or deleted, its Shape will change when
939 // that does happen so we don't need to guard on the GetterSetter.
940 if (holderIsConstant
&& !holder
->hadGetterSetterChange()) {
944 size_t slot
= prop
.slot();
945 Value slotVal
= holder
->getSlot(slot
);
946 MOZ_ASSERT(slotVal
.isPrivateGCThing());
948 if (holder
->isFixedSlot(slot
)) {
949 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
950 writer
.guardFixedSlotValue(holderId
, offset
, slotVal
);
952 size_t offset
= holder
->dynamicSlotIndex(slot
) * sizeof(Value
);
953 writer
.guardDynamicSlotValue(holderId
, offset
, slotVal
);
957 static void EmitCallGetterResultGuards(CacheIRWriter
& writer
, NativeObject
* obj
,
958 NativeObject
* holder
, HandleId id
,
959 PropertyInfo prop
, ObjOperandId objId
,
960 ICState::Mode mode
) {
961 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
962 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
963 // require outerizing).
965 MOZ_ASSERT(holder
->containsPure(id
, prop
));
967 if (mode
== ICState::Mode::Specialized
|| IsWindow(obj
)) {
968 TestMatchingNativeReceiver(writer
, obj
, objId
);
971 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
973 // Guard on the holder's shape.
974 ObjOperandId holderId
= writer
.loadObject(holder
);
975 TestMatchingHolder(writer
, holder
, holderId
);
977 EmitGuardGetterSetterSlot(writer
, holder
, prop
, holderId
,
978 /* holderIsConstant = */ true);
980 EmitGuardGetterSetterSlot(writer
, holder
, prop
, objId
);
983 GetterSetter
* gs
= holder
->getGetterSetter(prop
);
984 writer
.guardHasGetterSetter(objId
, id
, gs
);
988 static void EmitCallGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
989 NativeGetPropKind kind
, NativeObject
* obj
,
990 NativeObject
* holder
, HandleId id
,
991 PropertyInfo prop
, ObjOperandId objId
,
992 ValOperandId receiverId
, ICState::Mode mode
) {
993 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
, mode
);
994 EmitCallGetterResultNoGuards(cx
, writer
, kind
, obj
, holder
, prop
, receiverId
);
997 static bool CanAttachDOMCall(JSContext
* cx
, JSJitInfo::OpType type
,
998 JSObject
* obj
, JSFunction
* fun
,
999 ICState::Mode mode
) {
1000 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
||
1001 type
== JSJitInfo::Method
);
1003 if (mode
!= ICState::Mode::Specialized
) {
1007 if (!fun
->hasJitInfo()) {
1011 if (cx
->realm() != fun
->realm()) {
1015 const JSJitInfo
* jitInfo
= fun
->jitInfo();
1016 if (jitInfo
->type() != type
) {
1020 MOZ_ASSERT_IF(IsWindow(obj
), !jitInfo
->needsOuterizedThisObject());
1022 const JSClass
* clasp
= obj
->getClass();
1023 if (!clasp
->isDOMClass()) {
1027 if (type
!= JSJitInfo::Method
&& clasp
->isProxyObject()) {
1031 // Ion codegen expects DOM_OBJECT_SLOT to be a fixed slot in LoadDOMPrivate.
1032 // It can be a dynamic slot if we transplanted this reflector object with a
1034 if (obj
->is
<NativeObject
>() && obj
->as
<NativeObject
>().numFixedSlots() == 0) {
1038 // Tell the analysis the |DOMInstanceClassHasProtoAtDepth| hook can't GC.
1039 JS::AutoSuppressGCAnalysis nogc
;
1041 DOMInstanceClassHasProtoAtDepth instanceChecker
=
1042 cx
->runtime()->DOMcallbacks
->instanceClassMatchesProto
;
1043 return instanceChecker(clasp
, jitInfo
->protoID
, jitInfo
->depth
);
1046 static bool CanAttachDOMGetterSetter(JSContext
* cx
, JSJitInfo::OpType type
,
1047 NativeObject
* obj
, NativeObject
* holder
,
1048 PropertyInfo prop
, ICState::Mode mode
) {
1049 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
);
1051 JSObject
* accessor
= type
== JSJitInfo::Getter
? holder
->getGetter(prop
)
1052 : holder
->getSetter(prop
);
1053 JSFunction
* fun
= &accessor
->as
<JSFunction
>();
1055 return CanAttachDOMCall(cx
, type
, obj
, fun
, mode
);
1058 static void EmitCallDOMGetterResultNoGuards(CacheIRWriter
& writer
,
1059 NativeObject
* holder
,
1061 ObjOperandId objId
) {
1062 JSFunction
* getter
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1063 writer
.callDOMGetterResult(objId
, getter
->jitInfo());
1064 writer
.returnFromIC();
1067 static void EmitCallDOMGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
1068 NativeObject
* obj
, NativeObject
* holder
,
1069 HandleId id
, PropertyInfo prop
,
1070 ObjOperandId objId
) {
1071 // Note: this relies on EmitCallGetterResultGuards emitting a shape guard
1072 // for specialized stubs.
1073 // The shape guard ensures the receiver's Class is valid for this DOM getter.
1074 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
,
1075 ICState::Mode::Specialized
);
1076 EmitCallDOMGetterResultNoGuards(writer
, holder
, prop
, objId
);
1079 static ValOperandId
EmitLoadSlot(CacheIRWriter
& writer
, NativeObject
* holder
,
1080 ObjOperandId holderId
, uint32_t slot
) {
1081 if (holder
->isFixedSlot(slot
)) {
1082 return writer
.loadFixedSlot(holderId
,
1083 NativeObject::getFixedSlotOffset(slot
));
1085 size_t dynamicSlotIndex
= holder
->dynamicSlotIndex(slot
);
1086 return writer
.loadDynamicSlot(holderId
, dynamicSlotIndex
);
1089 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId
,
1091 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1093 // We don't support GetBoundName because environment objects have
1094 // lookupProperty hooks and GetBoundName is usually not megamorphic.
1095 MOZ_ASSERT(JSOp(*pc_
) != JSOp::GetBoundName
);
1097 if (cacheKind_
== CacheKind::GetProp
||
1098 cacheKind_
== CacheKind::GetPropSuper
) {
1099 writer
.megamorphicLoadSlotResult(objId
, id
);
1101 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
1102 cacheKind_
== CacheKind::GetElemSuper
);
1103 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1105 writer
.returnFromIC();
1107 trackAttached("GetProp.MegamorphicNativeSlot");
1110 AttachDecision
GetPropIRGenerator::tryAttachNative(HandleObject obj
,
1113 ValOperandId receiverId
) {
1114 Maybe
<PropertyInfo
> prop
;
1115 NativeObject
* holder
= nullptr;
1117 NativeGetPropKind kind
=
1118 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
1120 case NativeGetPropKind::None
:
1121 return AttachDecision::NoAction
;
1122 case NativeGetPropKind::Missing
:
1123 case NativeGetPropKind::Slot
: {
1124 auto* nobj
= &obj
->as
<NativeObject
>();
1126 if (mode_
== ICState::Mode::Megamorphic
&&
1127 JSOp(*pc_
) != JSOp::GetBoundName
) {
1128 attachMegamorphicNativeSlot(objId
, id
);
1129 return AttachDecision::Attach
;
1132 maybeEmitIdGuard(id
);
1133 if (kind
== NativeGetPropKind::Slot
) {
1134 EmitReadSlotResult(writer
, nobj
, holder
, *prop
, objId
);
1135 writer
.returnFromIC();
1136 trackAttached("GetProp.NativeSlot");
1138 EmitMissingPropResult(writer
, nobj
, objId
);
1139 writer
.returnFromIC();
1140 trackAttached("GetProp.Missing");
1142 return AttachDecision::Attach
;
1144 case NativeGetPropKind::ScriptedGetter
:
1145 case NativeGetPropKind::NativeGetter
: {
1146 auto* nobj
= &obj
->as
<NativeObject
>();
1148 maybeEmitIdGuard(id
);
1150 if (!isSuper() && CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, nobj
,
1151 holder
, *prop
, mode_
)) {
1152 EmitCallDOMGetterResult(cx_
, writer
, nobj
, holder
, id
, *prop
, objId
);
1154 trackAttached("GetProp.DOMGetter");
1155 return AttachDecision::Attach
;
1158 EmitCallGetterResult(cx_
, writer
, kind
, nobj
, holder
, id
, *prop
, objId
,
1161 trackAttached("GetProp.NativeGetter");
1162 return AttachDecision::Attach
;
1166 MOZ_CRASH("Bad NativeGetPropKind");
1169 // Returns whether obj is a WindowProxy wrapping the script's global.
1170 static bool IsWindowProxyForScriptGlobal(JSScript
* script
, JSObject
* obj
) {
1171 if (!IsWindowProxy(obj
)) {
1175 MOZ_ASSERT(obj
->getClass() ==
1176 script
->runtimeFromMainThread()->maybeWindowProxyClass());
1178 JSObject
* window
= ToWindowIfWindowProxy(obj
);
1180 // Ion relies on the WindowProxy's group changing (and the group getting
1181 // marked as having unknown properties) on navigation. If we ever stop
1182 // transplanting same-compartment WindowProxies, this assert will fail and we
1183 // need to fix that code.
1184 MOZ_ASSERT(window
== &obj
->nonCCWGlobal());
1186 // This must be a WindowProxy for a global in this compartment. Else it would
1187 // be a cross-compartment wrapper and IsWindowProxy returns false for
1189 MOZ_ASSERT(script
->compartment() == obj
->compartment());
1191 // Only optimize lookups on the WindowProxy for the current global. Other
1192 // WindowProxies in the compartment may require security checks (based on
1193 // mutable document.domain). See bug 1516775.
1194 return window
== &script
->global();
1197 // Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
1198 static ObjOperandId
GuardAndLoadWindowProxyWindow(CacheIRWriter
& writer
,
1200 GlobalObject
* windowObj
) {
1201 writer
.guardClass(objId
, GuardClassKind::WindowProxy
);
1202 ObjOperandId windowObjId
= writer
.loadWrapperTarget(objId
,
1203 /*fallible = */ false);
1204 writer
.guardSpecificObject(windowObjId
, windowObj
);
1208 // Whether a getter/setter on the global should have the WindowProxy as |this|
1209 // value instead of the Window (the global object). This always returns true for
1210 // scripted functions.
1211 static bool GetterNeedsWindowProxyThis(NativeObject
* holder
,
1212 PropertyInfo prop
) {
1213 JSFunction
* callee
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1214 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1216 static bool SetterNeedsWindowProxyThis(NativeObject
* holder
,
1217 PropertyInfo prop
) {
1218 JSFunction
* callee
= &holder
->getSetter(prop
)->as
<JSFunction
>();
1219 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1222 AttachDecision
GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
1225 // Attach a stub when the receiver is a WindowProxy and we can do the lookup
1226 // on the Window (the global object).
1228 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
1229 return AttachDecision::NoAction
;
1232 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1234 if (mode_
== ICState::Mode::Megamorphic
) {
1235 return AttachDecision::NoAction
;
1238 // Now try to do the lookup on the Window (the current global).
1239 GlobalObject
* windowObj
= cx_
->global();
1240 NativeObject
* holder
= nullptr;
1241 Maybe
<PropertyInfo
> prop
;
1242 NativeGetPropKind kind
=
1243 CanAttachNativeGetProp(cx_
, windowObj
, id
, &holder
, &prop
, pc_
);
1245 case NativeGetPropKind::None
:
1246 return AttachDecision::NoAction
;
1248 case NativeGetPropKind::Slot
: {
1249 maybeEmitIdGuard(id
);
1250 ObjOperandId windowObjId
=
1251 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1252 EmitReadSlotResult(writer
, windowObj
, holder
, *prop
, windowObjId
);
1253 writer
.returnFromIC();
1255 trackAttached("GetProp.WindowProxySlot");
1256 return AttachDecision::Attach
;
1259 case NativeGetPropKind::Missing
: {
1260 maybeEmitIdGuard(id
);
1261 ObjOperandId windowObjId
=
1262 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1263 EmitMissingPropResult(writer
, windowObj
, windowObjId
);
1264 writer
.returnFromIC();
1266 trackAttached("GetProp.WindowProxyMissing");
1267 return AttachDecision::Attach
;
1270 case NativeGetPropKind::NativeGetter
:
1271 case NativeGetPropKind::ScriptedGetter
: {
1272 // If a |super| access, it is not worth the complexity to attach an IC.
1274 return AttachDecision::NoAction
;
1277 bool needsWindowProxy
= GetterNeedsWindowProxyThis(holder
, *prop
);
1279 // Guard the incoming object is a WindowProxy and inline a getter call
1280 // based on the Window object.
1281 maybeEmitIdGuard(id
);
1282 ObjOperandId windowObjId
=
1283 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1285 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, windowObj
, holder
,
1287 MOZ_ASSERT(!needsWindowProxy
);
1288 EmitCallDOMGetterResult(cx_
, writer
, windowObj
, holder
, id
, *prop
,
1290 trackAttached("GetProp.WindowProxyDOMGetter");
1292 ValOperandId receiverId
=
1293 writer
.boxObject(needsWindowProxy
? objId
: windowObjId
);
1294 EmitCallGetterResult(cx_
, writer
, kind
, windowObj
, holder
, id
, *prop
,
1295 windowObjId
, receiverId
, mode_
);
1296 trackAttached("GetProp.WindowProxyGetter");
1299 return AttachDecision::Attach
;
1303 MOZ_CRASH("Unreachable");
1306 AttachDecision
GetPropIRGenerator::tryAttachCrossCompartmentWrapper(
1307 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
1308 // We can only optimize this very wrapper-handler, because others might
1309 // have a security policy.
1310 if (!IsWrapper(obj
) ||
1311 Wrapper::wrapperHandler(obj
) != &CrossCompartmentWrapper::singleton
) {
1312 return AttachDecision::NoAction
;
1315 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1317 if (mode_
== ICState::Mode::Megamorphic
) {
1318 return AttachDecision::NoAction
;
1321 RootedObject
unwrapped(cx_
, Wrapper::wrappedObject(obj
));
1322 MOZ_ASSERT(unwrapped
== UnwrapOneCheckedStatic(obj
));
1323 MOZ_ASSERT(!IsCrossCompartmentWrapper(unwrapped
),
1324 "CCWs must not wrap other CCWs");
1326 // If we allowed different zones we would have to wrap strings.
1327 if (unwrapped
->compartment()->zone() != cx_
->compartment()->zone()) {
1328 return AttachDecision::NoAction
;
1331 // Take the unwrapped object's global, and wrap in a
1332 // this-compartment wrapper. This is what will be stored in the IC
1333 // keep the compartment alive.
1334 RootedObject
wrappedTargetGlobal(cx_
, &unwrapped
->nonCCWGlobal());
1335 if (!cx_
->compartment()->wrap(cx_
, &wrappedTargetGlobal
)) {
1336 cx_
->clearPendingException();
1337 return AttachDecision::NoAction
;
1340 NativeObject
* holder
= nullptr;
1341 Maybe
<PropertyInfo
> prop
;
1343 // Enter realm of target to prevent failing compartment assertions when doing
1346 AutoRealm
ar(cx_
, unwrapped
);
1348 NativeGetPropKind kind
=
1349 CanAttachNativeGetProp(cx_
, unwrapped
, id
, &holder
, &prop
, pc_
);
1350 if (kind
!= NativeGetPropKind::Slot
&& kind
!= NativeGetPropKind::Missing
) {
1351 return AttachDecision::NoAction
;
1354 auto* unwrappedNative
= &unwrapped
->as
<NativeObject
>();
1356 maybeEmitIdGuard(id
);
1357 writer
.guardIsProxy(objId
);
1358 writer
.guardHasProxyHandler(objId
, Wrapper::wrapperHandler(obj
));
1360 // Load the object wrapped by the CCW
1361 ObjOperandId wrapperTargetId
=
1362 writer
.loadWrapperTarget(objId
, /*fallible = */ false);
1364 // If the compartment of the wrapped object is different we should fail.
1365 writer
.guardCompartment(wrapperTargetId
, wrappedTargetGlobal
,
1366 unwrappedNative
->compartment());
1368 ObjOperandId unwrappedId
= wrapperTargetId
;
1370 EmitReadSlotResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
, holder
,
1371 *prop
, unwrappedId
);
1372 writer
.wrapResult();
1373 writer
.returnFromIC();
1374 trackAttached("GetProp.CCWSlot");
1376 EmitMissingPropResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
,
1378 writer
.returnFromIC();
1379 trackAttached("GetProp.CCWMissing");
1381 return AttachDecision::Attach
;
1384 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
1385 Handle
<NativeObject
*> obj
);
1387 static bool GetXrayExpandoShapeWrapper(JSContext
* cx
, HandleObject xray
,
1388 MutableHandleObject wrapper
) {
1389 Value v
= GetProxyReservedSlot(xray
, GetXrayJitInfo()->xrayHolderSlot
);
1391 NativeObject
* holder
= &v
.toObject().as
<NativeObject
>();
1392 v
= holder
->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot
);
1394 Rooted
<NativeObject
*> expando(
1395 cx
, &UncheckedUnwrap(&v
.toObject())->as
<NativeObject
>());
1396 wrapper
.set(NewWrapperWithObjectShape(cx
, expando
));
1397 return wrapper
!= nullptr;
1400 wrapper
.set(nullptr);
1404 AttachDecision
GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(
1405 HandleObject obj
, ObjOperandId objId
, HandleId id
,
1406 ValOperandId receiverId
) {
1407 if (!obj
->is
<ProxyObject
>()) {
1408 return AttachDecision::NoAction
;
1411 JS::XrayJitInfo
* info
= GetXrayJitInfo();
1412 if (!info
|| !info
->isCrossCompartmentXray(GetProxyHandler(obj
))) {
1413 return AttachDecision::NoAction
;
1416 if (!info
->compartmentHasExclusiveExpandos(obj
)) {
1417 return AttachDecision::NoAction
;
1420 RootedObject
target(cx_
, UncheckedUnwrap(obj
));
1422 RootedObject
expandoShapeWrapper(cx_
);
1423 if (!GetXrayExpandoShapeWrapper(cx_
, obj
, &expandoShapeWrapper
)) {
1424 cx_
->recoverFromOutOfMemory();
1425 return AttachDecision::NoAction
;
1428 // Look for a getter we can call on the xray or its prototype chain.
1429 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx_
);
1430 RootedObject
holder(cx_
, obj
);
1431 RootedObjectVector
prototypes(cx_
);
1432 RootedObjectVector
prototypeExpandoShapeWrappers(cx_
);
1434 if (!GetOwnPropertyDescriptor(cx_
, holder
, id
, &desc
)) {
1435 cx_
->clearPendingException();
1436 return AttachDecision::NoAction
;
1438 if (desc
.isSome()) {
1441 if (!GetPrototype(cx_
, holder
, &holder
)) {
1442 cx_
->clearPendingException();
1443 return AttachDecision::NoAction
;
1445 if (!holder
|| !holder
->is
<ProxyObject
>() ||
1446 !info
->isCrossCompartmentXray(GetProxyHandler(holder
))) {
1447 return AttachDecision::NoAction
;
1449 RootedObject
prototypeExpandoShapeWrapper(cx_
);
1450 if (!GetXrayExpandoShapeWrapper(cx_
, holder
,
1451 &prototypeExpandoShapeWrapper
) ||
1452 !prototypes
.append(holder
) ||
1453 !prototypeExpandoShapeWrappers
.append(prototypeExpandoShapeWrapper
)) {
1454 cx_
->recoverFromOutOfMemory();
1455 return AttachDecision::NoAction
;
1458 if (!desc
->isAccessorDescriptor()) {
1459 return AttachDecision::NoAction
;
1462 RootedObject
getter(cx_
, desc
->getter());
1463 if (!getter
|| !getter
->is
<JSFunction
>() ||
1464 !getter
->as
<JSFunction
>().isNativeWithoutJitEntry()) {
1465 return AttachDecision::NoAction
;
1468 maybeEmitIdGuard(id
);
1469 writer
.guardIsProxy(objId
);
1470 writer
.guardHasProxyHandler(objId
, GetProxyHandler(obj
));
1472 // Load the object wrapped by the CCW
1473 ObjOperandId wrapperTargetId
=
1474 writer
.loadWrapperTarget(objId
, /*fallible = */ false);
1476 // Test the wrapped object's class. The properties held by xrays or their
1477 // prototypes will be invariant for objects of a given class, except for
1478 // changes due to xray expandos or xray prototype mutations.
1479 writer
.guardAnyClass(wrapperTargetId
, target
->getClass());
1481 // Make sure the expandos on the xray and its prototype chain match up with
1482 // what we expect. The expando shape needs to be consistent, to ensure it
1483 // has not had any shadowing properties added, and the expando cannot have
1484 // any custom prototype (xray prototypes are stable otherwise).
1486 // We can only do this for xrays with exclusive access to their expandos
1487 // (as we checked earlier), which store a pointer to their expando
1488 // directly. Xrays in other compartments may share their expandos with each
1489 // other and a VM call is needed just to find the expando.
1490 if (expandoShapeWrapper
) {
1491 writer
.guardXrayExpandoShapeAndDefaultProto(objId
, expandoShapeWrapper
);
1493 writer
.guardXrayNoExpando(objId
);
1495 for (size_t i
= 0; i
< prototypes
.length(); i
++) {
1496 JSObject
* proto
= prototypes
[i
];
1497 ObjOperandId protoId
= writer
.loadObject(proto
);
1498 if (JSObject
* protoShapeWrapper
= prototypeExpandoShapeWrappers
[i
]) {
1499 writer
.guardXrayExpandoShapeAndDefaultProto(protoId
, protoShapeWrapper
);
1501 writer
.guardXrayNoExpando(protoId
);
1505 bool sameRealm
= cx_
->realm() == getter
->as
<JSFunction
>().realm();
1506 writer
.callNativeGetterResult(receiverId
, &getter
->as
<JSFunction
>(),
1508 writer
.returnFromIC();
1510 trackAttached("GetProp.XrayCCW");
1511 return AttachDecision::Attach
;
1515 AttachDecision
GetPropIRGenerator::tryAttachScriptedProxy(
1516 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1517 if (cacheKind_
!= CacheKind::GetProp
&& cacheKind_
!= CacheKind::GetElem
) {
1518 return AttachDecision::NoAction
;
1520 if (cacheKind_
== CacheKind::GetElem
) {
1521 if (!idVal_
.isString() && !idVal_
.isInt32() && !idVal_
.isSymbol()) {
1522 return AttachDecision::NoAction
;
1526 JSObject
* handlerObj
= ScriptedProxyHandler::handlerObject(obj
);
1528 return AttachDecision::NoAction
;
1531 NativeObject
* trapHolder
= nullptr;
1532 Maybe
<PropertyInfo
> trapProp
;
1533 // We call with pc_ even though that's not the actual corresponding pc. It
1534 // should, however, be fine, because it's just used to check if this is a
1535 // GetBoundName, which it's not.
1536 NativeGetPropKind trapKind
= CanAttachNativeGetProp(
1537 cx_
, handlerObj
, NameToId(cx_
->names().get
), &trapHolder
, &trapProp
, pc_
);
1539 if (trapKind
!= NativeGetPropKind::Missing
&&
1540 trapKind
!= NativeGetPropKind::Slot
) {
1541 return AttachDecision::NoAction
;
1544 if (trapKind
!= NativeGetPropKind::Missing
) {
1545 uint32_t trapSlot
= trapProp
->slot();
1546 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1547 if (!trapVal
.isObject()) {
1548 return AttachDecision::NoAction
;
1551 JSObject
* trapObj
= &trapVal
.toObject();
1552 if (!trapObj
->is
<JSFunction
>()) {
1553 return AttachDecision::NoAction
;
1556 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1557 if (trapFn
->isClassConstructor()) {
1558 return AttachDecision::NoAction
;
1561 if (!trapFn
->hasJitEntry()) {
1562 return AttachDecision::NoAction
;
1565 if (cx_
->realm() != trapFn
->realm()) {
1566 return AttachDecision::NoAction
;
1570 NativeObject
* nHandlerObj
= &handlerObj
->as
<NativeObject
>();
1571 JSObject
* targetObj
= obj
->target();
1572 MOZ_ASSERT(targetObj
, "Guaranteed by the scripted Proxy constructor");
1574 // We just require that the target is a NativeObject to make our lives
1575 // easier. There's too much nonsense we might have to handle otherwise and
1576 // we're not set up to recursively call GetPropIRGenerator::tryAttachStub
1577 // for the target object.
1578 if (!targetObj
->is
<NativeObject
>()) {
1579 return AttachDecision::NoAction
;
1582 writer
.guardIsProxy(objId
);
1583 writer
.guardHasProxyHandler(objId
, &ScriptedProxyHandler::singleton
);
1584 ObjOperandId handlerObjId
= writer
.loadScriptedProxyHandler(objId
);
1585 ObjOperandId targetObjId
=
1586 writer
.loadWrapperTarget(objId
, /*fallible =*/true);
1588 writer
.guardIsNativeObject(targetObjId
);
1590 if (trapKind
== NativeGetPropKind::Missing
) {
1591 EmitMissingPropGuard(writer
, nHandlerObj
, handlerObjId
);
1592 if (cacheKind_
== CacheKind::GetProp
) {
1593 writer
.megamorphicLoadSlotResult(targetObjId
, id
);
1595 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1598 uint32_t trapSlot
= trapProp
->slot();
1599 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1600 JSObject
* trapObj
= &trapVal
.toObject();
1601 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1602 ObjOperandId trapHolderId
=
1603 EmitReadSlotGuard(writer
, nHandlerObj
, trapHolder
, handlerObjId
);
1605 ValOperandId fnValId
=
1606 EmitLoadSlot(writer
, trapHolder
, trapHolderId
, trapSlot
);
1607 ObjOperandId fnObjId
= writer
.guardToObject(fnValId
);
1608 writer
.guardSpecificFunction(fnObjId
, trapFn
);
1609 ValOperandId targetValId
= writer
.boxObject(targetObjId
);
1610 if (cacheKind_
== CacheKind::GetProp
) {
1611 writer
.callScriptedProxyGetResult(targetValId
, objId
, handlerObjId
,
1614 ValOperandId idId
= getElemKeyValueId();
1615 ValOperandId stringIdId
= writer
.idToStringOrSymbol(idId
);
1616 writer
.callScriptedProxyGetByValueResult(targetValId
, objId
, handlerObjId
,
1617 stringIdId
, trapFn
);
1620 writer
.returnFromIC();
1622 trackAttached("GetScriptedProxy");
1623 return AttachDecision::Attach
;
1627 AttachDecision
GetPropIRGenerator::tryAttachGenericProxy(
1628 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1629 bool handleDOMProxies
) {
1630 writer
.guardIsProxy(objId
);
1632 if (!handleDOMProxies
) {
1633 // Ensure that the incoming object is not a DOM proxy, so that we can get to
1634 // the specialized stubs
1635 writer
.guardIsNotDOMProxy(objId
);
1638 if (cacheKind_
== CacheKind::GetProp
|| mode_
== ICState::Mode::Specialized
) {
1639 MOZ_ASSERT(!isSuper());
1640 maybeEmitIdGuard(id
);
1641 writer
.proxyGetResult(objId
, id
);
1643 // Attach a stub that handles every id.
1644 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
1645 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1646 MOZ_ASSERT(!isSuper());
1647 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
1650 writer
.returnFromIC();
1652 trackAttached("GetProp.GenericProxy");
1653 return AttachDecision::Attach
;
1656 static bool ValueIsInt64Index(const Value
& val
, int64_t* index
) {
1657 // Try to convert the Value to a TypedArray index or DataView offset.
1659 if (val
.isInt32()) {
1660 *index
= val
.toInt32();
1664 if (val
.isDouble()) {
1665 // Use NumberEqualsInt64 because ToPropertyKey(-0) is 0.
1666 return mozilla::NumberEqualsInt64(val
.toDouble(), index
);
1672 IntPtrOperandId
IRGenerator::guardToIntPtrIndex(const Value
& index
,
1673 ValOperandId indexId
,
1677 MOZ_ASSERT_IF(!supportOOB
, ValueIsInt64Index(index
, &indexInt64
));
1680 if (index
.isInt32()) {
1681 Int32OperandId int32IndexId
= writer
.guardToInt32(indexId
);
1682 return writer
.int32ToIntPtr(int32IndexId
);
1685 MOZ_ASSERT(index
.isNumber());
1686 NumberOperandId numberIndexId
= writer
.guardIsNumber(indexId
);
1687 return writer
.guardNumberToIntPtrIndex(numberIndexId
, supportOOB
);
1690 ObjOperandId
IRGenerator::guardDOMProxyExpandoObjectAndShape(
1691 ProxyObject
* obj
, ObjOperandId objId
, const Value
& expandoVal
,
1692 NativeObject
* expandoObj
) {
1693 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1695 TestMatchingProxyReceiver(writer
, obj
, objId
);
1697 // Shape determines Class, so now it must be a DOM proxy.
1698 ValOperandId expandoValId
;
1699 if (expandoVal
.isObject()) {
1700 expandoValId
= writer
.loadDOMExpandoValue(objId
);
1702 expandoValId
= writer
.loadDOMExpandoValueIgnoreGeneration(objId
);
1705 // Guard the expando is an object and shape guard.
1706 ObjOperandId expandoObjId
= writer
.guardToObject(expandoValId
);
1707 TestMatchingHolder(writer
, expandoObj
, expandoObjId
);
1708 return expandoObjId
;
1711 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyExpando(
1712 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1713 ValOperandId receiverId
) {
1714 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1716 Value expandoVal
= GetProxyPrivate(obj
);
1717 JSObject
* expandoObj
;
1718 if (expandoVal
.isObject()) {
1719 expandoObj
= &expandoVal
.toObject();
1721 MOZ_ASSERT(!expandoVal
.isUndefined(),
1722 "How did a missing expando manage to shadow things?");
1723 auto expandoAndGeneration
=
1724 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1725 MOZ_ASSERT(expandoAndGeneration
);
1726 expandoObj
= &expandoAndGeneration
->expando
.toObject();
1729 // Try to do the lookup on the expando object.
1730 NativeObject
* holder
= nullptr;
1731 Maybe
<PropertyInfo
> prop
;
1732 NativeGetPropKind kind
=
1733 CanAttachNativeGetProp(cx_
, expandoObj
, id
, &holder
, &prop
, pc_
);
1734 if (kind
== NativeGetPropKind::None
) {
1735 return AttachDecision::NoAction
;
1738 return AttachDecision::NoAction
;
1740 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
1742 MOZ_ASSERT(holder
== nativeExpandoObj
);
1744 maybeEmitIdGuard(id
);
1745 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
1746 obj
, objId
, expandoVal
, nativeExpandoObj
);
1748 if (kind
== NativeGetPropKind::Slot
) {
1749 // Load from the expando's slots.
1750 EmitLoadSlotResult(writer
, expandoObjId
, nativeExpandoObj
, *prop
);
1751 writer
.returnFromIC();
1753 // Call the getter. Note that we pass objId, the DOM proxy, as |this|
1754 // and not the expando object.
1755 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1756 kind
== NativeGetPropKind::ScriptedGetter
);
1757 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
1758 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeExpandoObj
,
1759 nativeExpandoObj
, *prop
, receiverId
);
1762 trackAttached("GetProp.DOMProxyExpando");
1763 return AttachDecision::Attach
;
1766 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyShadowed(
1767 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1768 MOZ_ASSERT(!isSuper());
1769 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1771 maybeEmitIdGuard(id
);
1772 TestMatchingProxyReceiver(writer
, obj
, objId
);
1773 writer
.proxyGetResult(objId
, id
);
1774 writer
.returnFromIC();
1776 trackAttached("GetProp.DOMProxyShadowed");
1777 return AttachDecision::Attach
;
1780 // Emit CacheIR to guard the DOM proxy doesn't shadow |id|. There are two types
1783 // (a) DOM proxies marked LegacyOverrideBuiltIns in WebIDL, for example
1784 // HTMLDocument or HTMLFormElement. These proxies look up properties in this
1787 // (1) The expando object.
1788 // (2) The proxy's named-property handler.
1789 // (3) The prototype chain.
1791 // To optimize properties on the prototype chain, we have to guard that (1)
1792 // and (2) don't shadow (3). We handle (1) by either emitting a shape guard
1793 // for the expando object or by guarding the proxy has no expando object. To
1794 // efficiently handle (2), the proxy must have an ExpandoAndGeneration*
1795 // stored as PrivateValue. We guard on its generation field to ensure the
1796 // set of names hasn't changed.
1798 // Missing properties can be optimized in a similar way by emitting shape
1799 // guards for the prototype chain.
1801 // (b) Other DOM proxies. These proxies look up properties in this
1804 // (1) The expando object.
1805 // (2) The prototype chain.
1806 // (3) The proxy's named-property handler.
1808 // To optimize properties on the prototype chain, we only have to guard the
1809 // expando object doesn't shadow it.
1811 // Missing properties can't be optimized in this case because we don't have
1812 // an efficient way to guard against the proxy handler shadowing the
1813 // property (there's no ExpandoAndGeneration*).
1816 // * DOMProxyShadows in DOMJSProxyHandler.cpp
1817 // * https://webidl.spec.whatwg.org/#dfn-named-property-visibility (the Note at
1820 // Callers are expected to have already guarded on the shape of the
1821 // object, which guarantees the object is a DOM proxy.
1822 static void CheckDOMProxyDoesNotShadow(CacheIRWriter
& writer
, ProxyObject
* obj
,
1823 jsid id
, ObjOperandId objId
,
1824 bool* canOptimizeMissing
) {
1825 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1827 Value expandoVal
= GetProxyPrivate(obj
);
1829 ValOperandId expandoId
;
1830 if (!expandoVal
.isObject() && !expandoVal
.isUndefined()) {
1832 auto expandoAndGeneration
=
1833 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1834 uint64_t generation
= expandoAndGeneration
->generation
;
1835 expandoId
= writer
.loadDOMExpandoValueGuardGeneration(
1836 objId
, expandoAndGeneration
, generation
);
1837 expandoVal
= expandoAndGeneration
->expando
;
1838 *canOptimizeMissing
= true;
1841 expandoId
= writer
.loadDOMExpandoValue(objId
);
1842 *canOptimizeMissing
= false;
1845 if (expandoVal
.isUndefined()) {
1846 // Guard there's no expando object.
1847 writer
.guardNonDoubleType(expandoId
, ValueType::Undefined
);
1848 } else if (expandoVal
.isObject()) {
1849 // Guard the proxy either has no expando object or, if it has one, that
1850 // the shape matches the current expando object.
1851 NativeObject
& expandoObj
= expandoVal
.toObject().as
<NativeObject
>();
1852 MOZ_ASSERT(!expandoObj
.containsPure(id
));
1853 writer
.guardDOMExpandoMissingOrGuardShape(expandoId
, expandoObj
.shape());
1855 MOZ_CRASH("Invalid expando value");
1859 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
1860 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1861 ValOperandId receiverId
) {
1862 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1864 JSObject
* protoObj
= obj
->staticPrototype();
1866 return AttachDecision::NoAction
;
1869 NativeObject
* holder
= nullptr;
1870 Maybe
<PropertyInfo
> prop
;
1871 NativeGetPropKind kind
=
1872 CanAttachNativeGetProp(cx_
, protoObj
, id
, &holder
, &prop
, pc_
);
1873 if (kind
== NativeGetPropKind::None
) {
1874 return AttachDecision::NoAction
;
1876 auto* nativeProtoObj
= &protoObj
->as
<NativeObject
>();
1878 maybeEmitIdGuard(id
);
1880 // Guard that our proxy (expando) object hasn't started shadowing this
1882 TestMatchingProxyReceiver(writer
, obj
, objId
);
1883 bool canOptimizeMissing
= false;
1884 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
1887 // Found the property on the prototype chain. Treat it like a native
1889 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
1891 // Guard on the holder of the property.
1892 ObjOperandId holderId
= writer
.loadObject(holder
);
1893 TestMatchingHolder(writer
, holder
, holderId
);
1895 if (kind
== NativeGetPropKind::Slot
) {
1896 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
1897 writer
.returnFromIC();
1899 // EmitCallGetterResultNoGuards expects |obj| to be the object the
1900 // property is on to do some checks. Since we actually looked at
1901 // checkObj, and no extra guards will be generated, we can just
1902 // pass that instead.
1903 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1904 kind
== NativeGetPropKind::ScriptedGetter
);
1905 MOZ_ASSERT(!isSuper());
1906 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
1907 /* holderIsConstant = */ true);
1908 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeProtoObj
, holder
,
1912 // Property was not found on the prototype chain.
1913 MOZ_ASSERT(kind
== NativeGetPropKind::Missing
);
1914 if (canOptimizeMissing
) {
1915 // We already guarded on the proxy's shape, so now shape guard the proto
1917 ObjOperandId protoId
= writer
.loadObject(nativeProtoObj
);
1918 EmitMissingPropResult(writer
, nativeProtoObj
, protoId
);
1920 MOZ_ASSERT(!isSuper());
1921 writer
.proxyGetResult(objId
, id
);
1923 writer
.returnFromIC();
1926 trackAttached("GetProp.DOMProxyUnshadowed");
1927 return AttachDecision::Attach
;
1930 AttachDecision
GetPropIRGenerator::tryAttachProxy(HandleObject obj
,
1933 ValOperandId receiverId
) {
1934 // The proxy stubs don't currently support |super| access.
1936 return AttachDecision::NoAction
;
1939 // Always try to attach scripted proxy get even if we're megamorphic.
1940 // In Speedometer 3 we'll often run into cases where we're megamorphic
1941 // overall, but monomorphic for the proxy case. This is because there
1942 // are functions which lazily turn various differently-shaped objects
1943 // into proxies. So the un-proxified objects are megamorphic, but the
1944 // proxy handlers are actually monomorphic. There is room for a bit
1945 // more sophistication here, but this should do for now.
1946 if (!obj
->is
<ProxyObject
>()) {
1947 return AttachDecision::NoAction
;
1949 auto proxy
= obj
.as
<ProxyObject
>();
1951 if (proxy
->handler()->isScripted()) {
1952 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, id
));
1956 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
1957 if (type
== ProxyStubType::None
) {
1958 return AttachDecision::NoAction
;
1961 if (mode_
== ICState::Mode::Megamorphic
) {
1962 return tryAttachGenericProxy(proxy
, objId
, id
,
1963 /* handleDOMProxies = */ true);
1967 case ProxyStubType::None
:
1969 case ProxyStubType::DOMExpando
:
1970 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, receiverId
));
1971 [[fallthrough
]]; // Fall through to the generic shadowed case.
1972 case ProxyStubType::DOMShadowed
:
1973 return tryAttachDOMProxyShadowed(proxy
, objId
, id
);
1974 case ProxyStubType::DOMUnshadowed
:
1975 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, receiverId
));
1976 return tryAttachGenericProxy(proxy
, objId
, id
,
1977 /* handleDOMProxies = */ true);
1978 case ProxyStubType::Generic
:
1979 return tryAttachGenericProxy(proxy
, objId
, id
,
1980 /* handleDOMProxies = */ false);
1983 MOZ_CRASH("Unexpected ProxyStubType");
1986 const JSClass
* js::jit::ClassFor(GuardClassKind kind
) {
1988 case GuardClassKind::Array
:
1989 return &ArrayObject::class_
;
1990 case GuardClassKind::PlainObject
:
1991 return &PlainObject::class_
;
1992 case GuardClassKind::FixedLengthArrayBuffer
:
1993 return &FixedLengthArrayBufferObject::class_
;
1994 case GuardClassKind::ResizableArrayBuffer
:
1995 return &ResizableArrayBufferObject::class_
;
1996 case GuardClassKind::FixedLengthSharedArrayBuffer
:
1997 return &FixedLengthSharedArrayBufferObject::class_
;
1998 case GuardClassKind::GrowableSharedArrayBuffer
:
1999 return &GrowableSharedArrayBufferObject::class_
;
2000 case GuardClassKind::FixedLengthDataView
:
2001 return &FixedLengthDataViewObject::class_
;
2002 case GuardClassKind::ResizableDataView
:
2003 return &ResizableDataViewObject::class_
;
2004 case GuardClassKind::MappedArguments
:
2005 return &MappedArgumentsObject::class_
;
2006 case GuardClassKind::UnmappedArguments
:
2007 return &UnmappedArgumentsObject::class_
;
2008 case GuardClassKind::WindowProxy
:
2009 // Caller needs to handle this case, see
2010 // JSRuntime::maybeWindowProxyClass().
2012 case GuardClassKind::JSFunction
:
2013 // Caller needs to handle this case. Can be either |js::FunctionClass| or
2014 // |js::ExtendedFunctionClass|.
2016 case GuardClassKind::BoundFunction
:
2017 return &BoundFunctionObject::class_
;
2018 case GuardClassKind::Set
:
2019 return &SetObject::class_
;
2020 case GuardClassKind::Map
:
2021 return &MapObject::class_
;
2023 MOZ_CRASH("unexpected kind");
2026 // Guards the class of an object. Because shape implies class, and a shape guard
2027 // is faster than a class guard, if this is our first time attaching a stub, we
2028 // instead generate a shape guard.
2029 void IRGenerator::emitOptimisticClassGuard(ObjOperandId objId
, JSObject
* obj
,
2030 GuardClassKind kind
) {
2033 case GuardClassKind::Array
:
2034 case GuardClassKind::PlainObject
:
2035 case GuardClassKind::FixedLengthArrayBuffer
:
2036 case GuardClassKind::ResizableArrayBuffer
:
2037 case GuardClassKind::FixedLengthSharedArrayBuffer
:
2038 case GuardClassKind::GrowableSharedArrayBuffer
:
2039 case GuardClassKind::FixedLengthDataView
:
2040 case GuardClassKind::ResizableDataView
:
2041 case GuardClassKind::Set
:
2042 case GuardClassKind::Map
:
2043 MOZ_ASSERT(obj
->hasClass(ClassFor(kind
)));
2046 case GuardClassKind::MappedArguments
:
2047 case GuardClassKind::UnmappedArguments
:
2048 case GuardClassKind::JSFunction
:
2049 case GuardClassKind::BoundFunction
:
2050 case GuardClassKind::WindowProxy
:
2051 // Arguments, functions, and the global object have
2052 // less consistent shapes.
2053 MOZ_CRASH("GuardClassKind not supported");
2058 writer
.guardShapeForClass(objId
, obj
->shape());
2060 writer
.guardClass(objId
, kind
);
2064 static void AssertArgumentsCustomDataProp(ArgumentsObject
* obj
,
2067 // The property must still be a custom data property if it has been resolved.
2068 // If this assertion fails, we're probably missing a call to mark this
2069 // property overridden.
2070 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(key
);
2071 MOZ_ASSERT_IF(prop
, prop
->isCustomDataProperty());
2075 AttachDecision
GetPropIRGenerator::tryAttachObjectLength(HandleObject obj
,
2078 if (!id
.isAtom(cx_
->names().length
)) {
2079 return AttachDecision::NoAction
;
2082 if (obj
->is
<ArrayObject
>()) {
2083 if (obj
->as
<ArrayObject
>().length() > INT32_MAX
) {
2084 return AttachDecision::NoAction
;
2087 maybeEmitIdGuard(id
);
2088 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
2089 writer
.loadInt32ArrayLengthResult(objId
);
2090 writer
.returnFromIC();
2092 trackAttached("GetProp.ArrayLength");
2093 return AttachDecision::Attach
;
2096 if (obj
->is
<ArgumentsObject
>() &&
2097 !obj
->as
<ArgumentsObject
>().hasOverriddenLength()) {
2098 AssertArgumentsCustomDataProp(&obj
->as
<ArgumentsObject
>(), id
);
2099 maybeEmitIdGuard(id
);
2100 if (obj
->is
<MappedArgumentsObject
>()) {
2101 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2103 MOZ_ASSERT(obj
->is
<UnmappedArgumentsObject
>());
2104 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2106 writer
.loadArgumentsObjectLengthResult(objId
);
2107 writer
.returnFromIC();
2109 trackAttached("GetProp.ArgumentsObjectLength");
2110 return AttachDecision::Attach
;
2113 return AttachDecision::NoAction
;
2116 AttachDecision
GetPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
2119 if (!obj
->is
<TypedArrayObject
>()) {
2120 return AttachDecision::NoAction
;
2123 if (mode_
!= ICState::Mode::Specialized
) {
2124 return AttachDecision::NoAction
;
2127 // Receiver should be the object.
2129 return AttachDecision::NoAction
;
2132 bool isLength
= id
.isAtom(cx_
->names().length
);
2133 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2134 if (!isLength
&& !isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2135 return AttachDecision::NoAction
;
2138 NativeObject
* holder
= nullptr;
2139 Maybe
<PropertyInfo
> prop
;
2140 NativeGetPropKind kind
=
2141 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2142 if (kind
!= NativeGetPropKind::NativeGetter
) {
2143 return AttachDecision::NoAction
;
2146 JSFunction
& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2148 if (!TypedArrayObject::isOriginalLengthGetter(fun
.native())) {
2149 return AttachDecision::NoAction
;
2151 } else if (isByteOffset
) {
2152 if (!TypedArrayObject::isOriginalByteOffsetGetter(fun
.native())) {
2153 return AttachDecision::NoAction
;
2156 if (!TypedArrayObject::isOriginalByteLengthGetter(fun
.native())) {
2157 return AttachDecision::NoAction
;
2161 auto* tarr
= &obj
->as
<TypedArrayObject
>();
2163 maybeEmitIdGuard(id
);
2164 // Emit all the normal guards for calling this native, but specialize
2165 // callNativeGetterResult.
2166 EmitCallGetterResultGuards(writer
, tarr
, holder
, id
, *prop
, objId
, mode_
);
2168 size_t length
= tarr
->length().valueOr(0);
2169 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
2170 if (length
<= INT32_MAX
) {
2171 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2173 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2176 if (length
<= INT32_MAX
) {
2177 writer
.resizableTypedArrayLengthInt32Result(objId
);
2179 writer
.resizableTypedArrayLengthDoubleResult(objId
);
2182 trackAttached("GetProp.TypedArrayLength");
2183 } else if (isByteOffset
) {
2184 // byteOffset doesn't need to use different code paths for fixed-length and
2185 // resizable TypedArrays.
2186 size_t byteOffset
= tarr
->byteOffset().valueOr(0);
2187 if (byteOffset
<= INT32_MAX
) {
2188 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2190 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2192 trackAttached("GetProp.TypedArrayByteOffset");
2194 size_t byteLength
= tarr
->byteLength().valueOr(0);
2195 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
2196 if (byteLength
<= INT32_MAX
) {
2197 writer
.typedArrayByteLengthInt32Result(objId
);
2199 writer
.typedArrayByteLengthDoubleResult(objId
);
2202 if (byteLength
<= INT32_MAX
) {
2203 writer
.resizableTypedArrayByteLengthInt32Result(objId
);
2205 writer
.resizableTypedArrayByteLengthDoubleResult(objId
);
2208 trackAttached("GetProp.TypedArrayByteLength");
2210 writer
.returnFromIC();
2212 return AttachDecision::Attach
;
2215 AttachDecision
GetPropIRGenerator::tryAttachDataView(HandleObject obj
,
2218 if (!obj
->is
<DataViewObject
>()) {
2219 return AttachDecision::NoAction
;
2221 auto* dv
= &obj
->as
<DataViewObject
>();
2223 if (mode_
!= ICState::Mode::Specialized
) {
2224 return AttachDecision::NoAction
;
2227 // Receiver should be the object.
2229 return AttachDecision::NoAction
;
2232 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2233 if (!isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2234 return AttachDecision::NoAction
;
2237 // byteOffset and byteLength both throw when the ArrayBuffer is detached.
2238 if (dv
->hasDetachedBuffer()) {
2239 return AttachDecision::NoAction
;
2242 // byteOffset and byteLength both throw when the ArrayBuffer is out-of-bounds.
2243 if (dv
->is
<ResizableDataViewObject
>() &&
2244 dv
->as
<ResizableDataViewObject
>().isOutOfBounds()) {
2245 return AttachDecision::NoAction
;
2248 NativeObject
* holder
= nullptr;
2249 Maybe
<PropertyInfo
> prop
;
2250 NativeGetPropKind kind
=
2251 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2252 if (kind
!= NativeGetPropKind::NativeGetter
) {
2253 return AttachDecision::NoAction
;
2256 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2258 if (!DataViewObject::isOriginalByteOffsetGetter(fun
.native())) {
2259 return AttachDecision::NoAction
;
2262 if (!DataViewObject::isOriginalByteLengthGetter(fun
.native())) {
2263 return AttachDecision::NoAction
;
2267 maybeEmitIdGuard(id
);
2268 // Emit all the normal guards for calling this native, but specialize
2269 // callNativeGetterResult.
2270 EmitCallGetterResultGuards(writer
, dv
, holder
, id
, *prop
, objId
, mode_
);
2271 writer
.guardHasAttachedArrayBuffer(objId
);
2272 if (dv
->is
<ResizableDataViewObject
>()) {
2273 writer
.guardResizableArrayBufferViewInBounds(objId
);
2276 // byteOffset doesn't need to use different code paths for fixed-length and
2277 // resizable DataViews.
2278 size_t byteOffset
= dv
->byteOffset().valueOr(0);
2279 if (byteOffset
<= INT32_MAX
) {
2280 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2282 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2284 trackAttached("GetProp.DataViewByteOffset");
2286 size_t byteLength
= dv
->byteLength().valueOr(0);
2287 if (dv
->is
<FixedLengthDataViewObject
>()) {
2288 if (byteLength
<= INT32_MAX
) {
2289 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2291 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2294 if (byteLength
<= INT32_MAX
) {
2295 writer
.resizableDataViewByteLengthInt32Result(objId
);
2297 writer
.resizableDataViewByteLengthDoubleResult(objId
);
2300 trackAttached("GetProp.DataViewByteLength");
2302 writer
.returnFromIC();
2304 return AttachDecision::Attach
;
2307 AttachDecision
GetPropIRGenerator::tryAttachArrayBufferMaybeShared(
2308 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2309 if (!obj
->is
<ArrayBufferObjectMaybeShared
>()) {
2310 return AttachDecision::NoAction
;
2312 auto* buf
= &obj
->as
<ArrayBufferObjectMaybeShared
>();
2314 if (mode_
!= ICState::Mode::Specialized
) {
2315 return AttachDecision::NoAction
;
2318 // Receiver should be the object.
2320 return AttachDecision::NoAction
;
2323 if (!id
.isAtom(cx_
->names().byteLength
)) {
2324 return AttachDecision::NoAction
;
2327 NativeObject
* holder
= nullptr;
2328 Maybe
<PropertyInfo
> prop
;
2329 NativeGetPropKind kind
=
2330 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2331 if (kind
!= NativeGetPropKind::NativeGetter
) {
2332 return AttachDecision::NoAction
;
2335 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2336 if (buf
->is
<ArrayBufferObject
>()) {
2337 if (!ArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2338 return AttachDecision::NoAction
;
2341 if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2342 return AttachDecision::NoAction
;
2346 maybeEmitIdGuard(id
);
2347 // Emit all the normal guards for calling this native, but specialize
2348 // callNativeGetterResult.
2349 EmitCallGetterResultGuards(writer
, buf
, holder
, id
, *prop
, objId
, mode_
);
2350 if (!buf
->is
<GrowableSharedArrayBufferObject
>()) {
2351 if (buf
->byteLength() <= INT32_MAX
) {
2352 writer
.loadArrayBufferByteLengthInt32Result(objId
);
2354 writer
.loadArrayBufferByteLengthDoubleResult(objId
);
2357 if (buf
->byteLength() <= INT32_MAX
) {
2358 writer
.growableSharedArrayBufferByteLengthInt32Result(objId
);
2360 writer
.growableSharedArrayBufferByteLengthDoubleResult(objId
);
2363 writer
.returnFromIC();
2365 trackAttached("GetProp.ArrayBufferMaybeSharedByteLength");
2366 return AttachDecision::Attach
;
2369 AttachDecision
GetPropIRGenerator::tryAttachRegExp(HandleObject obj
,
2372 if (!obj
->is
<RegExpObject
>()) {
2373 return AttachDecision::NoAction
;
2375 auto* regExp
= &obj
->as
<RegExpObject
>();
2377 if (mode_
!= ICState::Mode::Specialized
) {
2378 return AttachDecision::NoAction
;
2381 // Receiver should be the object.
2383 return AttachDecision::NoAction
;
2386 NativeObject
* holder
= nullptr;
2387 Maybe
<PropertyInfo
> prop
;
2388 NativeGetPropKind kind
=
2389 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2390 if (kind
!= NativeGetPropKind::NativeGetter
) {
2391 return AttachDecision::NoAction
;
2394 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2395 JS::RegExpFlags flags
= JS::RegExpFlag::NoFlags
;
2396 if (!RegExpObject::isOriginalFlagGetter(fun
.native(), &flags
)) {
2397 return AttachDecision::NoAction
;
2400 maybeEmitIdGuard(id
);
2401 // Emit all the normal guards for calling this native, but specialize
2402 // callNativeGetterResult.
2403 EmitCallGetterResultGuards(writer
, regExp
, holder
, id
, *prop
, objId
, mode_
);
2405 writer
.regExpFlagResult(objId
, flags
.value());
2406 writer
.returnFromIC();
2408 trackAttached("GetProp.RegExpFlag");
2409 return AttachDecision::Attach
;
2412 AttachDecision
GetPropIRGenerator::tryAttachMap(HandleObject obj
,
2415 if (!obj
->is
<MapObject
>()) {
2416 return AttachDecision::NoAction
;
2418 auto* mapObj
= &obj
->as
<MapObject
>();
2420 if (mode_
!= ICState::Mode::Specialized
) {
2421 return AttachDecision::NoAction
;
2424 // Receiver should be the object.
2426 return AttachDecision::NoAction
;
2429 if (!id
.isAtom(cx_
->names().size
)) {
2430 return AttachDecision::NoAction
;
2433 NativeObject
* holder
= nullptr;
2434 Maybe
<PropertyInfo
> prop
;
2435 NativeGetPropKind kind
=
2436 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2437 if (kind
!= NativeGetPropKind::NativeGetter
) {
2438 return AttachDecision::NoAction
;
2441 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2442 if (!MapObject::isOriginalSizeGetter(fun
.native())) {
2443 return AttachDecision::NoAction
;
2446 maybeEmitIdGuard(id
);
2448 // Emit all the normal guards for calling this native, but specialize
2449 // callNativeGetterResult.
2450 EmitCallGetterResultGuards(writer
, mapObj
, holder
, id
, *prop
, objId
, mode_
);
2452 writer
.mapSizeResult(objId
);
2453 writer
.returnFromIC();
2455 trackAttached("GetProp.MapSize");
2456 return AttachDecision::Attach
;
2459 AttachDecision
GetPropIRGenerator::tryAttachSet(HandleObject obj
,
2462 if (!obj
->is
<SetObject
>()) {
2463 return AttachDecision::NoAction
;
2465 auto* setObj
= &obj
->as
<SetObject
>();
2467 if (mode_
!= ICState::Mode::Specialized
) {
2468 return AttachDecision::NoAction
;
2471 // Receiver should be the object.
2473 return AttachDecision::NoAction
;
2476 if (!id
.isAtom(cx_
->names().size
)) {
2477 return AttachDecision::NoAction
;
2480 NativeObject
* holder
= nullptr;
2481 Maybe
<PropertyInfo
> prop
;
2482 NativeGetPropKind kind
=
2483 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2484 if (kind
!= NativeGetPropKind::NativeGetter
) {
2485 return AttachDecision::NoAction
;
2488 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2489 if (!SetObject::isOriginalSizeGetter(fun
.native())) {
2490 return AttachDecision::NoAction
;
2493 maybeEmitIdGuard(id
);
2495 // Emit all the normal guards for calling this native, but specialize
2496 // callNativeGetterResult.
2497 EmitCallGetterResultGuards(writer
, setObj
, holder
, id
, *prop
, objId
, mode_
);
2499 writer
.setSizeResult(objId
);
2500 writer
.returnFromIC();
2502 trackAttached("GetProp.SetSize");
2503 return AttachDecision::Attach
;
2506 AttachDecision
GetPropIRGenerator::tryAttachFunction(HandleObject obj
,
2509 // Function properties are lazily resolved so they might not be defined yet.
2510 // And we might end up in a situation where we always have a fresh function
2511 // object during the IC generation.
2512 if (!obj
->is
<JSFunction
>()) {
2513 return AttachDecision::NoAction
;
2516 bool isLength
= id
.isAtom(cx_
->names().length
);
2517 if (!isLength
&& !id
.isAtom(cx_
->names().name
)) {
2518 return AttachDecision::NoAction
;
2521 NativeObject
* holder
= nullptr;
2522 PropertyResult prop
;
2523 // If this property exists already, don't attach the stub.
2524 if (LookupPropertyPure(cx_
, obj
, id
, &holder
, &prop
)) {
2525 return AttachDecision::NoAction
;
2528 JSFunction
* fun
= &obj
->as
<JSFunction
>();
2531 // length was probably deleted from the function.
2532 if (fun
->hasResolvedLength()) {
2533 return AttachDecision::NoAction
;
2536 // Lazy functions don't store the length.
2537 if (!fun
->hasBytecode()) {
2538 return AttachDecision::NoAction
;
2541 // name was probably deleted from the function.
2542 if (fun
->hasResolvedName()) {
2543 return AttachDecision::NoAction
;
2547 maybeEmitIdGuard(id
);
2548 writer
.guardClass(objId
, GuardClassKind::JSFunction
);
2550 writer
.loadFunctionLengthResult(objId
);
2551 writer
.returnFromIC();
2552 trackAttached("GetProp.FunctionLength");
2554 writer
.loadFunctionNameResult(objId
);
2555 writer
.returnFromIC();
2556 trackAttached("GetProp.FunctionName");
2558 return AttachDecision::Attach
;
2561 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectIterator(
2562 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2563 if (!obj
->is
<ArgumentsObject
>()) {
2564 return AttachDecision::NoAction
;
2567 if (!id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
2568 return AttachDecision::NoAction
;
2571 Handle
<ArgumentsObject
*> args
= obj
.as
<ArgumentsObject
>();
2572 if (args
->hasOverriddenIterator()) {
2573 return AttachDecision::NoAction
;
2576 AssertArgumentsCustomDataProp(args
, id
);
2578 RootedValue
iterator(cx_
);
2579 if (!ArgumentsObject::getArgumentsIterator(cx_
, &iterator
)) {
2580 cx_
->recoverFromOutOfMemory();
2581 return AttachDecision::NoAction
;
2583 MOZ_ASSERT(iterator
.isObject());
2585 maybeEmitIdGuard(id
);
2586 if (args
->is
<MappedArgumentsObject
>()) {
2587 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2589 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2590 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2592 uint32_t flags
= ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
;
2593 writer
.guardArgumentsObjectFlags(objId
, flags
);
2595 ObjOperandId iterId
= writer
.loadObject(&iterator
.toObject());
2596 writer
.loadObjectResult(iterId
);
2597 writer
.returnFromIC();
2599 trackAttached("GetProp.ArgumentsObjectIterator");
2600 return AttachDecision::Attach
;
2603 AttachDecision
GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj
,
2606 if (!obj
->is
<ModuleNamespaceObject
>()) {
2607 return AttachDecision::NoAction
;
2610 auto* ns
= &obj
->as
<ModuleNamespaceObject
>();
2611 ModuleEnvironmentObject
* env
= nullptr;
2612 Maybe
<PropertyInfo
> prop
;
2613 if (!ns
->bindings().lookup(id
, &env
, &prop
)) {
2614 return AttachDecision::NoAction
;
2617 // Don't emit a stub until the target binding has been initialized.
2618 if (env
->getSlot(prop
->slot()).isMagic(JS_UNINITIALIZED_LEXICAL
)) {
2619 return AttachDecision::NoAction
;
2622 // Check for the specific namespace object.
2623 maybeEmitIdGuard(id
);
2624 writer
.guardSpecificObject(objId
, ns
);
2626 ObjOperandId envId
= writer
.loadObject(env
);
2627 EmitLoadSlotResult(writer
, envId
, env
, *prop
);
2628 writer
.returnFromIC();
2630 trackAttached("GetProp.ModuleNamespace");
2631 return AttachDecision::Attach
;
2634 AttachDecision
GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId
,
2636 MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
2638 JSProtoKey protoKey
;
2639 switch (val_
.type()) {
2640 case ValueType::String
:
2641 if (id
.isAtom(cx_
->names().length
)) {
2642 // String length is special-cased, see js::GetProperty.
2643 return AttachDecision::NoAction
;
2645 protoKey
= JSProto_String
;
2647 case ValueType::Int32
:
2648 case ValueType::Double
:
2649 protoKey
= JSProto_Number
;
2651 case ValueType::Boolean
:
2652 protoKey
= JSProto_Boolean
;
2654 case ValueType::Symbol
:
2655 protoKey
= JSProto_Symbol
;
2657 case ValueType::BigInt
:
2658 protoKey
= JSProto_BigInt
;
2660 case ValueType::Null
:
2661 case ValueType::Undefined
:
2662 case ValueType::Magic
:
2663 return AttachDecision::NoAction
;
2664 #ifdef ENABLE_RECORD_TUPLE
2665 case ValueType::ExtendedPrimitive
:
2667 case ValueType::Object
:
2668 case ValueType::PrivateGCThing
:
2669 MOZ_CRASH("unexpected type");
2672 JSObject
* proto
= GlobalObject::getOrCreatePrototype(cx_
, protoKey
);
2674 cx_
->recoverFromOutOfMemory();
2675 return AttachDecision::NoAction
;
2678 NativeObject
* holder
= nullptr;
2679 Maybe
<PropertyInfo
> prop
;
2680 NativeGetPropKind kind
=
2681 CanAttachNativeGetProp(cx_
, proto
, id
, &holder
, &prop
, pc_
);
2683 case NativeGetPropKind::None
:
2684 return AttachDecision::NoAction
;
2685 case NativeGetPropKind::Missing
:
2686 case NativeGetPropKind::Slot
: {
2687 auto* nproto
= &proto
->as
<NativeObject
>();
2689 if (val_
.isNumber()) {
2690 writer
.guardIsNumber(valId
);
2692 writer
.guardNonDoubleType(valId
, val_
.type());
2694 maybeEmitIdGuard(id
);
2696 ObjOperandId protoId
= writer
.loadObject(nproto
);
2697 if (kind
== NativeGetPropKind::Slot
) {
2698 EmitReadSlotResult(writer
, nproto
, holder
, *prop
, protoId
);
2699 writer
.returnFromIC();
2700 trackAttached("GetProp.PrimitiveSlot");
2702 EmitMissingPropResult(writer
, nproto
, protoId
);
2703 writer
.returnFromIC();
2704 trackAttached("GetProp.PrimitiveMissing");
2706 return AttachDecision::Attach
;
2708 case NativeGetPropKind::ScriptedGetter
:
2709 case NativeGetPropKind::NativeGetter
: {
2710 auto* nproto
= &proto
->as
<NativeObject
>();
2712 if (val_
.isNumber()) {
2713 writer
.guardIsNumber(valId
);
2715 writer
.guardNonDoubleType(valId
, val_
.type());
2717 maybeEmitIdGuard(id
);
2719 ObjOperandId protoId
= writer
.loadObject(nproto
);
2720 EmitCallGetterResult(cx_
, writer
, kind
, nproto
, holder
, id
, *prop
,
2721 protoId
, valId
, mode_
);
2723 trackAttached("GetProp.PrimitiveGetter");
2724 return AttachDecision::Attach
;
2728 MOZ_CRASH("Bad NativeGetPropKind");
2731 AttachDecision
GetPropIRGenerator::tryAttachStringLength(ValOperandId valId
,
2733 if (!val_
.isString() || !id
.isAtom(cx_
->names().length
)) {
2734 return AttachDecision::NoAction
;
2737 StringOperandId strId
= writer
.guardToString(valId
);
2738 maybeEmitIdGuard(id
);
2739 writer
.loadStringLengthResult(strId
);
2740 writer
.returnFromIC();
2742 trackAttached("GetProp.StringLength");
2743 return AttachDecision::Attach
;
2746 enum class AttachStringChar
{ No
, Yes
, Linearize
, OutOfBounds
};
2748 static AttachStringChar
CanAttachStringChar(const Value
& val
,
2751 if (!val
.isString() || !idVal
.isInt32()) {
2752 return AttachStringChar::No
;
2755 JSString
* str
= val
.toString();
2756 int32_t index
= idVal
.toInt32();
2758 if (index
< 0 && kind
== StringChar::At
) {
2759 static_assert(JSString::MAX_LENGTH
<= INT32_MAX
,
2760 "string length fits in int32");
2761 index
+= int32_t(str
->length());
2764 if (index
< 0 || size_t(index
) >= str
->length()) {
2765 return AttachStringChar::OutOfBounds
;
2768 // This follows JSString::getChar and MacroAssembler::loadStringChar.
2769 if (str
->isRope()) {
2770 JSRope
* rope
= &str
->asRope();
2771 if (size_t(index
) < rope
->leftChild()->length()) {
2772 str
= rope
->leftChild();
2774 // MacroAssembler::loadStringChar doesn't support surrogate pairs which
2775 // are split between the left and right child of a rope.
2776 if (kind
== StringChar::CodePointAt
&&
2777 size_t(index
) + 1 == str
->length() && str
->isLinear()) {
2778 // Linearize the string when the last character of the left child is a
2779 // a lead surrogate.
2780 char16_t ch
= str
->asLinear().latin1OrTwoByteChar(index
);
2781 if (unicode::IsLeadSurrogate(ch
)) {
2782 return AttachStringChar::Linearize
;
2786 str
= rope
->rightChild();
2790 if (!str
->isLinear()) {
2791 return AttachStringChar::Linearize
;
2794 return AttachStringChar::Yes
;
2797 AttachDecision
GetPropIRGenerator::tryAttachStringChar(ValOperandId valId
,
2798 ValOperandId indexId
) {
2799 MOZ_ASSERT(idVal_
.isInt32());
2801 auto attach
= CanAttachStringChar(val_
, idVal_
, StringChar::CharAt
);
2802 if (attach
== AttachStringChar::No
) {
2803 return AttachDecision::NoAction
;
2806 // Can't attach for out-of-bounds access without guarding that indexed
2807 // properties aren't present along the prototype chain of |String.prototype|.
2808 if (attach
== AttachStringChar::OutOfBounds
) {
2809 return AttachDecision::NoAction
;
2812 StringOperandId strId
= writer
.guardToString(valId
);
2813 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
2814 if (attach
== AttachStringChar::Linearize
) {
2815 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
2817 writer
.loadStringCharResult(strId
, int32IndexId
, /* handleOOB = */ false);
2818 writer
.returnFromIC();
2820 trackAttached("GetProp.StringChar");
2821 return AttachDecision::Attach
;
2824 static bool ClassCanHaveExtraProperties(const JSClass
* clasp
) {
2825 return clasp
->getResolve() || clasp
->getOpsLookupProperty() ||
2826 clasp
->getOpsGetProperty() || IsTypedArrayClass(clasp
);
2829 enum class OwnProperty
: bool { No
, Yes
};
2830 enum class AllowIndexedReceiver
: bool { No
, Yes
};
2831 enum class AllowExtraReceiverProperties
: bool { No
, Yes
};
2833 static bool CanAttachDenseElementHole(
2834 NativeObject
* obj
, OwnProperty ownProp
,
2835 AllowIndexedReceiver allowIndexedReceiver
= AllowIndexedReceiver::No
,
2836 AllowExtraReceiverProperties allowExtraReceiverProperties
=
2837 AllowExtraReceiverProperties::No
) {
2838 // Make sure the objects on the prototype don't have any indexed properties
2839 // or that such properties can't appear without a shape change.
2840 // Otherwise returning undefined for holes would obviously be incorrect,
2841 // because we would have to lookup a property on the prototype instead.
2843 // The first two checks are also relevant to the receiver object.
2844 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
2847 allowIndexedReceiver
= AllowIndexedReceiver::No
;
2849 if (allowExtraReceiverProperties
== AllowExtraReceiverProperties::No
&&
2850 ClassCanHaveExtraProperties(obj
->getClass())) {
2853 allowExtraReceiverProperties
= AllowExtraReceiverProperties::No
;
2855 // Don't need to check prototype for OwnProperty checks
2856 if (ownProp
== OwnProperty::Yes
) {
2860 JSObject
* proto
= obj
->staticPrototype();
2865 if (!proto
->is
<NativeObject
>()) {
2869 // Make sure objects on the prototype don't have dense elements.
2870 if (proto
->as
<NativeObject
>().getDenseInitializedLength() != 0) {
2874 obj
= &proto
->as
<NativeObject
>();
2880 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArg(
2881 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2882 Int32OperandId indexId
) {
2883 if (!obj
->is
<ArgumentsObject
>()) {
2884 return AttachDecision::NoAction
;
2886 auto* args
= &obj
->as
<ArgumentsObject
>();
2888 // No elements must have been overridden or deleted.
2889 if (args
->hasOverriddenElement()) {
2890 return AttachDecision::NoAction
;
2894 if (index
>= args
->initialLength()) {
2895 return AttachDecision::NoAction
;
2898 AssertArgumentsCustomDataProp(args
, PropertyKey::Int(index
));
2900 // And finally also check that the argument isn't forwarded.
2901 if (args
->argIsForwarded(index
)) {
2902 return AttachDecision::NoAction
;
2905 if (args
->is
<MappedArgumentsObject
>()) {
2906 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2908 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2909 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2912 writer
.loadArgumentsObjectArgResult(objId
, indexId
);
2913 writer
.returnFromIC();
2915 trackAttached("GetProp.ArgumentsObjectArg");
2916 return AttachDecision::Attach
;
2919 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
2920 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2921 Int32OperandId indexId
) {
2922 if (!obj
->is
<ArgumentsObject
>()) {
2923 return AttachDecision::NoAction
;
2925 auto* args
= &obj
->as
<ArgumentsObject
>();
2927 // No elements must have been overridden or deleted.
2928 if (args
->hasOverriddenElement()) {
2929 return AttachDecision::NoAction
;
2932 // And also check that the argument isn't forwarded.
2933 if (index
< args
->initialLength() && args
->argIsForwarded(index
)) {
2934 return AttachDecision::NoAction
;
2937 if (!CanAttachDenseElementHole(args
, OwnProperty::No
,
2938 AllowIndexedReceiver::Yes
,
2939 AllowExtraReceiverProperties::Yes
)) {
2940 return AttachDecision::NoAction
;
2943 // We don't need to guard on the shape, because we check if any element is
2944 // overridden. Elements are marked as overridden iff any element is defined,
2945 // irrespective of whether the element is in-bounds or out-of-bounds. So when
2946 // that flag isn't set, we can guarantee that the arguments object doesn't
2947 // have any additional own elements.
2949 if (args
->is
<MappedArgumentsObject
>()) {
2950 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2952 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2953 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2956 GeneratePrototypeHoleGuards(writer
, args
, objId
,
2957 /* alwaysGuardFirstProto = */ true);
2959 writer
.loadArgumentsObjectArgHoleResult(objId
, indexId
);
2960 writer
.returnFromIC();
2962 trackAttached("GetProp.ArgumentsObjectArgHole");
2963 return AttachDecision::Attach
;
2966 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectCallee(
2967 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2968 // Only mapped arguments objects have a `callee` property.
2969 if (!obj
->is
<MappedArgumentsObject
>()) {
2970 return AttachDecision::NoAction
;
2973 if (!id
.isAtom(cx_
->names().callee
)) {
2974 return AttachDecision::NoAction
;
2977 // The callee must not have been overridden or deleted.
2978 MappedArgumentsObject
* args
= &obj
->as
<MappedArgumentsObject
>();
2979 if (args
->hasOverriddenCallee()) {
2980 return AttachDecision::NoAction
;
2983 AssertArgumentsCustomDataProp(args
, id
);
2985 maybeEmitIdGuard(id
);
2986 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2988 uint32_t flags
= ArgumentsObject::CALLEE_OVERRIDDEN_BIT
;
2989 writer
.guardArgumentsObjectFlags(objId
, flags
);
2991 writer
.loadFixedSlotResult(objId
,
2992 MappedArgumentsObject::getCalleeSlotOffset());
2993 writer
.returnFromIC();
2995 trackAttached("GetProp.ArgumentsObjectCallee");
2996 return AttachDecision::Attach
;
2999 AttachDecision
GetPropIRGenerator::tryAttachDenseElement(
3000 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3001 Int32OperandId indexId
) {
3002 if (!obj
->is
<NativeObject
>()) {
3003 return AttachDecision::NoAction
;
3006 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3007 if (!nobj
->containsDenseElement(index
)) {
3008 return AttachDecision::NoAction
;
3011 if (mode_
== ICState::Mode::Megamorphic
) {
3012 writer
.guardIsNativeObject(objId
);
3014 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3016 writer
.loadDenseElementResult(objId
, indexId
);
3017 writer
.returnFromIC();
3019 trackAttached("GetProp.DenseElement");
3020 return AttachDecision::Attach
;
3023 AttachDecision
GetPropIRGenerator::tryAttachDenseElementHole(
3024 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3025 Int32OperandId indexId
) {
3026 if (!obj
->is
<NativeObject
>()) {
3027 return AttachDecision::NoAction
;
3030 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3031 if (nobj
->containsDenseElement(index
)) {
3032 return AttachDecision::NoAction
;
3034 if (!CanAttachDenseElementHole(nobj
, OwnProperty::No
)) {
3035 return AttachDecision::NoAction
;
3038 // Guard on the shape, to prevent non-dense elements from appearing.
3039 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3040 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3041 /* alwaysGuardFirstProto = */ false);
3042 writer
.loadDenseElementHoleResult(objId
, indexId
);
3043 writer
.returnFromIC();
3045 trackAttached("GetProp.DenseElementHole");
3046 return AttachDecision::Attach
;
3049 AttachDecision
GetPropIRGenerator::tryAttachSparseElement(
3050 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3051 Int32OperandId indexId
) {
3052 if (!obj
->is
<NativeObject
>()) {
3053 return AttachDecision::NoAction
;
3055 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3057 // Stub doesn't handle negative indices.
3058 if (index
> INT32_MAX
) {
3059 return AttachDecision::NoAction
;
3062 // The object must have sparse elements.
3063 if (!nobj
->isIndexed()) {
3064 return AttachDecision::NoAction
;
3067 // The index must not be for a dense element.
3068 if (nobj
->containsDenseElement(index
)) {
3069 return AttachDecision::NoAction
;
3072 // Only handle ArrayObject and PlainObject in this stub.
3073 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
3074 return AttachDecision::NoAction
;
3077 // GetSparseElementHelper assumes that the target and the receiver
3080 return AttachDecision::NoAction
;
3083 // Here, we ensure that the prototype chain does not define any sparse
3084 // indexed properties on the shape lineage. This allows us to guard on
3085 // the shapes up the prototype chain to ensure that no indexed properties
3086 // exist outside of the dense elements.
3088 // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
3089 // as well as ensure that no prototypes contain dense elements, allowing
3090 // us to perform a pure shape-search for out-of-bounds integer-indexed
3091 // properties on the receiver object.
3092 if (PrototypeMayHaveIndexedProperties(nobj
)) {
3093 return AttachDecision::NoAction
;
3096 // Ensure that obj is an ArrayObject or PlainObject.
3097 if (nobj
->is
<ArrayObject
>()) {
3098 writer
.guardClass(objId
, GuardClassKind::Array
);
3100 MOZ_ASSERT(nobj
->is
<PlainObject
>());
3101 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
3104 // The helper we are going to call only applies to non-dense elements.
3105 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3107 // Ensures we are able to efficiently able to map to an integral jsid.
3108 writer
.guardInt32IsNonNegative(indexId
);
3110 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
3111 // The helper function also ensures that the index does not appear within the
3112 // dense element set of the prototypes.
3113 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3114 /* alwaysGuardFirstProto = */ true);
3116 // At this point, we are guaranteed that the indexed property will not
3117 // be found on one of the prototypes. We are assured that we only have
3118 // to check that the receiving object has the property.
3120 writer
.callGetSparseElementResult(objId
, indexId
);
3121 writer
.returnFromIC();
3123 trackAttached("GetProp.SparseElement");
3124 return AttachDecision::Attach
;
3127 // For Uint32Array we let the stub return an Int32 if we have not seen a
3128 // double, to allow better codegen in Warp while avoiding bailout loops.
3129 static bool ForceDoubleForUint32Array(TypedArrayObject
* tarr
, uint64_t index
) {
3130 MOZ_ASSERT(index
< tarr
->length().valueOr(0));
3132 if (tarr
->type() != Scalar::Type::Uint32
) {
3133 // Return value is only relevant for Uint32Array.
3138 MOZ_ALWAYS_TRUE(tarr
->getElementPure(index
, &res
));
3139 MOZ_ASSERT(res
.isNumber());
3140 return res
.isDouble();
3143 static ArrayBufferViewKind
ToArrayBufferViewKind(const TypedArrayObject
* obj
) {
3144 if (obj
->is
<FixedLengthTypedArrayObject
>()) {
3145 return ArrayBufferViewKind::FixedLength
;
3148 MOZ_ASSERT(obj
->is
<ResizableTypedArrayObject
>());
3149 return ArrayBufferViewKind::Resizable
;
3152 static ArrayBufferViewKind
ToArrayBufferViewKind(const DataViewObject
* obj
) {
3153 if (obj
->is
<FixedLengthDataViewObject
>()) {
3154 return ArrayBufferViewKind::FixedLength
;
3157 MOZ_ASSERT(obj
->is
<ResizableDataViewObject
>());
3158 return ArrayBufferViewKind::Resizable
;
3161 AttachDecision
GetPropIRGenerator::tryAttachTypedArrayElement(
3162 HandleObject obj
, ObjOperandId objId
) {
3163 if (!obj
->is
<TypedArrayObject
>()) {
3164 return AttachDecision::NoAction
;
3167 if (!idVal_
.isNumber()) {
3168 return AttachDecision::NoAction
;
3171 auto* tarr
= &obj
->as
<TypedArrayObject
>();
3173 bool handleOOB
= false;
3175 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
3176 uint64_t(indexInt64
) >= tarr
->length().valueOr(0)) {
3180 // If the number is not representable as an integer the result will be
3181 // |undefined| so we leave |forceDoubleForUint32| as false.
3182 bool forceDoubleForUint32
= false;
3184 uint64_t index
= uint64_t(indexInt64
);
3185 forceDoubleForUint32
= ForceDoubleForUint32Array(tarr
, index
);
3188 writer
.guardShapeForClass(objId
, tarr
->shape());
3190 ValOperandId keyId
= getElemKeyValueId();
3191 IntPtrOperandId intPtrIndexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
3193 auto viewKind
= ToArrayBufferViewKind(tarr
);
3194 writer
.loadTypedArrayElementResult(objId
, intPtrIndexId
, tarr
->type(),
3195 handleOOB
, forceDoubleForUint32
, viewKind
);
3196 writer
.returnFromIC();
3198 trackAttached("GetProp.TypedElement");
3199 return AttachDecision::Attach
;
3202 AttachDecision
GetPropIRGenerator::tryAttachGenericElement(
3203 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3204 Int32OperandId indexId
, ValOperandId receiverId
) {
3205 if (!obj
->is
<NativeObject
>()) {
3206 return AttachDecision::NoAction
;
3209 #ifdef JS_CODEGEN_X86
3211 // There aren't enough registers available on x86.
3212 return AttachDecision::NoAction
;
3216 // To allow other types to attach in the non-megamorphic case we test the
3217 // specific matching native receiver; however, once megamorphic we can attach
3219 if (mode_
== ICState::Mode::Megamorphic
) {
3220 writer
.guardIsNativeObject(objId
);
3222 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3223 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3225 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3227 writer
.callNativeGetElementSuperResult(objId
, indexId
, receiverId
);
3229 writer
.callNativeGetElementResult(objId
, indexId
);
3231 writer
.returnFromIC();
3233 trackAttached(mode_
== ICState::Mode::Megamorphic
3234 ? "GenericElementMegamorphic"
3235 : "GenericElement");
3236 return AttachDecision::Attach
;
3239 AttachDecision
GetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3240 ObjOperandId objId
) {
3241 if (!obj
->is
<ProxyObject
>()) {
3242 return AttachDecision::NoAction
;
3245 // The proxy stubs don't currently support |super| access.
3247 return AttachDecision::NoAction
;
3251 auto proxy
= obj
.as
<ProxyObject
>();
3252 if (proxy
->handler()->isScripted()) {
3253 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, JS::VoidHandlePropertyKey
));
3257 writer
.guardIsProxy(objId
);
3259 // We are not guarding against DOM proxies here, because there is no other
3260 // specialized DOM IC we could attach.
3261 // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
3262 // but for GetElem we prefer to attach a stub that can handle any Value
3263 // so we don't attach a new stub for every id.
3264 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
3265 MOZ_ASSERT(!isSuper());
3266 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
3267 writer
.returnFromIC();
3269 trackAttached("GetProp.ProxyElement");
3270 return AttachDecision::Attach
;
3273 void GetPropIRGenerator::trackAttached(const char* name
) {
3274 stubName_
= name
? name
: "NotAttached";
3275 #ifdef JS_CACHEIR_SPEW
3276 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3277 sp
.valueProperty("base", val_
);
3278 sp
.valueProperty("property", idVal_
);
3283 void IRGenerator::emitIdGuard(ValOperandId valId
, const Value
& idVal
, jsid id
) {
3284 if (id
.isSymbol()) {
3285 MOZ_ASSERT(idVal
.toSymbol() == id
.toSymbol());
3286 SymbolOperandId symId
= writer
.guardToSymbol(valId
);
3287 writer
.guardSpecificSymbol(symId
, id
.toSymbol());
3289 MOZ_ASSERT(id
.isAtom());
3290 if (idVal
.isUndefined()) {
3291 MOZ_ASSERT(id
.isAtom(cx_
->names().undefined
));
3292 writer
.guardIsUndefined(valId
);
3293 } else if (idVal
.isNull()) {
3294 MOZ_ASSERT(id
.isAtom(cx_
->names().null
));
3295 writer
.guardIsNull(valId
);
3297 MOZ_ASSERT(idVal
.isString());
3298 StringOperandId strId
= writer
.guardToString(valId
);
3299 writer
.guardSpecificAtom(strId
, id
.toAtom());
3304 void GetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3305 if (cacheKind_
== CacheKind::GetProp
||
3306 cacheKind_
== CacheKind::GetPropSuper
) {
3307 // Constant PropertyName, no guards necessary.
3308 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3312 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
3313 cacheKind_
== CacheKind::GetElemSuper
);
3314 emitIdGuard(getElemKeyValueId(), idVal_
, id
);
3317 void SetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3318 if (cacheKind_
== CacheKind::SetProp
) {
3319 // Constant PropertyName, no guards necessary.
3320 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3324 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
3325 emitIdGuard(setElemKeyValueId(), idVal_
, id
);
3328 GetNameIRGenerator::GetNameIRGenerator(JSContext
* cx
, HandleScript script
,
3329 jsbytecode
* pc
, ICState state
,
3331 Handle
<PropertyName
*> name
)
3332 : IRGenerator(cx
, script
, pc
, CacheKind::GetName
, state
),
3336 AttachDecision
GetNameIRGenerator::tryAttachStub() {
3337 MOZ_ASSERT(cacheKind_
== CacheKind::GetName
);
3339 AutoAssertNoPendingException
aanpe(cx_
);
3341 ObjOperandId
envId(writer
.setInputOperandId(0));
3342 RootedId
id(cx_
, NameToId(name_
));
3344 TRY_ATTACH(tryAttachGlobalNameValue(envId
, id
));
3345 TRY_ATTACH(tryAttachGlobalNameGetter(envId
, id
));
3346 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3348 trackAttached(IRGenerator::NotAttached
);
3349 return AttachDecision::NoAction
;
3352 static bool CanAttachGlobalName(JSContext
* cx
,
3353 GlobalLexicalEnvironmentObject
* globalLexical
,
3354 PropertyKey id
, NativeObject
** holder
,
3355 Maybe
<PropertyInfo
>* prop
) {
3356 // The property must be found, and it must be found as a normal data property.
3357 NativeObject
* current
= globalLexical
;
3359 *prop
= current
->lookup(cx
, id
);
3360 if (prop
->isSome()) {
3364 if (current
== globalLexical
) {
3365 current
= &globalLexical
->global();
3367 // In the browser the global prototype chain should be immutable.
3368 if (!current
->staticPrototypeIsImmutable()) {
3372 JSObject
* proto
= current
->staticPrototype();
3373 if (!proto
|| !proto
->is
<NativeObject
>()) {
3377 current
= &proto
->as
<NativeObject
>();
3385 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId
,
3387 if (!IsGlobalOp(JSOp(*pc_
))) {
3388 return AttachDecision::NoAction
;
3390 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3392 auto* globalLexical
= &env_
->as
<GlobalLexicalEnvironmentObject
>();
3394 NativeObject
* holder
= nullptr;
3395 Maybe
<PropertyInfo
> prop
;
3396 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3397 return AttachDecision::NoAction
;
3400 // The property must be found, and it must be found as a normal data property.
3401 if (!prop
->isDataProperty()) {
3402 return AttachDecision::NoAction
;
3405 // This might still be an uninitialized lexical.
3406 if (holder
->getSlot(prop
->slot()).isMagic()) {
3407 return AttachDecision::NoAction
;
3410 if (holder
== globalLexical
) {
3411 // There is no need to guard on the shape. Lexical bindings are
3412 // non-configurable, and this stub cannot be shared across globals.
3413 size_t dynamicSlotOffset
=
3414 holder
->dynamicSlotIndex(prop
->slot()) * sizeof(Value
);
3415 writer
.loadDynamicSlotResult(objId
, dynamicSlotOffset
);
3416 } else if (holder
== &globalLexical
->global()) {
3417 MOZ_ASSERT(globalLexical
->global().isGenerationCountedGlobal());
3418 writer
.guardGlobalGeneration(
3419 globalLexical
->global().generationCount(),
3420 globalLexical
->global().addressOfGenerationCount());
3421 ObjOperandId holderId
= writer
.loadObject(holder
);
3423 writer
.assertPropertyLookup(holderId
, id
, prop
->slot());
3425 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3427 // Check the prototype chain from the global to the holder
3428 // prototype. Ignore the global lexical scope as it doesn't figure
3429 // into the prototype chain. We guard on the global lexical
3430 // scope's shape independently.
3431 if (!IsCacheableGetPropSlot(&globalLexical
->global(), holder
, *prop
)) {
3432 return AttachDecision::NoAction
;
3435 // Shape guard for global lexical.
3436 writer
.guardShape(objId
, globalLexical
->shape());
3438 // Guard on the shape of the GlobalObject.
3439 ObjOperandId globalId
= writer
.loadObject(&globalLexical
->global());
3440 writer
.guardShape(globalId
, globalLexical
->global().shape());
3442 // Shape guard holder.
3443 ObjOperandId holderId
= writer
.loadObject(holder
);
3444 writer
.guardShape(holderId
, holder
->shape());
3446 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3449 writer
.returnFromIC();
3451 trackAttached("GetName.GlobalNameValue");
3452 return AttachDecision::Attach
;
3455 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId
,
3457 if (!IsGlobalOp(JSOp(*pc_
))) {
3458 return AttachDecision::NoAction
;
3460 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3462 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3463 env_
.as
<GlobalLexicalEnvironmentObject
>();
3464 MOZ_ASSERT(globalLexical
->isGlobal());
3466 NativeObject
* holder
= nullptr;
3467 Maybe
<PropertyInfo
> prop
;
3468 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3469 return AttachDecision::NoAction
;
3472 if (holder
== globalLexical
) {
3473 return AttachDecision::NoAction
;
3476 GlobalObject
* global
= &globalLexical
->global();
3478 NativeGetPropKind kind
= IsCacheableGetPropCall(global
, holder
, *prop
, pc_
);
3479 if (kind
!= NativeGetPropKind::NativeGetter
&&
3480 kind
!= NativeGetPropKind::ScriptedGetter
) {
3481 return AttachDecision::NoAction
;
3484 bool needsWindowProxy
=
3485 IsWindow(global
) && GetterNeedsWindowProxyThis(holder
, *prop
);
3487 // Shape guard for global lexical.
3488 writer
.guardShape(objId
, globalLexical
->shape());
3490 // Guard on the shape of the GlobalObject.
3491 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3492 writer
.guardShape(globalId
, global
->shape());
3494 if (holder
!= global
) {
3495 // Shape guard holder.
3496 ObjOperandId holderId
= writer
.loadObject(holder
);
3497 writer
.guardShape(holderId
, holder
->shape());
3498 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
3499 /* holderIsConstant = */ true);
3501 // Note: pass true for |holderIsConstant| because the holder must be the
3502 // current global object.
3503 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, globalId
,
3504 /* holderIsConstant = */ true);
3507 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, global
, holder
, *prop
,
3509 // The global shape guard above ensures the instance JSClass is correct.
3510 MOZ_ASSERT(!needsWindowProxy
);
3511 EmitCallDOMGetterResultNoGuards(writer
, holder
, *prop
, globalId
);
3512 trackAttached("GetName.GlobalNameDOMGetter");
3514 ObjOperandId receiverObjId
;
3515 if (needsWindowProxy
) {
3516 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
3517 receiverObjId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
3519 receiverObjId
= globalId
;
3521 ValOperandId receiverId
= writer
.boxObject(receiverObjId
);
3522 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, global
, holder
, *prop
,
3524 trackAttached("GetName.GlobalNameGetter");
3527 return AttachDecision::Attach
;
3530 static bool NeedEnvironmentShapeGuard(JSContext
* cx
, JSObject
* envObj
) {
3531 if (!envObj
->is
<CallObject
>()) {
3535 // We can skip a guard on the call object if the script's bindings are
3536 // guaranteed to be immutable (and thus cannot introduce shadowing variables).
3537 // If the function is a relazified self-hosted function it has no BaseScript
3538 // and we pessimistically create the guard.
3539 CallObject
* callObj
= &envObj
->as
<CallObject
>();
3540 JSFunction
* fun
= &callObj
->callee();
3541 if (!fun
->hasBaseScript() || fun
->baseScript()->funHasExtensibleScope() ||
3542 DebugEnvironments::hasDebugEnvironment(cx
, *callObj
)) {
3549 AttachDecision
GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3551 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3552 return AttachDecision::NoAction
;
3555 JSObject
* env
= env_
;
3556 Maybe
<PropertyInfo
> prop
;
3557 NativeObject
* holder
= nullptr;
3560 if (env
->is
<GlobalObject
>()) {
3561 prop
= env
->as
<GlobalObject
>().lookup(cx_
, id
);
3562 if (prop
.isSome()) {
3565 return AttachDecision::NoAction
;
3568 if (!env
->is
<EnvironmentObject
>() || env
->is
<WithEnvironmentObject
>()) {
3569 return AttachDecision::NoAction
;
3572 // Check for an 'own' property on the env. There is no need to
3573 // check the prototype as non-with scopes do not inherit properties
3574 // from any prototype.
3575 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3576 if (prop
.isSome()) {
3580 env
= env
->enclosingEnvironment();
3583 holder
= &env
->as
<NativeObject
>();
3584 if (!IsCacheableGetPropSlot(holder
, holder
, *prop
)) {
3585 return AttachDecision::NoAction
;
3587 if (holder
->getSlot(prop
->slot()).isMagic()) {
3588 MOZ_ASSERT(holder
->is
<EnvironmentObject
>());
3589 return AttachDecision::NoAction
;
3592 ObjOperandId lastObjId
= objId
;
3595 if (NeedEnvironmentShapeGuard(cx_
, env
)) {
3596 writer
.guardShape(lastObjId
, env
->shape());
3599 if (env
== holder
) {
3603 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3604 env
= env
->enclosingEnvironment();
3607 ValOperandId resId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3608 if (holder
->is
<EnvironmentObject
>()) {
3609 writer
.guardIsNotUninitializedLexical(resId
);
3611 writer
.loadOperandResult(resId
);
3612 writer
.returnFromIC();
3614 trackAttached("GetName.EnvironmentName");
3615 return AttachDecision::Attach
;
3618 void GetNameIRGenerator::trackAttached(const char* name
) {
3619 stubName_
= name
? name
: "NotAttached";
3620 #ifdef JS_CACHEIR_SPEW
3621 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3622 sp
.valueProperty("base", ObjectValue(*env_
));
3623 sp
.valueProperty("property", StringValue(name_
));
3628 BindNameIRGenerator::BindNameIRGenerator(JSContext
* cx
, HandleScript script
,
3629 jsbytecode
* pc
, ICState state
,
3631 Handle
<PropertyName
*> name
)
3632 : IRGenerator(cx
, script
, pc
, CacheKind::BindName
, state
),
3636 AttachDecision
BindNameIRGenerator::tryAttachStub() {
3637 MOZ_ASSERT(cacheKind_
== CacheKind::BindName
);
3639 AutoAssertNoPendingException
aanpe(cx_
);
3641 ObjOperandId
envId(writer
.setInputOperandId(0));
3642 RootedId
id(cx_
, NameToId(name_
));
3644 TRY_ATTACH(tryAttachGlobalName(envId
, id
));
3645 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3647 trackAttached(IRGenerator::NotAttached
);
3648 return AttachDecision::NoAction
;
3651 AttachDecision
BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId
,
3653 if (!IsGlobalOp(JSOp(*pc_
))) {
3654 return AttachDecision::NoAction
;
3656 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3658 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3659 env_
.as
<GlobalLexicalEnvironmentObject
>();
3660 MOZ_ASSERT(globalLexical
->isGlobal());
3662 JSObject
* result
= nullptr;
3663 if (Maybe
<PropertyInfo
> prop
= globalLexical
->lookup(cx_
, id
)) {
3664 // If this is an uninitialized lexical or a const, we need to return a
3665 // RuntimeLexicalErrorObject.
3666 if (globalLexical
->getSlot(prop
->slot()).isMagic() || !prop
->writable()) {
3667 return AttachDecision::NoAction
;
3669 result
= globalLexical
;
3671 result
= &globalLexical
->global();
3674 if (result
== globalLexical
) {
3675 // Lexical bindings are non-configurable so we can just return the
3677 writer
.loadObjectResult(objId
);
3679 // If the property exists on the global and is non-configurable, it cannot
3680 // be shadowed by the lexical scope so we can just return the global without
3682 Maybe
<PropertyInfo
> prop
= result
->as
<GlobalObject
>().lookup(cx_
, id
);
3683 if (prop
.isNothing() || prop
->configurable()) {
3684 writer
.guardShape(objId
, globalLexical
->shape());
3686 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3687 writer
.loadObjectResult(globalId
);
3689 writer
.returnFromIC();
3691 trackAttached("BindName.GlobalName");
3692 return AttachDecision::Attach
;
3695 AttachDecision
BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3697 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3698 return AttachDecision::NoAction
;
3701 JSObject
* env
= env_
;
3702 Maybe
<PropertyInfo
> prop
;
3704 if (!env
->is
<GlobalObject
>() && !env
->is
<EnvironmentObject
>()) {
3705 return AttachDecision::NoAction
;
3707 if (env
->is
<WithEnvironmentObject
>()) {
3708 return AttachDecision::NoAction
;
3711 // When we reach an unqualified variables object (like the global) we
3712 // have to stop looking and return that object.
3713 if (env
->isUnqualifiedVarObj()) {
3717 // Check for an 'own' property on the env. There is no need to
3718 // check the prototype as non-with scopes do not inherit properties
3719 // from any prototype.
3720 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3721 if (prop
.isSome()) {
3725 env
= env
->enclosingEnvironment();
3728 // If this is an uninitialized lexical or a const, we need to return a
3729 // RuntimeLexicalErrorObject.
3730 auto* holder
= &env
->as
<NativeObject
>();
3731 if (prop
.isSome() && holder
->is
<EnvironmentObject
>() &&
3732 (holder
->getSlot(prop
->slot()).isMagic() || !prop
->writable())) {
3733 return AttachDecision::NoAction
;
3736 ObjOperandId lastObjId
= objId
;
3739 if (NeedEnvironmentShapeGuard(cx_
, env
) && !env
->is
<GlobalObject
>()) {
3740 writer
.guardShape(lastObjId
, env
->shape());
3743 if (env
== holder
) {
3747 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3748 env
= env
->enclosingEnvironment();
3751 if (prop
.isSome() && holder
->is
<EnvironmentObject
>()) {
3752 ValOperandId valId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3753 writer
.guardIsNotUninitializedLexical(valId
);
3756 writer
.loadObjectResult(lastObjId
);
3757 writer
.returnFromIC();
3759 trackAttached("BindName.EnvironmentName");
3760 return AttachDecision::Attach
;
3763 void BindNameIRGenerator::trackAttached(const char* name
) {
3764 stubName_
= name
? name
: "NotAttached";
3765 #ifdef JS_CACHEIR_SPEW
3766 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3767 sp
.valueProperty("base", ObjectValue(*env_
));
3768 sp
.valueProperty("property", StringValue(name_
));
3773 HasPropIRGenerator::HasPropIRGenerator(JSContext
* cx
, HandleScript script
,
3774 jsbytecode
* pc
, ICState state
,
3775 CacheKind cacheKind
, HandleValue idVal
,
3777 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
3779 AttachDecision
HasPropIRGenerator::tryAttachDense(HandleObject obj
,
3782 Int32OperandId indexId
) {
3783 if (!obj
->is
<NativeObject
>()) {
3784 return AttachDecision::NoAction
;
3787 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3788 if (!nobj
->containsDenseElement(index
)) {
3789 return AttachDecision::NoAction
;
3792 if (mode_
== ICState::Mode::Megamorphic
) {
3793 writer
.guardIsNativeObject(objId
);
3795 // Guard shape to ensure object class is NativeObject.
3796 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3798 writer
.loadDenseElementExistsResult(objId
, indexId
);
3799 writer
.returnFromIC();
3801 trackAttached("HasProp.Dense");
3802 return AttachDecision::Attach
;
3805 AttachDecision
HasPropIRGenerator::tryAttachDenseHole(HandleObject obj
,
3808 Int32OperandId indexId
) {
3809 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3810 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3812 if (!obj
->is
<NativeObject
>()) {
3813 return AttachDecision::NoAction
;
3816 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3817 if (nobj
->containsDenseElement(index
)) {
3818 return AttachDecision::NoAction
;
3820 if (!CanAttachDenseElementHole(nobj
, ownProp
)) {
3821 return AttachDecision::NoAction
;
3824 // Guard shape to ensure class is NativeObject and to prevent non-dense
3825 // elements being added. Also ensures prototype doesn't change if dynamic
3826 // checks aren't emitted.
3827 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3829 // Generate prototype guards if needed. This includes monitoring that
3830 // properties were not added in the chain.
3832 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3833 /* alwaysGuardFirstProto = */ false);
3836 writer
.loadDenseElementHoleExistsResult(objId
, indexId
);
3837 writer
.returnFromIC();
3839 trackAttached("HasProp.DenseHole");
3840 return AttachDecision::Attach
;
3843 AttachDecision
HasPropIRGenerator::tryAttachSparse(HandleObject obj
,
3845 Int32OperandId indexId
) {
3846 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3847 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3849 if (!obj
->is
<NativeObject
>()) {
3850 return AttachDecision::NoAction
;
3852 auto* nobj
= &obj
->as
<NativeObject
>();
3854 if (!nobj
->isIndexed()) {
3855 return AttachDecision::NoAction
;
3857 if (!CanAttachDenseElementHole(nobj
, ownProp
, AllowIndexedReceiver::Yes
)) {
3858 return AttachDecision::NoAction
;
3861 // Guard that this is a native object.
3862 writer
.guardIsNativeObject(objId
);
3864 // Generate prototype guards if needed. This includes monitoring that
3865 // properties were not added in the chain.
3867 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3868 /* alwaysGuardFirstProto = */ true);
3871 // Because of the prototype guard we know that the prototype chain
3872 // does not include any dense or sparse (i.e indexed) properties.
3873 writer
.callObjectHasSparseElementResult(objId
, indexId
);
3874 writer
.returnFromIC();
3876 trackAttached("HasProp.Sparse");
3877 return AttachDecision::Attach
;
3880 AttachDecision
HasPropIRGenerator::tryAttachArgumentsObjectArg(
3881 HandleObject obj
, ObjOperandId objId
, Int32OperandId indexId
) {
3882 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3883 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3885 if (!obj
->is
<ArgumentsObject
>()) {
3886 return AttachDecision::NoAction
;
3888 auto* args
= &obj
->as
<ArgumentsObject
>();
3890 // No elements must have been overridden or deleted.
3891 if (args
->hasOverriddenElement()) {
3892 return AttachDecision::NoAction
;
3895 if (!CanAttachDenseElementHole(args
, ownProp
, AllowIndexedReceiver::Yes
,
3896 AllowExtraReceiverProperties::Yes
)) {
3897 return AttachDecision::NoAction
;
3900 if (args
->is
<MappedArgumentsObject
>()) {
3901 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3903 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
3904 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
3908 GeneratePrototypeHoleGuards(writer
, args
, objId
,
3909 /* alwaysGuardFirstProto = */ true);
3912 writer
.loadArgumentsObjectArgExistsResult(objId
, indexId
);
3913 writer
.returnFromIC();
3915 trackAttached("HasProp.ArgumentsObjectArg");
3916 return AttachDecision::Attach
;
3919 AttachDecision
HasPropIRGenerator::tryAttachNamedProp(HandleObject obj
,
3922 ValOperandId keyId
) {
3923 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3925 Rooted
<NativeObject
*> holder(cx_
);
3926 PropertyResult prop
;
3929 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
3930 return AttachDecision::NoAction
;
3933 holder
.set(&obj
->as
<NativeObject
>());
3935 NativeObject
* nHolder
= nullptr;
3936 if (!LookupPropertyPure(cx_
, obj
, key
, &nHolder
, &prop
)) {
3937 return AttachDecision::NoAction
;
3939 holder
.set(nHolder
);
3941 if (prop
.isNotFound()) {
3942 return AttachDecision::NoAction
;
3945 TRY_ATTACH(tryAttachSmallObjectVariableKey(obj
, objId
, key
, keyId
));
3946 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
3947 TRY_ATTACH(tryAttachNative(&obj
->as
<NativeObject
>(), objId
, key
, keyId
, prop
,
3950 return AttachDecision::NoAction
;
3953 AttachDecision
HasPropIRGenerator::tryAttachSmallObjectVariableKey(
3954 HandleObject obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
3955 MOZ_ASSERT(obj
->is
<NativeObject
>());
3957 if (cacheKind_
!= CacheKind::HasOwn
) {
3958 return AttachDecision::NoAction
;
3961 if (mode_
!= ICState::Mode::Megamorphic
) {
3962 return AttachDecision::NoAction
;
3965 if (numOptimizedStubs_
!= 0) {
3966 return AttachDecision::NoAction
;
3969 if (!key
.isString()) {
3970 return AttachDecision::NoAction
;
3973 if (!obj
->as
<NativeObject
>().hasEmptyElements()) {
3974 return AttachDecision::NoAction
;
3977 if (obj
->getClass()->getResolve()) {
3978 return AttachDecision::NoAction
;
3981 if (!obj
->shape()->isShared()) {
3982 return AttachDecision::NoAction
;
3985 static constexpr size_t SMALL_OBJECT_SIZE
= 5;
3987 if (obj
->shape()->asShared().slotSpan() > SMALL_OBJECT_SIZE
) {
3988 return AttachDecision::NoAction
;
3991 Rooted
<ListObject
*> keyListObj(cx_
, ListObject::create(cx_
));
3993 cx_
->recoverFromOutOfMemory();
3994 return AttachDecision::NoAction
;
3997 for (SharedShapePropertyIter
<CanGC
> iter(cx_
, &obj
->shape()->asShared());
3998 !iter
.done(); iter
++) {
3999 if (!iter
->key().isAtom()) {
4000 return AttachDecision::NoAction
;
4003 if (keyListObj
->length() == SMALL_OBJECT_SIZE
) {
4004 return AttachDecision::NoAction
;
4007 RootedValue
key(cx_
, StringValue(iter
->key().toAtom()));
4008 if (!keyListObj
->append(cx_
, key
)) {
4009 cx_
->recoverFromOutOfMemory();
4010 return AttachDecision::NoAction
;
4014 writer
.guardShape(objId
, obj
->shape());
4015 writer
.guardNoDenseElements(objId
);
4016 StringOperandId keyStrId
= writer
.guardToString(keyId
);
4017 StringOperandId keyAtomId
= writer
.stringToAtom(keyStrId
);
4018 writer
.smallObjectVariableKeyHasOwnResult(keyAtomId
, keyListObj
,
4020 writer
.returnFromIC();
4021 trackAttached("HasProp.SmallObjectVariableKey");
4022 return AttachDecision::Attach
;
4025 AttachDecision
HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId
,
4026 ValOperandId keyId
) {
4027 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4029 if (mode_
!= ICState::Mode::Megamorphic
) {
4030 return AttachDecision::NoAction
;
4033 writer
.megamorphicHasPropResult(objId
, keyId
, hasOwn
);
4034 writer
.returnFromIC();
4035 trackAttached("HasProp.Megamorphic");
4036 return AttachDecision::Attach
;
4039 AttachDecision
HasPropIRGenerator::tryAttachNative(NativeObject
* obj
,
4040 ObjOperandId objId
, jsid key
,
4042 PropertyResult prop
,
4043 NativeObject
* holder
) {
4044 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4046 if (!prop
.isNativeProperty()) {
4047 return AttachDecision::NoAction
;
4050 emitIdGuard(keyId
, idVal_
, key
);
4051 EmitReadSlotGuard(writer
, obj
, holder
, objId
);
4052 writer
.loadBooleanResult(true);
4053 writer
.returnFromIC();
4055 trackAttached("HasProp.Native");
4056 return AttachDecision::Attach
;
4059 static void EmitGuardTypedArray(CacheIRWriter
& writer
, TypedArrayObject
* obj
,
4060 ObjOperandId objId
) {
4061 if (obj
->is
<FixedLengthTypedArrayObject
>()) {
4062 writer
.guardIsFixedLengthTypedArray(objId
);
4064 writer
.guardIsResizableTypedArray(objId
);
4068 AttachDecision
HasPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
4070 ValOperandId keyId
) {
4071 if (!obj
->is
<TypedArrayObject
>()) {
4072 return AttachDecision::NoAction
;
4076 if (!ValueIsInt64Index(idVal_
, &index
)) {
4077 return AttachDecision::NoAction
;
4080 auto* tarr
= &obj
->as
<TypedArrayObject
>();
4081 EmitGuardTypedArray(writer
, tarr
, objId
);
4083 IntPtrOperandId intPtrIndexId
=
4084 guardToIntPtrIndex(idVal_
, keyId
, /* supportOOB = */ true);
4086 auto viewKind
= ToArrayBufferViewKind(tarr
);
4087 writer
.loadTypedArrayElementExistsResult(objId
, intPtrIndexId
, viewKind
);
4088 writer
.returnFromIC();
4090 trackAttached("HasProp.TypedArrayObject");
4091 return AttachDecision::Attach
;
4094 AttachDecision
HasPropIRGenerator::tryAttachSlotDoesNotExist(
4095 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
4096 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4098 emitIdGuard(keyId
, idVal_
, key
);
4100 TestMatchingNativeReceiver(writer
, obj
, objId
);
4102 EmitMissingPropGuard(writer
, obj
, objId
);
4104 writer
.loadBooleanResult(false);
4105 writer
.returnFromIC();
4107 trackAttached("HasProp.DoesNotExist");
4108 return AttachDecision::Attach
;
4111 AttachDecision
HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj
,
4114 ValOperandId keyId
) {
4115 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4117 // Check that property doesn't exist on |obj| or it's prototype chain. These
4118 // checks allow NativeObjects with a NativeObject prototype chain. They return
4119 // NoAction if unknown such as resolve hooks or proxies.
4121 if (!CheckHasNoSuchOwnProperty(cx_
, obj
, key
)) {
4122 return AttachDecision::NoAction
;
4125 if (!CheckHasNoSuchProperty(cx_
, obj
, key
)) {
4126 return AttachDecision::NoAction
;
4130 TRY_ATTACH(tryAttachSmallObjectVariableKey(obj
, objId
, key
, keyId
));
4131 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
4133 tryAttachSlotDoesNotExist(&obj
->as
<NativeObject
>(), objId
, key
, keyId
));
4135 return AttachDecision::NoAction
;
4138 AttachDecision
HasPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
4140 ValOperandId keyId
) {
4141 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4143 if (!obj
->is
<ProxyObject
>()) {
4144 return AttachDecision::NoAction
;
4147 writer
.guardIsProxy(objId
);
4148 writer
.proxyHasPropResult(objId
, keyId
, hasOwn
);
4149 writer
.returnFromIC();
4151 trackAttached("HasProp.ProxyElement");
4152 return AttachDecision::Attach
;
4155 AttachDecision
HasPropIRGenerator::tryAttachStub() {
4156 MOZ_ASSERT(cacheKind_
== CacheKind::In
|| cacheKind_
== CacheKind::HasOwn
);
4158 AutoAssertNoPendingException
aanpe(cx_
);
4160 // NOTE: Argument order is PROPERTY, OBJECT
4161 ValOperandId
keyId(writer
.setInputOperandId(0));
4162 ValOperandId
valId(writer
.setInputOperandId(1));
4164 if (!val_
.isObject()) {
4165 trackAttached(IRGenerator::NotAttached
);
4166 return AttachDecision::NoAction
;
4168 RootedObject
obj(cx_
, &val_
.toObject());
4169 ObjOperandId objId
= writer
.guardToObject(valId
);
4172 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, keyId
));
4176 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4177 cx_
->clearPendingException();
4178 return AttachDecision::NoAction
;
4182 TRY_ATTACH(tryAttachNamedProp(obj
, objId
, id
, keyId
));
4183 TRY_ATTACH(tryAttachDoesNotExist(obj
, objId
, id
, keyId
));
4185 trackAttached(IRGenerator::NotAttached
);
4186 return AttachDecision::NoAction
;
4189 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, keyId
));
4192 Int32OperandId indexId
;
4193 if (maybeGuardInt32Index(idVal_
, keyId
, &index
, &indexId
)) {
4194 TRY_ATTACH(tryAttachDense(obj
, objId
, index
, indexId
));
4195 TRY_ATTACH(tryAttachDenseHole(obj
, objId
, index
, indexId
));
4196 TRY_ATTACH(tryAttachSparse(obj
, objId
, indexId
));
4197 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, indexId
));
4199 trackAttached(IRGenerator::NotAttached
);
4200 return AttachDecision::NoAction
;
4203 trackAttached(IRGenerator::NotAttached
);
4204 return AttachDecision::NoAction
;
4207 void HasPropIRGenerator::trackAttached(const char* name
) {
4208 stubName_
= name
? name
: "NotAttached";
4209 #ifdef JS_CACHEIR_SPEW
4210 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4211 sp
.valueProperty("base", val_
);
4212 sp
.valueProperty("property", idVal_
);
4217 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
4218 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
4219 CacheKind cacheKind
, HandleValue idVal
, HandleValue val
)
4220 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {
4221 MOZ_ASSERT(idVal
.isSymbol() && idVal
.toSymbol()->isPrivateName());
4224 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachStub() {
4225 AutoAssertNoPendingException
aanpe(cx_
);
4227 ValOperandId
valId(writer
.setInputOperandId(0));
4228 ValOperandId
keyId(writer
.setInputOperandId(1));
4230 if (!val_
.isObject()) {
4231 trackAttached(IRGenerator::NotAttached
);
4232 return AttachDecision::NoAction
;
4234 JSObject
* obj
= &val_
.toObject();
4235 ObjOperandId objId
= writer
.guardToObject(valId
);
4236 PropertyKey key
= PropertyKey::Symbol(idVal_
.toSymbol());
4238 ThrowCondition condition
;
4239 ThrowMsgKind msgKind
;
4240 GetCheckPrivateFieldOperands(pc_
, &condition
, &msgKind
);
4242 PropertyResult prop
;
4243 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
4244 return AttachDecision::NoAction
;
4247 if (CheckPrivateFieldWillThrow(condition
, prop
.isFound())) {
4248 // Don't attach a stub if the operation will throw.
4249 return AttachDecision::NoAction
;
4252 auto* nobj
= &obj
->as
<NativeObject
>();
4254 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
));
4256 return AttachDecision::NoAction
;
4259 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachNative(
4260 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
,
4261 PropertyResult prop
) {
4262 MOZ_ASSERT(prop
.isNativeProperty() || prop
.isNotFound());
4264 emitIdGuard(keyId
, idVal_
, key
);
4265 TestMatchingNativeReceiver(writer
, obj
, objId
);
4266 writer
.loadBooleanResult(prop
.isFound());
4267 writer
.returnFromIC();
4269 trackAttached("CheckPrivateField.Native");
4270 return AttachDecision::Attach
;
4273 void CheckPrivateFieldIRGenerator::trackAttached(const char* name
) {
4274 stubName_
= name
? name
: "NotAttached";
4275 #ifdef JS_CACHEIR_SPEW
4276 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4277 sp
.valueProperty("base", val_
);
4278 sp
.valueProperty("property", idVal_
);
4283 bool IRGenerator::maybeGuardInt32Index(const Value
& index
, ValOperandId indexId
,
4284 uint32_t* int32Index
,
4285 Int32OperandId
* int32IndexId
) {
4286 if (index
.isNumber()) {
4287 int32_t indexSigned
;
4288 if (index
.isInt32()) {
4289 indexSigned
= index
.toInt32();
4291 // We allow negative zero here.
4292 if (!mozilla::NumberEqualsInt32(index
.toDouble(), &indexSigned
)) {
4297 if (indexSigned
< 0) {
4301 *int32Index
= uint32_t(indexSigned
);
4302 *int32IndexId
= writer
.guardToInt32Index(indexId
);
4306 if (index
.isString()) {
4307 int32_t indexSigned
= GetIndexFromString(index
.toString());
4308 if (indexSigned
< 0) {
4312 StringOperandId strId
= writer
.guardToString(indexId
);
4313 *int32Index
= uint32_t(indexSigned
);
4314 *int32IndexId
= writer
.guardStringToIndex(strId
);
4321 SetPropIRGenerator::SetPropIRGenerator(JSContext
* cx
, HandleScript script
,
4322 jsbytecode
* pc
, CacheKind cacheKind
,
4323 ICState state
, HandleValue lhsVal
,
4324 HandleValue idVal
, HandleValue rhsVal
)
4325 : IRGenerator(cx
, script
, pc
, cacheKind
, state
),
4330 AttachDecision
SetPropIRGenerator::tryAttachStub() {
4331 AutoAssertNoPendingException
aanpe(cx_
);
4333 ValOperandId
objValId(writer
.setInputOperandId(0));
4334 ValOperandId rhsValId
;
4335 if (cacheKind_
== CacheKind::SetProp
) {
4336 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
4338 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4339 MOZ_ASSERT(setElemKeyValueId().id() == 1);
4340 writer
.setInputOperandId(1);
4341 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
4346 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4347 cx_
->clearPendingException();
4348 return AttachDecision::NoAction
;
4351 if (lhsVal_
.isObject()) {
4352 RootedObject
obj(cx_
, &lhsVal_
.toObject());
4354 ObjOperandId objId
= writer
.guardToObject(objValId
);
4355 if (IsPropertySetOp(JSOp(*pc_
))) {
4356 TRY_ATTACH(tryAttachMegamorphicSetElement(obj
, objId
, rhsValId
));
4359 TRY_ATTACH(tryAttachNativeSetSlot(obj
, objId
, id
, rhsValId
));
4360 if (IsPropertySetOp(JSOp(*pc_
))) {
4361 TRY_ATTACH(tryAttachSetArrayLength(obj
, objId
, id
, rhsValId
));
4362 TRY_ATTACH(tryAttachSetter(obj
, objId
, id
, rhsValId
));
4363 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
, rhsValId
));
4364 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, rhsValId
));
4365 TRY_ATTACH(tryAttachMegamorphicSetSlot(obj
, objId
, id
, rhsValId
));
4367 if (canAttachAddSlotStub(obj
, id
)) {
4368 deferType_
= DeferType::AddSlot
;
4369 return AttachDecision::Deferred
;
4371 return AttachDecision::NoAction
;
4374 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4376 if (IsPropertySetOp(JSOp(*pc_
))) {
4377 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, rhsValId
));
4380 TRY_ATTACH(tryAttachSetTypedArrayElement(obj
, objId
, rhsValId
));
4383 Int32OperandId indexId
;
4384 if (maybeGuardInt32Index(idVal_
, setElemKeyValueId(), &index
, &indexId
)) {
4386 tryAttachSetDenseElement(obj
, objId
, index
, indexId
, rhsValId
));
4388 tryAttachSetDenseElementHole(obj
, objId
, index
, indexId
, rhsValId
));
4389 TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj
, objId
, index
, indexId
,
4391 return AttachDecision::NoAction
;
4394 return AttachDecision::NoAction
;
4397 static void EmitStoreSlotAndReturn(CacheIRWriter
& writer
, ObjOperandId objId
,
4398 NativeObject
* nobj
, PropertyInfo prop
,
4399 ValOperandId rhsId
) {
4400 if (nobj
->isFixedSlot(prop
.slot())) {
4401 size_t offset
= NativeObject::getFixedSlotOffset(prop
.slot());
4402 writer
.storeFixedSlot(objId
, offset
, rhsId
);
4404 size_t offset
= nobj
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
4405 writer
.storeDynamicSlot(objId
, offset
, rhsId
);
4407 writer
.returnFromIC();
4410 static Maybe
<PropertyInfo
> LookupShapeForSetSlot(JSOp op
, NativeObject
* obj
,
4412 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(id
);
4413 if (prop
.isNothing() || !prop
->isDataProperty() || !prop
->writable()) {
4414 return mozilla::Nothing();
4417 // If this is a property init operation, the property's attributes may have to
4418 // be changed too, so make sure the current flags match.
4419 if (IsPropertyInitOp(op
)) {
4420 // Don't support locked init operations.
4421 if (IsLockedInitOp(op
)) {
4422 return mozilla::Nothing();
4425 // Can't redefine a non-configurable property.
4426 if (!prop
->configurable()) {
4427 return mozilla::Nothing();
4430 // Make sure the enumerable flag matches the init operation.
4431 if (IsHiddenInitOp(op
) == prop
->enumerable()) {
4432 return mozilla::Nothing();
4439 static bool CanAttachNativeSetSlot(JSOp op
, JSObject
* obj
, PropertyKey id
,
4440 Maybe
<PropertyInfo
>* prop
) {
4441 if (!obj
->is
<NativeObject
>()) {
4445 if (Watchtower::watchesPropertyModification(&obj
->as
<NativeObject
>())) {
4449 *prop
= LookupShapeForSetSlot(op
, &obj
->as
<NativeObject
>(), id
);
4450 return prop
->isSome();
4453 // There is no need to guard on the shape. Global lexical bindings are
4454 // non-configurable and can not be shadowed.
4455 static bool IsGlobalLexicalSetGName(JSOp op
, NativeObject
* obj
,
4456 PropertyInfo prop
) {
4457 // Ensure that the env can't change.
4458 if (op
!= JSOp::SetGName
&& op
!= JSOp::StrictSetGName
) {
4462 if (!obj
->is
<GlobalLexicalEnvironmentObject
>()) {
4466 // Uninitialized let bindings use a RuntimeLexicalErrorObject.
4467 MOZ_ASSERT(!obj
->getSlot(prop
.slot()).isMagic());
4468 MOZ_ASSERT(prop
.writable());
4469 MOZ_ASSERT(!prop
.configurable());
4473 AttachDecision
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj
,
4476 ValOperandId rhsId
) {
4477 Maybe
<PropertyInfo
> prop
;
4478 if (!CanAttachNativeSetSlot(JSOp(*pc_
), obj
, id
, &prop
)) {
4479 return AttachDecision::NoAction
;
4482 if (mode_
== ICState::Mode::Megamorphic
&& cacheKind_
== CacheKind::SetProp
&&
4483 IsPropertySetOp(JSOp(*pc_
))) {
4484 return AttachDecision::NoAction
;
4487 maybeEmitIdGuard(id
);
4489 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4490 if (!IsGlobalLexicalSetGName(JSOp(*pc_
), nobj
, *prop
)) {
4491 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4493 EmitStoreSlotAndReturn(writer
, objId
, nobj
, *prop
, rhsId
);
4495 trackAttached("SetProp.NativeSlot");
4496 return AttachDecision::Attach
;
4499 static bool ValueCanConvertToNumeric(Scalar::Type type
, const Value
& val
) {
4500 if (Scalar::isBigIntType(type
)) {
4501 return val
.isBigInt();
4503 return val
.isNumber() || val
.isNullOrUndefined() || val
.isBoolean() ||
4507 OperandId
IRGenerator::emitNumericGuard(ValOperandId valId
, const Value
& v
,
4508 Scalar::Type type
) {
4509 MOZ_ASSERT(ValueCanConvertToNumeric(type
, v
));
4514 case Scalar::Uint16
:
4516 case Scalar::Uint32
: {
4518 return writer
.guardToInt32ModUint32(valId
);
4520 if (v
.isNullOrUndefined()) {
4521 writer
.guardIsNullOrUndefined(valId
);
4522 return writer
.loadInt32Constant(0);
4524 if (v
.isBoolean()) {
4525 return writer
.guardBooleanToInt32(valId
);
4527 MOZ_ASSERT(v
.isString());
4528 StringOperandId strId
= writer
.guardToString(valId
);
4529 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4530 return writer
.truncateDoubleToUInt32(numId
);
4533 case Scalar::Float32
:
4534 case Scalar::Float64
: {
4536 return writer
.guardIsNumber(valId
);
4539 writer
.guardIsNull(valId
);
4540 return writer
.loadDoubleConstant(0.0);
4542 if (v
.isUndefined()) {
4543 writer
.guardIsUndefined(valId
);
4544 return writer
.loadDoubleConstant(JS::GenericNaN());
4546 if (v
.isBoolean()) {
4547 BooleanOperandId boolId
= writer
.guardToBoolean(valId
);
4548 return writer
.booleanToNumber(boolId
);
4550 MOZ_ASSERT(v
.isString());
4551 StringOperandId strId
= writer
.guardToString(valId
);
4552 return writer
.guardStringToNumber(strId
);
4555 case Scalar::Uint8Clamped
: {
4557 return writer
.guardToUint8Clamped(valId
);
4559 if (v
.isNullOrUndefined()) {
4560 writer
.guardIsNullOrUndefined(valId
);
4561 return writer
.loadInt32Constant(0);
4563 if (v
.isBoolean()) {
4564 return writer
.guardBooleanToInt32(valId
);
4566 MOZ_ASSERT(v
.isString());
4567 StringOperandId strId
= writer
.guardToString(valId
);
4568 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4569 return writer
.doubleToUint8Clamped(numId
);
4572 case Scalar::BigInt64
:
4573 case Scalar::BigUint64
:
4574 MOZ_ASSERT(v
.isBigInt());
4575 return writer
.guardToBigInt(valId
);
4577 case Scalar::MaxTypedArrayViewType
:
4579 case Scalar::Simd128
:
4582 MOZ_CRASH("Unsupported TypedArray type");
4585 void SetPropIRGenerator::trackAttached(const char* name
) {
4586 stubName_
= name
? name
: "NotAttached";
4587 #ifdef JS_CACHEIR_SPEW
4588 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4589 sp
.opcodeProperty("op", JSOp(*pc_
));
4590 sp
.valueProperty("base", lhsVal_
);
4591 sp
.valueProperty("property", idVal_
);
4592 sp
.valueProperty("value", rhsVal_
);
4597 static bool IsCacheableSetPropCallNative(NativeObject
* obj
,
4598 NativeObject
* holder
,
4599 PropertyInfo prop
) {
4600 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4602 if (!prop
.isAccessorProperty()) {
4606 JSObject
* setterObject
= holder
->getSetter(prop
);
4607 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4611 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4612 if (!setter
.isNativeWithoutJitEntry()) {
4616 if (setter
.isClassConstructor()) {
4623 static bool IsCacheableSetPropCallScripted(NativeObject
* obj
,
4624 NativeObject
* holder
,
4625 PropertyInfo prop
) {
4626 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4628 if (!prop
.isAccessorProperty()) {
4632 JSObject
* setterObject
= holder
->getSetter(prop
);
4633 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4637 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4638 if (setter
.isClassConstructor()) {
4642 // Scripted functions and natives with JIT entry can use the scripted path.
4643 return setter
.hasJitEntry();
4646 static bool CanAttachSetter(JSContext
* cx
, jsbytecode
* pc
, JSObject
* obj
,
4647 PropertyKey id
, NativeObject
** holder
,
4648 Maybe
<PropertyInfo
>* propInfo
) {
4649 // Don't attach a setter stub for ops like JSOp::InitElem.
4650 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
4652 PropertyResult prop
;
4653 if (!LookupPropertyPure(cx
, obj
, id
, holder
, &prop
)) {
4656 auto* nobj
= &obj
->as
<NativeObject
>();
4658 if (!prop
.isNativeProperty()) {
4662 if (!IsCacheableSetPropCallScripted(nobj
, *holder
, prop
.propertyInfo()) &&
4663 !IsCacheableSetPropCallNative(nobj
, *holder
, prop
.propertyInfo())) {
4667 *propInfo
= mozilla::Some(prop
.propertyInfo());
4671 static void EmitCallSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4672 NativeObject
* obj
, NativeObject
* holder
,
4673 PropertyInfo prop
, ObjOperandId receiverId
,
4674 ValOperandId rhsId
) {
4675 JSFunction
* target
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4676 bool sameRealm
= cx
->realm() == target
->realm();
4678 if (target
->isNativeWithoutJitEntry()) {
4679 MOZ_ASSERT(IsCacheableSetPropCallNative(obj
, holder
, prop
));
4680 writer
.callNativeSetter(receiverId
, target
, rhsId
, sameRealm
);
4681 writer
.returnFromIC();
4685 MOZ_ASSERT(IsCacheableSetPropCallScripted(obj
, holder
, prop
));
4686 writer
.callScriptedSetter(receiverId
, target
, rhsId
, sameRealm
);
4687 writer
.returnFromIC();
4690 static void EmitCallDOMSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4691 NativeObject
* holder
, PropertyInfo prop
,
4692 ObjOperandId objId
, ValOperandId rhsId
) {
4693 JSFunction
* setter
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4694 MOZ_ASSERT(cx
->realm() == setter
->realm());
4696 writer
.callDOMSetter(objId
, setter
->jitInfo(), rhsId
);
4697 writer
.returnFromIC();
4700 AttachDecision
SetPropIRGenerator::tryAttachSetter(HandleObject obj
,
4703 ValOperandId rhsId
) {
4704 // Don't attach a setter stub for ops like JSOp::InitElem.
4705 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4707 NativeObject
* holder
= nullptr;
4708 Maybe
<PropertyInfo
> prop
;
4709 if (!CanAttachSetter(cx_
, pc_
, obj
, id
, &holder
, &prop
)) {
4710 return AttachDecision::NoAction
;
4712 auto* nobj
= &obj
->as
<NativeObject
>();
4714 bool needsWindowProxy
=
4715 IsWindow(nobj
) && SetterNeedsWindowProxyThis(holder
, *prop
);
4717 maybeEmitIdGuard(id
);
4719 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
4720 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
4721 // require outerizing).
4722 if (mode_
== ICState::Mode::Specialized
|| IsWindow(nobj
)) {
4723 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4725 if (nobj
!= holder
) {
4726 GeneratePrototypeGuards(writer
, nobj
, holder
, objId
);
4728 // Guard on the holder's shape.
4729 ObjOperandId holderId
= writer
.loadObject(holder
);
4730 TestMatchingHolder(writer
, holder
, holderId
);
4732 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4733 /* holderIsConstant = */ true);
4735 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, objId
);
4738 GetterSetter
* gs
= holder
->getGetterSetter(*prop
);
4739 writer
.guardHasGetterSetter(objId
, id
, gs
);
4742 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Setter
, nobj
, holder
, *prop
,
4744 MOZ_ASSERT(!needsWindowProxy
);
4745 EmitCallDOMSetterNoGuards(cx_
, writer
, holder
, *prop
, objId
, rhsId
);
4747 trackAttached("SetProp.DOMSetter");
4748 return AttachDecision::Attach
;
4751 ObjOperandId receiverId
;
4752 if (needsWindowProxy
) {
4753 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
4754 receiverId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
4758 EmitCallSetterNoGuards(cx_
, writer
, nobj
, holder
, *prop
, receiverId
, rhsId
);
4760 trackAttached("SetProp.Setter");
4761 return AttachDecision::Attach
;
4764 AttachDecision
SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj
,
4767 ValOperandId rhsId
) {
4768 // Don't attach an array length stub for ops like JSOp::InitElem.
4769 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4771 if (!obj
->is
<ArrayObject
>() || !id
.isAtom(cx_
->names().length
) ||
4772 !obj
->as
<ArrayObject
>().lengthIsWritable()) {
4773 return AttachDecision::NoAction
;
4776 maybeEmitIdGuard(id
);
4777 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
4778 writer
.callSetArrayLength(objId
, IsStrictSetPC(pc_
), rhsId
);
4779 writer
.returnFromIC();
4781 trackAttached("SetProp.ArrayLength");
4782 return AttachDecision::Attach
;
4785 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElement(
4786 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4787 Int32OperandId indexId
, ValOperandId rhsId
) {
4788 if (!obj
->is
<NativeObject
>()) {
4789 return AttachDecision::NoAction
;
4792 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4793 if (!nobj
->containsDenseElement(index
) || nobj
->denseElementsAreFrozen()) {
4794 return AttachDecision::NoAction
;
4797 // Setting holes requires extra code for marking the elements non-packed.
4798 MOZ_ASSERT(!rhsVal_
.isMagic(JS_ELEMENTS_HOLE
));
4800 JSOp op
= JSOp(*pc_
);
4802 // We don't currently emit locked init for any indexed properties.
4803 MOZ_ASSERT(!IsLockedInitOp(op
));
4805 // We don't currently emit hidden init for any existing indexed properties.
4806 MOZ_ASSERT(!IsHiddenInitOp(op
));
4808 // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
4809 // the elements are sealed, we have to throw an exception. Note that we have
4810 // to check !isExtensible instead of denseElementsAreSealed because sealing
4811 // a (non-extensible) object does not necessarily trigger a Shape change.
4812 if (IsPropertyInitOp(op
) && !nobj
->isExtensible()) {
4813 return AttachDecision::NoAction
;
4816 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4818 writer
.storeDenseElement(objId
, indexId
, rhsId
);
4819 writer
.returnFromIC();
4821 trackAttached("SetProp.DenseElement");
4822 return AttachDecision::Attach
;
4825 static bool CanAttachAddElement(NativeObject
* obj
, bool isInit
,
4826 AllowIndexedReceiver allowIndexedReceiver
) {
4827 // Make sure the receiver doesn't have any indexed properties and that such
4828 // properties can't appear without a shape change.
4829 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
4834 // This check is also relevant for the receiver object.
4835 const JSClass
* clasp
= obj
->getClass();
4836 if (clasp
!= &ArrayObject::class_
&&
4837 (clasp
->getAddProperty() || clasp
->getResolve() ||
4838 clasp
->getOpsLookupProperty() || clasp
->getOpsSetProperty())) {
4842 // If we're initializing a property instead of setting one, the objects
4843 // on the prototype are not relevant.
4848 JSObject
* proto
= obj
->staticPrototype();
4853 if (!proto
->is
<NativeObject
>()) {
4857 NativeObject
* nproto
= &proto
->as
<NativeObject
>();
4858 if (nproto
->isIndexed()) {
4862 // We have to make sure the proto has no non-writable (frozen) elements
4863 // because we're not allowed to shadow them.
4864 if (nproto
->denseElementsAreFrozen() &&
4865 nproto
->getDenseInitializedLength() > 0) {
4875 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElementHole(
4876 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4877 Int32OperandId indexId
, ValOperandId rhsId
) {
4878 if (!obj
->is
<NativeObject
>()) {
4879 return AttachDecision::NoAction
;
4882 // Setting holes requires extra code for marking the elements non-packed.
4883 if (rhsVal_
.isMagic(JS_ELEMENTS_HOLE
)) {
4884 return AttachDecision::NoAction
;
4887 JSOp op
= JSOp(*pc_
);
4888 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4890 // We don't currently emit locked init for any indexed properties.
4891 MOZ_ASSERT(!IsLockedInitOp(op
));
4893 // Hidden init can be emitted for absent indexed properties.
4894 if (IsHiddenInitOp(op
)) {
4895 MOZ_ASSERT(op
== JSOp::InitHiddenElem
);
4896 return AttachDecision::NoAction
;
4899 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4900 if (!nobj
->isExtensible()) {
4901 return AttachDecision::NoAction
;
4904 MOZ_ASSERT(!nobj
->denseElementsAreFrozen(),
4905 "Extensible objects should not have frozen elements");
4907 uint32_t initLength
= nobj
->getDenseInitializedLength();
4909 // Optimize if we're adding an element at initLength or writing to a hole.
4911 // In the case where index > initLength, we need noteHasDenseAdd to be called
4912 // to ensure Ion is aware that writes have occurred to-out-of-bound indexes
4915 // TODO(post-Warp): noteHasDenseAdd (nee: noteArrayWriteHole) no longer exists
4916 bool isAdd
= index
== initLength
;
4917 bool isHoleInBounds
=
4918 index
< initLength
&& !nobj
->containsDenseElement(index
);
4919 if (!isAdd
&& !isHoleInBounds
) {
4920 return AttachDecision::NoAction
;
4923 // Can't add new elements to arrays with non-writable length.
4924 if (isAdd
&& nobj
->is
<ArrayObject
>() &&
4925 !nobj
->as
<ArrayObject
>().lengthIsWritable()) {
4926 return AttachDecision::NoAction
;
4929 // Typed arrays don't have dense elements.
4930 if (nobj
->is
<TypedArrayObject
>()) {
4931 return AttachDecision::NoAction
;
4934 // Check for other indexed properties or class hooks.
4935 if (!CanAttachAddElement(nobj
, IsPropertyInitOp(op
),
4936 AllowIndexedReceiver::No
)) {
4937 return AttachDecision::NoAction
;
4940 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4942 // Also shape guard the proto chain, unless this is an InitElem.
4943 if (IsPropertySetOp(op
)) {
4944 ShapeGuardProtoChain(writer
, nobj
, objId
);
4947 writer
.storeDenseElementHole(objId
, indexId
, rhsId
, isAdd
);
4948 writer
.returnFromIC();
4950 trackAttached(isAdd
? "AddDenseElement" : "StoreDenseElementHole");
4951 return AttachDecision::Attach
;
4954 // Add an IC for adding or updating a sparse element.
4955 AttachDecision
SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
4956 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4957 Int32OperandId indexId
, ValOperandId rhsId
) {
4958 JSOp op
= JSOp(*pc_
);
4959 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4961 if (op
!= JSOp::SetElem
&& op
!= JSOp::StrictSetElem
) {
4962 return AttachDecision::NoAction
;
4965 if (!obj
->is
<NativeObject
>()) {
4966 return AttachDecision::NoAction
;
4968 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4970 // We cannot attach a stub to a non-extensible object
4971 if (!nobj
->isExtensible()) {
4972 return AttachDecision::NoAction
;
4975 // Stub doesn't handle negative indices.
4976 if (index
> INT32_MAX
) {
4977 return AttachDecision::NoAction
;
4980 // The index must not be for a dense element.
4981 if (nobj
->containsDenseElement(index
)) {
4982 return AttachDecision::NoAction
;
4985 // Only handle ArrayObject and PlainObject in this stub.
4986 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
4987 return AttachDecision::NoAction
;
4990 // Don't attach if we're adding to an array with non-writable length.
4991 if (nobj
->is
<ArrayObject
>()) {
4992 ArrayObject
* aobj
= &nobj
->as
<ArrayObject
>();
4993 bool isAdd
= (index
>= aobj
->length());
4994 if (isAdd
&& !aobj
->lengthIsWritable()) {
4995 return AttachDecision::NoAction
;
4999 // Check for class hooks or indexed properties on the prototype chain that
5000 // we're not allowed to shadow.
5001 if (!CanAttachAddElement(nobj
, /* isInit = */ false,
5002 AllowIndexedReceiver::Yes
)) {
5003 return AttachDecision::NoAction
;
5006 // Ensure that obj is an ArrayObject or PlainObject.
5007 if (nobj
->is
<ArrayObject
>()) {
5008 writer
.guardClass(objId
, GuardClassKind::Array
);
5010 MOZ_ASSERT(nobj
->is
<PlainObject
>());
5011 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
5014 // The helper we are going to call only applies to non-dense elements.
5015 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
5017 // Guard extensible: We may be trying to add a new element, and so we'd best
5018 // be able to do so safely.
5019 writer
.guardIsExtensible(objId
);
5021 // Ensures we are able to efficiently able to map to an integral jsid.
5022 writer
.guardInt32IsNonNegative(indexId
);
5024 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
5025 // Guard the prototype of the receiver explicitly, because the receiver's
5026 // shape is not being guarded as a proxy for that.
5027 GuardReceiverProto(writer
, nobj
, objId
);
5029 // Dense elements may appear on the prototype chain (and prototypes may
5030 // have a different notion of which elements are dense), but they can
5031 // only be data properties, so our specialized Set handler is ok to bind
5033 if (IsPropertySetOp(op
)) {
5034 ShapeGuardProtoChain(writer
, nobj
, objId
);
5037 // Ensure that if we're adding an element to the object, the object's
5038 // length is writable.
5039 if (nobj
->is
<ArrayObject
>()) {
5040 writer
.guardIndexIsValidUpdateOrAdd(objId
, indexId
);
5043 writer
.callAddOrUpdateSparseElementHelper(
5044 objId
, indexId
, rhsId
,
5045 /* strict = */ op
== JSOp::StrictSetElem
);
5046 writer
.returnFromIC();
5048 trackAttached("SetProp.AddOrUpdateSparseElement");
5049 return AttachDecision::Attach
;
5052 AttachDecision
SetPropIRGenerator::tryAttachSetTypedArrayElement(
5053 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5054 if (!obj
->is
<TypedArrayObject
>()) {
5055 return AttachDecision::NoAction
;
5057 if (!idVal_
.isNumber()) {
5058 return AttachDecision::NoAction
;
5061 auto* tarr
= &obj
->as
<TypedArrayObject
>();
5062 Scalar::Type elementType
= tarr
->type();
5064 // Don't attach if the input type doesn't match the guard added below.
5065 if (!ValueCanConvertToNumeric(elementType
, rhsVal_
)) {
5066 return AttachDecision::NoAction
;
5069 bool handleOOB
= false;
5071 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
5072 uint64_t(indexInt64
) >= tarr
->length().valueOr(0)) {
5076 JSOp op
= JSOp(*pc_
);
5078 // The only expected property init operation is InitElem.
5079 MOZ_ASSERT_IF(IsPropertyInitOp(op
), op
== JSOp::InitElem
);
5081 // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
5082 if (handleOOB
&& IsPropertyInitOp(op
)) {
5083 return AttachDecision::NoAction
;
5086 writer
.guardShapeForClass(objId
, tarr
->shape());
5088 OperandId rhsValId
= emitNumericGuard(rhsId
, rhsVal_
, elementType
);
5090 ValOperandId keyId
= setElemKeyValueId();
5091 IntPtrOperandId indexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
5093 auto viewKind
= ToArrayBufferViewKind(tarr
);
5094 writer
.storeTypedArrayElement(objId
, elementType
, indexId
, rhsValId
,
5095 handleOOB
, viewKind
);
5096 writer
.returnFromIC();
5098 trackAttached(handleOOB
? "SetTypedElementOOB" : "SetTypedElement");
5099 return AttachDecision::Attach
;
5102 AttachDecision
SetPropIRGenerator::tryAttachGenericProxy(
5103 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5104 ValOperandId rhsId
, bool handleDOMProxies
) {
5105 // Don't attach a proxy stub for ops like JSOp::InitElem.
5106 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5108 writer
.guardIsProxy(objId
);
5110 if (!handleDOMProxies
) {
5111 // Ensure that the incoming object is not a DOM proxy, so that we can
5112 // get to the specialized stubs. If handleDOMProxies is true, we were
5113 // unable to attach a specialized DOM stub, so we just handle all
5115 writer
.guardIsNotDOMProxy(objId
);
5118 if (cacheKind_
== CacheKind::SetProp
|| mode_
== ICState::Mode::Specialized
) {
5119 maybeEmitIdGuard(id
);
5120 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5122 // Attach a stub that handles every id.
5123 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5124 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
5125 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
,
5126 IsStrictSetPC(pc_
));
5129 writer
.returnFromIC();
5131 trackAttached("SetProp.GenericProxy");
5132 return AttachDecision::Attach
;
5135 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyShadowed(
5136 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5137 ValOperandId rhsId
) {
5138 // Don't attach a proxy stub for ops like JSOp::InitElem.
5139 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5141 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5143 maybeEmitIdGuard(id
);
5144 TestMatchingProxyReceiver(writer
, obj
, objId
);
5145 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5146 writer
.returnFromIC();
5148 trackAttached("SetProp.DOMProxyShadowed");
5149 return AttachDecision::Attach
;
5152 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
5153 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5154 ValOperandId rhsId
) {
5155 // Don't attach a proxy stub for ops like JSOp::InitElem.
5156 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5158 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5160 JSObject
* proto
= obj
->staticPrototype();
5162 return AttachDecision::NoAction
;
5165 NativeObject
* holder
= nullptr;
5166 Maybe
<PropertyInfo
> prop
;
5167 if (!CanAttachSetter(cx_
, pc_
, proto
, id
, &holder
, &prop
)) {
5168 return AttachDecision::NoAction
;
5170 auto* nproto
= &proto
->as
<NativeObject
>();
5172 maybeEmitIdGuard(id
);
5174 // Guard that our proxy (expando) object hasn't started shadowing this
5176 TestMatchingProxyReceiver(writer
, obj
, objId
);
5177 bool canOptimizeMissing
= false;
5178 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
5180 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
5182 // Guard on the holder of the property.
5183 ObjOperandId holderId
= writer
.loadObject(holder
);
5184 TestMatchingHolder(writer
, holder
, holderId
);
5186 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
5187 /* holderIsConstant = */ true);
5189 // EmitCallSetterNoGuards expects |obj| to be the object the property is
5190 // on to do some checks. Since we actually looked at proto, and no extra
5191 // guards will be generated, we can just pass that instead.
5192 EmitCallSetterNoGuards(cx_
, writer
, nproto
, holder
, *prop
, objId
, rhsId
);
5194 trackAttached("SetProp.DOMProxyUnshadowed");
5195 return AttachDecision::Attach
;
5198 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyExpando(
5199 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5200 ValOperandId rhsId
) {
5201 // Don't attach a proxy stub for ops like JSOp::InitElem.
5202 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5204 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5206 Value expandoVal
= GetProxyPrivate(obj
);
5207 JSObject
* expandoObj
;
5208 if (expandoVal
.isObject()) {
5209 expandoObj
= &expandoVal
.toObject();
5211 MOZ_ASSERT(!expandoVal
.isUndefined(),
5212 "How did a missing expando manage to shadow things?");
5213 auto expandoAndGeneration
=
5214 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
5215 MOZ_ASSERT(expandoAndGeneration
);
5216 expandoObj
= &expandoAndGeneration
->expando
.toObject();
5219 Maybe
<PropertyInfo
> prop
;
5220 if (CanAttachNativeSetSlot(JSOp(*pc_
), expandoObj
, id
, &prop
)) {
5221 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5223 maybeEmitIdGuard(id
);
5224 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5225 obj
, objId
, expandoVal
, nativeExpandoObj
);
5227 EmitStoreSlotAndReturn(writer
, expandoObjId
, nativeExpandoObj
, *prop
,
5229 trackAttached("SetProp.DOMProxyExpandoSlot");
5230 return AttachDecision::Attach
;
5233 NativeObject
* holder
= nullptr;
5234 if (CanAttachSetter(cx_
, pc_
, expandoObj
, id
, &holder
, &prop
)) {
5235 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5237 // Call the setter. Note that we pass objId, the DOM proxy, as |this|
5238 // and not the expando object.
5239 maybeEmitIdGuard(id
);
5240 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5241 obj
, objId
, expandoVal
, nativeExpandoObj
);
5243 MOZ_ASSERT(holder
== nativeExpandoObj
);
5244 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
5245 EmitCallSetterNoGuards(cx_
, writer
, nativeExpandoObj
, nativeExpandoObj
,
5246 *prop
, objId
, rhsId
);
5247 trackAttached("SetProp.DOMProxyExpandoSetter");
5248 return AttachDecision::Attach
;
5251 return AttachDecision::NoAction
;
5254 AttachDecision
SetPropIRGenerator::tryAttachProxy(HandleObject obj
,
5257 ValOperandId rhsId
) {
5258 // Don't attach a proxy stub for ops like JSOp::InitElem.
5259 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5261 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
5262 if (type
== ProxyStubType::None
) {
5263 return AttachDecision::NoAction
;
5265 auto proxy
= obj
.as
<ProxyObject
>();
5267 if (mode_
== ICState::Mode::Megamorphic
) {
5268 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5269 /* handleDOMProxies = */ true);
5273 case ProxyStubType::None
:
5275 case ProxyStubType::DOMExpando
:
5276 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, rhsId
));
5277 [[fallthrough
]]; // Fall through to the generic shadowed case.
5278 case ProxyStubType::DOMShadowed
:
5279 return tryAttachDOMProxyShadowed(proxy
, objId
, id
, rhsId
);
5280 case ProxyStubType::DOMUnshadowed
:
5281 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, rhsId
));
5282 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5283 /* handleDOMProxies = */ true);
5284 case ProxyStubType::Generic
:
5285 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5286 /* handleDOMProxies = */ false);
5289 MOZ_CRASH("Unexpected ProxyStubType");
5292 AttachDecision
SetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
5294 ValOperandId rhsId
) {
5295 // Don't attach a proxy stub for ops like JSOp::InitElem.
5296 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5298 if (!obj
->is
<ProxyObject
>()) {
5299 return AttachDecision::NoAction
;
5302 writer
.guardIsProxy(objId
);
5304 // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
5305 // proxies here as we don't have specialized DOM stubs for this.
5306 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5307 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
, IsStrictSetPC(pc_
));
5308 writer
.returnFromIC();
5310 trackAttached("SetProp.ProxyElement");
5311 return AttachDecision::Attach
;
5314 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetElement(
5315 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5316 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5318 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetElem
) {
5319 return AttachDecision::NoAction
;
5322 // The generic proxy stubs are faster.
5323 if (obj
->is
<ProxyObject
>()) {
5324 return AttachDecision::NoAction
;
5327 writer
.megamorphicSetElement(objId
, setElemKeyValueId(), rhsId
,
5328 IsStrictSetPC(pc_
));
5329 writer
.returnFromIC();
5331 trackAttached("SetProp.MegamorphicSetElement");
5332 return AttachDecision::Attach
;
5335 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetSlot(
5336 HandleObject obj
, ObjOperandId objId
, HandleId id
, ValOperandId rhsId
) {
5337 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetProp
) {
5338 return AttachDecision::NoAction
;
5341 writer
.megamorphicStoreSlot(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5342 writer
.returnFromIC();
5343 trackAttached("SetProp.MegamorphicNativeSlot");
5344 return AttachDecision::Attach
;
5347 AttachDecision
SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
5350 ValOperandId rhsId
) {
5351 // Don't attach a window proxy stub for ops like JSOp::InitElem.
5352 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5354 // Attach a stub when the receiver is a WindowProxy and we can do the set
5355 // on the Window (the global object).
5357 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
5358 return AttachDecision::NoAction
;
5361 // If we're megamorphic prefer a generic proxy stub that handles a lot more
5363 if (mode_
== ICState::Mode::Megamorphic
) {
5364 return AttachDecision::NoAction
;
5367 // Now try to do the set on the Window (the current global).
5368 GlobalObject
* windowObj
= cx_
->global();
5370 Maybe
<PropertyInfo
> prop
;
5371 if (!CanAttachNativeSetSlot(JSOp(*pc_
), windowObj
, id
, &prop
)) {
5372 return AttachDecision::NoAction
;
5375 maybeEmitIdGuard(id
);
5377 ObjOperandId windowObjId
=
5378 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
5379 writer
.guardShape(windowObjId
, windowObj
->shape());
5381 EmitStoreSlotAndReturn(writer
, windowObjId
, windowObj
, *prop
, rhsId
);
5383 trackAttached("SetProp.WindowProxySlot");
5384 return AttachDecision::Attach
;
5387 // Detect if |id| refers to the 'prototype' property of a function object. This
5388 // property is special-cased in canAttachAddSlotStub().
5389 static bool IsFunctionPrototype(const JSAtomState
& names
, JSObject
* obj
,
5391 return obj
->is
<JSFunction
>() && id
.isAtom(names
.prototype
);
5394 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj
, HandleId id
) {
5395 if (!obj
->is
<NativeObject
>()) {
5398 auto* nobj
= &obj
->as
<NativeObject
>();
5400 // Special-case JSFunction resolve hook to allow redefining the 'prototype'
5401 // property without triggering lazy expansion of property and object
5403 if (IsFunctionPrototype(cx_
->names(), nobj
, id
)) {
5404 MOZ_ASSERT(ClassMayResolveId(cx_
->names(), nobj
->getClass(), id
, nobj
));
5406 // We're only interested in functions that have a builtin .prototype
5407 // property (needsPrototypeProperty). The stub will guard on this because
5408 // the builtin .prototype property is non-configurable/non-enumerable and it
5409 // would be wrong to add a property with those attributes to a function that
5410 // doesn't have a builtin .prototype.
5412 // Inlining needsPrototypeProperty in JIT code is complicated so we use
5413 // isNonBuiltinConstructor as a stronger condition that's easier to check
5415 JSFunction
* fun
= &nobj
->as
<JSFunction
>();
5416 if (!fun
->isNonBuiltinConstructor()) {
5419 MOZ_ASSERT(fun
->needsPrototypeProperty());
5421 // If property exists this isn't an "add".
5422 if (fun
->lookupPure(id
)) {
5426 // Normal Case: If property exists this isn't an "add"
5427 PropertyResult prop
;
5428 if (!LookupOwnPropertyPure(cx_
, nobj
, id
, &prop
)) {
5431 if (prop
.isFound()) {
5436 // For now we don't optimize Watchtower-monitored objects.
5437 if (Watchtower::watchesPropertyAdd(nobj
)) {
5441 // Object must be extensible, or we must be initializing a private
5443 bool canAddNewProperty
= nobj
->isExtensible() || id
.isPrivateName();
5444 if (!canAddNewProperty
) {
5448 JSOp op
= JSOp(*pc_
);
5449 if (IsPropertyInitOp(op
)) {
5453 MOZ_ASSERT(IsPropertySetOp(op
));
5455 // Walk up the object prototype chain and ensure that all prototypes are
5456 // native, and that all prototypes have no setter defined on the property.
5457 for (JSObject
* proto
= nobj
->staticPrototype(); proto
;
5458 proto
= proto
->staticPrototype()) {
5459 if (!proto
->is
<NativeObject
>()) {
5463 // If prototype defines this property in a non-plain way, don't optimize.
5464 Maybe
<PropertyInfo
> protoProp
= proto
->as
<NativeObject
>().lookup(cx_
, id
);
5465 if (protoProp
.isSome() && !protoProp
->isDataProperty()) {
5469 // Otherwise, if there's no such property, watch out for a resolve hook
5470 // that would need to be invoked and thus prevent inlining of property
5471 // addition. Allow the JSFunction resolve hook as it only defines plain
5472 // data properties and we don't need to invoke it for objects on the
5474 if (ClassMayResolveId(cx_
->names(), proto
->getClass(), id
, proto
) &&
5475 !proto
->is
<JSFunction
>()) {
5483 static PropertyFlags
SetPropertyFlags(JSOp op
, bool isFunctionPrototype
) {
5484 // Locked properties are non-writable, non-enumerable, and non-configurable.
5485 if (IsLockedInitOp(op
)) {
5489 // Hidden properties are writable, non-enumerable, and configurable.
5490 if (IsHiddenInitOp(op
)) {
5492 PropertyFlag::Writable
,
5493 PropertyFlag::Configurable
,
5497 // This is a special case to overwrite an unresolved function.prototype
5498 // property. The initial property flags of this property are writable,
5499 // non-enumerable, and non-configurable. See canAttachAddSlotStub.
5500 if (isFunctionPrototype
) {
5502 PropertyFlag::Writable
,
5506 // Other properties are writable, enumerable, and configurable.
5507 return PropertyFlags::defaultDataPropFlags
;
5510 AttachDecision
SetPropIRGenerator::tryAttachAddSlotStub(
5511 Handle
<Shape
*> oldShape
) {
5512 ValOperandId
objValId(writer
.setInputOperandId(0));
5513 ValOperandId rhsValId
;
5514 if (cacheKind_
== CacheKind::SetProp
) {
5515 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
5517 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5518 MOZ_ASSERT(setElemKeyValueId().id() == 1);
5519 writer
.setInputOperandId(1);
5520 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
5525 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
5526 cx_
->clearPendingException();
5527 return AttachDecision::NoAction
;
5530 if (!lhsVal_
.isObject() || !nameOrSymbol
) {
5531 return AttachDecision::NoAction
;
5534 JSObject
* obj
= &lhsVal_
.toObject();
5536 PropertyResult prop
;
5537 if (!LookupOwnPropertyPure(cx_
, obj
, id
, &prop
)) {
5538 return AttachDecision::NoAction
;
5540 if (prop
.isNotFound()) {
5541 return AttachDecision::NoAction
;
5544 if (!obj
->is
<NativeObject
>()) {
5545 return AttachDecision::NoAction
;
5547 auto* nobj
= &obj
->as
<NativeObject
>();
5549 PropertyInfo propInfo
= prop
.propertyInfo();
5550 NativeObject
* holder
= nobj
;
5552 if (holder
->inDictionaryMode()) {
5553 return AttachDecision::NoAction
;
5556 SharedShape
* oldSharedShape
= &oldShape
->asShared();
5558 // The property must be the last added property of the object.
5559 SharedShape
* newShape
= holder
->sharedShape();
5560 MOZ_RELEASE_ASSERT(newShape
->lastProperty() == propInfo
);
5563 // Verify exactly one property was added by comparing the property map
5565 if (oldSharedShape
->propMapLength() == PropMap::Capacity
) {
5566 MOZ_ASSERT(newShape
->propMapLength() == 1);
5568 MOZ_ASSERT(newShape
->propMapLength() ==
5569 oldSharedShape
->propMapLength() + 1);
5573 bool isFunctionPrototype
= IsFunctionPrototype(cx_
->names(), nobj
, id
);
5575 JSOp op
= JSOp(*pc_
);
5576 PropertyFlags flags
= SetPropertyFlags(op
, isFunctionPrototype
);
5578 // Basic property checks.
5579 if (!propInfo
.isDataProperty() || propInfo
.flags() != flags
) {
5580 return AttachDecision::NoAction
;
5583 ObjOperandId objId
= writer
.guardToObject(objValId
);
5584 maybeEmitIdGuard(id
);
5586 // Shape guard the object.
5587 writer
.guardShape(objId
, oldShape
);
5589 // If this is the special function.prototype case, we need to guard the
5590 // function is a non-builtin constructor. See canAttachAddSlotStub.
5591 if (isFunctionPrototype
) {
5592 MOZ_ASSERT(nobj
->as
<JSFunction
>().isNonBuiltinConstructor());
5593 writer
.guardFunctionIsNonBuiltinCtor(objId
);
5596 // Also shape guard the proto chain, unless this is an InitElem.
5597 if (IsPropertySetOp(op
)) {
5598 ShapeGuardProtoChain(writer
, nobj
, objId
);
5601 // If the JSClass has an addProperty hook, we need to call a VM function to
5602 // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
5603 // anything for non-index properties.
5604 DebugOnly
<uint32_t> index
;
5605 MOZ_ASSERT_IF(obj
->is
<ArrayObject
>(), !IdIsIndex(id
, &index
));
5606 bool mustCallAddPropertyHook
=
5607 obj
->getClass()->getAddProperty() && !obj
->is
<ArrayObject
>();
5609 if (mustCallAddPropertyHook
) {
5610 writer
.addSlotAndCallAddPropHook(objId
, rhsValId
, newShape
);
5611 trackAttached("SetProp.AddSlotWithAddPropertyHook");
5612 } else if (holder
->isFixedSlot(propInfo
.slot())) {
5613 size_t offset
= NativeObject::getFixedSlotOffset(propInfo
.slot());
5614 writer
.addAndStoreFixedSlot(objId
, offset
, rhsValId
, newShape
);
5615 trackAttached("SetProp.AddSlotFixed");
5617 size_t offset
= holder
->dynamicSlotIndex(propInfo
.slot()) * sizeof(Value
);
5618 uint32_t numOldSlots
= NativeObject::calculateDynamicSlots(oldSharedShape
);
5619 uint32_t numNewSlots
= holder
->numDynamicSlots();
5620 if (numOldSlots
== numNewSlots
) {
5621 writer
.addAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
);
5622 trackAttached("SetProp.AddSlotDynamic");
5624 MOZ_ASSERT(numNewSlots
> numOldSlots
);
5625 writer
.allocateAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
,
5627 trackAttached("SetProp.AllocateSlot");
5630 writer
.returnFromIC();
5632 return AttachDecision::Attach
;
5635 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext
* cx
, HandleScript script
,
5636 jsbytecode
* pc
, ICState state
,
5637 HandleValue lhs
, HandleObject rhs
)
5638 : IRGenerator(cx
, script
, pc
, CacheKind::InstanceOf
, state
),
5642 AttachDecision
InstanceOfIRGenerator::tryAttachStub() {
5643 MOZ_ASSERT(cacheKind_
== CacheKind::InstanceOf
);
5644 AutoAssertNoPendingException
aanpe(cx_
);
5646 // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
5648 if (!rhsObj_
->is
<JSFunction
>()) {
5649 trackAttached(IRGenerator::NotAttached
);
5650 return AttachDecision::NoAction
;
5653 HandleFunction fun
= rhsObj_
.as
<JSFunction
>();
5655 // Look up the @@hasInstance property, and check that Function.__proto__ is
5656 // the property holder, and that no object further down the prototype chain
5657 // (including this function) has shadowed it; together with the fact that
5658 // Function.__proto__[@@hasInstance] is immutable, this ensures that the
5659 // hasInstance hook will not change without the need to guard on the actual
5661 PropertyResult hasInstanceProp
;
5662 NativeObject
* hasInstanceHolder
= nullptr;
5663 jsid hasInstanceID
= PropertyKey::Symbol(cx_
->wellKnownSymbols().hasInstance
);
5664 if (!LookupPropertyPure(cx_
, fun
, hasInstanceID
, &hasInstanceHolder
,
5665 &hasInstanceProp
) ||
5666 !hasInstanceProp
.isNativeProperty()) {
5667 trackAttached(IRGenerator::NotAttached
);
5668 return AttachDecision::NoAction
;
5671 JSObject
& funProto
= cx_
->global()->getPrototype(JSProto_Function
);
5672 if (hasInstanceHolder
!= &funProto
) {
5673 trackAttached(IRGenerator::NotAttached
);
5674 return AttachDecision::NoAction
;
5677 // If the above succeeded, then these should be true about @@hasInstance,
5678 // because the property on Function.__proto__ is an immutable data property:
5679 MOZ_ASSERT(hasInstanceProp
.propertyInfo().isDataProperty());
5680 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().configurable());
5681 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().writable());
5683 MOZ_ASSERT(IsCacheableProtoChain(fun
, hasInstanceHolder
));
5685 // Ensure that the function's prototype slot is the same.
5686 Maybe
<PropertyInfo
> prop
= fun
->lookupPure(cx_
->names().prototype
);
5687 if (prop
.isNothing() || !prop
->isDataProperty()) {
5688 trackAttached(IRGenerator::NotAttached
);
5689 return AttachDecision::NoAction
;
5692 uint32_t slot
= prop
->slot();
5693 MOZ_ASSERT(slot
>= fun
->numFixedSlots(), "Stub code relies on this");
5694 if (!fun
->getSlot(slot
).isObject()) {
5695 trackAttached(IRGenerator::NotAttached
);
5696 return AttachDecision::NoAction
;
5700 ValOperandId
lhs(writer
.setInputOperandId(0));
5701 ValOperandId
rhs(writer
.setInputOperandId(1));
5703 ObjOperandId rhsId
= writer
.guardToObject(rhs
);
5704 writer
.guardShape(rhsId
, fun
->shape());
5706 // Ensure that the shapes up the prototype chain for the RHS remain the same
5707 // so that @@hasInstance is not shadowed by some intermediate prototype
5709 if (hasInstanceHolder
!= fun
) {
5710 GeneratePrototypeGuards(writer
, fun
, hasInstanceHolder
, rhsId
);
5711 ObjOperandId holderId
= writer
.loadObject(hasInstanceHolder
);
5712 TestMatchingHolder(writer
, hasInstanceHolder
, holderId
);
5715 // Load the .prototype value and ensure it's an object.
5716 ValOperandId protoValId
=
5717 writer
.loadDynamicSlot(rhsId
, slot
- fun
->numFixedSlots());
5718 ObjOperandId protoId
= writer
.guardToObject(protoValId
);
5720 // Needn't guard LHS is object, because the actual stub can handle that
5721 // and correctly return false.
5722 writer
.loadInstanceOfObjectResult(lhs
, protoId
);
5723 writer
.returnFromIC();
5724 trackAttached("InstanceOf");
5725 return AttachDecision::Attach
;
5728 void InstanceOfIRGenerator::trackAttached(const char* name
) {
5729 stubName_
= name
? name
: "NotAttached";
5730 #ifdef JS_CACHEIR_SPEW
5731 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5732 sp
.valueProperty("lhs", lhsVal_
);
5733 sp
.valueProperty("rhs", ObjectValue(*rhsObj_
));
5736 // Silence Clang -Wunused-private-field warning.
5741 TypeOfIRGenerator::TypeOfIRGenerator(JSContext
* cx
, HandleScript script
,
5742 jsbytecode
* pc
, ICState state
,
5744 : IRGenerator(cx
, script
, pc
, CacheKind::TypeOf
, state
), val_(value
) {}
5746 void TypeOfIRGenerator::trackAttached(const char* name
) {
5747 stubName_
= name
? name
: "NotAttached";
5748 #ifdef JS_CACHEIR_SPEW
5749 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5750 sp
.valueProperty("val", val_
);
5755 AttachDecision
TypeOfIRGenerator::tryAttachStub() {
5756 MOZ_ASSERT(cacheKind_
== CacheKind::TypeOf
);
5758 AutoAssertNoPendingException
aanpe(cx_
);
5760 ValOperandId
valId(writer
.setInputOperandId(0));
5762 TRY_ATTACH(tryAttachPrimitive(valId
));
5763 TRY_ATTACH(tryAttachObject(valId
));
5765 MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
5766 return AttachDecision::NoAction
;
5769 AttachDecision
TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId
) {
5770 if (!val_
.isPrimitive()) {
5771 return AttachDecision::NoAction
;
5774 // Note: we don't use GuardIsNumber for int32 values because it's less
5775 // efficient in Warp (unboxing to double instead of int32).
5776 if (val_
.isDouble()) {
5777 writer
.guardIsNumber(valId
);
5779 writer
.guardNonDoubleType(valId
, val_
.type());
5782 writer
.loadConstantStringResult(
5783 TypeName(js::TypeOfValue(val_
), cx_
->names()));
5784 writer
.returnFromIC();
5785 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5786 trackAttached("TypeOf.Primitive");
5787 return AttachDecision::Attach
;
5790 AttachDecision
TypeOfIRGenerator::tryAttachObject(ValOperandId valId
) {
5791 if (!val_
.isObject()) {
5792 return AttachDecision::NoAction
;
5795 ObjOperandId objId
= writer
.guardToObject(valId
);
5796 writer
.loadTypeOfObjectResult(objId
);
5797 writer
.returnFromIC();
5798 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5799 trackAttached("TypeOf.Object");
5800 return AttachDecision::Attach
;
5803 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext
* cx
,
5804 HandleScript script
,
5805 jsbytecode
* pc
, ICState state
,
5807 : IRGenerator(cx
, script
, pc
, CacheKind::GetIterator
, state
), val_(value
) {}
5809 AttachDecision
GetIteratorIRGenerator::tryAttachStub() {
5810 MOZ_ASSERT(cacheKind_
== CacheKind::GetIterator
);
5812 AutoAssertNoPendingException
aanpe(cx_
);
5814 ValOperandId
valId(writer
.setInputOperandId(0));
5816 TRY_ATTACH(tryAttachObject(valId
));
5817 TRY_ATTACH(tryAttachNullOrUndefined(valId
));
5818 TRY_ATTACH(tryAttachGeneric(valId
));
5820 trackAttached(IRGenerator::NotAttached
);
5821 return AttachDecision::NoAction
;
5824 AttachDecision
GetIteratorIRGenerator::tryAttachObject(ValOperandId valId
) {
5825 if (!val_
.isObject()) {
5826 return AttachDecision::NoAction
;
5829 MOZ_ASSERT(val_
.toObject().compartment() == cx_
->compartment());
5831 ObjOperandId objId
= writer
.guardToObject(valId
);
5832 writer
.objectToIteratorResult(objId
, cx_
->compartment()->enumeratorsAddr());
5833 writer
.returnFromIC();
5835 trackAttached("GetIterator.Object");
5836 return AttachDecision::Attach
;
5839 AttachDecision
GetIteratorIRGenerator::tryAttachNullOrUndefined(
5840 ValOperandId valId
) {
5841 MOZ_ASSERT(JSOp(*pc_
) == JSOp::Iter
);
5843 // For null/undefined we can simply return the empty iterator singleton. This
5844 // works because this iterator is unlinked and immutable.
5846 if (!val_
.isNullOrUndefined()) {
5847 return AttachDecision::NoAction
;
5850 PropertyIteratorObject
* emptyIter
=
5851 GlobalObject::getOrCreateEmptyIterator(cx_
);
5853 cx_
->recoverFromOutOfMemory();
5854 return AttachDecision::NoAction
;
5857 writer
.guardIsNullOrUndefined(valId
);
5859 ObjOperandId iterId
= writer
.loadObject(emptyIter
);
5860 writer
.loadObjectResult(iterId
);
5861 writer
.returnFromIC();
5863 trackAttached("GetIterator.NullOrUndefined");
5864 return AttachDecision::Attach
;
5867 AttachDecision
GetIteratorIRGenerator::tryAttachGeneric(ValOperandId valId
) {
5868 writer
.valueToIteratorResult(valId
);
5869 writer
.returnFromIC();
5871 trackAttached("GetIterator.Generic");
5872 return AttachDecision::Attach
;
5875 void GetIteratorIRGenerator::trackAttached(const char* name
) {
5876 stubName_
= name
? name
: "NotAttached";
5877 #ifdef JS_CACHEIR_SPEW
5878 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5879 sp
.valueProperty("val", val_
);
5884 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
5885 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
5887 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeSpreadCall
, state
),
5890 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachStub() {
5891 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeSpreadCall
);
5893 AutoAssertNoPendingException
aanpe(cx_
);
5895 TRY_ATTACH(tryAttachArray());
5896 TRY_ATTACH(tryAttachArguments());
5897 TRY_ATTACH(tryAttachNotOptimizable());
5899 trackAttached(IRGenerator::NotAttached
);
5900 return AttachDecision::NoAction
;
5903 static bool IsArrayInstanceOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5904 MutableHandle
<NativeObject
*> arrProto
) {
5905 // Prototype must be Array.prototype.
5906 auto* proto
= cx
->global()->maybeGetArrayPrototype();
5907 if (!proto
|| arr
->staticPrototype() != proto
) {
5910 arrProto
.set(proto
);
5912 // The object must not have an own @@iterator property.
5913 PropertyKey iteratorKey
=
5914 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5915 return !arr
->lookupPure(iteratorKey
);
5918 static bool IsArrayPrototypeOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
5919 Handle
<NativeObject
*> arrProto
,
5921 MutableHandle
<JSFunction
*> iterFun
) {
5922 PropertyKey iteratorKey
=
5923 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
5924 // Ensure that Array.prototype's @@iterator slot is unchanged.
5925 Maybe
<PropertyInfo
> prop
= arrProto
->lookupPure(iteratorKey
);
5926 if (prop
.isNothing() || !prop
->isDataProperty()) {
5930 *slot
= prop
->slot();
5931 MOZ_ASSERT(arrProto
->numFixedSlots() == 0, "Stub code relies on this");
5933 const Value
& iterVal
= arrProto
->getSlot(*slot
);
5934 if (!iterVal
.isObject() || !iterVal
.toObject().is
<JSFunction
>()) {
5938 iterFun
.set(&iterVal
.toObject().as
<JSFunction
>());
5939 return IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
);
5942 enum class AllowIteratorReturn
: bool {
5946 static bool IsArrayIteratorPrototypeOptimizable(
5947 JSContext
* cx
, AllowIteratorReturn allowReturn
,
5948 MutableHandle
<NativeObject
*> arrIterProto
, uint32_t* slot
,
5949 MutableHandle
<JSFunction
*> nextFun
) {
5950 NativeObject
* proto
= nullptr;
5952 AutoEnterOOMUnsafeRegion oom
;
5953 proto
= GlobalObject::getOrCreateArrayIteratorPrototype(cx
, cx
->global());
5955 oom
.crash("failed to allocate Array iterator prototype");
5958 arrIterProto
.set(proto
);
5960 // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
5961 Maybe
<PropertyInfo
> prop
= proto
->lookupPure(cx
->names().next
);
5962 if (prop
.isNothing() || !prop
->isDataProperty()) {
5966 *slot
= prop
->slot();
5967 MOZ_ASSERT(proto
->numFixedSlots() == 0, "Stub code relies on this");
5969 const Value
& nextVal
= proto
->getSlot(*slot
);
5970 if (!nextVal
.isObject() || !nextVal
.toObject().is
<JSFunction
>()) {
5974 nextFun
.set(&nextVal
.toObject().as
<JSFunction
>());
5975 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
5979 if (allowReturn
== AllowIteratorReturn::No
) {
5980 // Ensure that %ArrayIteratorPrototype% doesn't define "return".
5981 if (!CheckHasNoSuchProperty(cx
, proto
, NameToId(cx
->names().return_
))) {
5989 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArray() {
5990 if (!isFirstStub_
) {
5991 return AttachDecision::NoAction
;
5994 // The value must be a packed array.
5995 if (!val_
.isObject()) {
5996 return AttachDecision::NoAction
;
5998 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
5999 if (!IsPackedArray(obj
)) {
6000 return AttachDecision::NoAction
;
6003 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
6005 Rooted
<NativeObject
*> arrProto(cx_
);
6006 uint32_t arrProtoIterSlot
;
6007 Rooted
<JSFunction
*> iterFun(cx_
);
6008 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
6009 return AttachDecision::NoAction
;
6012 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
6013 &arrProtoIterSlot
, &iterFun
)) {
6014 return AttachDecision::NoAction
;
6017 // %ArrayIteratorPrototype%.next must not be modified.
6018 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
6019 uint32_t iterNextSlot
;
6020 Rooted
<JSFunction
*> nextFun(cx_
);
6021 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
6022 &arrayIteratorProto
, &iterNextSlot
,
6024 return AttachDecision::NoAction
;
6027 ValOperandId
valId(writer
.setInputOperandId(0));
6028 ObjOperandId objId
= writer
.guardToObject(valId
);
6030 // Guard the object is a packed array with Array.prototype as proto.
6031 MOZ_ASSERT(obj
->is
<ArrayObject
>());
6032 writer
.guardShape(objId
, obj
->shape());
6033 writer
.guardArrayIsPacked(objId
);
6035 // Guard on Array.prototype[@@iterator].
6036 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
6037 ObjOperandId iterId
= writer
.loadObject(iterFun
);
6038 writer
.guardShape(arrProtoId
, arrProto
->shape());
6039 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
, arrProtoIterSlot
);
6041 // Guard on %ArrayIteratorPrototype%.next.
6042 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
6043 ObjOperandId nextId
= writer
.loadObject(nextFun
);
6044 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
6045 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, iterNextSlot
);
6047 writer
.loadObjectResult(objId
);
6048 writer
.returnFromIC();
6050 trackAttached("OptimizeSpreadCall.Array");
6051 return AttachDecision::Attach
;
6054 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArguments() {
6055 // The value must be an arguments object.
6056 if (!val_
.isObject()) {
6057 return AttachDecision::NoAction
;
6059 RootedObject
obj(cx_
, &val_
.toObject());
6060 if (!obj
->is
<ArgumentsObject
>()) {
6061 return AttachDecision::NoAction
;
6063 auto args
= obj
.as
<ArgumentsObject
>();
6065 // Ensure neither elements, nor the length, nor the iterator has been
6066 // overridden. Also ensure no args are forwarded to allow reading them
6067 // directly from the frame.
6068 if (args
->hasOverriddenElement() || args
->hasOverriddenLength() ||
6069 args
->hasOverriddenIterator() || args
->anyArgIsForwarded()) {
6070 return AttachDecision::NoAction
;
6073 Rooted
<Shape
*> shape(cx_
, GlobalObject::getArrayShapeWithDefaultProto(cx_
));
6075 cx_
->clearPendingException();
6076 return AttachDecision::NoAction
;
6079 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
6081 Rooted
<JSFunction
*> nextFun(cx_
);
6082 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
6083 &arrayIteratorProto
, &slot
,
6085 return AttachDecision::NoAction
;
6088 ValOperandId
valId(writer
.setInputOperandId(0));
6089 ObjOperandId objId
= writer
.guardToObject(valId
);
6091 if (args
->is
<MappedArgumentsObject
>()) {
6092 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6094 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6095 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6097 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6098 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6099 ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
|
6100 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6101 writer
.guardArgumentsObjectFlags(objId
, flags
);
6103 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
6104 ObjOperandId nextId
= writer
.loadObject(nextFun
);
6106 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
6108 // Ensure that proto[slot] == nextFun.
6109 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
6111 writer
.arrayFromArgumentsObjectResult(objId
, shape
);
6112 writer
.returnFromIC();
6114 trackAttached("OptimizeSpreadCall.Arguments");
6115 return AttachDecision::Attach
;
6118 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
6119 ValOperandId
valId(writer
.setInputOperandId(0));
6121 writer
.loadUndefinedResult();
6122 writer
.returnFromIC();
6124 trackAttached("OptimizeSpreadCall.NotOptimizable");
6125 return AttachDecision::Attach
;
6128 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name
) {
6129 stubName_
= name
? name
: "NotAttached";
6130 #ifdef JS_CACHEIR_SPEW
6131 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
6132 sp
.valueProperty("val", val_
);
6137 CallIRGenerator::CallIRGenerator(JSContext
* cx
, HandleScript script
,
6138 jsbytecode
* pc
, JSOp op
, ICState state
,
6139 uint32_t argc
, HandleValue callee
,
6140 HandleValue thisval
, HandleValue newTarget
,
6141 HandleValueArray args
)
6142 : IRGenerator(cx
, script
, pc
, CacheKind::Call
, state
),
6147 newTarget_(newTarget
),
6150 void InlinableNativeIRGenerator::emitNativeCalleeGuard() {
6151 // Note: we rely on GuardSpecificFunction to also guard against the same
6152 // native from a different realm.
6153 MOZ_ASSERT(callee_
->isNativeWithoutJitEntry());
6155 ObjOperandId calleeObjId
;
6156 if (flags_
.getArgFormat() == CallFlags::Standard
) {
6157 ValOperandId calleeValId
=
6158 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
6159 calleeObjId
= writer
.guardToObject(calleeValId
);
6160 } else if (flags_
.getArgFormat() == CallFlags::Spread
) {
6161 ValOperandId calleeValId
=
6162 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
6163 calleeObjId
= writer
.guardToObject(calleeValId
);
6164 } else if (flags_
.getArgFormat() == CallFlags::FunCall
) {
6165 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
6167 Int32OperandId
argcId(0);
6168 calleeObjId
= generator_
.emitFunCallOrApplyGuard(argcId
);
6170 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
6171 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
6173 Int32OperandId
argcId(0);
6174 calleeObjId
= generator_
.emitFunApplyGuard(argcId
);
6177 writer
.guardSpecificFunction(calleeObjId
, callee_
);
6179 // If we're constructing we also need to guard newTarget == callee.
6180 if (flags_
.isConstructing()) {
6181 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
6182 MOZ_ASSERT(&newTarget_
.toObject() == callee_
);
6184 ValOperandId newTargetValId
=
6185 writer
.loadArgumentFixedSlot(ArgumentKind::NewTarget
, argc_
, flags_
);
6186 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
6187 writer
.guardSpecificFunction(newTargetObjId
, callee_
);
6191 ObjOperandId
InlinableNativeIRGenerator::emitLoadArgsArray() {
6192 if (flags_
.getArgFormat() == CallFlags::Spread
) {
6193 return writer
.loadSpreadArgs();
6196 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
6197 return generator_
.emitFunApplyArgsGuard(flags_
.getArgFormat()).ref();
6200 void IRGenerator::emitCalleeGuard(ObjOperandId calleeId
, JSFunction
* callee
) {
6201 // Guarding on the callee JSFunction* is most efficient, but doesn't work well
6202 // for lambda clones (multiple functions with the same BaseScript). We guard
6203 // on the function's BaseScript if the callee is scripted and this isn't the
6206 // Self-hosted functions are more complicated: top-level functions can be
6207 // relazified using SelfHostedLazyScript and this means they don't have a
6208 // stable BaseScript pointer. These functions are never lambda clones, though,
6209 // so we can just always guard on the JSFunction*. Self-hosted lambdas are
6210 // never relazified so there we use the normal heuristics.
6211 if (isFirstStub_
|| !callee
->hasBaseScript() ||
6212 (callee
->isSelfHostedBuiltin() && !callee
->isLambda())) {
6213 writer
.guardSpecificFunction(calleeId
, callee
);
6215 MOZ_ASSERT_IF(callee
->isSelfHostedBuiltin(),
6216 !callee
->baseScript()->allowRelazify());
6217 writer
.guardClass(calleeId
, GuardClassKind::JSFunction
);
6218 writer
.guardFunctionScript(calleeId
, callee
->baseScript());
6222 ObjOperandId
CallIRGenerator::emitFunCallOrApplyGuard(Int32OperandId argcId
) {
6223 JSFunction
* callee
= &callee_
.toObject().as
<JSFunction
>();
6224 MOZ_ASSERT(callee
->native() == fun_call
|| callee
->native() == fun_apply
);
6226 // Guard that callee is the |fun_call| or |fun_apply| native function.
6227 ValOperandId calleeValId
=
6228 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
);
6229 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
6230 writer
.guardSpecificFunction(calleeObjId
, callee
);
6232 // Guard that |this| is an object.
6233 ValOperandId thisValId
=
6234 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
);
6235 return writer
.guardToObject(thisValId
);
6238 ObjOperandId
CallIRGenerator::emitFunCallGuard(Int32OperandId argcId
) {
6239 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_call
);
6241 return emitFunCallOrApplyGuard(argcId
);
6244 ObjOperandId
CallIRGenerator::emitFunApplyGuard(Int32OperandId argcId
) {
6245 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_apply
);
6247 return emitFunCallOrApplyGuard(argcId
);
6250 Maybe
<ObjOperandId
> CallIRGenerator::emitFunApplyArgsGuard(
6251 CallFlags::ArgFormat format
) {
6252 MOZ_ASSERT(argc_
== 2);
6254 ValOperandId argValId
=
6255 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6257 if (format
== CallFlags::FunApplyArgsObj
) {
6258 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6259 if (args_
[1].toObject().is
<MappedArgumentsObject
>()) {
6260 writer
.guardClass(argObjId
, GuardClassKind::MappedArguments
);
6262 MOZ_ASSERT(args_
[1].toObject().is
<UnmappedArgumentsObject
>());
6263 writer
.guardClass(argObjId
, GuardClassKind::UnmappedArguments
);
6265 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6266 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6267 writer
.guardArgumentsObjectFlags(argObjId
, flags
);
6268 return mozilla::Some(argObjId
);
6271 if (format
== CallFlags::FunApplyArray
) {
6272 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6273 emitOptimisticClassGuard(argObjId
, &args_
[1].toObject(),
6274 GuardClassKind::Array
);
6275 writer
.guardArrayIsPacked(argObjId
);
6276 return mozilla::Some(argObjId
);
6279 MOZ_ASSERT(format
== CallFlags::FunApplyNullUndefined
);
6280 writer
.guardIsNullOrUndefined(argValId
);
6281 return mozilla::Nothing();
6284 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPush() {
6285 // Only optimize on obj.push(val);
6286 if (argc_
!= 1 || !thisval_
.isObject()) {
6287 return AttachDecision::NoAction
;
6290 // Where |obj| is a native array.
6291 JSObject
* thisobj
= &thisval_
.toObject();
6292 if (!thisobj
->is
<ArrayObject
>()) {
6293 return AttachDecision::NoAction
;
6296 auto* thisarray
= &thisobj
->as
<ArrayObject
>();
6298 // Check for other indexed properties or class hooks.
6299 if (!CanAttachAddElement(thisarray
, /* isInit = */ false,
6300 AllowIndexedReceiver::No
)) {
6301 return AttachDecision::NoAction
;
6304 // Can't add new elements to arrays with non-writable length.
6305 if (!thisarray
->lengthIsWritable()) {
6306 return AttachDecision::NoAction
;
6309 // Check that array is extensible.
6310 if (!thisarray
->isExtensible()) {
6311 return AttachDecision::NoAction
;
6314 // Check that the array is completely initialized (no holes).
6315 if (thisarray
->getDenseInitializedLength() != thisarray
->length()) {
6316 return AttachDecision::NoAction
;
6319 MOZ_ASSERT(!thisarray
->denseElementsAreFrozen(),
6320 "Extensible arrays should not have frozen elements");
6322 // After this point, we can generate code fine.
6324 // Initialize the input operand.
6325 initializeInputOperand();
6327 // Guard callee is the 'push' native function.
6328 emitNativeCalleeGuard();
6330 // Guard this is an array object.
6331 ValOperandId thisValId
=
6332 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6333 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6335 // Guard that the shape matches.
6336 TestMatchingNativeReceiver(writer
, thisarray
, thisObjId
);
6338 // Guard proto chain shapes.
6339 ShapeGuardProtoChain(writer
, thisarray
, thisObjId
);
6341 // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
6342 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6343 writer
.arrayPush(thisObjId
, argId
);
6345 writer
.returnFromIC();
6347 trackAttached("ArrayPush");
6348 return AttachDecision::Attach
;
6351 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPopShift(
6352 InlinableNative native
) {
6353 // Expecting no arguments.
6355 return AttachDecision::NoAction
;
6358 // Only optimize if |this| is a packed array.
6359 if (!thisval_
.isObject() || !IsPackedArray(&thisval_
.toObject())) {
6360 return AttachDecision::NoAction
;
6363 // Other conditions:
6365 // * The array length needs to be writable because we're changing it.
6366 // * The array must be extensible. Non-extensible arrays require preserving
6367 // the |initializedLength == capacity| invariant on ObjectElements.
6368 // See NativeObject::shrinkCapacityToInitializedLength.
6369 // This also ensures the elements aren't sealed/frozen.
6370 // * There must not be a for-in iterator for the elements because the IC stub
6371 // does not suppress deleted properties.
6372 ArrayObject
* arr
= &thisval_
.toObject().as
<ArrayObject
>();
6373 if (!arr
->lengthIsWritable() || !arr
->isExtensible() ||
6374 arr
->denseElementsHaveMaybeInIterationFlag()) {
6375 return AttachDecision::NoAction
;
6378 // Initialize the input operand.
6379 initializeInputOperand();
6381 // Guard callee is the 'pop' or 'shift' native function.
6382 emitNativeCalleeGuard();
6384 ValOperandId thisValId
=
6385 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6386 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6387 emitOptimisticClassGuard(objId
, arr
, GuardClassKind::Array
);
6389 if (native
== InlinableNative::ArrayPop
) {
6390 writer
.packedArrayPopResult(objId
);
6392 MOZ_ASSERT(native
== InlinableNative::ArrayShift
);
6393 writer
.packedArrayShiftResult(objId
);
6396 writer
.returnFromIC();
6398 trackAttached("ArrayPopShift");
6399 return AttachDecision::Attach
;
6402 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayJoin() {
6403 // Only handle argc <= 1.
6405 return AttachDecision::NoAction
;
6408 // Only optimize if |this| is an array.
6409 if (!thisval_
.isObject() || !thisval_
.toObject().is
<ArrayObject
>()) {
6410 return AttachDecision::NoAction
;
6413 // The separator argument must be a string, if present.
6414 if (argc_
> 0 && !args_
[0].isString()) {
6415 return AttachDecision::NoAction
;
6418 // IC stub code can handle non-packed array.
6420 // Initialize the input operand.
6421 initializeInputOperand();
6423 // Guard callee is the 'join' native function.
6424 emitNativeCalleeGuard();
6426 // Guard this is an array object.
6427 ValOperandId thisValId
=
6428 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6429 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6430 emitOptimisticClassGuard(thisObjId
, &thisval_
.toObject(),
6431 GuardClassKind::Array
);
6433 StringOperandId sepId
;
6435 // If argcount is 1, guard that the argument is a string.
6436 ValOperandId argValId
=
6437 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6438 sepId
= writer
.guardToString(argValId
);
6440 sepId
= writer
.loadConstantString(cx_
->names().comma_
);
6444 writer
.arrayJoinResult(thisObjId
, sepId
);
6446 writer
.returnFromIC();
6448 trackAttached("ArrayJoin");
6449 return AttachDecision::Attach
;
6452 AttachDecision
InlinableNativeIRGenerator::tryAttachArraySlice() {
6453 // Only handle argc <= 2.
6455 return AttachDecision::NoAction
;
6458 // Only optimize if |this| is a packed array or an arguments object.
6459 if (!thisval_
.isObject()) {
6460 return AttachDecision::NoAction
;
6463 bool isPackedArray
= IsPackedArray(&thisval_
.toObject());
6464 if (!isPackedArray
) {
6465 if (!thisval_
.toObject().is
<ArgumentsObject
>()) {
6466 return AttachDecision::NoAction
;
6468 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6470 // No elements must have been overridden or deleted.
6471 if (args
->hasOverriddenElement()) {
6472 return AttachDecision::NoAction
;
6475 // The length property mustn't be overridden.
6476 if (args
->hasOverriddenLength()) {
6477 return AttachDecision::NoAction
;
6480 // And finally also check that no argument is forwarded.
6481 if (args
->anyArgIsForwarded()) {
6482 return AttachDecision::NoAction
;
6486 // Arguments for the sliced region must be integers.
6487 if (argc_
> 0 && !args_
[0].isInt32()) {
6488 return AttachDecision::NoAction
;
6490 if (argc_
> 1 && !args_
[1].isInt32()) {
6491 return AttachDecision::NoAction
;
6494 JSObject
* templateObj
= NewDenseFullyAllocatedArray(cx_
, 0, TenuredObject
);
6496 cx_
->recoverFromOutOfMemory();
6497 return AttachDecision::NoAction
;
6500 // Initialize the input operand.
6501 initializeInputOperand();
6503 // Guard callee is the 'slice' native function.
6504 emitNativeCalleeGuard();
6506 ValOperandId thisValId
=
6507 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6508 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6510 if (isPackedArray
) {
6511 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6512 GuardClassKind::Array
);
6514 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6516 if (args
->is
<MappedArgumentsObject
>()) {
6517 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6519 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6520 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6523 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6524 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6525 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6526 writer
.guardArgumentsObjectFlags(objId
, flags
);
6529 Int32OperandId int32BeginId
;
6531 ValOperandId beginId
=
6532 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6533 int32BeginId
= writer
.guardToInt32(beginId
);
6535 int32BeginId
= writer
.loadInt32Constant(0);
6538 Int32OperandId int32EndId
;
6540 ValOperandId endId
=
6541 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6542 int32EndId
= writer
.guardToInt32(endId
);
6543 } else if (isPackedArray
) {
6544 int32EndId
= writer
.loadInt32ArrayLength(objId
);
6546 int32EndId
= writer
.loadArgumentsObjectLength(objId
);
6549 if (isPackedArray
) {
6550 writer
.packedArraySliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6552 writer
.argumentsSliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6554 writer
.returnFromIC();
6556 trackAttached(isPackedArray
? "ArraySlice" : "ArgumentsSlice");
6557 return AttachDecision::Attach
;
6560 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayIsArray() {
6561 // Need a single argument.
6563 return AttachDecision::NoAction
;
6566 // Initialize the input operand.
6567 initializeInputOperand();
6569 // Guard callee is the 'isArray' native function.
6570 emitNativeCalleeGuard();
6572 // Check if the argument is an Array and return result.
6573 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6574 writer
.isArrayResult(argId
);
6575 writer
.returnFromIC();
6577 trackAttached("ArrayIsArray");
6578 return AttachDecision::Attach
;
6581 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewGet(
6582 Scalar::Type type
) {
6583 // Ensure |this| is a DataViewObject.
6584 if (!thisval_
.isObject() || !thisval_
.toObject().is
<DataViewObject
>()) {
6585 return AttachDecision::NoAction
;
6588 // Expected arguments: offset (number), optional littleEndian (boolean).
6589 if (argc_
< 1 || argc_
> 2) {
6590 return AttachDecision::NoAction
;
6592 int64_t offsetInt64
;
6593 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6594 return AttachDecision::NoAction
;
6596 if (argc_
> 1 && !args_
[1].isBoolean()) {
6597 return AttachDecision::NoAction
;
6600 auto* dv
= &thisval_
.toObject().as
<DataViewObject
>();
6602 // Bounds check the offset.
6603 size_t byteLength
= dv
->byteLength().valueOr(0);
6604 if (offsetInt64
< 0 || !DataViewObject::offsetIsInBounds(
6605 Scalar::byteSize(type
), offsetInt64
, byteLength
)) {
6606 return AttachDecision::NoAction
;
6609 // For getUint32 we let the stub return an Int32 if we have not seen a
6610 // double, to allow better codegen in Warp while avoiding bailout loops.
6611 bool forceDoubleForUint32
= false;
6612 if (type
== Scalar::Uint32
) {
6613 bool isLittleEndian
= argc_
> 1 && args_
[1].toBoolean();
6614 uint32_t res
= dv
->read
<uint32_t>(offsetInt64
, byteLength
, isLittleEndian
);
6615 forceDoubleForUint32
= res
>= INT32_MAX
;
6618 // Initialize the input operand.
6619 initializeInputOperand();
6621 // Guard callee is this DataView native function.
6622 emitNativeCalleeGuard();
6624 // Guard |this| is a DataViewObject.
6625 ValOperandId thisValId
=
6626 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6627 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6629 if (dv
->is
<FixedLengthDataViewObject
>()) {
6630 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6631 GuardClassKind::FixedLengthDataView
);
6633 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6634 GuardClassKind::ResizableDataView
);
6637 // Convert offset to intPtr.
6638 ValOperandId offsetId
=
6639 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6640 IntPtrOperandId intPtrOffsetId
=
6641 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6643 BooleanOperandId boolLittleEndianId
;
6645 ValOperandId littleEndianId
=
6646 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6647 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6649 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6652 auto viewKind
= ToArrayBufferViewKind(dv
);
6653 writer
.loadDataViewValueResult(objId
, intPtrOffsetId
, boolLittleEndianId
,
6654 type
, forceDoubleForUint32
, viewKind
);
6656 writer
.returnFromIC();
6658 trackAttached("DataViewGet");
6659 return AttachDecision::Attach
;
6662 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewSet(
6663 Scalar::Type type
) {
6664 // Ensure |this| is a DataViewObject.
6665 if (!thisval_
.isObject() || !thisval_
.toObject().is
<DataViewObject
>()) {
6666 return AttachDecision::NoAction
;
6669 // Expected arguments: offset (number), value, optional littleEndian (boolean)
6670 if (argc_
< 2 || argc_
> 3) {
6671 return AttachDecision::NoAction
;
6673 int64_t offsetInt64
;
6674 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6675 return AttachDecision::NoAction
;
6677 if (!ValueCanConvertToNumeric(type
, args_
[1])) {
6678 return AttachDecision::NoAction
;
6680 if (argc_
> 2 && !args_
[2].isBoolean()) {
6681 return AttachDecision::NoAction
;
6684 auto* dv
= &thisval_
.toObject().as
<DataViewObject
>();
6686 // Bounds check the offset.
6687 size_t byteLength
= dv
->byteLength().valueOr(0);
6688 if (offsetInt64
< 0 || !DataViewObject::offsetIsInBounds(
6689 Scalar::byteSize(type
), offsetInt64
, byteLength
)) {
6690 return AttachDecision::NoAction
;
6693 // Initialize the input operand.
6694 initializeInputOperand();
6696 // Guard callee is this DataView native function.
6697 emitNativeCalleeGuard();
6699 // Guard |this| is a DataViewObject.
6700 ValOperandId thisValId
=
6701 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6702 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6704 if (dv
->is
<FixedLengthDataViewObject
>()) {
6705 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6706 GuardClassKind::FixedLengthDataView
);
6708 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6709 GuardClassKind::ResizableDataView
);
6712 // Convert offset to intPtr.
6713 ValOperandId offsetId
=
6714 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6715 IntPtrOperandId intPtrOffsetId
=
6716 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6718 // Convert value to number or BigInt.
6719 ValOperandId valueId
=
6720 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6721 OperandId numericValueId
= emitNumericGuard(valueId
, args_
[1], type
);
6723 BooleanOperandId boolLittleEndianId
;
6725 ValOperandId littleEndianId
=
6726 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6727 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6729 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6732 auto viewKind
= ToArrayBufferViewKind(dv
);
6733 writer
.storeDataViewValueResult(objId
, intPtrOffsetId
, numericValueId
,
6734 boolLittleEndianId
, type
, viewKind
);
6736 writer
.returnFromIC();
6738 trackAttached("DataViewSet");
6739 return AttachDecision::Attach
;
6742 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeGetReservedSlot(
6743 InlinableNative native
) {
6744 // Self-hosted code calls this with (object, int32) arguments.
6745 MOZ_ASSERT(argc_
== 2);
6746 MOZ_ASSERT(args_
[0].isObject());
6747 MOZ_ASSERT(args_
[1].isInt32());
6748 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6750 uint32_t slot
= uint32_t(args_
[1].toInt32());
6751 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6752 return AttachDecision::NoAction
;
6754 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6756 // Initialize the input operand.
6757 initializeInputOperand();
6759 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6761 // Guard that the first argument is an object.
6762 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6763 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6765 // BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot ensures that the
6766 // slot argument is constant. (At least for direct calls)
6769 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
6770 writer
.loadFixedSlotResult(objId
, offset
);
6772 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
6773 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Object
);
6775 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
6776 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Int32
);
6778 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
6779 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::String
);
6782 MOZ_CRASH("unexpected native");
6785 writer
.returnFromIC();
6787 trackAttached("UnsafeGetReservedSlot");
6788 return AttachDecision::Attach
;
6791 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeSetReservedSlot() {
6792 // Self-hosted code calls this with (object, int32, value) arguments.
6793 MOZ_ASSERT(argc_
== 3);
6794 MOZ_ASSERT(args_
[0].isObject());
6795 MOZ_ASSERT(args_
[1].isInt32());
6796 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6798 uint32_t slot
= uint32_t(args_
[1].toInt32());
6799 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6800 return AttachDecision::NoAction
;
6802 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6804 // Initialize the input operand.
6805 initializeInputOperand();
6807 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6809 // Guard that the first argument is an object.
6810 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6811 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6813 // BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot ensures that the
6814 // slot argument is constant. (At least for direct calls)
6816 // Get the value to set.
6817 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6819 // Set the fixed slot and return undefined.
6820 writer
.storeFixedSlotUndefinedResult(objId
, offset
, valId
);
6822 // This stub always returns undefined.
6823 writer
.returnFromIC();
6825 trackAttached("UnsafeSetReservedSlot");
6826 return AttachDecision::Attach
;
6829 AttachDecision
InlinableNativeIRGenerator::tryAttachIsSuspendedGenerator() {
6830 // The IsSuspendedGenerator intrinsic is only called in
6831 // self-hosted code, so it's safe to assume we have a single
6832 // argument and the callee is our intrinsic.
6834 MOZ_ASSERT(argc_
== 1);
6836 initializeInputOperand();
6838 // Stack layout here is (bottom to top):
6841 // 0: Arg <-- Top of stack.
6842 // We only care about the argument.
6843 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6845 // Check whether the argument is a suspended generator.
6846 // We don't need guards, because IsSuspendedGenerator returns
6847 // false for values that are not generator objects.
6848 writer
.callIsSuspendedGeneratorResult(valId
);
6849 writer
.returnFromIC();
6851 trackAttached("IsSuspendedGenerator");
6852 return AttachDecision::Attach
;
6855 AttachDecision
InlinableNativeIRGenerator::tryAttachToObject() {
6856 // Self-hosted code calls this with a single argument.
6857 MOZ_ASSERT(argc_
== 1);
6859 // Need a single object argument.
6860 // TODO(Warp): Support all or more conversions to object.
6861 if (!args_
[0].isObject()) {
6862 return AttachDecision::NoAction
;
6865 // Initialize the input operand.
6866 initializeInputOperand();
6868 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6870 // Guard that the argument is an object.
6871 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6872 ObjOperandId objId
= writer
.guardToObject(argId
);
6874 // Return the object.
6875 writer
.loadObjectResult(objId
);
6876 writer
.returnFromIC();
6878 trackAttached("ToObject");
6879 return AttachDecision::Attach
;
6882 AttachDecision
InlinableNativeIRGenerator::tryAttachToInteger() {
6883 // Self-hosted code calls this with a single argument.
6884 MOZ_ASSERT(argc_
== 1);
6886 // Need a single int32 argument.
6887 // TODO(Warp): Support all or more conversions to integer.
6888 // Make sure to update this code correctly if we ever start
6889 // returning non-int32 integers.
6890 if (!args_
[0].isInt32()) {
6891 return AttachDecision::NoAction
;
6894 // Initialize the input operand.
6895 initializeInputOperand();
6897 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6899 // Guard that the argument is an int32.
6900 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6901 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
6903 // Return the int32.
6904 writer
.loadInt32Result(int32Id
);
6905 writer
.returnFromIC();
6907 trackAttached("ToInteger");
6908 return AttachDecision::Attach
;
6911 AttachDecision
InlinableNativeIRGenerator::tryAttachToLength() {
6912 // Self-hosted code calls this with a single argument.
6913 MOZ_ASSERT(argc_
== 1);
6915 // Need a single int32 argument.
6916 if (!args_
[0].isInt32()) {
6917 return AttachDecision::NoAction
;
6920 // Initialize the input operand.
6921 initializeInputOperand();
6923 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6925 // ToLength(int32) is equivalent to max(int32, 0).
6926 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6927 Int32OperandId int32ArgId
= writer
.guardToInt32(argId
);
6928 Int32OperandId zeroId
= writer
.loadInt32Constant(0);
6930 Int32OperandId maxId
= writer
.int32MinMax(isMax
, int32ArgId
, zeroId
);
6931 writer
.loadInt32Result(maxId
);
6932 writer
.returnFromIC();
6934 trackAttached("ToLength");
6935 return AttachDecision::Attach
;
6938 AttachDecision
InlinableNativeIRGenerator::tryAttachIsObject() {
6939 // Self-hosted code calls this with a single argument.
6940 MOZ_ASSERT(argc_
== 1);
6942 // Initialize the input operand.
6943 initializeInputOperand();
6945 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6947 // Type check the argument and return result.
6948 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6949 writer
.isObjectResult(argId
);
6950 writer
.returnFromIC();
6952 trackAttached("IsObject");
6953 return AttachDecision::Attach
;
6956 AttachDecision
InlinableNativeIRGenerator::tryAttachIsPackedArray() {
6957 // Self-hosted code calls this with a single object argument.
6958 MOZ_ASSERT(argc_
== 1);
6959 MOZ_ASSERT(args_
[0].isObject());
6961 // Initialize the input operand.
6962 initializeInputOperand();
6964 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6966 // Check if the argument is packed and return result.
6967 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6968 ObjOperandId objArgId
= writer
.guardToObject(argId
);
6969 writer
.isPackedArrayResult(objArgId
);
6970 writer
.returnFromIC();
6972 trackAttached("IsPackedArray");
6973 return AttachDecision::Attach
;
6976 AttachDecision
InlinableNativeIRGenerator::tryAttachIsCallable() {
6977 // Self-hosted code calls this with a single argument.
6978 MOZ_ASSERT(argc_
== 1);
6980 // Initialize the input operand.
6981 initializeInputOperand();
6983 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6985 // Check if the argument is callable and return result.
6986 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6987 writer
.isCallableResult(argId
);
6988 writer
.returnFromIC();
6990 trackAttached("IsCallable");
6991 return AttachDecision::Attach
;
6994 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructor() {
6995 // Self-hosted code calls this with a single argument.
6996 MOZ_ASSERT(argc_
== 1);
6998 // Need a single object argument.
6999 if (!args_
[0].isObject()) {
7000 return AttachDecision::NoAction
;
7003 // Initialize the input operand.
7004 initializeInputOperand();
7006 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7008 // Guard that the argument is an object.
7009 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7010 ObjOperandId objId
= writer
.guardToObject(argId
);
7012 // Check if the argument is a constructor and return result.
7013 writer
.isConstructorResult(objId
);
7014 writer
.returnFromIC();
7016 trackAttached("IsConstructor");
7017 return AttachDecision::Attach
;
7021 InlinableNativeIRGenerator::tryAttachIsCrossRealmArrayConstructor() {
7022 // Self-hosted code calls this with an object argument.
7023 MOZ_ASSERT(argc_
== 1);
7024 MOZ_ASSERT(args_
[0].isObject());
7026 if (args_
[0].toObject().is
<ProxyObject
>()) {
7027 return AttachDecision::NoAction
;
7030 // Initialize the input operand.
7031 initializeInputOperand();
7033 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7035 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7036 ObjOperandId objId
= writer
.guardToObject(argId
);
7037 writer
.guardIsNotProxy(objId
);
7038 writer
.isCrossRealmArrayConstructorResult(objId
);
7039 writer
.returnFromIC();
7041 trackAttached("IsCrossRealmArrayConstructor");
7042 return AttachDecision::Attach
;
7045 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
7046 InlinableNative native
) {
7047 // Self-hosted code calls this with an object argument.
7048 MOZ_ASSERT(argc_
== 1);
7049 MOZ_ASSERT(args_
[0].isObject());
7051 // Class must match.
7052 const JSClass
* clasp
= InlinableNativeGuardToClass(native
);
7053 if (args_
[0].toObject().getClass() != clasp
) {
7054 return AttachDecision::NoAction
;
7057 // Initialize the input operand.
7058 initializeInputOperand();
7060 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7062 // Guard that the argument is an object.
7063 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7064 ObjOperandId objId
= writer
.guardToObject(argId
);
7066 // Guard that the object has the correct class.
7067 writer
.guardAnyClass(objId
, clasp
);
7069 // Return the object.
7070 writer
.loadObjectResult(objId
);
7071 writer
.returnFromIC();
7073 trackAttached("GuardToClass");
7074 return AttachDecision::Attach
;
7077 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
7078 GuardClassKind kind
) {
7079 // Self-hosted code calls this with an object argument.
7080 MOZ_ASSERT(argc_
== 1);
7081 MOZ_ASSERT(args_
[0].isObject());
7083 // Class must match.
7084 const JSClass
* clasp
= ClassFor(kind
);
7085 if (args_
[0].toObject().getClass() != clasp
) {
7086 return AttachDecision::NoAction
;
7089 // Initialize the input operand.
7090 initializeInputOperand();
7092 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7094 // Guard that the argument is an object.
7095 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7096 ObjOperandId objId
= writer
.guardToObject(argId
);
7098 // Guard that the object has the correct class.
7099 writer
.guardClass(objId
, kind
);
7101 // Return the object.
7102 writer
.loadObjectResult(objId
);
7103 writer
.returnFromIC();
7105 trackAttached("GuardToClass");
7106 return AttachDecision::Attach
;
7109 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToEitherClass(
7110 GuardClassKind kind1
, GuardClassKind kind2
) {
7111 MOZ_ASSERT(kind1
!= kind2
,
7112 "prefer tryAttachGuardToClass for the same class case");
7114 // Self-hosted code calls this with an object argument.
7115 MOZ_ASSERT(argc_
== 1);
7116 MOZ_ASSERT(args_
[0].isObject());
7118 // Class must match.
7119 const JSClass
* clasp1
= ClassFor(kind1
);
7120 const JSClass
* clasp2
= ClassFor(kind2
);
7121 const JSClass
* objClass
= args_
[0].toObject().getClass();
7122 if (objClass
!= clasp1
&& objClass
!= clasp2
) {
7123 return AttachDecision::NoAction
;
7126 // Initialize the input operand.
7127 initializeInputOperand();
7129 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7131 // Guard that the argument is an object.
7132 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7133 ObjOperandId objId
= writer
.guardToObject(argId
);
7135 // Guard that the object has the correct class.
7136 writer
.guardEitherClass(objId
, kind1
, kind2
);
7138 // Return the object.
7139 writer
.loadObjectResult(objId
);
7140 writer
.returnFromIC();
7142 trackAttached("GuardToEitherClass");
7143 return AttachDecision::Attach
;
7146 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() {
7147 return tryAttachGuardToEitherClass(GuardClassKind::FixedLengthArrayBuffer
,
7148 GuardClassKind::ResizableArrayBuffer
);
7151 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToSharedArrayBuffer() {
7152 return tryAttachGuardToEitherClass(
7153 GuardClassKind::FixedLengthSharedArrayBuffer
,
7154 GuardClassKind::GrowableSharedArrayBuffer
);
7157 AttachDecision
InlinableNativeIRGenerator::tryAttachHasClass(
7158 const JSClass
* clasp
, bool isPossiblyWrapped
) {
7159 // Self-hosted code calls this with an object argument.
7160 MOZ_ASSERT(argc_
== 1);
7161 MOZ_ASSERT(args_
[0].isObject());
7163 // Only optimize when the object isn't a proxy.
7164 if (isPossiblyWrapped
&& args_
[0].toObject().is
<ProxyObject
>()) {
7165 return AttachDecision::NoAction
;
7168 // Initialize the input operand.
7169 initializeInputOperand();
7171 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7173 // Perform the Class check.
7174 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7175 ObjOperandId objId
= writer
.guardToObject(argId
);
7177 if (isPossiblyWrapped
) {
7178 writer
.guardIsNotProxy(objId
);
7181 writer
.hasClassResult(objId
, clasp
);
7182 writer
.returnFromIC();
7184 trackAttached("HasClass");
7185 return AttachDecision::Attach
;
7188 // Returns whether the .lastIndex property is a non-negative int32 value and is
7190 static bool HasOptimizableLastIndexSlot(RegExpObject
* regexp
, JSContext
* cx
) {
7191 auto lastIndexProp
= regexp
->lookupPure(cx
->names().lastIndex
);
7192 MOZ_ASSERT(lastIndexProp
->isDataProperty());
7193 if (!lastIndexProp
->writable()) {
7196 Value lastIndex
= regexp
->getLastIndex();
7197 if (!lastIndex
.isInt32() || lastIndex
.toInt32() < 0) {
7203 // Returns the RegExp stub used by the optimized code path for this intrinsic.
7204 // We store a pointer to this in the IC stub to ensure GC doesn't discard it.
7205 static JitCode
* GetOrCreateRegExpStub(JSContext
* cx
, InlinableNative native
) {
7206 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
7209 // The stubs assume the global has non-null RegExpStatics and match result
7211 if (!GlobalObject::getRegExpStatics(cx
, cx
->global()) ||
7212 !cx
->global()->regExpRealm().getOrCreateMatchResultShape(cx
)) {
7213 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
7214 cx
->clearPendingException();
7219 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
7220 case InlinableNative::IntrinsicRegExpExecForTest
:
7221 code
= cx
->zone()->jitZone()->ensureRegExpExecTestStubExists(cx
);
7223 case InlinableNative::IntrinsicRegExpBuiltinExec
:
7224 case InlinableNative::IntrinsicRegExpExec
:
7225 code
= cx
->zone()->jitZone()->ensureRegExpExecMatchStubExists(cx
);
7227 case InlinableNative::RegExpMatcher
:
7228 code
= cx
->zone()->jitZone()->ensureRegExpMatcherStubExists(cx
);
7230 case InlinableNative::RegExpSearcher
:
7231 code
= cx
->zone()->jitZone()->ensureRegExpSearcherStubExists(cx
);
7234 MOZ_CRASH("Unexpected native");
7237 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
7238 cx
->clearPendingException();
7245 static void EmitGuardLastIndexIsNonNegativeInt32(CacheIRWriter
& writer
,
7246 ObjOperandId regExpId
) {
7248 NativeObject::getFixedSlotOffset(RegExpObject::lastIndexSlot());
7249 ValOperandId lastIndexValId
= writer
.loadFixedSlot(regExpId
, offset
);
7250 Int32OperandId lastIndexId
= writer
.guardToInt32(lastIndexValId
);
7251 writer
.guardInt32IsNonNegative(lastIndexId
);
7254 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpBuiltinExec(
7255 InlinableNative native
) {
7256 // Self-hosted code calls this with (regexp, string) arguments.
7257 MOZ_ASSERT(argc_
== 2);
7258 MOZ_ASSERT(args_
[0].isObject());
7259 MOZ_ASSERT(args_
[1].isString());
7261 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7263 return AttachDecision::NoAction
;
7266 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
7267 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
7268 return AttachDecision::NoAction
;
7271 // Initialize the input operand.
7272 initializeInputOperand();
7274 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7276 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7277 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7278 writer
.guardShape(regExpId
, re
->shape());
7279 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7281 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7282 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7284 if (native
== InlinableNative::IntrinsicRegExpBuiltinExecForTest
) {
7285 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7287 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7289 writer
.returnFromIC();
7291 trackAttached("IntrinsicRegExpBuiltinExec");
7292 return AttachDecision::Attach
;
7295 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpExec(
7296 InlinableNative native
) {
7297 // Self-hosted code calls this with (object, string) arguments.
7298 MOZ_ASSERT(argc_
== 2);
7299 MOZ_ASSERT(args_
[0].isObject());
7300 MOZ_ASSERT(args_
[1].isString());
7302 if (!args_
[0].toObject().is
<RegExpObject
>()) {
7303 return AttachDecision::NoAction
;
7306 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7308 return AttachDecision::NoAction
;
7311 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
7312 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
7313 return AttachDecision::NoAction
;
7316 // Ensure regexp.exec is the original RegExp.prototype.exec function on the
7318 if (re
->containsPure(cx_
->names().exec
)) {
7319 return AttachDecision::NoAction
;
7321 MOZ_ASSERT(cx_
->global()->maybeGetRegExpPrototype());
7323 &cx_
->global()->maybeGetRegExpPrototype()->as
<NativeObject
>();
7324 if (re
->staticPrototype() != regExpProto
) {
7325 return AttachDecision::NoAction
;
7327 auto execProp
= regExpProto
->as
<NativeObject
>().lookupPure(cx_
->names().exec
);
7328 if (!execProp
|| !execProp
->isDataProperty()) {
7329 return AttachDecision::NoAction
;
7331 // It should be stored in a dynamic slot. We assert this in
7332 // FinishRegExpClassInit.
7333 if (regExpProto
->isFixedSlot(execProp
->slot())) {
7334 return AttachDecision::NoAction
;
7336 Value execVal
= regExpProto
->getSlot(execProp
->slot());
7337 PropertyName
* execName
= cx_
->names().RegExp_prototype_Exec
;
7338 if (!IsSelfHostedFunctionWithName(execVal
, execName
)) {
7339 return AttachDecision::NoAction
;
7341 JSFunction
* execFunction
= &execVal
.toObject().as
<JSFunction
>();
7343 // Initialize the input operand.
7344 initializeInputOperand();
7346 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7348 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7349 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7350 writer
.guardShape(regExpId
, re
->shape());
7351 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7353 // Emit guards for the RegExp.prototype.exec property.
7354 ObjOperandId regExpProtoId
= writer
.loadObject(regExpProto
);
7355 writer
.guardShape(regExpProtoId
, regExpProto
->shape());
7357 regExpProto
->dynamicSlotIndex(execProp
->slot()) * sizeof(Value
);
7358 writer
.guardDynamicSlotValue(regExpProtoId
, offset
,
7359 ObjectValue(*execFunction
));
7361 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7362 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7364 if (native
== InlinableNative::IntrinsicRegExpExecForTest
) {
7365 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7367 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7369 writer
.returnFromIC();
7371 trackAttached("IntrinsicRegExpExec");
7372 return AttachDecision::Attach
;
7375 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpMatcherSearcher(
7376 InlinableNative native
) {
7377 // Self-hosted code calls this with (object, string, number) arguments.
7378 MOZ_ASSERT(argc_
== 3);
7379 MOZ_ASSERT(args_
[0].isObject());
7380 MOZ_ASSERT(args_
[1].isString());
7381 MOZ_ASSERT(args_
[2].isNumber());
7383 // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
7384 if (!args_
[2].isInt32()) {
7385 return AttachDecision::NoAction
;
7388 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7390 return AttachDecision::NoAction
;
7393 // Initialize the input operand.
7394 initializeInputOperand();
7396 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7398 // Guard argument types.
7399 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7400 ObjOperandId reId
= writer
.guardToObject(arg0Id
);
7402 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7403 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7405 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7406 Int32OperandId lastIndexId
= writer
.guardToInt32(arg2Id
);
7409 case InlinableNative::RegExpMatcher
:
7410 writer
.callRegExpMatcherResult(reId
, inputId
, lastIndexId
, stub
);
7411 writer
.returnFromIC();
7412 trackAttached("RegExpMatcher");
7415 case InlinableNative::RegExpSearcher
:
7416 writer
.callRegExpSearcherResult(reId
, inputId
, lastIndexId
, stub
);
7417 writer
.returnFromIC();
7418 trackAttached("RegExpSearcher");
7422 MOZ_CRASH("Unexpected native");
7425 return AttachDecision::Attach
;
7428 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
7429 // Self-hosted code calls this with a string argument that's only used for an
7431 MOZ_ASSERT(argc_
== 1);
7432 MOZ_ASSERT(args_
[0].isString());
7434 // Initialize the input operand.
7435 initializeInputOperand();
7437 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7439 writer
.regExpSearcherLastLimitResult();
7440 writer
.returnFromIC();
7442 trackAttached("RegExpSearcherLastLimit");
7443 return AttachDecision::Attach
;
7446 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
7447 // Self-hosted code calls this with object and string arguments.
7448 MOZ_ASSERT(argc_
== 2);
7449 MOZ_ASSERT(args_
[0].toObject().is
<RegExpObject
>());
7450 MOZ_ASSERT(args_
[1].isString());
7452 // Initialize the input operand.
7453 initializeInputOperand();
7455 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7457 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7458 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7460 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7461 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7463 writer
.regExpHasCaptureGroupsResult(objId
, inputId
);
7464 writer
.returnFromIC();
7466 trackAttached("RegExpHasCaptureGroups");
7467 return AttachDecision::Attach
;
7471 InlinableNativeIRGenerator::tryAttachRegExpPrototypeOptimizable() {
7472 // Self-hosted code calls this with a single object argument.
7473 MOZ_ASSERT(argc_
== 1);
7474 MOZ_ASSERT(args_
[0].isObject());
7476 // Initialize the input operand.
7477 initializeInputOperand();
7479 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7481 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7482 ObjOperandId protoId
= writer
.guardToObject(arg0Id
);
7484 writer
.regExpPrototypeOptimizableResult(protoId
);
7485 writer
.returnFromIC();
7487 trackAttached("RegExpPrototypeOptimizable");
7488 return AttachDecision::Attach
;
7492 InlinableNativeIRGenerator::tryAttachRegExpInstanceOptimizable() {
7493 // Self-hosted code calls this with two object arguments.
7494 MOZ_ASSERT(argc_
== 2);
7495 MOZ_ASSERT(args_
[0].isObject());
7496 MOZ_ASSERT(args_
[1].isObject());
7498 // Initialize the input operand.
7499 initializeInputOperand();
7501 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7503 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7504 ObjOperandId regexpId
= writer
.guardToObject(arg0Id
);
7506 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7507 ObjOperandId protoId
= writer
.guardToObject(arg1Id
);
7509 writer
.regExpInstanceOptimizableResult(regexpId
, protoId
);
7510 writer
.returnFromIC();
7512 trackAttached("RegExpInstanceOptimizable");
7513 return AttachDecision::Attach
;
7516 AttachDecision
InlinableNativeIRGenerator::tryAttachGetFirstDollarIndex() {
7517 // Self-hosted code calls this with a single string argument.
7518 MOZ_ASSERT(argc_
== 1);
7519 MOZ_ASSERT(args_
[0].isString());
7521 // Initialize the input operand.
7522 initializeInputOperand();
7524 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7526 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7527 StringOperandId strId
= writer
.guardToString(arg0Id
);
7529 writer
.getFirstDollarIndexResult(strId
);
7530 writer
.returnFromIC();
7532 trackAttached("GetFirstDollarIndex");
7533 return AttachDecision::Attach
;
7536 AttachDecision
InlinableNativeIRGenerator::tryAttachSubstringKernel() {
7537 // Self-hosted code calls this with (string, int32, int32) arguments.
7538 MOZ_ASSERT(argc_
== 3);
7539 MOZ_ASSERT(args_
[0].isString());
7540 MOZ_ASSERT(args_
[1].isInt32());
7541 MOZ_ASSERT(args_
[2].isInt32());
7543 // Initialize the input operand.
7544 initializeInputOperand();
7546 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7548 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7549 StringOperandId strId
= writer
.guardToString(arg0Id
);
7551 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7552 Int32OperandId beginId
= writer
.guardToInt32(arg1Id
);
7554 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7555 Int32OperandId lengthId
= writer
.guardToInt32(arg2Id
);
7557 writer
.callSubstringKernelResult(strId
, beginId
, lengthId
);
7558 writer
.returnFromIC();
7560 trackAttached("SubstringKernel");
7561 return AttachDecision::Attach
;
7564 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectHasPrototype() {
7565 // Self-hosted code calls this with (object, object) arguments.
7566 MOZ_ASSERT(argc_
== 2);
7567 MOZ_ASSERT(args_
[0].isObject());
7568 MOZ_ASSERT(args_
[1].isObject());
7570 auto* obj
= &args_
[0].toObject().as
<NativeObject
>();
7571 auto* proto
= &args_
[1].toObject().as
<NativeObject
>();
7573 // Only attach when obj.__proto__ is proto.
7574 if (obj
->staticPrototype() != proto
) {
7575 return AttachDecision::NoAction
;
7578 // Initialize the input operand.
7579 initializeInputOperand();
7581 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7583 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7584 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7586 writer
.guardProto(objId
, proto
);
7587 writer
.loadBooleanResult(true);
7588 writer
.returnFromIC();
7590 trackAttached("ObjectHasPrototype");
7591 return AttachDecision::Attach
;
7594 static bool CanConvertToString(const Value
& v
) {
7595 return v
.isString() || v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
7598 AttachDecision
InlinableNativeIRGenerator::tryAttachString() {
7599 // Need a single argument that is or can be converted to a string.
7600 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7601 return AttachDecision::NoAction
;
7604 // Initialize the input operand.
7605 initializeInputOperand();
7607 // Guard callee is the 'String' function.
7608 emitNativeCalleeGuard();
7610 // Guard that the argument is a string or can be converted to one.
7611 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7612 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7614 // Return the string.
7615 writer
.loadStringResult(strId
);
7616 writer
.returnFromIC();
7618 trackAttached("String");
7619 return AttachDecision::Attach
;
7622 AttachDecision
InlinableNativeIRGenerator::tryAttachStringConstructor() {
7623 // Need a single argument that is or can be converted to a string.
7624 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7625 return AttachDecision::NoAction
;
7628 RootedString
emptyString(cx_
, cx_
->runtime()->emptyString
);
7629 JSObject
* templateObj
= StringObject::create(
7630 cx_
, emptyString
, /* proto = */ nullptr, TenuredObject
);
7632 cx_
->recoverFromOutOfMemory();
7633 return AttachDecision::NoAction
;
7636 // Initialize the input operand.
7637 initializeInputOperand();
7639 // Guard callee is the 'String' function.
7640 emitNativeCalleeGuard();
7642 // Guard on number and convert to string.
7643 ValOperandId argId
=
7644 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
7645 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7647 writer
.newStringObjectResult(templateObj
, strId
);
7648 writer
.returnFromIC();
7650 trackAttached("StringConstructor");
7651 return AttachDecision::Attach
;
7654 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToStringValueOf() {
7655 // Expecting no arguments.
7657 return AttachDecision::NoAction
;
7660 // Ensure |this| is a primitive string value.
7661 if (!thisval_
.isString()) {
7662 return AttachDecision::NoAction
;
7665 // Initialize the input operand.
7666 initializeInputOperand();
7668 // Guard callee is the 'toString' OR 'valueOf' native function.
7669 emitNativeCalleeGuard();
7671 // Guard |this| is a string.
7672 ValOperandId thisValId
=
7673 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7674 StringOperandId strId
= writer
.guardToString(thisValId
);
7676 // Return the string
7677 writer
.loadStringResult(strId
);
7678 writer
.returnFromIC();
7680 trackAttached("StringToStringValueOf");
7681 return AttachDecision::Attach
;
7684 AttachDecision
InlinableNativeIRGenerator::tryAttachStringReplaceString() {
7685 // Self-hosted code calls this with (string, string, string) arguments.
7686 MOZ_ASSERT(argc_
== 3);
7687 MOZ_ASSERT(args_
[0].isString());
7688 MOZ_ASSERT(args_
[1].isString());
7689 MOZ_ASSERT(args_
[2].isString());
7691 // Initialize the input operand.
7692 initializeInputOperand();
7694 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7696 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7697 StringOperandId strId
= writer
.guardToString(arg0Id
);
7699 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7700 StringOperandId patternId
= writer
.guardToString(arg1Id
);
7702 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7703 StringOperandId replacementId
= writer
.guardToString(arg2Id
);
7705 writer
.stringReplaceStringResult(strId
, patternId
, replacementId
);
7706 writer
.returnFromIC();
7708 trackAttached("StringReplaceString");
7709 return AttachDecision::Attach
;
7712 AttachDecision
InlinableNativeIRGenerator::tryAttachStringSplitString() {
7713 // Self-hosted code calls this with (string, string) arguments.
7714 MOZ_ASSERT(argc_
== 2);
7715 MOZ_ASSERT(args_
[0].isString());
7716 MOZ_ASSERT(args_
[1].isString());
7718 // Initialize the input operand.
7719 initializeInputOperand();
7721 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7723 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7724 StringOperandId strId
= writer
.guardToString(arg0Id
);
7726 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7727 StringOperandId separatorId
= writer
.guardToString(arg1Id
);
7729 writer
.stringSplitStringResult(strId
, separatorId
);
7730 writer
.returnFromIC();
7732 trackAttached("StringSplitString");
7733 return AttachDecision::Attach
;
7736 AttachDecision
InlinableNativeIRGenerator::tryAttachStringChar(
7738 // Need one argument.
7740 return AttachDecision::NoAction
;
7743 auto attach
= CanAttachStringChar(thisval_
, args_
[0], kind
);
7744 if (attach
== AttachStringChar::No
) {
7745 return AttachDecision::NoAction
;
7748 bool handleOOB
= attach
== AttachStringChar::OutOfBounds
;
7750 // Initialize the input operand.
7751 initializeInputOperand();
7753 // Guard callee is the 'charCodeAt', 'codePointAt', 'charAt', or 'at' native
7755 emitNativeCalleeGuard();
7757 // Guard this is a string.
7758 ValOperandId thisValId
=
7759 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7760 StringOperandId strId
= writer
.guardToString(thisValId
);
7762 // Guard int32 index.
7763 ValOperandId indexId
=
7764 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7765 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
7767 // Handle relative string indices, if necessary.
7768 if (kind
== StringChar::At
) {
7769 int32IndexId
= writer
.toRelativeStringIndex(int32IndexId
, strId
);
7772 // Linearize the string.
7774 // AttachStringChar doesn't have a separate state when OOB access happens on
7775 // a string which needs to be linearized, so just linearize unconditionally
7776 // for out-of-bounds accesses.
7777 if (attach
== AttachStringChar::Linearize
||
7778 attach
== AttachStringChar::OutOfBounds
) {
7780 case StringChar::CharCodeAt
:
7781 case StringChar::CharAt
:
7782 case StringChar::At
:
7783 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
7785 case StringChar::CodePointAt
:
7786 strId
= writer
.linearizeForCodePointAccess(strId
, int32IndexId
);
7791 // Load string char or code.
7793 case StringChar::CharCodeAt
:
7794 writer
.loadStringCharCodeResult(strId
, int32IndexId
, handleOOB
);
7796 case StringChar::CodePointAt
:
7797 writer
.loadStringCodePointResult(strId
, int32IndexId
, handleOOB
);
7799 case StringChar::CharAt
:
7800 writer
.loadStringCharResult(strId
, int32IndexId
, handleOOB
);
7802 case StringChar::At
:
7803 writer
.loadStringAtResult(strId
, int32IndexId
, handleOOB
);
7807 writer
.returnFromIC();
7810 case StringChar::CharCodeAt
:
7811 trackAttached("StringCharCodeAt");
7813 case StringChar::CodePointAt
:
7814 trackAttached("StringCodePointAt");
7816 case StringChar::CharAt
:
7817 trackAttached("StringCharAt");
7819 case StringChar::At
:
7820 trackAttached("StringAt");
7824 return AttachDecision::Attach
;
7827 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharCodeAt() {
7828 return tryAttachStringChar(StringChar::CharCodeAt
);
7831 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCodePointAt() {
7832 return tryAttachStringChar(StringChar::CodePointAt
);
7835 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharAt() {
7836 return tryAttachStringChar(StringChar::CharAt
);
7839 AttachDecision
InlinableNativeIRGenerator::tryAttachStringAt() {
7840 return tryAttachStringChar(StringChar::At
);
7843 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCharCode() {
7844 // Need one number argument.
7845 if (argc_
!= 1 || !args_
[0].isNumber()) {
7846 return AttachDecision::NoAction
;
7849 // Initialize the input operand.
7850 initializeInputOperand();
7852 // Guard callee is the 'fromCharCode' native function.
7853 emitNativeCalleeGuard();
7855 // Guard int32 argument.
7856 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7857 Int32OperandId codeId
;
7858 if (args_
[0].isInt32()) {
7859 codeId
= writer
.guardToInt32(argId
);
7861 // 'fromCharCode' performs ToUint16 on its input. We can use Uint32
7862 // semantics, because ToUint16(ToUint32(v)) == ToUint16(v).
7863 codeId
= writer
.guardToInt32ModUint32(argId
);
7866 // Return string created from code.
7867 writer
.stringFromCharCodeResult(codeId
);
7868 writer
.returnFromIC();
7870 trackAttached("StringFromCharCode");
7871 return AttachDecision::Attach
;
7874 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCodePoint() {
7875 // Need one int32 argument.
7876 if (argc_
!= 1 || !args_
[0].isInt32()) {
7877 return AttachDecision::NoAction
;
7880 // String.fromCodePoint throws for invalid code points.
7881 int32_t codePoint
= args_
[0].toInt32();
7882 if (codePoint
< 0 || codePoint
> int32_t(unicode::NonBMPMax
)) {
7883 return AttachDecision::NoAction
;
7886 // Initialize the input operand.
7887 initializeInputOperand();
7889 // Guard callee is the 'fromCodePoint' native function.
7890 emitNativeCalleeGuard();
7892 // Guard int32 argument.
7893 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7894 Int32OperandId codeId
= writer
.guardToInt32(argId
);
7896 // Return string created from code point.
7897 writer
.stringFromCodePointResult(codeId
);
7898 writer
.returnFromIC();
7900 trackAttached("StringFromCodePoint");
7901 return AttachDecision::Attach
;
7904 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIncludes() {
7905 // Need one string argument.
7906 if (argc_
!= 1 || !args_
[0].isString()) {
7907 return AttachDecision::NoAction
;
7910 // Ensure |this| is a primitive string value.
7911 if (!thisval_
.isString()) {
7912 return AttachDecision::NoAction
;
7915 // Initialize the input operand.
7916 initializeInputOperand();
7918 // Guard callee is the 'includes' native function.
7919 emitNativeCalleeGuard();
7921 // Guard this is a string.
7922 ValOperandId thisValId
=
7923 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7924 StringOperandId strId
= writer
.guardToString(thisValId
);
7926 // Guard string argument.
7927 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7928 StringOperandId searchStrId
= writer
.guardToString(argId
);
7930 writer
.stringIncludesResult(strId
, searchStrId
);
7931 writer
.returnFromIC();
7933 trackAttached("StringIncludes");
7934 return AttachDecision::Attach
;
7937 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIndexOf() {
7938 // Need one string argument.
7939 if (argc_
!= 1 || !args_
[0].isString()) {
7940 return AttachDecision::NoAction
;
7943 // Ensure |this| is a primitive string value.
7944 if (!thisval_
.isString()) {
7945 return AttachDecision::NoAction
;
7948 // Initialize the input operand.
7949 initializeInputOperand();
7951 // Guard callee is the 'indexOf' native function.
7952 emitNativeCalleeGuard();
7954 // Guard this is a string.
7955 ValOperandId thisValId
=
7956 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7957 StringOperandId strId
= writer
.guardToString(thisValId
);
7959 // Guard string argument.
7960 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7961 StringOperandId searchStrId
= writer
.guardToString(argId
);
7963 writer
.stringIndexOfResult(strId
, searchStrId
);
7964 writer
.returnFromIC();
7966 trackAttached("StringIndexOf");
7967 return AttachDecision::Attach
;
7970 AttachDecision
InlinableNativeIRGenerator::tryAttachStringLastIndexOf() {
7971 // Need one string argument.
7972 if (argc_
!= 1 || !args_
[0].isString()) {
7973 return AttachDecision::NoAction
;
7976 // Ensure |this| is a primitive string value.
7977 if (!thisval_
.isString()) {
7978 return AttachDecision::NoAction
;
7981 // Initialize the input operand.
7982 initializeInputOperand();
7984 // Guard callee is the 'lastIndexOf' native function.
7985 emitNativeCalleeGuard();
7987 // Guard this is a string.
7988 ValOperandId thisValId
=
7989 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7990 StringOperandId strId
= writer
.guardToString(thisValId
);
7992 // Guard string argument.
7993 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7994 StringOperandId searchStrId
= writer
.guardToString(argId
);
7996 writer
.stringLastIndexOfResult(strId
, searchStrId
);
7997 writer
.returnFromIC();
7999 trackAttached("StringLastIndexOf");
8000 return AttachDecision::Attach
;
8003 AttachDecision
InlinableNativeIRGenerator::tryAttachStringStartsWith() {
8004 // Need one string argument.
8005 if (argc_
!= 1 || !args_
[0].isString()) {
8006 return AttachDecision::NoAction
;
8009 // Ensure |this| is a primitive string value.
8010 if (!thisval_
.isString()) {
8011 return AttachDecision::NoAction
;
8014 // Initialize the input operand.
8015 initializeInputOperand();
8017 // Guard callee is the 'startsWith' native function.
8018 emitNativeCalleeGuard();
8020 // Guard this is a string.
8021 ValOperandId thisValId
=
8022 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8023 StringOperandId strId
= writer
.guardToString(thisValId
);
8025 // Guard string argument.
8026 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8027 StringOperandId searchStrId
= writer
.guardToString(argId
);
8029 writer
.stringStartsWithResult(strId
, searchStrId
);
8030 writer
.returnFromIC();
8032 trackAttached("StringStartsWith");
8033 return AttachDecision::Attach
;
8036 AttachDecision
InlinableNativeIRGenerator::tryAttachStringEndsWith() {
8037 // Need one string argument.
8038 if (argc_
!= 1 || !args_
[0].isString()) {
8039 return AttachDecision::NoAction
;
8042 // Ensure |this| is a primitive string value.
8043 if (!thisval_
.isString()) {
8044 return AttachDecision::NoAction
;
8047 // Initialize the input operand.
8048 initializeInputOperand();
8050 // Guard callee is the 'endsWith' native function.
8051 emitNativeCalleeGuard();
8053 // Guard this is a string.
8054 ValOperandId thisValId
=
8055 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8056 StringOperandId strId
= writer
.guardToString(thisValId
);
8058 // Guard string argument.
8059 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8060 StringOperandId searchStrId
= writer
.guardToString(argId
);
8062 writer
.stringEndsWithResult(strId
, searchStrId
);
8063 writer
.returnFromIC();
8065 trackAttached("StringEndsWith");
8066 return AttachDecision::Attach
;
8069 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToLowerCase() {
8070 // Expecting no arguments.
8072 return AttachDecision::NoAction
;
8075 // Ensure |this| is a primitive string value.
8076 if (!thisval_
.isString()) {
8077 return AttachDecision::NoAction
;
8080 // Initialize the input operand.
8081 initializeInputOperand();
8083 // Guard callee is the 'toLowerCase' native function.
8084 emitNativeCalleeGuard();
8086 // Guard this is a string.
8087 ValOperandId thisValId
=
8088 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8089 StringOperandId strId
= writer
.guardToString(thisValId
);
8091 // Return string converted to lower-case.
8092 writer
.stringToLowerCaseResult(strId
);
8093 writer
.returnFromIC();
8095 trackAttached("StringToLowerCase");
8096 return AttachDecision::Attach
;
8099 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToUpperCase() {
8100 // Expecting no arguments.
8102 return AttachDecision::NoAction
;
8105 // Ensure |this| is a primitive string value.
8106 if (!thisval_
.isString()) {
8107 return AttachDecision::NoAction
;
8110 // Initialize the input operand.
8111 initializeInputOperand();
8113 // Guard callee is the 'toUpperCase' native function.
8114 emitNativeCalleeGuard();
8116 // Guard this is a string.
8117 ValOperandId thisValId
=
8118 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8119 StringOperandId strId
= writer
.guardToString(thisValId
);
8121 // Return string converted to upper-case.
8122 writer
.stringToUpperCaseResult(strId
);
8123 writer
.returnFromIC();
8125 trackAttached("StringToUpperCase");
8126 return AttachDecision::Attach
;
8129 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrim() {
8130 // Expecting no arguments.
8132 return AttachDecision::NoAction
;
8135 // Ensure |this| is a primitive string value.
8136 if (!thisval_
.isString()) {
8137 return AttachDecision::NoAction
;
8140 // Initialize the input operand.
8141 initializeInputOperand();
8143 // Guard callee is the 'trim' native function.
8144 emitNativeCalleeGuard();
8146 // Guard this is a string.
8147 ValOperandId thisValId
=
8148 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8149 StringOperandId strId
= writer
.guardToString(thisValId
);
8151 writer
.stringTrimResult(strId
);
8152 writer
.returnFromIC();
8154 trackAttached("StringTrim");
8155 return AttachDecision::Attach
;
8158 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimStart() {
8159 // Expecting no arguments.
8161 return AttachDecision::NoAction
;
8164 // Ensure |this| is a primitive string value.
8165 if (!thisval_
.isString()) {
8166 return AttachDecision::NoAction
;
8169 // Initialize the input operand.
8170 initializeInputOperand();
8172 // Guard callee is the 'trimStart' native function.
8173 emitNativeCalleeGuard();
8175 // Guard this is a string.
8176 ValOperandId thisValId
=
8177 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8178 StringOperandId strId
= writer
.guardToString(thisValId
);
8180 writer
.stringTrimStartResult(strId
);
8181 writer
.returnFromIC();
8183 trackAttached("StringTrimStart");
8184 return AttachDecision::Attach
;
8187 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimEnd() {
8188 // Expecting no arguments.
8190 return AttachDecision::NoAction
;
8193 // Ensure |this| is a primitive string value.
8194 if (!thisval_
.isString()) {
8195 return AttachDecision::NoAction
;
8198 // Initialize the input operand.
8199 initializeInputOperand();
8201 // Guard callee is the 'trimEnd' native function.
8202 emitNativeCalleeGuard();
8204 // Guard this is a string.
8205 ValOperandId thisValId
=
8206 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8207 StringOperandId strId
= writer
.guardToString(thisValId
);
8209 writer
.stringTrimEndResult(strId
);
8210 writer
.returnFromIC();
8212 trackAttached("StringTrimEnd");
8213 return AttachDecision::Attach
;
8216 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRandom() {
8217 // Expecting no arguments.
8219 return AttachDecision::NoAction
;
8222 MOZ_ASSERT(cx_
->realm() == callee_
->realm(),
8223 "Shouldn't inline cross-realm Math.random because per-realm RNG");
8225 // Initialize the input operand.
8226 initializeInputOperand();
8228 // Guard callee is the 'random' native function.
8229 emitNativeCalleeGuard();
8231 mozilla::non_crypto::XorShift128PlusRNG
* rng
=
8232 &cx_
->realm()->getOrCreateRandomNumberGenerator();
8233 writer
.mathRandomResult(rng
);
8235 writer
.returnFromIC();
8237 trackAttached("MathRandom");
8238 return AttachDecision::Attach
;
8241 AttachDecision
InlinableNativeIRGenerator::tryAttachMathAbs() {
8242 // Need one argument.
8244 return AttachDecision::NoAction
;
8247 if (!args_
[0].isNumber()) {
8248 return AttachDecision::NoAction
;
8251 // Initialize the input operand.
8252 initializeInputOperand();
8254 // Guard callee is the 'abs' native function.
8255 emitNativeCalleeGuard();
8257 ValOperandId argumentId
=
8258 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8260 // abs(INT_MIN) is a double.
8261 if (args_
[0].isInt32() && args_
[0].toInt32() != INT_MIN
) {
8262 Int32OperandId int32Id
= writer
.guardToInt32(argumentId
);
8263 writer
.mathAbsInt32Result(int32Id
);
8265 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8266 writer
.mathAbsNumberResult(numberId
);
8269 writer
.returnFromIC();
8271 trackAttached("MathAbs");
8272 return AttachDecision::Attach
;
8275 AttachDecision
InlinableNativeIRGenerator::tryAttachMathClz32() {
8276 // Need one (number) argument.
8277 if (argc_
!= 1 || !args_
[0].isNumber()) {
8278 return AttachDecision::NoAction
;
8281 // Initialize the input operand.
8282 initializeInputOperand();
8284 // Guard callee is the 'clz32' native function.
8285 emitNativeCalleeGuard();
8287 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8289 Int32OperandId int32Id
;
8290 if (args_
[0].isInt32()) {
8291 int32Id
= writer
.guardToInt32(argId
);
8293 MOZ_ASSERT(args_
[0].isDouble());
8294 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8295 int32Id
= writer
.truncateDoubleToUInt32(numId
);
8297 writer
.mathClz32Result(int32Id
);
8298 writer
.returnFromIC();
8300 trackAttached("MathClz32");
8301 return AttachDecision::Attach
;
8304 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSign() {
8305 // Need one (number) argument.
8306 if (argc_
!= 1 || !args_
[0].isNumber()) {
8307 return AttachDecision::NoAction
;
8310 // Initialize the input operand.
8311 initializeInputOperand();
8313 // Guard callee is the 'sign' native function.
8314 emitNativeCalleeGuard();
8316 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8318 if (args_
[0].isInt32()) {
8319 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
8320 writer
.mathSignInt32Result(int32Id
);
8322 // Math.sign returns a double only if the input is -0 or NaN so try to
8323 // optimize the common Number => Int32 case.
8324 double d
= math_sign_impl(args_
[0].toDouble());
8326 bool resultIsInt32
= mozilla::NumberIsInt32(d
, &unused
);
8328 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8329 if (resultIsInt32
) {
8330 writer
.mathSignNumberToInt32Result(numId
);
8332 writer
.mathSignNumberResult(numId
);
8336 writer
.returnFromIC();
8338 trackAttached("MathSign");
8339 return AttachDecision::Attach
;
8342 AttachDecision
InlinableNativeIRGenerator::tryAttachMathImul() {
8343 // Need two (number) arguments.
8344 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8345 return AttachDecision::NoAction
;
8348 // Initialize the input operand.
8349 initializeInputOperand();
8351 // Guard callee is the 'imul' native function.
8352 emitNativeCalleeGuard();
8354 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8355 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8357 Int32OperandId int32Arg0Id
, int32Arg1Id
;
8358 if (args_
[0].isInt32() && args_
[1].isInt32()) {
8359 int32Arg0Id
= writer
.guardToInt32(arg0Id
);
8360 int32Arg1Id
= writer
.guardToInt32(arg1Id
);
8362 // Treat both arguments as numbers if at least one of them is non-int32.
8363 NumberOperandId numArg0Id
= writer
.guardIsNumber(arg0Id
);
8364 NumberOperandId numArg1Id
= writer
.guardIsNumber(arg1Id
);
8365 int32Arg0Id
= writer
.truncateDoubleToUInt32(numArg0Id
);
8366 int32Arg1Id
= writer
.truncateDoubleToUInt32(numArg1Id
);
8368 writer
.mathImulResult(int32Arg0Id
, int32Arg1Id
);
8369 writer
.returnFromIC();
8371 trackAttached("MathImul");
8372 return AttachDecision::Attach
;
8375 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFloor() {
8376 // Need one (number) argument.
8377 if (argc_
!= 1 || !args_
[0].isNumber()) {
8378 return AttachDecision::NoAction
;
8381 // Check if the result fits in int32.
8382 double res
= math_floor_impl(args_
[0].toNumber());
8384 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8386 // Initialize the input operand.
8387 initializeInputOperand();
8389 // Guard callee is the 'floor' native function.
8390 emitNativeCalleeGuard();
8392 ValOperandId argumentId
=
8393 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8395 if (args_
[0].isInt32()) {
8396 MOZ_ASSERT(resultIsInt32
);
8398 // Use an indirect truncation to inform the optimizer it needs to preserve
8399 // a bailout when the input can't be represented as an int32, even if the
8400 // final result is fully truncated.
8401 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8402 writer
.indirectTruncateInt32Result(intId
);
8404 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8406 if (resultIsInt32
) {
8407 writer
.mathFloorToInt32Result(numberId
);
8409 writer
.mathFloorNumberResult(numberId
);
8413 writer
.returnFromIC();
8415 trackAttached("MathFloor");
8416 return AttachDecision::Attach
;
8419 AttachDecision
InlinableNativeIRGenerator::tryAttachMathCeil() {
8420 // Need one (number) argument.
8421 if (argc_
!= 1 || !args_
[0].isNumber()) {
8422 return AttachDecision::NoAction
;
8425 // Check if the result fits in int32.
8426 double res
= math_ceil_impl(args_
[0].toNumber());
8428 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8430 // Initialize the input operand.
8431 initializeInputOperand();
8433 // Guard callee is the 'ceil' native function.
8434 emitNativeCalleeGuard();
8436 ValOperandId argumentId
=
8437 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8439 if (args_
[0].isInt32()) {
8440 MOZ_ASSERT(resultIsInt32
);
8442 // Use an indirect truncation to inform the optimizer it needs to preserve
8443 // a bailout when the input can't be represented as an int32, even if the
8444 // final result is fully truncated.
8445 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8446 writer
.indirectTruncateInt32Result(intId
);
8448 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8450 if (resultIsInt32
) {
8451 writer
.mathCeilToInt32Result(numberId
);
8453 writer
.mathCeilNumberResult(numberId
);
8457 writer
.returnFromIC();
8459 trackAttached("MathCeil");
8460 return AttachDecision::Attach
;
8463 AttachDecision
InlinableNativeIRGenerator::tryAttachMathTrunc() {
8464 // Need one (number) argument.
8465 if (argc_
!= 1 || !args_
[0].isNumber()) {
8466 return AttachDecision::NoAction
;
8469 // Check if the result fits in int32.
8470 double res
= math_trunc_impl(args_
[0].toNumber());
8472 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8474 // Initialize the input operand.
8475 initializeInputOperand();
8477 // Guard callee is the 'trunc' native function.
8478 emitNativeCalleeGuard();
8480 ValOperandId argumentId
=
8481 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8483 if (args_
[0].isInt32()) {
8484 MOZ_ASSERT(resultIsInt32
);
8486 // We don't need an indirect truncation barrier here, because Math.trunc
8487 // always truncates, but never rounds its input away from zero.
8488 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8489 writer
.loadInt32Result(intId
);
8491 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8493 if (resultIsInt32
) {
8494 writer
.mathTruncToInt32Result(numberId
);
8496 writer
.mathTruncNumberResult(numberId
);
8500 writer
.returnFromIC();
8502 trackAttached("MathTrunc");
8503 return AttachDecision::Attach
;
8506 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRound() {
8507 // Need one (number) argument.
8508 if (argc_
!= 1 || !args_
[0].isNumber()) {
8509 return AttachDecision::NoAction
;
8512 // Check if the result fits in int32.
8513 double res
= math_round_impl(args_
[0].toNumber());
8515 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8517 // Initialize the input operand.
8518 initializeInputOperand();
8520 // Guard callee is the 'round' native function.
8521 emitNativeCalleeGuard();
8523 ValOperandId argumentId
=
8524 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8526 if (args_
[0].isInt32()) {
8527 MOZ_ASSERT(resultIsInt32
);
8529 // Use an indirect truncation to inform the optimizer it needs to preserve
8530 // a bailout when the input can't be represented as an int32, even if the
8531 // final result is fully truncated.
8532 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8533 writer
.indirectTruncateInt32Result(intId
);
8535 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8537 if (resultIsInt32
) {
8538 writer
.mathRoundToInt32Result(numberId
);
8540 writer
.mathFunctionNumberResult(numberId
, UnaryMathFunction::Round
);
8544 writer
.returnFromIC();
8546 trackAttached("MathRound");
8547 return AttachDecision::Attach
;
8550 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSqrt() {
8551 // Need one (number) argument.
8552 if (argc_
!= 1 || !args_
[0].isNumber()) {
8553 return AttachDecision::NoAction
;
8556 // Initialize the input operand.
8557 initializeInputOperand();
8559 // Guard callee is the 'sqrt' native function.
8560 emitNativeCalleeGuard();
8562 ValOperandId argumentId
=
8563 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8564 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8565 writer
.mathSqrtNumberResult(numberId
);
8566 writer
.returnFromIC();
8568 trackAttached("MathSqrt");
8569 return AttachDecision::Attach
;
8572 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFRound() {
8573 // Need one (number) argument.
8574 if (argc_
!= 1 || !args_
[0].isNumber()) {
8575 return AttachDecision::NoAction
;
8578 // Initialize the input operand.
8579 initializeInputOperand();
8581 // Guard callee is the 'fround' native function.
8582 emitNativeCalleeGuard();
8584 ValOperandId argumentId
=
8585 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8586 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8587 writer
.mathFRoundNumberResult(numberId
);
8588 writer
.returnFromIC();
8590 trackAttached("MathFRound");
8591 return AttachDecision::Attach
;
8594 static bool CanAttachInt32Pow(const Value
& baseVal
, const Value
& powerVal
) {
8595 auto valToInt32
= [](const Value
& v
) {
8599 if (v
.isBoolean()) {
8600 return int32_t(v
.toBoolean());
8602 MOZ_ASSERT(v
.isNull());
8605 int32_t base
= valToInt32(baseVal
);
8606 int32_t power
= valToInt32(powerVal
);
8608 // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
8609 // gets large enough. It's hard to determine when exactly y is "large enough",
8610 // so we don't use Int32PowResult when x != 1 and y < 0.
8611 // Note: it's important for this condition to match the code generated by
8612 // MacroAssembler::pow32 to prevent failure loops.
8617 double res
= powi(base
, power
);
8619 return mozilla::NumberIsInt32(res
, &unused
);
8622 AttachDecision
InlinableNativeIRGenerator::tryAttachMathPow() {
8623 // Need two number arguments.
8624 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8625 return AttachDecision::NoAction
;
8628 // Initialize the input operand.
8629 initializeInputOperand();
8631 // Guard callee is the 'pow' function.
8632 emitNativeCalleeGuard();
8634 ValOperandId baseId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8635 ValOperandId exponentId
=
8636 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8638 if (args_
[0].isInt32() && args_
[1].isInt32() &&
8639 CanAttachInt32Pow(args_
[0], args_
[1])) {
8640 Int32OperandId baseInt32Id
= writer
.guardToInt32(baseId
);
8641 Int32OperandId exponentInt32Id
= writer
.guardToInt32(exponentId
);
8642 writer
.int32PowResult(baseInt32Id
, exponentInt32Id
);
8644 NumberOperandId baseNumberId
= writer
.guardIsNumber(baseId
);
8645 NumberOperandId exponentNumberId
= writer
.guardIsNumber(exponentId
);
8646 writer
.doublePowResult(baseNumberId
, exponentNumberId
);
8649 writer
.returnFromIC();
8651 trackAttached("MathPow");
8652 return AttachDecision::Attach
;
8655 AttachDecision
InlinableNativeIRGenerator::tryAttachMathHypot() {
8656 // Only optimize if there are 2-4 arguments.
8657 if (argc_
< 2 || argc_
> 4) {
8658 return AttachDecision::NoAction
;
8661 for (size_t i
= 0; i
< argc_
; i
++) {
8662 if (!args_
[i
].isNumber()) {
8663 return AttachDecision::NoAction
;
8667 // Initialize the input operand.
8668 initializeInputOperand();
8670 // Guard callee is the 'hypot' native function.
8671 emitNativeCalleeGuard();
8673 ValOperandId firstId
=
8674 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8675 ValOperandId secondId
=
8676 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8678 NumberOperandId firstNumId
= writer
.guardIsNumber(firstId
);
8679 NumberOperandId secondNumId
= writer
.guardIsNumber(secondId
);
8681 ValOperandId thirdId
;
8682 ValOperandId fourthId
;
8683 NumberOperandId thirdNumId
;
8684 NumberOperandId fourthNumId
;
8688 writer
.mathHypot2NumberResult(firstNumId
, secondNumId
);
8691 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8692 thirdNumId
= writer
.guardIsNumber(thirdId
);
8693 writer
.mathHypot3NumberResult(firstNumId
, secondNumId
, thirdNumId
);
8696 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8697 fourthId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8698 thirdNumId
= writer
.guardIsNumber(thirdId
);
8699 fourthNumId
= writer
.guardIsNumber(fourthId
);
8700 writer
.mathHypot4NumberResult(firstNumId
, secondNumId
, thirdNumId
,
8704 MOZ_CRASH("Unexpected number of arguments to hypot function.");
8707 writer
.returnFromIC();
8709 trackAttached("MathHypot");
8710 return AttachDecision::Attach
;
8713 AttachDecision
InlinableNativeIRGenerator::tryAttachMathATan2() {
8714 // Requires two numbers as arguments.
8715 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8716 return AttachDecision::NoAction
;
8719 // Initialize the input operand.
8720 initializeInputOperand();
8722 // Guard callee is the 'atan2' native function.
8723 emitNativeCalleeGuard();
8725 ValOperandId yId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8726 ValOperandId xId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8728 NumberOperandId yNumberId
= writer
.guardIsNumber(yId
);
8729 NumberOperandId xNumberId
= writer
.guardIsNumber(xId
);
8731 writer
.mathAtan2NumberResult(yNumberId
, xNumberId
);
8732 writer
.returnFromIC();
8734 trackAttached("MathAtan2");
8735 return AttachDecision::Attach
;
8738 AttachDecision
InlinableNativeIRGenerator::tryAttachMathMinMax(bool isMax
) {
8739 // For now only optimize if there are 1-4 arguments.
8740 if (argc_
< 1 || argc_
> 4) {
8741 return AttachDecision::NoAction
;
8744 // Ensure all arguments are numbers.
8745 bool allInt32
= true;
8746 for (size_t i
= 0; i
< argc_
; i
++) {
8747 if (!args_
[i
].isNumber()) {
8748 return AttachDecision::NoAction
;
8750 if (!args_
[i
].isInt32()) {
8755 // Initialize the input operand.
8756 initializeInputOperand();
8758 // Guard callee is this Math function.
8759 emitNativeCalleeGuard();
8762 ValOperandId valId
=
8763 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8764 Int32OperandId resId
= writer
.guardToInt32(valId
);
8765 for (size_t i
= 1; i
< argc_
; i
++) {
8766 ValOperandId argId
=
8767 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8768 Int32OperandId argInt32Id
= writer
.guardToInt32(argId
);
8769 resId
= writer
.int32MinMax(isMax
, resId
, argInt32Id
);
8771 writer
.loadInt32Result(resId
);
8773 ValOperandId valId
=
8774 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8775 NumberOperandId resId
= writer
.guardIsNumber(valId
);
8776 for (size_t i
= 1; i
< argc_
; i
++) {
8777 ValOperandId argId
=
8778 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8779 NumberOperandId argNumId
= writer
.guardIsNumber(argId
);
8780 resId
= writer
.numberMinMax(isMax
, resId
, argNumId
);
8782 writer
.loadDoubleResult(resId
);
8785 writer
.returnFromIC();
8787 trackAttached(isMax
? "MathMax" : "MathMin");
8788 return AttachDecision::Attach
;
8791 AttachDecision
InlinableNativeIRGenerator::tryAttachSpreadMathMinMax(
8793 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Spread
||
8794 flags_
.getArgFormat() == CallFlags::FunApplyArray
);
8796 // The result will be an int32 if there is at least one argument,
8797 // and all the arguments are int32.
8798 bool int32Result
= args_
.length() > 0;
8799 for (size_t i
= 0; i
< args_
.length(); i
++) {
8800 if (!args_
[i
].isNumber()) {
8801 return AttachDecision::NoAction
;
8803 if (!args_
[i
].isInt32()) {
8804 int32Result
= false;
8808 // Initialize the input operand.
8809 initializeInputOperand();
8811 // Guard callee is this Math function.
8812 emitNativeCalleeGuard();
8814 // Load the argument array.
8815 ObjOperandId argsId
= emitLoadArgsArray();
8818 writer
.int32MinMaxArrayResult(argsId
, isMax
);
8820 writer
.numberMinMaxArrayResult(argsId
, isMax
);
8823 writer
.returnFromIC();
8825 trackAttached(isMax
? "MathMaxArray" : "MathMinArray");
8826 return AttachDecision::Attach
;
8829 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFunction(
8830 UnaryMathFunction fun
) {
8831 // Need one argument.
8833 return AttachDecision::NoAction
;
8836 if (!args_
[0].isNumber()) {
8837 return AttachDecision::NoAction
;
8840 if (math_use_fdlibm_for_sin_cos_tan() ||
8841 callee_
->realm()->creationOptions().alwaysUseFdlibm()) {
8843 case UnaryMathFunction::SinNative
:
8844 fun
= UnaryMathFunction::SinFdlibm
;
8846 case UnaryMathFunction::CosNative
:
8847 fun
= UnaryMathFunction::CosFdlibm
;
8849 case UnaryMathFunction::TanNative
:
8850 fun
= UnaryMathFunction::TanFdlibm
;
8857 // Initialize the input operand.
8858 initializeInputOperand();
8860 // Guard callee is this Math function.
8861 emitNativeCalleeGuard();
8863 ValOperandId argumentId
=
8864 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8865 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8866 writer
.mathFunctionNumberResult(numberId
, fun
);
8867 writer
.returnFromIC();
8869 trackAttached("MathFunction");
8870 return AttachDecision::Attach
;
8873 AttachDecision
InlinableNativeIRGenerator::tryAttachNumber() {
8874 // Expect a single string argument.
8875 if (argc_
!= 1 || !args_
[0].isString()) {
8876 return AttachDecision::NoAction
;
8880 if (!StringToNumber(cx_
, args_
[0].toString(), &num
)) {
8881 cx_
->recoverFromOutOfMemory();
8882 return AttachDecision::NoAction
;
8885 // Initialize the input operand.
8886 initializeInputOperand();
8888 // Guard callee is the `Number` function.
8889 emitNativeCalleeGuard();
8891 // Guard that the argument is a string.
8892 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8893 StringOperandId strId
= writer
.guardToString(argId
);
8895 // Return either an Int32 or Double result.
8897 if (mozilla::NumberIsInt32(num
, &unused
)) {
8898 Int32OperandId resultId
= writer
.guardStringToInt32(strId
);
8899 writer
.loadInt32Result(resultId
);
8901 NumberOperandId resultId
= writer
.guardStringToNumber(strId
);
8902 writer
.loadDoubleResult(resultId
);
8904 writer
.returnFromIC();
8906 trackAttached("Number");
8907 return AttachDecision::Attach
;
8910 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberParseInt() {
8911 // Expected arguments: input (string or number), optional radix (int32).
8912 if (argc_
< 1 || argc_
> 2) {
8913 return AttachDecision::NoAction
;
8915 if (!args_
[0].isString() && !args_
[0].isNumber()) {
8916 return AttachDecision::NoAction
;
8918 if (args_
[0].isDouble()) {
8919 double d
= args_
[0].toDouble();
8921 // See num_parseInt for why we have to reject numbers smaller than 1.0e-6.
8922 // Negative numbers in the exclusive range (-1, -0) return -0.
8923 bool canTruncateToInt32
=
8924 (DOUBLE_DECIMAL_IN_SHORTEST_LOW
<= d
&& d
<= double(INT32_MAX
)) ||
8925 (double(INT32_MIN
) <= d
&& d
<= -1.0) || (d
== 0.0);
8926 if (!canTruncateToInt32
) {
8927 return AttachDecision::NoAction
;
8930 if (argc_
> 1 && !args_
[1].isInt32(10)) {
8931 return AttachDecision::NoAction
;
8934 // Initialize the input operand.
8935 initializeInputOperand();
8937 // Guard callee is the 'parseInt' native function.
8938 emitNativeCalleeGuard();
8940 auto guardRadix
= [&]() {
8941 ValOperandId radixId
=
8942 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8943 Int32OperandId intRadixId
= writer
.guardToInt32(radixId
);
8944 writer
.guardSpecificInt32(intRadixId
, 10);
8948 ValOperandId inputId
=
8949 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8951 if (args_
[0].isString()) {
8952 StringOperandId strId
= writer
.guardToString(inputId
);
8954 Int32OperandId intRadixId
;
8956 intRadixId
= guardRadix();
8958 intRadixId
= writer
.loadInt32Constant(0);
8961 writer
.numberParseIntResult(strId
, intRadixId
);
8962 } else if (args_
[0].isInt32()) {
8963 Int32OperandId intId
= writer
.guardToInt32(inputId
);
8967 writer
.loadInt32Result(intId
);
8969 MOZ_ASSERT(args_
[0].isDouble());
8971 NumberOperandId numId
= writer
.guardIsNumber(inputId
);
8975 writer
.doubleParseIntResult(numId
);
8978 writer
.returnFromIC();
8980 trackAttached("NumberParseInt");
8981 return AttachDecision::Attach
;
8984 StringOperandId
IRGenerator::emitToStringGuard(ValOperandId id
,
8986 MOZ_ASSERT(CanConvertToString(v
));
8988 return writer
.guardToString(id
);
8990 if (v
.isBoolean()) {
8991 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
8992 return writer
.booleanToString(boolId
);
8995 writer
.guardIsNull(id
);
8996 return writer
.loadConstantString(cx_
->names().null
);
8998 if (v
.isUndefined()) {
8999 writer
.guardIsUndefined(id
);
9000 return writer
.loadConstantString(cx_
->names().undefined
);
9003 Int32OperandId intId
= writer
.guardToInt32(id
);
9004 return writer
.callInt32ToString(intId
);
9006 // At this point we are creating an IC that will handle
9007 // both Int32 and Double cases.
9008 MOZ_ASSERT(v
.isNumber());
9009 NumberOperandId numId
= writer
.guardIsNumber(id
);
9010 return writer
.callNumberToString(numId
);
9013 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberToString() {
9014 // Expecting no arguments or a single int32 argument.
9016 return AttachDecision::NoAction
;
9018 if (argc_
== 1 && !args_
[0].isInt32()) {
9019 return AttachDecision::NoAction
;
9022 // Ensure |this| is a primitive number value.
9023 if (!thisval_
.isNumber()) {
9024 return AttachDecision::NoAction
;
9027 // No arguments means base 10.
9030 base
= args_
[0].toInt32();
9031 if (base
< 2 || base
> 36) {
9032 return AttachDecision::NoAction
;
9035 // Non-decimal bases currently only support int32 inputs.
9036 if (base
!= 10 && !thisval_
.isInt32()) {
9037 return AttachDecision::NoAction
;
9040 MOZ_ASSERT(2 <= base
&& base
<= 36);
9042 // Initialize the input operand.
9043 initializeInputOperand();
9045 // Guard callee is the 'toString' native function.
9046 emitNativeCalleeGuard();
9048 // Initialize the |this| operand.
9049 ValOperandId thisValId
=
9050 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9052 // Guard on number and convert to string.
9054 // If an explicit base was passed, guard its value.
9056 // Guard the `base` argument is an int32.
9057 ValOperandId baseId
=
9058 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9059 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
9061 // Guard `base` is 10 for decimal toString representation.
9062 writer
.guardSpecificInt32(intBaseId
, 10);
9065 StringOperandId strId
= emitToStringGuard(thisValId
, thisval_
);
9067 // Return the string.
9068 writer
.loadStringResult(strId
);
9070 MOZ_ASSERT(argc_
> 0);
9072 // Guard the |this| value is an int32.
9073 Int32OperandId thisIntId
= writer
.guardToInt32(thisValId
);
9075 // Guard the `base` argument is an int32.
9076 ValOperandId baseId
=
9077 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9078 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
9080 // Return the string.
9081 writer
.int32ToStringWithBaseResult(thisIntId
, intBaseId
);
9084 writer
.returnFromIC();
9086 trackAttached("NumberToString");
9087 return AttachDecision::Attach
;
9090 AttachDecision
InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
9091 // Need one argument.
9093 return AttachDecision::NoAction
;
9096 if (!args_
[0].isObject()) {
9097 return AttachDecision::NoAction
;
9100 // Initialize the input operand.
9101 initializeInputOperand();
9103 // Guard callee is the 'getPrototypeOf' native function.
9104 emitNativeCalleeGuard();
9106 ValOperandId argumentId
=
9107 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9108 ObjOperandId objId
= writer
.guardToObject(argumentId
);
9110 writer
.reflectGetPrototypeOfResult(objId
);
9111 writer
.returnFromIC();
9113 trackAttached("ReflectGetPrototypeOf");
9114 return AttachDecision::Attach
;
9117 static bool AtomicsMeetsPreconditions(TypedArrayObject
* typedArray
,
9118 const Value
& index
) {
9119 switch (typedArray
->type()) {
9123 case Scalar::Uint16
:
9125 case Scalar::Uint32
:
9126 case Scalar::BigInt64
:
9127 case Scalar::BigUint64
:
9130 case Scalar::Float32
:
9131 case Scalar::Float64
:
9132 case Scalar::Uint8Clamped
:
9133 // Exclude floating types and Uint8Clamped.
9136 case Scalar::MaxTypedArrayViewType
:
9138 case Scalar::Simd128
:
9139 MOZ_CRASH("Unsupported TypedArray type");
9142 // Bounds check the index argument.
9144 if (!ValueIsInt64Index(index
, &indexInt64
)) {
9147 if (indexInt64
< 0 ||
9148 uint64_t(indexInt64
) >= typedArray
->length().valueOr(0)) {
9155 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
9156 if (!JitSupportsAtomics()) {
9157 return AttachDecision::NoAction
;
9160 // Need four arguments.
9162 return AttachDecision::NoAction
;
9165 // Arguments: typedArray, index (number), expected, replacement.
9166 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9167 return AttachDecision::NoAction
;
9169 if (!args_
[1].isNumber()) {
9170 return AttachDecision::NoAction
;
9173 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9174 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9175 return AttachDecision::NoAction
;
9178 Scalar::Type elementType
= typedArray
->type();
9179 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
9180 return AttachDecision::NoAction
;
9182 if (!ValueCanConvertToNumeric(elementType
, args_
[3])) {
9183 return AttachDecision::NoAction
;
9186 // Initialize the input operand.
9187 initializeInputOperand();
9189 // Guard callee is the `compareExchange` native function.
9190 emitNativeCalleeGuard();
9192 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9193 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9194 writer
.guardShapeForClass(objId
, typedArray
->shape());
9196 // Convert index to intPtr.
9197 ValOperandId indexId
=
9198 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9199 IntPtrOperandId intPtrIndexId
=
9200 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9202 // Convert expected value to int32/BigInt.
9203 ValOperandId expectedId
=
9204 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9205 OperandId numericExpectedId
=
9206 emitNumericGuard(expectedId
, args_
[2], elementType
);
9208 // Convert replacement value to int32/BigInt.
9209 ValOperandId replacementId
=
9210 writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
9211 OperandId numericReplacementId
=
9212 emitNumericGuard(replacementId
, args_
[3], elementType
);
9214 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9215 writer
.atomicsCompareExchangeResult(objId
, intPtrIndexId
, numericExpectedId
,
9216 numericReplacementId
, typedArray
->type(),
9218 writer
.returnFromIC();
9220 trackAttached("AtomicsCompareExchange");
9221 return AttachDecision::Attach
;
9224 bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
9225 if (!JitSupportsAtomics()) {
9229 // Need three arguments.
9234 // Arguments: typedArray, index (number), value.
9235 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9238 if (!args_
[1].isNumber()) {
9242 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9243 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9246 if (!ValueCanConvertToNumeric(typedArray
->type(), args_
[2])) {
9252 InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands
9253 InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() {
9254 MOZ_ASSERT(canAttachAtomicsReadWriteModify());
9256 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9258 // Initialize the input operand.
9259 initializeInputOperand();
9261 // Guard callee is this Atomics function.
9262 emitNativeCalleeGuard();
9264 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9265 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9266 writer
.guardShapeForClass(objId
, typedArray
->shape());
9268 // Convert index to intPtr.
9269 ValOperandId indexId
=
9270 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9271 IntPtrOperandId intPtrIndexId
=
9272 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9274 // Convert value to int32/BigInt.
9275 ValOperandId valueId
=
9276 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9277 OperandId numericValueId
=
9278 emitNumericGuard(valueId
, args_
[2], typedArray
->type());
9280 return {objId
, intPtrIndexId
, numericValueId
};
9283 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsExchange() {
9284 if (!canAttachAtomicsReadWriteModify()) {
9285 return AttachDecision::NoAction
;
9288 auto [objId
, intPtrIndexId
, numericValueId
] =
9289 emitAtomicsReadWriteModifyOperands();
9291 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9292 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9294 writer
.atomicsExchangeResult(objId
, intPtrIndexId
, numericValueId
,
9295 typedArray
->type(), viewKind
);
9296 writer
.returnFromIC();
9298 trackAttached("AtomicsExchange");
9299 return AttachDecision::Attach
;
9302 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAdd() {
9303 if (!canAttachAtomicsReadWriteModify()) {
9304 return AttachDecision::NoAction
;
9307 auto [objId
, intPtrIndexId
, numericValueId
] =
9308 emitAtomicsReadWriteModifyOperands();
9310 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9311 bool forEffect
= ignoresResult();
9312 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9314 writer
.atomicsAddResult(objId
, intPtrIndexId
, numericValueId
,
9315 typedArray
->type(), forEffect
, viewKind
);
9316 writer
.returnFromIC();
9318 trackAttached("AtomicsAdd");
9319 return AttachDecision::Attach
;
9322 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsSub() {
9323 if (!canAttachAtomicsReadWriteModify()) {
9324 return AttachDecision::NoAction
;
9327 auto [objId
, intPtrIndexId
, numericValueId
] =
9328 emitAtomicsReadWriteModifyOperands();
9330 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9331 bool forEffect
= ignoresResult();
9332 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9334 writer
.atomicsSubResult(objId
, intPtrIndexId
, numericValueId
,
9335 typedArray
->type(), forEffect
, viewKind
);
9336 writer
.returnFromIC();
9338 trackAttached("AtomicsSub");
9339 return AttachDecision::Attach
;
9342 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAnd() {
9343 if (!canAttachAtomicsReadWriteModify()) {
9344 return AttachDecision::NoAction
;
9347 auto [objId
, intPtrIndexId
, numericValueId
] =
9348 emitAtomicsReadWriteModifyOperands();
9350 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9351 bool forEffect
= ignoresResult();
9352 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9354 writer
.atomicsAndResult(objId
, intPtrIndexId
, numericValueId
,
9355 typedArray
->type(), forEffect
, viewKind
);
9356 writer
.returnFromIC();
9358 trackAttached("AtomicsAnd");
9359 return AttachDecision::Attach
;
9362 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsOr() {
9363 if (!canAttachAtomicsReadWriteModify()) {
9364 return AttachDecision::NoAction
;
9367 auto [objId
, intPtrIndexId
, numericValueId
] =
9368 emitAtomicsReadWriteModifyOperands();
9370 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9371 bool forEffect
= ignoresResult();
9372 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9374 writer
.atomicsOrResult(objId
, intPtrIndexId
, numericValueId
,
9375 typedArray
->type(), forEffect
, viewKind
);
9376 writer
.returnFromIC();
9378 trackAttached("AtomicsOr");
9379 return AttachDecision::Attach
;
9382 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsXor() {
9383 if (!canAttachAtomicsReadWriteModify()) {
9384 return AttachDecision::NoAction
;
9387 auto [objId
, intPtrIndexId
, numericValueId
] =
9388 emitAtomicsReadWriteModifyOperands();
9390 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9391 bool forEffect
= ignoresResult();
9392 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9394 writer
.atomicsXorResult(objId
, intPtrIndexId
, numericValueId
,
9395 typedArray
->type(), forEffect
, viewKind
);
9396 writer
.returnFromIC();
9398 trackAttached("AtomicsXor");
9399 return AttachDecision::Attach
;
9402 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
9403 if (!JitSupportsAtomics()) {
9404 return AttachDecision::NoAction
;
9407 // Need two arguments.
9409 return AttachDecision::NoAction
;
9412 // Arguments: typedArray, index (number).
9413 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9414 return AttachDecision::NoAction
;
9416 if (!args_
[1].isNumber()) {
9417 return AttachDecision::NoAction
;
9420 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9421 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9422 return AttachDecision::NoAction
;
9425 // Initialize the input operand.
9426 initializeInputOperand();
9428 // Guard callee is the `load` native function.
9429 emitNativeCalleeGuard();
9431 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9432 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9433 writer
.guardShapeForClass(objId
, typedArray
->shape());
9435 // Convert index to intPtr.
9436 ValOperandId indexId
=
9437 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9438 IntPtrOperandId intPtrIndexId
=
9439 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9441 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9442 writer
.atomicsLoadResult(objId
, intPtrIndexId
, typedArray
->type(), viewKind
);
9443 writer
.returnFromIC();
9445 trackAttached("AtomicsLoad");
9446 return AttachDecision::Attach
;
9449 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsStore() {
9450 if (!JitSupportsAtomics()) {
9451 return AttachDecision::NoAction
;
9454 // Need three arguments.
9456 return AttachDecision::NoAction
;
9459 // Atomics.store() is annoying because it returns the result of converting the
9460 // value by ToInteger(), not the input value, nor the result of converting the
9461 // value by ToInt32(). It is especially annoying because almost nobody uses
9462 // the result value.
9464 // As an expedient compromise, therefore, we inline only if the result is
9465 // obviously unused or if the argument is already Int32 and thus requires no
9468 // Arguments: typedArray, index (number), value.
9469 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9470 return AttachDecision::NoAction
;
9472 if (!args_
[1].isNumber()) {
9473 return AttachDecision::NoAction
;
9476 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9477 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9478 return AttachDecision::NoAction
;
9481 Scalar::Type elementType
= typedArray
->type();
9482 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
9483 return AttachDecision::NoAction
;
9486 bool guardIsInt32
= !Scalar::isBigIntType(elementType
) && !ignoresResult();
9488 if (guardIsInt32
&& !args_
[2].isInt32()) {
9489 return AttachDecision::NoAction
;
9492 // Initialize the input operand.
9493 initializeInputOperand();
9495 // Guard callee is the `store` native function.
9496 emitNativeCalleeGuard();
9498 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9499 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9500 writer
.guardShapeForClass(objId
, typedArray
->shape());
9502 // Convert index to intPtr.
9503 ValOperandId indexId
=
9504 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9505 IntPtrOperandId intPtrIndexId
=
9506 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9508 // Ensure value is int32 or BigInt.
9509 ValOperandId valueId
=
9510 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9511 OperandId numericValueId
;
9513 numericValueId
= writer
.guardToInt32(valueId
);
9515 numericValueId
= emitNumericGuard(valueId
, args_
[2], elementType
);
9518 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9519 writer
.atomicsStoreResult(objId
, intPtrIndexId
, numericValueId
,
9520 typedArray
->type(), viewKind
);
9521 writer
.returnFromIC();
9523 trackAttached("AtomicsStore");
9524 return AttachDecision::Attach
;
9527 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsIsLockFree() {
9528 // Need one argument.
9530 return AttachDecision::NoAction
;
9533 if (!args_
[0].isInt32()) {
9534 return AttachDecision::NoAction
;
9537 // Initialize the input operand.
9538 initializeInputOperand();
9540 // Guard callee is the `isLockFree` native function.
9541 emitNativeCalleeGuard();
9543 // Ensure value is int32.
9544 ValOperandId valueId
=
9545 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9546 Int32OperandId int32ValueId
= writer
.guardToInt32(valueId
);
9548 writer
.atomicsIsLockFreeResult(int32ValueId
);
9549 writer
.returnFromIC();
9551 trackAttached("AtomicsIsLockFree");
9552 return AttachDecision::Attach
;
9555 AttachDecision
InlinableNativeIRGenerator::tryAttachBoolean() {
9556 // Need zero or one argument.
9558 return AttachDecision::NoAction
;
9561 // Initialize the input operand.
9562 initializeInputOperand();
9564 // Guard callee is the 'Boolean' native function.
9565 emitNativeCalleeGuard();
9568 writer
.loadBooleanResult(false);
9570 ValOperandId valId
=
9571 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9573 writer
.loadValueTruthyResult(valId
);
9576 writer
.returnFromIC();
9578 trackAttached("Boolean");
9579 return AttachDecision::Attach
;
9582 AttachDecision
InlinableNativeIRGenerator::tryAttachBailout() {
9583 // Expecting no arguments.
9585 return AttachDecision::NoAction
;
9588 // Initialize the input operand.
9589 initializeInputOperand();
9591 // Guard callee is the 'bailout' native function.
9592 emitNativeCalleeGuard();
9595 writer
.loadUndefinedResult();
9596 writer
.returnFromIC();
9598 trackAttached("Bailout");
9599 return AttachDecision::Attach
;
9602 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertFloat32() {
9603 // Expecting two arguments.
9605 return AttachDecision::NoAction
;
9608 // Initialize the input operand.
9609 initializeInputOperand();
9611 // Guard callee is the 'assertFloat32' native function.
9612 emitNativeCalleeGuard();
9614 // TODO: Warp doesn't yet optimize Float32 (bug 1655773).
9616 // NOP when not in IonMonkey.
9617 writer
.loadUndefinedResult();
9618 writer
.returnFromIC();
9620 trackAttached("AssertFloat32");
9621 return AttachDecision::Attach
;
9624 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertRecoveredOnBailout() {
9625 // Expecting two arguments.
9627 return AttachDecision::NoAction
;
9630 // (Fuzzing unsafe) testing function which must be called with a constant
9631 // boolean as its second argument.
9632 bool mustBeRecovered
= args_
[1].toBoolean();
9634 // Initialize the input operand.
9635 initializeInputOperand();
9637 // Guard callee is the 'assertRecoveredOnBailout' native function.
9638 emitNativeCalleeGuard();
9640 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9642 writer
.assertRecoveredOnBailoutResult(valId
, mustBeRecovered
);
9643 writer
.returnFromIC();
9645 trackAttached("AssertRecoveredOnBailout");
9646 return AttachDecision::Attach
;
9649 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIs() {
9650 // Need two arguments.
9652 return AttachDecision::NoAction
;
9655 // Initialize the input operand.
9656 initializeInputOperand();
9658 // Guard callee is the `is` native function.
9659 emitNativeCalleeGuard();
9661 ValOperandId lhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9662 ValOperandId rhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9664 HandleValue lhs
= args_
[0];
9665 HandleValue rhs
= args_
[1];
9667 if (!isFirstStub()) {
9668 writer
.sameValueResult(lhsId
, rhsId
);
9669 } else if (lhs
.isNumber() && rhs
.isNumber() &&
9670 !(lhs
.isInt32() && rhs
.isInt32())) {
9671 NumberOperandId lhsNumId
= writer
.guardIsNumber(lhsId
);
9672 NumberOperandId rhsNumId
= writer
.guardIsNumber(rhsId
);
9673 writer
.compareDoubleSameValueResult(lhsNumId
, rhsNumId
);
9674 } else if (!SameType(lhs
, rhs
)) {
9675 // Compare tags for strictly different types.
9676 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
9677 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
9678 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
9679 writer
.loadBooleanResult(false);
9681 MOZ_ASSERT(lhs
.type() == rhs
.type());
9682 MOZ_ASSERT(lhs
.type() != JS::ValueType::Double
);
9684 switch (lhs
.type()) {
9685 case JS::ValueType::Int32
: {
9686 Int32OperandId lhsIntId
= writer
.guardToInt32(lhsId
);
9687 Int32OperandId rhsIntId
= writer
.guardToInt32(rhsId
);
9688 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9691 case JS::ValueType::Boolean
: {
9692 Int32OperandId lhsIntId
= writer
.guardBooleanToInt32(lhsId
);
9693 Int32OperandId rhsIntId
= writer
.guardBooleanToInt32(rhsId
);
9694 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9697 case JS::ValueType::Undefined
: {
9698 writer
.guardIsUndefined(lhsId
);
9699 writer
.guardIsUndefined(rhsId
);
9700 writer
.loadBooleanResult(true);
9703 case JS::ValueType::Null
: {
9704 writer
.guardIsNull(lhsId
);
9705 writer
.guardIsNull(rhsId
);
9706 writer
.loadBooleanResult(true);
9709 case JS::ValueType::String
: {
9710 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
9711 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
9712 writer
.compareStringResult(JSOp::StrictEq
, lhsStrId
, rhsStrId
);
9715 case JS::ValueType::Symbol
: {
9716 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
9717 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
9718 writer
.compareSymbolResult(JSOp::StrictEq
, lhsSymId
, rhsSymId
);
9721 case JS::ValueType::BigInt
: {
9722 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
9723 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
9724 writer
.compareBigIntResult(JSOp::StrictEq
, lhsBigIntId
, rhsBigIntId
);
9727 case JS::ValueType::Object
: {
9728 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
9729 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
9730 writer
.compareObjectResult(JSOp::StrictEq
, lhsObjId
, rhsObjId
);
9734 #ifdef ENABLE_RECORD_TUPLE
9735 case ValueType::ExtendedPrimitive
:
9737 case JS::ValueType::Double
:
9738 case JS::ValueType::Magic
:
9739 case JS::ValueType::PrivateGCThing
:
9740 MOZ_CRASH("Unexpected type");
9744 writer
.returnFromIC();
9746 trackAttached("ObjectIs");
9747 return AttachDecision::Attach
;
9750 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIsPrototypeOf() {
9751 // Ensure |this| is an object.
9752 if (!thisval_
.isObject()) {
9753 return AttachDecision::NoAction
;
9756 // Need a single argument.
9758 return AttachDecision::NoAction
;
9761 // Initialize the input operand.
9762 initializeInputOperand();
9764 // Guard callee is the `isPrototypeOf` native function.
9765 emitNativeCalleeGuard();
9767 // Guard that |this| is an object.
9768 ValOperandId thisValId
=
9769 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9770 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9772 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9774 writer
.loadInstanceOfObjectResult(argId
, thisObjId
);
9775 writer
.returnFromIC();
9777 trackAttached("ObjectIsPrototypeOf");
9778 return AttachDecision::Attach
;
9781 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectKeys() {
9782 // Only handle argc <= 1.
9784 return AttachDecision::NoAction
;
9787 // Do not attach any IC if the argument is not an object.
9788 if (!args_
[0].isObject()) {
9789 return AttachDecision::NoAction
;
9791 // Do not attach any IC if the argument is a Proxy. While implementation could
9792 // work with proxies the goal of this implementation is to provide an
9793 // optimization for calls of `Object.keys(obj)` where there is no side-effect,
9794 // and where the computation of the array of property name can be moved.
9795 const JSClass
* clasp
= args_
[0].toObject().getClass();
9796 if (clasp
->isProxyObject()) {
9797 return AttachDecision::NoAction
;
9800 // Generate cache IR code to attach a new inline cache which will delegate the
9801 // call to Object.keys to the native function.
9802 initializeInputOperand();
9804 // Guard callee is the 'keys' native function.
9805 emitNativeCalleeGuard();
9807 // Implicit: Note `Object.keys` is a property of the `Object` global. The fact
9808 // that we are in this function implies that we already identify the function
9809 // as being the proper one. Thus there should not be any need to validate that
9810 // this is the proper function. (test: ion/object-keys-05)
9812 // Guard `arg0` is an object.
9813 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9814 ObjOperandId argObjId
= writer
.guardToObject(argId
);
9816 // Guard against proxies.
9817 writer
.guardIsNotProxy(argObjId
);
9819 // Compute the keys array.
9820 writer
.objectKeysResult(argObjId
);
9822 writer
.returnFromIC();
9824 trackAttached("ObjectKeys");
9825 return AttachDecision::Attach
;
9828 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectToString() {
9829 // Expecting no arguments.
9831 return AttachDecision::NoAction
;
9834 // Ensure |this| is an object.
9835 if (!thisval_
.isObject()) {
9836 return AttachDecision::NoAction
;
9839 // Don't attach if the object has @@toStringTag or is a proxy.
9840 if (!ObjectClassToString(cx_
, &thisval_
.toObject())) {
9841 return AttachDecision::NoAction
;
9844 // Initialize the input operand.
9845 initializeInputOperand();
9847 // Guard callee is the 'toString' native function.
9848 emitNativeCalleeGuard();
9850 // Guard that |this| is an object.
9851 ValOperandId thisValId
=
9852 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9853 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9855 writer
.objectToStringResult(thisObjId
);
9856 writer
.returnFromIC();
9858 trackAttached("ObjectToString");
9859 return AttachDecision::Attach
;
9862 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsIntN() {
9863 // Need two arguments (Int32, BigInt).
9864 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9865 return AttachDecision::NoAction
;
9868 // Negative bits throws an error.
9869 if (args_
[0].toInt32() < 0) {
9870 return AttachDecision::NoAction
;
9873 // Initialize the input operand.
9874 initializeInputOperand();
9876 // Guard callee is the 'BigInt.asIntN' native function.
9877 emitNativeCalleeGuard();
9879 // Convert bits to int32.
9880 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9881 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9883 // Number of bits mustn't be negative.
9884 writer
.guardInt32IsNonNegative(int32BitsId
);
9886 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9887 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9889 writer
.bigIntAsIntNResult(int32BitsId
, bigIntId
);
9890 writer
.returnFromIC();
9892 trackAttached("BigIntAsIntN");
9893 return AttachDecision::Attach
;
9896 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsUintN() {
9897 // Need two arguments (Int32, BigInt).
9898 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
9899 return AttachDecision::NoAction
;
9902 // Negative bits throws an error.
9903 if (args_
[0].toInt32() < 0) {
9904 return AttachDecision::NoAction
;
9907 // Initialize the input operand.
9908 initializeInputOperand();
9910 // Guard callee is the 'BigInt.asUintN' native function.
9911 emitNativeCalleeGuard();
9913 // Convert bits to int32.
9914 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9915 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
9917 // Number of bits mustn't be negative.
9918 writer
.guardInt32IsNonNegative(int32BitsId
);
9920 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9921 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
9923 writer
.bigIntAsUintNResult(int32BitsId
, bigIntId
);
9924 writer
.returnFromIC();
9926 trackAttached("BigIntAsUintN");
9927 return AttachDecision::Attach
;
9930 AttachDecision
InlinableNativeIRGenerator::tryAttachSetHas() {
9931 // Ensure |this| is a SetObject.
9932 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
9933 return AttachDecision::NoAction
;
9936 // Need a single argument.
9938 return AttachDecision::NoAction
;
9941 // Initialize the input operand.
9942 initializeInputOperand();
9944 // Guard callee is the 'has' native function.
9945 emitNativeCalleeGuard();
9947 // Guard |this| is a SetObject.
9948 ValOperandId thisValId
=
9949 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9950 ObjOperandId objId
= writer
.guardToObject(thisValId
);
9951 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Set
);
9953 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9955 #ifndef JS_CODEGEN_X86
9956 // Assume the hash key will likely always have the same type when attaching
9957 // the first stub. If the call is polymorphic on the hash key, attach a stub
9958 // which handles any value.
9959 if (isFirstStub()) {
9960 switch (args_
[0].type()) {
9961 case ValueType::Double
:
9962 case ValueType::Int32
:
9963 case ValueType::Boolean
:
9964 case ValueType::Undefined
:
9965 case ValueType::Null
: {
9966 writer
.guardToNonGCThing(argId
);
9967 writer
.setHasNonGCThingResult(objId
, argId
);
9970 case ValueType::String
: {
9971 StringOperandId strId
= writer
.guardToString(argId
);
9972 writer
.setHasStringResult(objId
, strId
);
9975 case ValueType::Symbol
: {
9976 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
9977 writer
.setHasSymbolResult(objId
, symId
);
9980 case ValueType::BigInt
: {
9981 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
9982 writer
.setHasBigIntResult(objId
, bigIntId
);
9985 case ValueType::Object
: {
9986 // Currently only supported on 64-bit platforms.
9988 ObjOperandId valId
= writer
.guardToObject(argId
);
9989 writer
.setHasObjectResult(objId
, valId
);
9991 writer
.setHasResult(objId
, argId
);
9996 # ifdef ENABLE_RECORD_TUPLE
9997 case ValueType::ExtendedPrimitive
:
9999 case ValueType::Magic
:
10000 case ValueType::PrivateGCThing
:
10001 MOZ_CRASH("Unexpected type");
10004 writer
.setHasResult(objId
, argId
);
10007 // The optimized versions require too many registers on x86.
10008 writer
.setHasResult(objId
, argId
);
10011 writer
.returnFromIC();
10013 trackAttached("SetHas");
10014 return AttachDecision::Attach
;
10017 AttachDecision
InlinableNativeIRGenerator::tryAttachSetSize() {
10018 // Ensure |this| is a SetObject.
10019 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
10020 return AttachDecision::NoAction
;
10023 // Expecting no arguments.
10025 return AttachDecision::NoAction
;
10028 // Initialize the input operand.
10029 initializeInputOperand();
10031 // Guard callee is the 'size' native function.
10032 emitNativeCalleeGuard();
10034 // Guard |this| is a SetObject.
10035 ValOperandId thisValId
=
10036 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10037 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10038 writer
.guardClass(objId
, GuardClassKind::Set
);
10040 writer
.setSizeResult(objId
);
10041 writer
.returnFromIC();
10043 trackAttached("SetSize");
10044 return AttachDecision::Attach
;
10047 AttachDecision
InlinableNativeIRGenerator::tryAttachMapHas() {
10048 // Ensure |this| is a MapObject.
10049 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
10050 return AttachDecision::NoAction
;
10053 // Need a single argument.
10055 return AttachDecision::NoAction
;
10058 // Initialize the input operand.
10059 initializeInputOperand();
10061 // Guard callee is the 'has' native function.
10062 emitNativeCalleeGuard();
10064 // Guard |this| is a MapObject.
10065 ValOperandId thisValId
=
10066 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10067 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10068 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
10070 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10072 #ifndef JS_CODEGEN_X86
10073 // Assume the hash key will likely always have the same type when attaching
10074 // the first stub. If the call is polymorphic on the hash key, attach a stub
10075 // which handles any value.
10076 if (isFirstStub()) {
10077 switch (args_
[0].type()) {
10078 case ValueType::Double
:
10079 case ValueType::Int32
:
10080 case ValueType::Boolean
:
10081 case ValueType::Undefined
:
10082 case ValueType::Null
: {
10083 writer
.guardToNonGCThing(argId
);
10084 writer
.mapHasNonGCThingResult(objId
, argId
);
10087 case ValueType::String
: {
10088 StringOperandId strId
= writer
.guardToString(argId
);
10089 writer
.mapHasStringResult(objId
, strId
);
10092 case ValueType::Symbol
: {
10093 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
10094 writer
.mapHasSymbolResult(objId
, symId
);
10097 case ValueType::BigInt
: {
10098 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
10099 writer
.mapHasBigIntResult(objId
, bigIntId
);
10102 case ValueType::Object
: {
10103 // Currently only supported on 64-bit platforms.
10104 # ifdef JS_PUNBOX64
10105 ObjOperandId valId
= writer
.guardToObject(argId
);
10106 writer
.mapHasObjectResult(objId
, valId
);
10108 writer
.mapHasResult(objId
, argId
);
10113 # ifdef ENABLE_RECORD_TUPLE
10114 case ValueType::ExtendedPrimitive
:
10116 case ValueType::Magic
:
10117 case ValueType::PrivateGCThing
:
10118 MOZ_CRASH("Unexpected type");
10121 writer
.mapHasResult(objId
, argId
);
10124 // The optimized versions require too many registers on x86.
10125 writer
.mapHasResult(objId
, argId
);
10128 writer
.returnFromIC();
10130 trackAttached("MapHas");
10131 return AttachDecision::Attach
;
10134 AttachDecision
InlinableNativeIRGenerator::tryAttachMapGet() {
10135 // Ensure |this| is a MapObject.
10136 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
10137 return AttachDecision::NoAction
;
10140 // Need a single argument.
10142 return AttachDecision::NoAction
;
10145 // Initialize the input operand.
10146 initializeInputOperand();
10148 // Guard callee is the 'get' native function.
10149 emitNativeCalleeGuard();
10151 // Guard |this| is a MapObject.
10152 ValOperandId thisValId
=
10153 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10154 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10155 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
10157 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10159 #ifndef JS_CODEGEN_X86
10160 // Assume the hash key will likely always have the same type when attaching
10161 // the first stub. If the call is polymorphic on the hash key, attach a stub
10162 // which handles any value.
10163 if (isFirstStub()) {
10164 switch (args_
[0].type()) {
10165 case ValueType::Double
:
10166 case ValueType::Int32
:
10167 case ValueType::Boolean
:
10168 case ValueType::Undefined
:
10169 case ValueType::Null
: {
10170 writer
.guardToNonGCThing(argId
);
10171 writer
.mapGetNonGCThingResult(objId
, argId
);
10174 case ValueType::String
: {
10175 StringOperandId strId
= writer
.guardToString(argId
);
10176 writer
.mapGetStringResult(objId
, strId
);
10179 case ValueType::Symbol
: {
10180 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
10181 writer
.mapGetSymbolResult(objId
, symId
);
10184 case ValueType::BigInt
: {
10185 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
10186 writer
.mapGetBigIntResult(objId
, bigIntId
);
10189 case ValueType::Object
: {
10190 // Currently only supported on 64-bit platforms.
10191 # ifdef JS_PUNBOX64
10192 ObjOperandId valId
= writer
.guardToObject(argId
);
10193 writer
.mapGetObjectResult(objId
, valId
);
10195 writer
.mapGetResult(objId
, argId
);
10200 # ifdef ENABLE_RECORD_TUPLE
10201 case ValueType::ExtendedPrimitive
:
10203 case ValueType::Magic
:
10204 case ValueType::PrivateGCThing
:
10205 MOZ_CRASH("Unexpected type");
10208 writer
.mapGetResult(objId
, argId
);
10211 // The optimized versions require too many registers on x86.
10212 writer
.mapGetResult(objId
, argId
);
10215 writer
.returnFromIC();
10217 trackAttached("MapGet");
10218 return AttachDecision::Attach
;
10221 AttachDecision
CallIRGenerator::tryAttachFunCall(HandleFunction callee
) {
10222 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
10224 if (callee
->native() != fun_call
) {
10225 return AttachDecision::NoAction
;
10228 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
10229 return AttachDecision::NoAction
;
10231 RootedFunction
target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
10233 bool isScripted
= target
->hasJitEntry();
10234 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
10236 if (target
->isClassConstructor()) {
10237 return AttachDecision::NoAction
;
10239 Int32OperandId
argcId(writer
.setInputOperandId(0));
10241 CallFlags
targetFlags(CallFlags::FunCall
);
10242 if (mode_
== ICState::Mode::Specialized
) {
10243 if (cx_
->realm() == target
->realm()) {
10244 targetFlags
.setIsSameRealm();
10248 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&& argc_
> 0) {
10249 // The stack layout is already in the correct form for calls with at least
10252 // clang-format off
10254 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
10255 // Callee <-- argc+1
10256 // ThisValue <-- argc
10257 // Args: | Arg0 | <-- argc-1
10258 // | Arg1 | <-- argc-2
10262 // When passing |argc-1| as the number of arguments, we get:
10264 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
10265 // Callee <-- (argc-1)+1 = argc = ThisValue
10266 // ThisValue <-- (argc-1) = argc-1 = Arg0
10267 // Args: | Arg0 | <-- (argc-1)-1 = argc-2 = Arg1
10268 // | Arg1 | <-- (argc-1)-2 = argc-3 = Arg2
10273 // This allows to call |loadArgumentFixedSlot(ArgumentKind::Arg0)| and we
10274 // still load the correct argument index from |ArgumentKind::Arg1|.
10276 // When no arguments are passed, i.e. |argc==0|, we have to replace
10277 // |ArgumentKind::Arg0| with the undefined value. But we don't yet support
10279 HandleValue newTarget
= NullHandleValue
;
10280 HandleValue thisValue
= args_
[0];
10281 HandleValueArray args
=
10282 HandleValueArray::subarray(args_
, 1, args_
.length() - 1);
10284 // Check for specific native-function optimizations.
10285 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10286 args
, targetFlags
);
10287 TRY_ATTACH(nativeGen
.tryAttachStub());
10290 ObjOperandId thisObjId
= emitFunCallGuard(argcId
);
10292 if (mode_
== ICState::Mode::Specialized
) {
10293 // Ensure that |this| is the expected target function.
10294 emitCalleeGuard(thisObjId
, target
);
10297 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10298 ClampFixedArgc(argc_
));
10300 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
10301 ClampFixedArgc(argc_
));
10304 // Guard that |this| is a function.
10305 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
10307 // Guard that function is not a class constructor.
10308 writer
.guardNotClassConstructor(thisObjId
);
10311 writer
.guardFunctionHasJitEntry(thisObjId
, /*isConstructing =*/false);
10312 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10313 ClampFixedArgc(argc_
));
10315 writer
.guardFunctionHasNoJitEntry(thisObjId
);
10316 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
,
10317 ClampFixedArgc(argc_
));
10321 writer
.returnFromIC();
10324 trackAttached("Scripted fun_call");
10326 trackAttached("Native fun_call");
10329 return AttachDecision::Attach
;
10332 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArray(
10333 bool isPossiblyWrapped
) {
10334 // Self-hosted code calls this with a single object argument.
10335 MOZ_ASSERT(argc_
== 1);
10336 MOZ_ASSERT(args_
[0].isObject());
10338 // Initialize the input operand.
10339 initializeInputOperand();
10341 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10343 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10344 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10345 writer
.isTypedArrayResult(objArgId
, isPossiblyWrapped
);
10346 writer
.returnFromIC();
10348 trackAttached(isPossiblyWrapped
? "IsPossiblyWrappedTypedArray"
10350 return AttachDecision::Attach
;
10353 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArrayConstructor() {
10354 // Self-hosted code calls this with a single object argument.
10355 MOZ_ASSERT(argc_
== 1);
10356 MOZ_ASSERT(args_
[0].isObject());
10358 // Initialize the input operand.
10359 initializeInputOperand();
10361 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10363 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10364 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10365 writer
.isTypedArrayConstructorResult(objArgId
);
10366 writer
.returnFromIC();
10368 trackAttached("IsTypedArrayConstructor");
10369 return AttachDecision::Attach
;
10372 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() {
10373 // Self-hosted code calls this with a single TypedArrayObject argument.
10374 MOZ_ASSERT(argc_
== 1);
10375 MOZ_ASSERT(args_
[0].isObject());
10376 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10378 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10380 // Initialize the input operand.
10381 initializeInputOperand();
10383 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10385 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10386 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10388 EmitGuardTypedArray(writer
, tarr
, objArgId
);
10390 size_t byteOffset
= tarr
->byteOffsetMaybeOutOfBounds();
10391 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
10392 if (byteOffset
<= INT32_MAX
) {
10393 writer
.arrayBufferViewByteOffsetInt32Result(objArgId
);
10395 writer
.arrayBufferViewByteOffsetDoubleResult(objArgId
);
10398 if (byteOffset
<= INT32_MAX
) {
10399 writer
.resizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result(objArgId
);
10401 writer
.resizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult(
10406 writer
.returnFromIC();
10408 trackAttached("IntrinsicTypedArrayByteOffset");
10409 return AttachDecision::Attach
;
10412 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayElementSize() {
10413 // Self-hosted code calls this with a single TypedArrayObject argument.
10414 MOZ_ASSERT(argc_
== 1);
10415 MOZ_ASSERT(args_
[0].isObject());
10416 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10418 // Initialize the input operand.
10419 initializeInputOperand();
10421 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10423 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10424 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10425 writer
.typedArrayElementSizeResult(objArgId
);
10426 writer
.returnFromIC();
10428 trackAttached("TypedArrayElementSize");
10429 return AttachDecision::Attach
;
10432 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayLength(
10433 bool isPossiblyWrapped
, bool allowOutOfBounds
) {
10434 // Self-hosted code calls this with a single, possibly wrapped,
10435 // TypedArrayObject argument.
10436 MOZ_ASSERT(argc_
== 1);
10437 MOZ_ASSERT(args_
[0].isObject());
10439 // Only optimize when the object isn't a wrapper.
10440 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10441 return AttachDecision::NoAction
;
10444 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10446 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10448 // Don't optimize when a resizable TypedArray is out-of-bounds and
10449 // out-of-bounds isn't allowed.
10450 auto length
= tarr
->length();
10451 if (length
.isNothing() && !tarr
->hasDetachedBuffer()) {
10452 MOZ_ASSERT(tarr
->is
<ResizableTypedArrayObject
>());
10453 MOZ_ASSERT(tarr
->isOutOfBounds());
10455 if (!allowOutOfBounds
) {
10456 return AttachDecision::NoAction
;
10460 // Initialize the input operand.
10461 initializeInputOperand();
10463 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10465 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10466 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10468 if (isPossiblyWrapped
) {
10469 writer
.guardIsNotProxy(objArgId
);
10472 EmitGuardTypedArray(writer
, tarr
, objArgId
);
10474 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
10475 if (length
.valueOr(0) <= INT32_MAX
) {
10476 writer
.loadArrayBufferViewLengthInt32Result(objArgId
);
10478 writer
.loadArrayBufferViewLengthDoubleResult(objArgId
);
10481 if (!allowOutOfBounds
) {
10482 writer
.guardResizableArrayBufferViewInBoundsOrDetached(objArgId
);
10485 if (length
.valueOr(0) <= INT32_MAX
) {
10486 writer
.resizableTypedArrayLengthInt32Result(objArgId
);
10488 writer
.resizableTypedArrayLengthDoubleResult(objArgId
);
10491 writer
.returnFromIC();
10493 trackAttached("IntrinsicTypedArrayLength");
10494 return AttachDecision::Attach
;
10497 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayBufferByteLength(
10498 bool isPossiblyWrapped
) {
10499 // Self-hosted code calls this with a single, possibly wrapped,
10500 // ArrayBufferObject argument.
10501 MOZ_ASSERT(argc_
== 1);
10502 MOZ_ASSERT(args_
[0].isObject());
10504 // Only optimize when the object isn't a wrapper.
10505 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10506 return AttachDecision::NoAction
;
10509 MOZ_ASSERT(args_
[0].toObject().is
<ArrayBufferObject
>());
10511 auto* buffer
= &args_
[0].toObject().as
<ArrayBufferObject
>();
10513 // Initialize the input operand.
10514 initializeInputOperand();
10516 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10518 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10519 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10521 if (isPossiblyWrapped
) {
10522 writer
.guardIsNotProxy(objArgId
);
10525 if (buffer
->byteLength() <= INT32_MAX
) {
10526 writer
.loadArrayBufferByteLengthInt32Result(objArgId
);
10528 writer
.loadArrayBufferByteLengthDoubleResult(objArgId
);
10530 writer
.returnFromIC();
10532 trackAttached("ArrayBufferByteLength");
10533 return AttachDecision::Attach
;
10536 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructing() {
10537 // Self-hosted code calls this with no arguments in function scripts.
10538 MOZ_ASSERT(argc_
== 0);
10539 MOZ_ASSERT(script()->isFunction());
10541 // Initialize the input operand.
10542 initializeInputOperand();
10544 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10546 writer
.frameIsConstructingResult();
10547 writer
.returnFromIC();
10549 trackAttached("IsConstructing");
10550 return AttachDecision::Attach
;
10554 InlinableNativeIRGenerator::tryAttachGetNextMapSetEntryForIterator(bool isMap
) {
10555 // Self-hosted code calls this with two objects.
10556 MOZ_ASSERT(argc_
== 2);
10558 MOZ_ASSERT(args_
[0].toObject().is
<MapIteratorObject
>());
10560 MOZ_ASSERT(args_
[0].toObject().is
<SetIteratorObject
>());
10562 MOZ_ASSERT(args_
[1].toObject().is
<ArrayObject
>());
10564 // Initialize the input operand.
10565 initializeInputOperand();
10567 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10569 ValOperandId iterId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10570 ObjOperandId objIterId
= writer
.guardToObject(iterId
);
10572 ValOperandId resultArrId
=
10573 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10574 ObjOperandId objResultArrId
= writer
.guardToObject(resultArrId
);
10576 writer
.getNextMapSetEntryForIteratorResult(objIterId
, objResultArrId
, isMap
);
10577 writer
.returnFromIC();
10579 trackAttached("GetNextMapSetEntryForIterator");
10580 return AttachDecision::Attach
;
10583 AttachDecision
InlinableNativeIRGenerator::tryAttachNewArrayIterator() {
10584 // Self-hosted code calls this without any arguments
10585 MOZ_ASSERT(argc_
== 0);
10587 JSObject
* templateObj
= NewArrayIteratorTemplate(cx_
);
10588 if (!templateObj
) {
10589 cx_
->recoverFromOutOfMemory();
10590 return AttachDecision::NoAction
;
10593 // Initialize the input operand.
10594 initializeInputOperand();
10596 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10598 writer
.newArrayIteratorResult(templateObj
);
10599 writer
.returnFromIC();
10601 trackAttached("NewArrayIterator");
10602 return AttachDecision::Attach
;
10605 AttachDecision
InlinableNativeIRGenerator::tryAttachNewStringIterator() {
10606 // Self-hosted code calls this without any arguments
10607 MOZ_ASSERT(argc_
== 0);
10609 JSObject
* templateObj
= NewStringIteratorTemplate(cx_
);
10610 if (!templateObj
) {
10611 cx_
->recoverFromOutOfMemory();
10612 return AttachDecision::NoAction
;
10615 // Initialize the input operand.
10616 initializeInputOperand();
10618 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10620 writer
.newStringIteratorResult(templateObj
);
10621 writer
.returnFromIC();
10623 trackAttached("NewStringIterator");
10624 return AttachDecision::Attach
;
10627 AttachDecision
InlinableNativeIRGenerator::tryAttachNewRegExpStringIterator() {
10628 // Self-hosted code calls this without any arguments
10629 MOZ_ASSERT(argc_
== 0);
10631 JSObject
* templateObj
= NewRegExpStringIteratorTemplate(cx_
);
10632 if (!templateObj
) {
10633 cx_
->recoverFromOutOfMemory();
10634 return AttachDecision::NoAction
;
10637 // Initialize the input operand.
10638 initializeInputOperand();
10640 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10642 writer
.newRegExpStringIteratorResult(templateObj
);
10643 writer
.returnFromIC();
10645 trackAttached("NewRegExpStringIterator");
10646 return AttachDecision::Attach
;
10650 InlinableNativeIRGenerator::tryAttachArrayIteratorPrototypeOptimizable() {
10651 // Self-hosted code calls this without any arguments
10652 MOZ_ASSERT(argc_
== 0);
10654 if (!isFirstStub()) {
10655 // Attach only once to prevent slowdowns for polymorphic calls.
10656 return AttachDecision::NoAction
;
10659 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
10661 Rooted
<JSFunction
*> nextFun(cx_
);
10662 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
10663 &arrayIteratorProto
, &slot
,
10665 return AttachDecision::NoAction
;
10668 // Initialize the input operand.
10669 initializeInputOperand();
10671 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10673 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
10674 ObjOperandId nextId
= writer
.loadObject(nextFun
);
10676 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
10678 // Ensure that proto[slot] == nextFun.
10679 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
10680 writer
.loadBooleanResult(true);
10681 writer
.returnFromIC();
10683 trackAttached("ArrayIteratorPrototypeOptimizable");
10684 return AttachDecision::Attach
;
10687 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectCreate() {
10688 // Need a single object-or-null argument.
10689 if (argc_
!= 1 || !args_
[0].isObjectOrNull()) {
10690 return AttachDecision::NoAction
;
10693 if (!isFirstStub()) {
10694 // Attach only once to prevent slowdowns for polymorphic calls.
10695 return AttachDecision::NoAction
;
10698 RootedObject
proto(cx_
, args_
[0].toObjectOrNull());
10699 JSObject
* templateObj
= ObjectCreateImpl(cx_
, proto
, TenuredObject
);
10700 if (!templateObj
) {
10701 cx_
->recoverFromOutOfMemory();
10702 return AttachDecision::NoAction
;
10705 // Initialize the input operand.
10706 initializeInputOperand();
10708 // Guard callee is the 'create' native function.
10709 emitNativeCalleeGuard();
10711 // Guard on the proto argument.
10712 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10714 ObjOperandId protoId
= writer
.guardToObject(argId
);
10715 writer
.guardSpecificObject(protoId
, proto
);
10717 writer
.guardIsNull(argId
);
10720 writer
.objectCreateResult(templateObj
);
10721 writer
.returnFromIC();
10723 trackAttached("ObjectCreate");
10724 return AttachDecision::Attach
;
10727 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectConstructor() {
10728 // Expecting no arguments or a single object argument.
10729 // TODO(Warp): Support all or more conversions to object.
10731 return AttachDecision::NoAction
;
10733 if (argc_
== 1 && !args_
[0].isObject()) {
10734 return AttachDecision::NoAction
;
10737 PlainObject
* templateObj
= nullptr;
10739 // Stub doesn't support metadata builder
10740 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
10741 return AttachDecision::NoAction
;
10744 // Create a temporary object to act as the template object.
10745 templateObj
= NewPlainObjectWithAllocKind(cx_
, NewObjectGCKind());
10746 if (!templateObj
) {
10747 cx_
->recoverFromOutOfMemory();
10748 return AttachDecision::NoAction
;
10752 // Initialize the input operand.
10753 initializeInputOperand();
10755 // Guard callee and newTarget (if constructing) are this Object constructor
10757 emitNativeCalleeGuard();
10760 // TODO: Support pre-tenuring.
10761 gc::AllocSite
* site
=
10762 script()->zone()->unknownAllocSite(JS::TraceKind::Object
);
10765 uint32_t numFixedSlots
= templateObj
->numUsedFixedSlots();
10766 uint32_t numDynamicSlots
= templateObj
->numDynamicSlots();
10767 gc::AllocKind allocKind
= templateObj
->allocKindForTenure();
10768 Shape
* shape
= templateObj
->shape();
10770 writer
.guardNoAllocationMetadataBuilder(
10771 cx_
->realm()->addressOfMetadataBuilder());
10772 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
,
10775 // Use standard call flags when this is an inline Function.prototype.call(),
10776 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10777 CallFlags flags
= flags_
;
10778 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10779 flags
= CallFlags(CallFlags::Standard
);
10782 // Guard that the argument is an object.
10783 ValOperandId argId
=
10784 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10785 ObjOperandId objId
= writer
.guardToObject(argId
);
10787 // Return the object.
10788 writer
.loadObjectResult(objId
);
10791 writer
.returnFromIC();
10793 trackAttached("ObjectConstructor");
10794 return AttachDecision::Attach
;
10797 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayConstructor() {
10798 // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
10799 // for now. Note that self-hosted code calls this without |new| via std_Array.
10801 return AttachDecision::NoAction
;
10803 if (argc_
== 1 && !args_
[0].isInt32()) {
10804 return AttachDecision::NoAction
;
10807 int32_t length
= (argc_
== 1) ? args_
[0].toInt32() : 0;
10808 if (length
< 0 || uint32_t(length
) > ArrayObject::EagerAllocationMaxLength
) {
10809 return AttachDecision::NoAction
;
10812 // We allow inlining this function across realms so make sure the template
10813 // object is allocated in that realm. See CanInlineNativeCrossRealm.
10814 JSObject
* templateObj
;
10816 AutoRealm
ar(cx_
, callee_
);
10817 templateObj
= NewDenseFullyAllocatedArray(cx_
, length
, TenuredObject
);
10818 if (!templateObj
) {
10819 cx_
->clearPendingException();
10820 return AttachDecision::NoAction
;
10824 // Initialize the input operand.
10825 initializeInputOperand();
10827 // Guard callee and newTarget (if constructing) are this Array constructor
10829 emitNativeCalleeGuard();
10831 Int32OperandId lengthId
;
10833 // Use standard call flags when this is an inline Function.prototype.call(),
10834 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10835 CallFlags flags
= flags_
;
10836 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10837 flags
= CallFlags(CallFlags::Standard
);
10840 ValOperandId arg0Id
=
10841 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10842 lengthId
= writer
.guardToInt32(arg0Id
);
10844 MOZ_ASSERT(argc_
== 0);
10845 lengthId
= writer
.loadInt32Constant(0);
10848 writer
.newArrayFromLengthResult(templateObj
, lengthId
);
10849 writer
.returnFromIC();
10851 trackAttached("ArrayConstructor");
10852 return AttachDecision::Attach
;
10855 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() {
10856 MOZ_ASSERT(flags_
.isConstructing());
10858 if (argc_
== 0 || argc_
> 3) {
10859 return AttachDecision::NoAction
;
10862 if (!isFirstStub()) {
10863 // Attach only once to prevent slowdowns for polymorphic calls.
10864 return AttachDecision::NoAction
;
10867 // The first argument must be int32 or a non-proxy object.
10868 if (!args_
[0].isInt32() && !args_
[0].isObject()) {
10869 return AttachDecision::NoAction
;
10871 if (args_
[0].isObject() && args_
[0].toObject().is
<ProxyObject
>()) {
10872 return AttachDecision::NoAction
;
10875 #ifdef JS_CODEGEN_X86
10876 // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
10877 // we can easily support on 32-bit x86 for now.
10878 if (args_
[0].isObject() &&
10879 args_
[0].toObject().is
<ArrayBufferObjectMaybeShared
>()) {
10880 return AttachDecision::NoAction
;
10884 RootedObject
templateObj(cx_
);
10885 if (!TypedArrayObject::GetTemplateObjectForNative(cx_
, callee_
->native(),
10886 args_
, &templateObj
)) {
10887 cx_
->recoverFromOutOfMemory();
10888 return AttachDecision::NoAction
;
10891 if (!templateObj
) {
10892 // This can happen for large length values.
10893 MOZ_ASSERT(args_
[0].isInt32());
10894 return AttachDecision::NoAction
;
10897 // Initialize the input operand.
10898 initializeInputOperand();
10900 // Guard callee and newTarget are this TypedArray constructor function.
10901 emitNativeCalleeGuard();
10903 ValOperandId arg0Id
=
10904 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
10906 if (args_
[0].isInt32()) {
10908 Int32OperandId lengthId
= writer
.guardToInt32(arg0Id
);
10909 writer
.newTypedArrayFromLengthResult(templateObj
, lengthId
);
10911 JSObject
* obj
= &args_
[0].toObject();
10912 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
10914 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
10915 // From ArrayBuffer.
10916 if (obj
->is
<FixedLengthArrayBufferObject
>()) {
10917 writer
.guardClass(objId
, GuardClassKind::FixedLengthArrayBuffer
);
10918 } else if (obj
->is
<FixedLengthSharedArrayBufferObject
>()) {
10919 writer
.guardClass(objId
, GuardClassKind::FixedLengthSharedArrayBuffer
);
10920 } else if (obj
->is
<ResizableArrayBufferObject
>()) {
10921 writer
.guardClass(objId
, GuardClassKind::ResizableArrayBuffer
);
10923 MOZ_ASSERT(obj
->is
<GrowableSharedArrayBufferObject
>());
10924 writer
.guardClass(objId
, GuardClassKind::GrowableSharedArrayBuffer
);
10926 ValOperandId byteOffsetId
;
10929 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
, flags_
);
10931 byteOffsetId
= writer
.loadUndefined();
10933 ValOperandId lengthId
;
10936 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
, flags_
);
10938 lengthId
= writer
.loadUndefined();
10940 writer
.newTypedArrayFromArrayBufferResult(templateObj
, objId
,
10941 byteOffsetId
, lengthId
);
10943 // From Array-like.
10944 writer
.guardIsNotArrayBufferMaybeShared(objId
);
10945 writer
.guardIsNotProxy(objId
);
10946 writer
.newTypedArrayFromArrayResult(templateObj
, objId
);
10950 writer
.returnFromIC();
10952 trackAttached("TypedArrayConstructor");
10953 return AttachDecision::Attach
;
10956 AttachDecision
InlinableNativeIRGenerator::tryAttachSpecializedFunctionBind(
10957 Handle
<JSObject
*> target
, Handle
<BoundFunctionObject
*> templateObj
) {
10958 // Try to attach a faster stub that's more specialized than what we emit in
10959 // tryAttachFunctionBind. This lets us allocate and initialize a bound
10960 // function object in Ion without calling into C++.
10962 // We can do this if:
10964 // * The target's prototype is Function.prototype, because that's the proto we
10965 // use for the template object.
10966 // * All bound arguments can be stored inline.
10967 // * The `.name`, `.length`, and `IsConstructor` values match `target`.
10969 // We initialize the template object with the bound function's name, length,
10970 // and flags. At runtime we then only have to clone the template object and
10971 // initialize the slots for the target, the bound `this` and the bound
10974 if (!isFirstStub()) {
10975 return AttachDecision::NoAction
;
10977 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
10978 return AttachDecision::NoAction
;
10980 if (target
->staticPrototype() != &cx_
->global()->getFunctionPrototype()) {
10981 return AttachDecision::NoAction
;
10983 size_t numBoundArgs
= argc_
> 0 ? argc_
- 1 : 0;
10984 if (numBoundArgs
> BoundFunctionObject::MaxInlineBoundArgs
) {
10985 return AttachDecision::NoAction
;
10988 const bool targetIsConstructor
= target
->isConstructor();
10989 Rooted
<JSAtom
*> targetName(cx_
);
10990 uint32_t targetLength
= 0;
10992 if (target
->is
<JSFunction
>()) {
10993 Rooted
<JSFunction
*> fun(cx_
, &target
->as
<JSFunction
>());
10994 if (fun
->isNativeFun()) {
10995 return AttachDecision::NoAction
;
10997 if (fun
->hasResolvedLength() || fun
->hasResolvedName()) {
10998 return AttachDecision::NoAction
;
11001 if (!JSFunction::getUnresolvedLength(cx_
, fun
, &len
)) {
11002 cx_
->clearPendingException();
11003 return AttachDecision::NoAction
;
11005 targetName
= fun
->getUnresolvedName(cx_
);
11007 cx_
->clearPendingException();
11008 return AttachDecision::NoAction
;
11011 targetLength
= len
;
11013 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
11014 if (!targetIsConstructor
) {
11015 // Only support constructors for now. This lets us use
11016 // GuardBoundFunctionIsConstructor.
11017 return AttachDecision::NoAction
;
11019 Shape
* initialShape
=
11020 cx_
->global()->maybeBoundFunctionShapeWithDefaultProto();
11021 if (bound
->shape() != initialShape
) {
11022 return AttachDecision::NoAction
;
11024 Value lenVal
= bound
->getLengthForInitialShape();
11025 Value nameVal
= bound
->getNameForInitialShape();
11026 if (!lenVal
.isInt32() || lenVal
.toInt32() < 0 || !nameVal
.isString() ||
11027 !nameVal
.toString()->isAtom()) {
11028 return AttachDecision::NoAction
;
11030 targetName
= &nameVal
.toString()->asAtom();
11031 targetLength
= uint32_t(lenVal
.toInt32());
11034 if (!templateObj
->initTemplateSlotsForSpecializedBind(
11035 cx_
, numBoundArgs
, targetIsConstructor
, targetLength
, targetName
)) {
11036 cx_
->recoverFromOutOfMemory();
11037 return AttachDecision::NoAction
;
11040 initializeInputOperand();
11041 emitNativeCalleeGuard();
11043 ValOperandId thisValId
=
11044 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
11045 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
11047 // Ensure the JSClass and proto match, and that the `length` and `name`
11048 // properties haven't been redefined.
11049 writer
.guardShape(targetId
, target
->shape());
11051 // Emit guards for the `IsConstructor`, `.length`, and `.name` values.
11052 if (target
->is
<JSFunction
>()) {
11054 // * The BaseScript (because that's what JSFunction uses for the `length`).
11055 // Because MGuardFunctionScript doesn't support self-hosted functions yet,
11056 // we use GuardSpecificFunction instead in this case.
11057 // See assertion in MGuardFunctionScript::getAliasSet.
11058 // * The flags slot (for the CONSTRUCTOR, RESOLVED_NAME, RESOLVED_LENGTH,
11059 // HAS_INFERRED_NAME, and HAS_GUESSED_ATOM flags).
11060 // * The atom slot.
11061 JSFunction
* fun
= &target
->as
<JSFunction
>();
11062 if (fun
->isSelfHostedBuiltin()) {
11063 writer
.guardSpecificFunction(targetId
, fun
);
11065 writer
.guardFunctionScript(targetId
, fun
->baseScript());
11067 writer
.guardFixedSlotValue(
11068 targetId
, JSFunction::offsetOfFlagsAndArgCount(),
11069 fun
->getReservedSlot(JSFunction::FlagsAndArgCountSlot
));
11070 writer
.guardFixedSlotValue(targetId
, JSFunction::offsetOfAtom(),
11071 fun
->getReservedSlot(JSFunction::AtomSlot
));
11073 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
11074 writer
.guardBoundFunctionIsConstructor(targetId
);
11075 writer
.guardFixedSlotValue(targetId
,
11076 BoundFunctionObject::offsetOfLengthSlot(),
11077 bound
->getLengthForInitialShape());
11078 writer
.guardFixedSlotValue(targetId
,
11079 BoundFunctionObject::offsetOfNameSlot(),
11080 bound
->getNameForInitialShape());
11083 writer
.specializedBindFunctionResult(targetId
, argc_
, templateObj
);
11084 writer
.returnFromIC();
11086 trackAttached("SpecializedFunctionBind");
11087 return AttachDecision::Attach
;
11090 AttachDecision
InlinableNativeIRGenerator::tryAttachFunctionBind() {
11091 // Ensure |this| (the target) is a function object or a bound function object.
11092 // We could support other callables too, but note that we rely on the target
11093 // having a static prototype in BoundFunctionObject::functionBindImpl.
11094 if (!thisval_
.isObject()) {
11095 return AttachDecision::NoAction
;
11097 Rooted
<JSObject
*> target(cx_
, &thisval_
.toObject());
11098 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
11099 return AttachDecision::NoAction
;
11102 // Only support standard, non-spread calls.
11103 if (flags_
.getArgFormat() != CallFlags::Standard
) {
11104 return AttachDecision::NoAction
;
11107 // Only optimize if the number of arguments is small. This ensures we don't
11108 // compile a lot of different stubs (because we bake in argc) and that we
11109 // don't get anywhere near ARGS_LENGTH_MAX.
11110 static constexpr size_t MaxArguments
= 6;
11111 if (argc_
> MaxArguments
) {
11112 return AttachDecision::NoAction
;
11115 Rooted
<BoundFunctionObject
*> templateObj(
11116 cx_
, BoundFunctionObject::createTemplateObject(cx_
));
11117 if (!templateObj
) {
11118 cx_
->recoverFromOutOfMemory();
11119 return AttachDecision::NoAction
;
11122 TRY_ATTACH(tryAttachSpecializedFunctionBind(target
, templateObj
));
11124 initializeInputOperand();
11126 emitNativeCalleeGuard();
11128 // Guard |this| is a function object or a bound function object.
11129 ValOperandId thisValId
=
11130 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
11131 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
11132 if (target
->is
<JSFunction
>()) {
11133 writer
.guardClass(targetId
, GuardClassKind::JSFunction
);
11135 MOZ_ASSERT(target
->is
<BoundFunctionObject
>());
11136 writer
.guardClass(targetId
, GuardClassKind::BoundFunction
);
11139 writer
.bindFunctionResult(targetId
, argc_
, templateObj
);
11140 writer
.returnFromIC();
11142 trackAttached("FunctionBind");
11143 return AttachDecision::Attach
;
11146 AttachDecision
CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc
) {
11147 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
11149 if (calleeFunc
->native() != fun_apply
) {
11150 return AttachDecision::NoAction
;
11154 return AttachDecision::NoAction
;
11157 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
11158 return AttachDecision::NoAction
;
11160 Rooted
<JSFunction
*> target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
11162 bool isScripted
= target
->hasJitEntry();
11163 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
11165 if (target
->isClassConstructor()) {
11166 return AttachDecision::NoAction
;
11169 CallFlags::ArgFormat format
= CallFlags::Standard
;
11171 // |fun.apply()| and |fun.apply(thisValue)| are equivalent to |fun.call()|
11172 // resp. |fun.call(thisValue)|.
11173 format
= CallFlags::FunCall
;
11174 } else if (args_
[1].isNullOrUndefined()) {
11175 // |fun.apply(thisValue, null)| and |fun.apply(thisValue, undefined)| are
11176 // also equivalent to |fun.call(thisValue)|, but we can't use FunCall
11177 // because we have to discard the second argument.
11178 format
= CallFlags::FunApplyNullUndefined
;
11179 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArgumentsObject
>()) {
11180 auto* argsObj
= &args_
[1].toObject().as
<ArgumentsObject
>();
11181 if (argsObj
->hasOverriddenElement() || argsObj
->anyArgIsForwarded() ||
11182 argsObj
->hasOverriddenLength() ||
11183 argsObj
->initialLength() > JIT_ARGS_LENGTH_MAX
) {
11184 return AttachDecision::NoAction
;
11186 format
= CallFlags::FunApplyArgsObj
;
11187 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArrayObject
>() &&
11188 args_
[1].toObject().as
<ArrayObject
>().length() <=
11189 JIT_ARGS_LENGTH_MAX
&&
11190 IsPackedArray(&args_
[1].toObject())) {
11191 format
= CallFlags::FunApplyArray
;
11193 return AttachDecision::NoAction
;
11196 Int32OperandId
argcId(writer
.setInputOperandId(0));
11198 CallFlags
targetFlags(format
);
11199 if (mode_
== ICState::Mode::Specialized
) {
11200 if (cx_
->realm() == target
->realm()) {
11201 targetFlags
.setIsSameRealm();
11205 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
11206 format
== CallFlags::FunApplyArray
) {
11207 HandleValue newTarget
= NullHandleValue
;
11208 HandleValue thisValue
= args_
[0];
11209 Rooted
<ArrayObject
*> aobj(cx_
, &args_
[1].toObject().as
<ArrayObject
>());
11210 HandleValueArray args
= HandleValueArray::fromMarkedLocation(
11211 aobj
->length(), aobj
->getDenseElements());
11213 // Check for specific native-function optimizations.
11214 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
11215 args
, targetFlags
);
11216 TRY_ATTACH(nativeGen
.tryAttachStub());
11219 // Don't inline when no arguments are passed, cf. |tryAttachFunCall()|.
11220 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
11221 format
== CallFlags::FunCall
&& argc_
> 0) {
11222 MOZ_ASSERT(argc_
== 1);
11224 HandleValue newTarget
= NullHandleValue
;
11225 HandleValue thisValue
= args_
[0];
11226 HandleValueArray args
= HandleValueArray::empty();
11228 // Check for specific native-function optimizations.
11229 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
11230 args
, targetFlags
);
11231 TRY_ATTACH(nativeGen
.tryAttachStub());
11234 ObjOperandId thisObjId
= emitFunApplyGuard(argcId
);
11236 uint32_t fixedArgc
;
11237 if (format
== CallFlags::FunApplyArray
||
11238 format
== CallFlags::FunApplyArgsObj
||
11239 format
== CallFlags::FunApplyNullUndefined
) {
11240 emitFunApplyArgsGuard(format
);
11242 // We always use MaxUnrolledArgCopy here because the fixed argc is
11243 // meaningless in a FunApply case.
11244 fixedArgc
= MaxUnrolledArgCopy
;
11246 MOZ_ASSERT(format
== CallFlags::FunCall
);
11248 // Whereas for the FunCall case we need to use the actual fixed argc value.
11249 fixedArgc
= ClampFixedArgc(argc_
);
11252 if (mode_
== ICState::Mode::Specialized
) {
11253 // Ensure that |this| is the expected target function.
11254 emitCalleeGuard(thisObjId
, target
);
11257 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11259 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
11263 // Guard that |this| is a function.
11264 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
11266 // Guard that function is not a class constructor.
11267 writer
.guardNotClassConstructor(thisObjId
);
11270 // Guard that function is scripted.
11271 writer
.guardFunctionHasJitEntry(thisObjId
, /*constructing =*/false);
11272 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11274 // Guard that function is native.
11275 writer
.guardFunctionHasNoJitEntry(thisObjId
);
11276 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11280 writer
.returnFromIC();
11283 trackAttached("Call.ScriptedFunApply");
11285 trackAttached("Call.NativeFunApply");
11288 return AttachDecision::Attach
;
11291 AttachDecision
CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc
) {
11292 // Try to optimize calls into Wasm code by emitting the CallWasmFunction
11293 // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
11294 // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
11297 // Note: some code refers to these optimized Wasm calls as "inlined" calls.
11299 MOZ_ASSERT(calleeFunc
->isWasmWithJitEntry());
11301 if (!JitOptions
.enableWasmIonFastCalls
) {
11302 return AttachDecision::NoAction
;
11304 if (!isFirstStub_
) {
11305 return AttachDecision::NoAction
;
11307 JSOp op
= JSOp(*pc_
);
11308 if (op
!= JSOp::Call
&& op
!= JSOp::CallContent
&&
11309 op
!= JSOp::CallIgnoresRv
) {
11310 return AttachDecision::NoAction
;
11312 if (cx_
->realm() != calleeFunc
->realm()) {
11313 return AttachDecision::NoAction
;
11316 wasm::Instance
& inst
= wasm::ExportedFunctionToInstance(calleeFunc
);
11317 uint32_t funcIndex
= inst
.code().getFuncIndex(calleeFunc
);
11319 auto bestTier
= inst
.code().bestTier();
11320 const wasm::FuncExport
& funcExport
=
11321 inst
.metadata(bestTier
).lookupFuncExport(funcIndex
);
11322 const wasm::FuncType
& sig
= inst
.metadata().getFuncExportType(funcExport
);
11324 MOZ_ASSERT(!IsInsideNursery(inst
.object()));
11325 MOZ_ASSERT(sig
.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
11327 // If there are too many arguments, don't optimize (we won't be able to store
11328 // the arguments in the LIR node).
11329 static_assert(wasm::MaxArgsForJitInlineCall
<= ArgumentKindArgIndexLimit
);
11330 if (sig
.args().length() > wasm::MaxArgsForJitInlineCall
||
11331 argc_
> ArgumentKindArgIndexLimit
) {
11332 return AttachDecision::NoAction
;
11335 // If there are too many results, don't optimize as Warp currently doesn't
11336 // have code to handle this.
11337 if (sig
.results().length() > wasm::MaxResultsForJitInlineCall
) {
11338 return AttachDecision::NoAction
;
11341 // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
11342 // because it is more difficult (because it requires multiple LIR arguments
11345 // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
11346 // spilled to the stack because it causes problems with register allocation.
11348 constexpr bool optimizeWithI64
= true;
11350 constexpr bool optimizeWithI64
= false;
11352 ABIArgGenerator abi
;
11353 for (const auto& valType
: sig
.args()) {
11354 MIRType mirType
= valType
.toMIRType();
11355 ABIArg abiArg
= abi
.next(mirType
);
11356 if (mirType
!= MIRType::Int64
) {
11359 if (!optimizeWithI64
|| abiArg
.kind() == ABIArg::Stack
) {
11360 return AttachDecision::NoAction
;
11364 // Check that all arguments can be converted to the Wasm type in Warp code
11365 // without bailing out.
11366 for (size_t i
= 0; i
< sig
.args().length(); i
++) {
11367 Value argVal
= i
< argc_
? args_
[i
] : UndefinedValue();
11368 switch (sig
.args()[i
].kind()) {
11369 case wasm::ValType::I32
:
11370 case wasm::ValType::F32
:
11371 case wasm::ValType::F64
:
11372 if (!argVal
.isNumber() && !argVal
.isBoolean() &&
11373 !argVal
.isUndefined()) {
11374 return AttachDecision::NoAction
;
11377 case wasm::ValType::I64
:
11378 if (!argVal
.isBigInt() && !argVal
.isBoolean() && !argVal
.isString()) {
11379 return AttachDecision::NoAction
;
11382 case wasm::ValType::V128
:
11383 MOZ_CRASH("Function should not have a Wasm JitEntry");
11384 case wasm::ValType::Ref
:
11385 // canHaveJitEntry restricts args to externref, where all JS values are
11386 // valid and can be boxed.
11387 MOZ_ASSERT(sig
.args()[i
].refType().isExtern(),
11388 "Unexpected type for Wasm JitEntry");
11393 CallFlags
flags(/* isConstructing = */ false, /* isSpread = */ false,
11394 /* isSameRealm = */ true);
11397 Int32OperandId
argcId(writer
.setInputOperandId(0));
11399 // Load the callee and ensure it is an object
11400 ValOperandId calleeValId
=
11401 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags
);
11402 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11404 // Ensure the callee is this Wasm function.
11405 emitCalleeGuard(calleeObjId
, calleeFunc
);
11407 // Guard the argument types.
11408 uint32_t guardedArgs
= std::min
<uint32_t>(sig
.args().length(), argc_
);
11409 for (uint32_t i
= 0; i
< guardedArgs
; i
++) {
11410 ArgumentKind argKind
= ArgumentKindForArgIndex(i
);
11411 ValOperandId argId
= writer
.loadArgumentFixedSlot(argKind
, argc_
, flags
);
11412 writer
.guardWasmArg(argId
, sig
.args()[i
].kind());
11415 writer
.callWasmFunction(calleeObjId
, argcId
, flags
, ClampFixedArgc(argc_
),
11416 &funcExport
, inst
.object());
11417 writer
.returnFromIC();
11419 trackAttached("Call.WasmCall");
11421 return AttachDecision::Attach
;
11424 AttachDecision
CallIRGenerator::tryAttachInlinableNative(HandleFunction callee
,
11426 MOZ_ASSERT(mode_
== ICState::Mode::Specialized
);
11427 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
11428 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
||
11429 flags
.getArgFormat() == CallFlags::Spread
);
11431 // Special case functions are only optimized for normal calls.
11432 if (op_
!= JSOp::Call
&& op_
!= JSOp::CallContent
&& op_
!= JSOp::New
&&
11433 op_
!= JSOp::NewContent
&& op_
!= JSOp::CallIgnoresRv
&&
11434 op_
!= JSOp::SpreadCall
) {
11435 return AttachDecision::NoAction
;
11438 InlinableNativeIRGenerator
nativeGen(*this, callee
, newTarget_
, thisval_
,
11440 return nativeGen
.tryAttachStub();
11443 #ifdef FUZZING_JS_FUZZILLI
11444 AttachDecision
InlinableNativeIRGenerator::tryAttachFuzzilliHash() {
11446 return AttachDecision::NoAction
;
11449 // Initialize the input operand.
11450 initializeInputOperand();
11452 // Guard callee is the 'fuzzilli_hash' native function.
11453 emitNativeCalleeGuard();
11455 ValOperandId argValId
=
11456 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
11458 writer
.fuzzilliHashResult(argValId
);
11459 writer
.returnFromIC();
11461 trackAttached("FuzzilliHash");
11462 return AttachDecision::Attach
;
11466 AttachDecision
InlinableNativeIRGenerator::tryAttachStub() {
11467 if (!callee_
->hasJitInfo() ||
11468 callee_
->jitInfo()->type() != JSJitInfo::InlinableNative
) {
11469 return AttachDecision::NoAction
;
11472 InlinableNative native
= callee_
->jitInfo()->inlinableNative
;
11474 // Not all natives can be inlined cross-realm.
11475 if (cx_
->realm() != callee_
->realm() && !CanInlineNativeCrossRealm(native
)) {
11476 return AttachDecision::NoAction
;
11479 // Check for special-cased native constructors.
11480 if (flags_
.isConstructing()) {
11481 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
11483 // newTarget must match the callee. CacheIR for this is emitted in
11484 // emitNativeCalleeGuard.
11485 if (ObjectValue(*callee_
) != newTarget_
) {
11486 return AttachDecision::NoAction
;
11489 case InlinableNative::Array
:
11490 return tryAttachArrayConstructor();
11491 case InlinableNative::TypedArrayConstructor
:
11492 return tryAttachTypedArrayConstructor();
11493 case InlinableNative::String
:
11494 return tryAttachStringConstructor();
11495 case InlinableNative::Object
:
11496 return tryAttachObjectConstructor();
11500 return AttachDecision::NoAction
;
11503 // Check for special-cased native spread calls.
11504 if (flags_
.getArgFormat() == CallFlags::Spread
||
11505 flags_
.getArgFormat() == CallFlags::FunApplyArray
) {
11507 case InlinableNative::MathMin
:
11508 return tryAttachSpreadMathMinMax(/*isMax = */ false);
11509 case InlinableNative::MathMax
:
11510 return tryAttachSpreadMathMinMax(/*isMax = */ true);
11514 return AttachDecision::NoAction
;
11517 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
||
11518 flags_
.getArgFormat() == CallFlags::FunCall
);
11520 // Check for special-cased native functions.
11523 case InlinableNative::Array
:
11524 return tryAttachArrayConstructor();
11525 case InlinableNative::ArrayPush
:
11526 return tryAttachArrayPush();
11527 case InlinableNative::ArrayPop
:
11528 case InlinableNative::ArrayShift
:
11529 return tryAttachArrayPopShift(native
);
11530 case InlinableNative::ArrayJoin
:
11531 return tryAttachArrayJoin();
11532 case InlinableNative::ArraySlice
:
11533 return tryAttachArraySlice();
11534 case InlinableNative::ArrayIsArray
:
11535 return tryAttachArrayIsArray();
11537 // DataView natives.
11538 case InlinableNative::DataViewGetInt8
:
11539 return tryAttachDataViewGet(Scalar::Int8
);
11540 case InlinableNative::DataViewGetUint8
:
11541 return tryAttachDataViewGet(Scalar::Uint8
);
11542 case InlinableNative::DataViewGetInt16
:
11543 return tryAttachDataViewGet(Scalar::Int16
);
11544 case InlinableNative::DataViewGetUint16
:
11545 return tryAttachDataViewGet(Scalar::Uint16
);
11546 case InlinableNative::DataViewGetInt32
:
11547 return tryAttachDataViewGet(Scalar::Int32
);
11548 case InlinableNative::DataViewGetUint32
:
11549 return tryAttachDataViewGet(Scalar::Uint32
);
11550 case InlinableNative::DataViewGetFloat32
:
11551 return tryAttachDataViewGet(Scalar::Float32
);
11552 case InlinableNative::DataViewGetFloat64
:
11553 return tryAttachDataViewGet(Scalar::Float64
);
11554 case InlinableNative::DataViewGetBigInt64
:
11555 return tryAttachDataViewGet(Scalar::BigInt64
);
11556 case InlinableNative::DataViewGetBigUint64
:
11557 return tryAttachDataViewGet(Scalar::BigUint64
);
11558 case InlinableNative::DataViewSetInt8
:
11559 return tryAttachDataViewSet(Scalar::Int8
);
11560 case InlinableNative::DataViewSetUint8
:
11561 return tryAttachDataViewSet(Scalar::Uint8
);
11562 case InlinableNative::DataViewSetInt16
:
11563 return tryAttachDataViewSet(Scalar::Int16
);
11564 case InlinableNative::DataViewSetUint16
:
11565 return tryAttachDataViewSet(Scalar::Uint16
);
11566 case InlinableNative::DataViewSetInt32
:
11567 return tryAttachDataViewSet(Scalar::Int32
);
11568 case InlinableNative::DataViewSetUint32
:
11569 return tryAttachDataViewSet(Scalar::Uint32
);
11570 case InlinableNative::DataViewSetFloat32
:
11571 return tryAttachDataViewSet(Scalar::Float32
);
11572 case InlinableNative::DataViewSetFloat64
:
11573 return tryAttachDataViewSet(Scalar::Float64
);
11574 case InlinableNative::DataViewSetBigInt64
:
11575 return tryAttachDataViewSet(Scalar::BigInt64
);
11576 case InlinableNative::DataViewSetBigUint64
:
11577 return tryAttachDataViewSet(Scalar::BigUint64
);
11579 // Function natives.
11580 case InlinableNative::FunctionBind
:
11581 return tryAttachFunctionBind();
11584 case InlinableNative::IntlGuardToCollator
:
11585 case InlinableNative::IntlGuardToDateTimeFormat
:
11586 case InlinableNative::IntlGuardToDisplayNames
:
11587 case InlinableNative::IntlGuardToListFormat
:
11588 case InlinableNative::IntlGuardToNumberFormat
:
11589 case InlinableNative::IntlGuardToPluralRules
:
11590 case InlinableNative::IntlGuardToRelativeTimeFormat
:
11591 case InlinableNative::IntlGuardToSegmenter
:
11592 case InlinableNative::IntlGuardToSegments
:
11593 case InlinableNative::IntlGuardToSegmentIterator
:
11594 return tryAttachGuardToClass(native
);
11596 // Slot intrinsics.
11597 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
11598 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
11599 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
11600 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
11601 return tryAttachUnsafeGetReservedSlot(native
);
11602 case InlinableNative::IntrinsicUnsafeSetReservedSlot
:
11603 return tryAttachUnsafeSetReservedSlot();
11606 case InlinableNative::IntrinsicIsSuspendedGenerator
:
11607 return tryAttachIsSuspendedGenerator();
11608 case InlinableNative::IntrinsicToObject
:
11609 return tryAttachToObject();
11610 case InlinableNative::IntrinsicToInteger
:
11611 return tryAttachToInteger();
11612 case InlinableNative::IntrinsicToLength
:
11613 return tryAttachToLength();
11614 case InlinableNative::IntrinsicIsObject
:
11615 return tryAttachIsObject();
11616 case InlinableNative::IntrinsicIsPackedArray
:
11617 return tryAttachIsPackedArray();
11618 case InlinableNative::IntrinsicIsCallable
:
11619 return tryAttachIsCallable();
11620 case InlinableNative::IntrinsicIsConstructor
:
11621 return tryAttachIsConstructor();
11622 case InlinableNative::IntrinsicIsCrossRealmArrayConstructor
:
11623 return tryAttachIsCrossRealmArrayConstructor();
11624 case InlinableNative::IntrinsicGuardToArrayIterator
:
11625 case InlinableNative::IntrinsicGuardToMapIterator
:
11626 case InlinableNative::IntrinsicGuardToSetIterator
:
11627 case InlinableNative::IntrinsicGuardToStringIterator
:
11628 case InlinableNative::IntrinsicGuardToRegExpStringIterator
:
11629 case InlinableNative::IntrinsicGuardToWrapForValidIterator
:
11630 case InlinableNative::IntrinsicGuardToIteratorHelper
:
11631 case InlinableNative::IntrinsicGuardToAsyncIteratorHelper
:
11632 return tryAttachGuardToClass(native
);
11633 case InlinableNative::IntrinsicSubstringKernel
:
11634 return tryAttachSubstringKernel();
11635 case InlinableNative::IntrinsicIsConstructing
:
11636 return tryAttachIsConstructing();
11637 case InlinableNative::IntrinsicNewArrayIterator
:
11638 return tryAttachNewArrayIterator();
11639 case InlinableNative::IntrinsicNewStringIterator
:
11640 return tryAttachNewStringIterator();
11641 case InlinableNative::IntrinsicNewRegExpStringIterator
:
11642 return tryAttachNewRegExpStringIterator();
11643 case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable
:
11644 return tryAttachArrayIteratorPrototypeOptimizable();
11645 case InlinableNative::IntrinsicObjectHasPrototype
:
11646 return tryAttachObjectHasPrototype();
11649 case InlinableNative::IsRegExpObject
:
11650 return tryAttachHasClass(&RegExpObject::class_
,
11651 /* isPossiblyWrapped = */ false);
11652 case InlinableNative::IsPossiblyWrappedRegExpObject
:
11653 return tryAttachHasClass(&RegExpObject::class_
,
11654 /* isPossiblyWrapped = */ true);
11655 case InlinableNative::RegExpMatcher
:
11656 case InlinableNative::RegExpSearcher
:
11657 return tryAttachRegExpMatcherSearcher(native
);
11658 case InlinableNative::RegExpSearcherLastLimit
:
11659 return tryAttachRegExpSearcherLastLimit();
11660 case InlinableNative::RegExpHasCaptureGroups
:
11661 return tryAttachRegExpHasCaptureGroups();
11662 case InlinableNative::RegExpPrototypeOptimizable
:
11663 return tryAttachRegExpPrototypeOptimizable();
11664 case InlinableNative::RegExpInstanceOptimizable
:
11665 return tryAttachRegExpInstanceOptimizable();
11666 case InlinableNative::GetFirstDollarIndex
:
11667 return tryAttachGetFirstDollarIndex();
11668 case InlinableNative::IntrinsicRegExpBuiltinExec
:
11669 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
11670 return tryAttachIntrinsicRegExpBuiltinExec(native
);
11671 case InlinableNative::IntrinsicRegExpExec
:
11672 case InlinableNative::IntrinsicRegExpExecForTest
:
11673 return tryAttachIntrinsicRegExpExec(native
);
11676 case InlinableNative::String
:
11677 return tryAttachString();
11678 case InlinableNative::StringToString
:
11679 case InlinableNative::StringValueOf
:
11680 return tryAttachStringToStringValueOf();
11681 case InlinableNative::StringCharCodeAt
:
11682 return tryAttachStringCharCodeAt();
11683 case InlinableNative::StringCodePointAt
:
11684 return tryAttachStringCodePointAt();
11685 case InlinableNative::StringCharAt
:
11686 return tryAttachStringCharAt();
11687 case InlinableNative::StringAt
:
11688 return tryAttachStringAt();
11689 case InlinableNative::StringFromCharCode
:
11690 return tryAttachStringFromCharCode();
11691 case InlinableNative::StringFromCodePoint
:
11692 return tryAttachStringFromCodePoint();
11693 case InlinableNative::StringIncludes
:
11694 return tryAttachStringIncludes();
11695 case InlinableNative::StringIndexOf
:
11696 return tryAttachStringIndexOf();
11697 case InlinableNative::StringLastIndexOf
:
11698 return tryAttachStringLastIndexOf();
11699 case InlinableNative::StringStartsWith
:
11700 return tryAttachStringStartsWith();
11701 case InlinableNative::StringEndsWith
:
11702 return tryAttachStringEndsWith();
11703 case InlinableNative::StringToLowerCase
:
11704 return tryAttachStringToLowerCase();
11705 case InlinableNative::StringToUpperCase
:
11706 return tryAttachStringToUpperCase();
11707 case InlinableNative::StringTrim
:
11708 return tryAttachStringTrim();
11709 case InlinableNative::StringTrimStart
:
11710 return tryAttachStringTrimStart();
11711 case InlinableNative::StringTrimEnd
:
11712 return tryAttachStringTrimEnd();
11713 case InlinableNative::IntrinsicStringReplaceString
:
11714 return tryAttachStringReplaceString();
11715 case InlinableNative::IntrinsicStringSplitString
:
11716 return tryAttachStringSplitString();
11719 case InlinableNative::MathRandom
:
11720 return tryAttachMathRandom();
11721 case InlinableNative::MathAbs
:
11722 return tryAttachMathAbs();
11723 case InlinableNative::MathClz32
:
11724 return tryAttachMathClz32();
11725 case InlinableNative::MathSign
:
11726 return tryAttachMathSign();
11727 case InlinableNative::MathImul
:
11728 return tryAttachMathImul();
11729 case InlinableNative::MathFloor
:
11730 return tryAttachMathFloor();
11731 case InlinableNative::MathCeil
:
11732 return tryAttachMathCeil();
11733 case InlinableNative::MathTrunc
:
11734 return tryAttachMathTrunc();
11735 case InlinableNative::MathRound
:
11736 return tryAttachMathRound();
11737 case InlinableNative::MathSqrt
:
11738 return tryAttachMathSqrt();
11739 case InlinableNative::MathFRound
:
11740 return tryAttachMathFRound();
11741 case InlinableNative::MathHypot
:
11742 return tryAttachMathHypot();
11743 case InlinableNative::MathATan2
:
11744 return tryAttachMathATan2();
11745 case InlinableNative::MathSin
:
11746 return tryAttachMathFunction(UnaryMathFunction::SinNative
);
11747 case InlinableNative::MathTan
:
11748 return tryAttachMathFunction(UnaryMathFunction::TanNative
);
11749 case InlinableNative::MathCos
:
11750 return tryAttachMathFunction(UnaryMathFunction::CosNative
);
11751 case InlinableNative::MathExp
:
11752 return tryAttachMathFunction(UnaryMathFunction::Exp
);
11753 case InlinableNative::MathLog
:
11754 return tryAttachMathFunction(UnaryMathFunction::Log
);
11755 case InlinableNative::MathASin
:
11756 return tryAttachMathFunction(UnaryMathFunction::ASin
);
11757 case InlinableNative::MathATan
:
11758 return tryAttachMathFunction(UnaryMathFunction::ATan
);
11759 case InlinableNative::MathACos
:
11760 return tryAttachMathFunction(UnaryMathFunction::ACos
);
11761 case InlinableNative::MathLog10
:
11762 return tryAttachMathFunction(UnaryMathFunction::Log10
);
11763 case InlinableNative::MathLog2
:
11764 return tryAttachMathFunction(UnaryMathFunction::Log2
);
11765 case InlinableNative::MathLog1P
:
11766 return tryAttachMathFunction(UnaryMathFunction::Log1P
);
11767 case InlinableNative::MathExpM1
:
11768 return tryAttachMathFunction(UnaryMathFunction::ExpM1
);
11769 case InlinableNative::MathCosH
:
11770 return tryAttachMathFunction(UnaryMathFunction::CosH
);
11771 case InlinableNative::MathSinH
:
11772 return tryAttachMathFunction(UnaryMathFunction::SinH
);
11773 case InlinableNative::MathTanH
:
11774 return tryAttachMathFunction(UnaryMathFunction::TanH
);
11775 case InlinableNative::MathACosH
:
11776 return tryAttachMathFunction(UnaryMathFunction::ACosH
);
11777 case InlinableNative::MathASinH
:
11778 return tryAttachMathFunction(UnaryMathFunction::ASinH
);
11779 case InlinableNative::MathATanH
:
11780 return tryAttachMathFunction(UnaryMathFunction::ATanH
);
11781 case InlinableNative::MathCbrt
:
11782 return tryAttachMathFunction(UnaryMathFunction::Cbrt
);
11783 case InlinableNative::MathPow
:
11784 return tryAttachMathPow();
11785 case InlinableNative::MathMin
:
11786 return tryAttachMathMinMax(/* isMax = */ false);
11787 case InlinableNative::MathMax
:
11788 return tryAttachMathMinMax(/* isMax = */ true);
11791 case InlinableNative::IntrinsicGuardToMapObject
:
11792 return tryAttachGuardToClass(GuardClassKind::Map
);
11793 case InlinableNative::IntrinsicGetNextMapEntryForIterator
:
11794 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true);
11797 case InlinableNative::Number
:
11798 return tryAttachNumber();
11799 case InlinableNative::NumberParseInt
:
11800 return tryAttachNumberParseInt();
11801 case InlinableNative::NumberToString
:
11802 return tryAttachNumberToString();
11805 case InlinableNative::Object
:
11806 return tryAttachObjectConstructor();
11807 case InlinableNative::ObjectCreate
:
11808 return tryAttachObjectCreate();
11809 case InlinableNative::ObjectIs
:
11810 return tryAttachObjectIs();
11811 case InlinableNative::ObjectIsPrototypeOf
:
11812 return tryAttachObjectIsPrototypeOf();
11813 case InlinableNative::ObjectKeys
:
11814 return tryAttachObjectKeys();
11815 case InlinableNative::ObjectToString
:
11816 return tryAttachObjectToString();
11819 case InlinableNative::IntrinsicGuardToSetObject
:
11820 return tryAttachGuardToClass(GuardClassKind::Set
);
11821 case InlinableNative::IntrinsicGetNextSetEntryForIterator
:
11822 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false);
11824 // ArrayBuffer intrinsics.
11825 case InlinableNative::IntrinsicGuardToArrayBuffer
:
11826 return tryAttachGuardToArrayBuffer();
11827 case InlinableNative::IntrinsicArrayBufferByteLength
:
11828 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false);
11829 case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength
:
11830 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true);
11832 // SharedArrayBuffer intrinsics.
11833 case InlinableNative::IntrinsicGuardToSharedArrayBuffer
:
11834 return tryAttachGuardToSharedArrayBuffer();
11836 // TypedArray intrinsics.
11837 case InlinableNative::TypedArrayConstructor
:
11838 return AttachDecision::NoAction
; // Not callable.
11839 case InlinableNative::IntrinsicIsTypedArray
:
11840 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ false);
11841 case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray
:
11842 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ true);
11843 case InlinableNative::IntrinsicIsTypedArrayConstructor
:
11844 return tryAttachIsTypedArrayConstructor();
11845 case InlinableNative::IntrinsicTypedArrayByteOffset
:
11846 return tryAttachTypedArrayByteOffset();
11847 case InlinableNative::IntrinsicTypedArrayElementSize
:
11848 return tryAttachTypedArrayElementSize();
11849 case InlinableNative::IntrinsicTypedArrayLength
:
11850 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false,
11851 /* allowOutOfBounds = */ false);
11852 case InlinableNative::IntrinsicTypedArrayLengthZeroOnOutOfBounds
:
11853 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false,
11854 /* allowOutOfBounds = */ true);
11855 case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength
:
11856 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true,
11857 /* allowOutOfBounds = */ false);
11859 // Reflect natives.
11860 case InlinableNative::ReflectGetPrototypeOf
:
11861 return tryAttachReflectGetPrototypeOf();
11863 // Atomics intrinsics:
11864 case InlinableNative::AtomicsCompareExchange
:
11865 return tryAttachAtomicsCompareExchange();
11866 case InlinableNative::AtomicsExchange
:
11867 return tryAttachAtomicsExchange();
11868 case InlinableNative::AtomicsAdd
:
11869 return tryAttachAtomicsAdd();
11870 case InlinableNative::AtomicsSub
:
11871 return tryAttachAtomicsSub();
11872 case InlinableNative::AtomicsAnd
:
11873 return tryAttachAtomicsAnd();
11874 case InlinableNative::AtomicsOr
:
11875 return tryAttachAtomicsOr();
11876 case InlinableNative::AtomicsXor
:
11877 return tryAttachAtomicsXor();
11878 case InlinableNative::AtomicsLoad
:
11879 return tryAttachAtomicsLoad();
11880 case InlinableNative::AtomicsStore
:
11881 return tryAttachAtomicsStore();
11882 case InlinableNative::AtomicsIsLockFree
:
11883 return tryAttachAtomicsIsLockFree();
11886 case InlinableNative::BigIntAsIntN
:
11887 return tryAttachBigIntAsIntN();
11888 case InlinableNative::BigIntAsUintN
:
11889 return tryAttachBigIntAsUintN();
11891 // Boolean natives.
11892 case InlinableNative::Boolean
:
11893 return tryAttachBoolean();
11896 case InlinableNative::SetHas
:
11897 return tryAttachSetHas();
11898 case InlinableNative::SetSize
:
11899 return tryAttachSetSize();
11902 case InlinableNative::MapHas
:
11903 return tryAttachMapHas();
11904 case InlinableNative::MapGet
:
11905 return tryAttachMapGet();
11907 // Testing functions.
11908 case InlinableNative::TestBailout
:
11909 if (js::SupportDifferentialTesting()) {
11910 return AttachDecision::NoAction
;
11912 return tryAttachBailout();
11913 case InlinableNative::TestAssertFloat32
:
11914 return tryAttachAssertFloat32();
11915 case InlinableNative::TestAssertRecoveredOnBailout
:
11916 if (js::SupportDifferentialTesting()) {
11917 return AttachDecision::NoAction
;
11919 return tryAttachAssertRecoveredOnBailout();
11921 #ifdef FUZZING_JS_FUZZILLI
11922 // Fuzzilli function
11923 case InlinableNative::FuzzilliHash
:
11924 return tryAttachFuzzilliHash();
11927 case InlinableNative::Limit
:
11931 MOZ_CRASH("Shouldn't get here");
11934 // Remember the shape of the this object for any script being called as a
11935 // constructor, for later use during Ion compilation.
11936 ScriptedThisResult
CallIRGenerator::getThisShapeForScripted(
11937 HandleFunction calleeFunc
, Handle
<JSObject
*> newTarget
,
11938 MutableHandle
<Shape
*> result
) {
11939 // Some constructors allocate their own |this| object.
11940 if (calleeFunc
->constructorNeedsUninitializedThis()) {
11941 return ScriptedThisResult::UninitializedThis
;
11944 // Only attach a stub if the newTarget is a function with a
11945 // nonconfigurable prototype.
11946 if (!newTarget
->is
<JSFunction
>() ||
11947 !newTarget
->as
<JSFunction
>().hasNonConfigurablePrototypeDataProperty()) {
11948 return ScriptedThisResult::NoAction
;
11951 AutoRealm
ar(cx_
, calleeFunc
);
11952 Shape
* thisShape
= ThisShapeForFunction(cx_
, calleeFunc
, newTarget
);
11954 cx_
->clearPendingException();
11955 return ScriptedThisResult::NoAction
;
11958 MOZ_ASSERT(thisShape
->realm() == calleeFunc
->realm());
11959 result
.set(thisShape
);
11960 return ScriptedThisResult::PlainObjectShape
;
11963 static bool CanOptimizeScriptedCall(JSFunction
* callee
, bool isConstructing
) {
11964 if (!callee
->hasJitEntry()) {
11968 // If callee is not an interpreted constructor, we have to throw.
11969 if (isConstructing
&& !callee
->isConstructor()) {
11973 // Likewise, if the callee is a class constructor, we have to throw.
11974 if (!isConstructing
&& callee
->isClassConstructor()) {
11981 void CallIRGenerator::emitCallScriptedGuards(ObjOperandId calleeObjId
,
11982 JSFunction
* calleeFunc
,
11983 Int32OperandId argcId
,
11984 CallFlags flags
, Shape
* thisShape
,
11985 bool isBoundFunction
) {
11986 bool isConstructing
= flags
.isConstructing();
11988 if (mode_
== ICState::Mode::Specialized
) {
11989 MOZ_ASSERT_IF(isConstructing
, thisShape
|| flags
.needsUninitializedThis());
11991 // Ensure callee matches this stub's callee
11992 emitCalleeGuard(calleeObjId
, calleeFunc
);
11994 // Emit guards to ensure the newTarget's .prototype property is what we
11995 // expect. Note that getThisForScripted checked newTarget is a function
11996 // with a non-configurable .prototype data property.
11998 JSFunction
* newTarget
;
11999 ObjOperandId newTargetObjId
;
12000 if (isBoundFunction
) {
12001 newTarget
= calleeFunc
;
12002 newTargetObjId
= calleeObjId
;
12004 newTarget
= &newTarget_
.toObject().as
<JSFunction
>();
12005 ValOperandId newTargetValId
= writer
.loadArgumentDynamicSlot(
12006 ArgumentKind::NewTarget
, argcId
, flags
);
12007 newTargetObjId
= writer
.guardToObject(newTargetValId
);
12010 Maybe
<PropertyInfo
> prop
= newTarget
->lookupPure(cx_
->names().prototype
);
12011 MOZ_ASSERT(prop
.isSome());
12012 uint32_t slot
= prop
->slot();
12013 MOZ_ASSERT(slot
>= newTarget
->numFixedSlots(),
12014 "Stub code relies on this");
12016 writer
.guardShape(newTargetObjId
, newTarget
->shape());
12018 const Value
& value
= newTarget
->getSlot(slot
);
12019 if (value
.isObject()) {
12020 JSObject
* prototypeObject
= &value
.toObject();
12022 ObjOperandId protoId
= writer
.loadObject(prototypeObject
);
12023 writer
.guardDynamicSlotIsSpecificObject(
12024 newTargetObjId
, protoId
, slot
- newTarget
->numFixedSlots());
12026 writer
.guardDynamicSlotIsNotObject(newTargetObjId
,
12027 slot
- newTarget
->numFixedSlots());
12030 // Call metaScriptedThisShape before emitting the call, so that Warp can
12031 // use the shape to create the |this| object before transpiling the call.
12032 writer
.metaScriptedThisShape(thisShape
);
12035 // Guard that object is a scripted function
12036 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
12037 writer
.guardFunctionHasJitEntry(calleeObjId
, isConstructing
);
12039 if (isConstructing
) {
12040 // If callee is not a constructor, we have to throw.
12041 writer
.guardFunctionIsConstructor(calleeObjId
);
12043 // If callee is a class constructor, we have to throw.
12044 writer
.guardNotClassConstructor(calleeObjId
);
12049 AttachDecision
CallIRGenerator::tryAttachCallScripted(
12050 HandleFunction calleeFunc
) {
12051 MOZ_ASSERT(calleeFunc
->hasJitEntry());
12053 if (calleeFunc
->isWasmWithJitEntry()) {
12054 TRY_ATTACH(tryAttachWasmCall(calleeFunc
));
12057 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
12059 bool isConstructing
= IsConstructPC(pc_
);
12060 bool isSpread
= IsSpreadPC(pc_
);
12061 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
12062 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
12064 if (!CanOptimizeScriptedCall(calleeFunc
, isConstructing
)) {
12065 return AttachDecision::NoAction
;
12068 if (isConstructing
&& !calleeFunc
->hasJitScript()) {
12069 // If we're constructing, require the callee to have a JitScript. This isn't
12070 // required for correctness but avoids allocating a template object below
12071 // for constructors that aren't hot. See bug 1419758.
12072 return AttachDecision::TemporarilyUnoptimizable
;
12075 // Verify that spread calls have a reasonable number of arguments.
12076 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
12077 return AttachDecision::NoAction
;
12080 Rooted
<Shape
*> thisShape(cx_
);
12081 if (isConstructing
&& isSpecialized
) {
12082 Rooted
<JSObject
*> newTarget(cx_
, &newTarget_
.toObject());
12083 switch (getThisShapeForScripted(calleeFunc
, newTarget
, &thisShape
)) {
12084 case ScriptedThisResult::PlainObjectShape
:
12086 case ScriptedThisResult::UninitializedThis
:
12087 flags
.setNeedsUninitializedThis();
12089 case ScriptedThisResult::NoAction
:
12090 return AttachDecision::NoAction
;
12095 Int32OperandId
argcId(writer
.setInputOperandId(0));
12097 // Load the callee and ensure it is an object
12098 ValOperandId calleeValId
=
12099 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12100 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12102 emitCallScriptedGuards(calleeObjId
, calleeFunc
, argcId
, flags
, thisShape
,
12103 /* isBoundFunction = */ false);
12105 writer
.callScriptedFunction(calleeObjId
, argcId
, flags
,
12106 ClampFixedArgc(argc_
));
12107 writer
.returnFromIC();
12109 if (isSpecialized
) {
12110 trackAttached("Call.CallScripted");
12112 trackAttached("Call.CallAnyScripted");
12115 return AttachDecision::Attach
;
12118 AttachDecision
CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc
) {
12119 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12121 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
12123 bool isSpread
= IsSpreadPC(pc_
);
12124 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
12125 bool isConstructing
= IsConstructPC(pc_
);
12126 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
12128 if (isConstructing
&& !calleeFunc
->isConstructor()) {
12129 return AttachDecision::NoAction
;
12132 // Verify that spread calls have a reasonable number of arguments.
12133 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
12134 return AttachDecision::NoAction
;
12137 // Check for specific native-function optimizations.
12138 if (isSpecialized
) {
12139 TRY_ATTACH(tryAttachInlinableNative(calleeFunc
, flags
));
12143 Int32OperandId
argcId(writer
.setInputOperandId(0));
12145 // Load the callee and ensure it is an object
12146 ValOperandId calleeValId
=
12147 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12148 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12150 // DOM calls need an additional guard so only try optimizing the first stub.
12151 // Can only optimize normal (non-spread) calls.
12152 if (isFirstStub_
&& !isSpread
&& thisval_
.isObject() &&
12153 CanAttachDOMCall(cx_
, JSJitInfo::Method
, &thisval_
.toObject(), calleeFunc
,
12155 MOZ_ASSERT(!isConstructing
, "DOM functions are not constructors");
12157 // Guard that |this| is an object.
12158 ValOperandId thisValId
=
12159 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
, flags
);
12160 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
12162 // Guard on the |this| shape to make sure it's the right instance. This also
12163 // ensures DOM_OBJECT_SLOT is stored in a fixed slot. See CanAttachDOMCall.
12164 writer
.guardShape(thisObjId
, thisval_
.toObject().shape());
12166 // Ensure callee matches this stub's callee
12167 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
12168 writer
.callDOMFunction(calleeObjId
, argcId
, thisObjId
, calleeFunc
, flags
,
12169 ClampFixedArgc(argc_
));
12171 trackAttached("Call.CallDOM");
12172 } else if (isSpecialized
) {
12173 // Ensure callee matches this stub's callee
12174 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
12175 writer
.callNativeFunction(calleeObjId
, argcId
, op_
, calleeFunc
, flags
,
12176 ClampFixedArgc(argc_
));
12178 trackAttached("Call.CallNative");
12180 // Guard that object is a native function
12181 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
12182 writer
.guardFunctionHasNoJitEntry(calleeObjId
);
12184 if (isConstructing
) {
12185 // If callee is not a constructor, we have to throw.
12186 writer
.guardFunctionIsConstructor(calleeObjId
);
12188 // If callee is a class constructor, we have to throw.
12189 writer
.guardNotClassConstructor(calleeObjId
);
12191 writer
.callAnyNativeFunction(calleeObjId
, argcId
, flags
,
12192 ClampFixedArgc(argc_
));
12194 trackAttached("Call.CallAnyNative");
12197 writer
.returnFromIC();
12199 return AttachDecision::Attach
;
12202 AttachDecision
CallIRGenerator::tryAttachCallHook(HandleObject calleeObj
) {
12203 if (mode_
!= ICState::Mode::Specialized
) {
12204 // We do not have megamorphic call hook stubs.
12205 // TODO: Should we attach specialized call hook stubs in
12206 // megamorphic mode to avoid going generic?
12207 return AttachDecision::NoAction
;
12210 bool isSpread
= IsSpreadPC(pc_
);
12211 bool isConstructing
= IsConstructPC(pc_
);
12212 CallFlags
flags(isConstructing
, isSpread
);
12214 isConstructing
? calleeObj
->constructHook() : calleeObj
->callHook();
12216 return AttachDecision::NoAction
;
12219 // Bound functions have a JSClass construct hook but are not always
12221 if (isConstructing
&& !calleeObj
->isConstructor()) {
12222 return AttachDecision::NoAction
;
12225 // We don't support spread calls in the transpiler yet.
12227 return AttachDecision::NoAction
;
12231 Int32OperandId
argcId(writer
.setInputOperandId(0));
12233 // Load the callee and ensure it is an object
12234 ValOperandId calleeValId
=
12235 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12236 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12238 // Ensure the callee's class matches the one in this stub.
12239 writer
.guardAnyClass(calleeObjId
, calleeObj
->getClass());
12241 if (isConstructing
&& calleeObj
->is
<BoundFunctionObject
>()) {
12242 writer
.guardBoundFunctionIsConstructor(calleeObjId
);
12245 writer
.callClassHook(calleeObjId
, argcId
, hook
, flags
, ClampFixedArgc(argc_
));
12246 writer
.returnFromIC();
12248 trackAttached("Call.CallHook");
12250 return AttachDecision::Attach
;
12253 AttachDecision
CallIRGenerator::tryAttachBoundFunction(
12254 Handle
<BoundFunctionObject
*> calleeObj
) {
12255 // The target must be a JSFunction with a JitEntry.
12256 if (!calleeObj
->getTarget()->is
<JSFunction
>()) {
12257 return AttachDecision::NoAction
;
12260 bool isSpread
= IsSpreadPC(pc_
);
12261 bool isConstructing
= IsConstructPC(pc_
);
12263 // Spread calls are not supported yet.
12265 return AttachDecision::NoAction
;
12268 Rooted
<JSFunction
*> target(cx_
, &calleeObj
->getTarget()->as
<JSFunction
>());
12269 if (!CanOptimizeScriptedCall(target
, isConstructing
)) {
12270 return AttachDecision::NoAction
;
12273 // Limit the number of bound arguments to prevent us from compiling many
12274 // different stubs (we bake in numBoundArgs and it's usually very small).
12275 static constexpr size_t MaxBoundArgs
= 10;
12276 size_t numBoundArgs
= calleeObj
->numBoundArgs();
12277 if (numBoundArgs
> MaxBoundArgs
) {
12278 return AttachDecision::NoAction
;
12281 // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
12282 if (numBoundArgs
+ argc_
> JIT_ARGS_LENGTH_MAX
) {
12283 return AttachDecision::NoAction
;
12286 CallFlags
flags(isConstructing
, isSpread
);
12288 if (mode_
== ICState::Mode::Specialized
) {
12289 if (cx_
->realm() == target
->realm()) {
12290 flags
.setIsSameRealm();
12294 Rooted
<Shape
*> thisShape(cx_
);
12295 if (isConstructing
) {
12296 // Only optimize if newTarget == callee. This is the common case and ensures
12297 // we can always pass the bound function's target as newTarget.
12298 if (newTarget_
!= ObjectValue(*calleeObj
)) {
12299 return AttachDecision::NoAction
;
12302 if (mode_
== ICState::Mode::Specialized
) {
12303 Handle
<JSFunction
*> newTarget
= target
;
12304 switch (getThisShapeForScripted(target
, newTarget
, &thisShape
)) {
12305 case ScriptedThisResult::PlainObjectShape
:
12307 case ScriptedThisResult::UninitializedThis
:
12308 flags
.setNeedsUninitializedThis();
12310 case ScriptedThisResult::NoAction
:
12311 return AttachDecision::NoAction
;
12317 Int32OperandId
argcId(writer
.setInputOperandId(0));
12319 // Load the callee and ensure it's a bound function.
12320 ValOperandId calleeValId
=
12321 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12322 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12323 writer
.guardClass(calleeObjId
, GuardClassKind::BoundFunction
);
12325 // Ensure numBoundArgs matches.
12326 Int32OperandId numBoundArgsId
= writer
.loadBoundFunctionNumArgs(calleeObjId
);
12327 writer
.guardSpecificInt32(numBoundArgsId
, numBoundArgs
);
12329 if (isConstructing
) {
12330 // Guard newTarget == callee. We depend on this in CallBoundScriptedFunction
12331 // and in emitCallScriptedGuards by using boundTarget as newTarget.
12332 ValOperandId newTargetValId
=
12333 writer
.loadArgumentDynamicSlot(ArgumentKind::NewTarget
, argcId
, flags
);
12334 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
12335 writer
.guardObjectIdentity(newTargetObjId
, calleeObjId
);
12338 ObjOperandId targetId
= writer
.loadBoundFunctionTarget(calleeObjId
);
12340 emitCallScriptedGuards(targetId
, target
, argcId
, flags
, thisShape
,
12341 /* isBoundFunction = */ true);
12343 writer
.callBoundScriptedFunction(calleeObjId
, targetId
, argcId
, flags
,
12345 writer
.returnFromIC();
12347 trackAttached("Call.BoundFunction");
12348 return AttachDecision::Attach
;
12351 AttachDecision
CallIRGenerator::tryAttachStub() {
12352 AutoAssertNoPendingException
aanpe(cx_
);
12354 // Some opcodes are not yet supported.
12357 case JSOp::CallContent
:
12358 case JSOp::CallIgnoresRv
:
12359 case JSOp::CallIter
:
12360 case JSOp::CallContentIter
:
12361 case JSOp::SpreadCall
:
12363 case JSOp::NewContent
:
12364 case JSOp::SpreadNew
:
12365 case JSOp::SuperCall
:
12366 case JSOp::SpreadSuperCall
:
12369 return AttachDecision::NoAction
;
12372 MOZ_ASSERT(mode_
!= ICState::Mode::Generic
);
12374 // Ensure callee is a function.
12375 if (!callee_
.isObject()) {
12376 return AttachDecision::NoAction
;
12379 RootedObject
calleeObj(cx_
, &callee_
.toObject());
12380 if (calleeObj
->is
<BoundFunctionObject
>()) {
12381 TRY_ATTACH(tryAttachBoundFunction(calleeObj
.as
<BoundFunctionObject
>()));
12383 if (!calleeObj
->is
<JSFunction
>()) {
12384 return tryAttachCallHook(calleeObj
);
12387 HandleFunction calleeFunc
= calleeObj
.as
<JSFunction
>();
12389 // Check for scripted optimizations.
12390 if (calleeFunc
->hasJitEntry()) {
12391 return tryAttachCallScripted(calleeFunc
);
12394 // Check for native-function optimizations.
12395 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12397 // Try inlining Function.prototype.{call,apply}. We don't use the
12398 // InlinableNative mechanism for this because we want to optimize these more
12399 // aggressively than other natives.
12400 if (op_
== JSOp::Call
|| op_
== JSOp::CallContent
||
12401 op_
== JSOp::CallIgnoresRv
) {
12402 TRY_ATTACH(tryAttachFunCall(calleeFunc
));
12403 TRY_ATTACH(tryAttachFunApply(calleeFunc
));
12406 return tryAttachCallNative(calleeFunc
);
12409 void CallIRGenerator::trackAttached(const char* name
) {
12410 stubName_
= name
? name
: "NotAttached";
12411 #ifdef JS_CACHEIR_SPEW
12412 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12413 sp
.valueProperty("callee", callee_
);
12414 sp
.valueProperty("thisval", thisval_
);
12415 sp
.valueProperty("argc", Int32Value(argc_
));
12417 // Try to log the first two arguments.
12418 if (args_
.length() >= 1) {
12419 sp
.valueProperty("arg0", args_
[0]);
12421 if (args_
.length() >= 2) {
12422 sp
.valueProperty("arg1", args_
[1]);
12428 // Class which holds a shape pointer for use when caches might reference data in
12430 static const JSClass shapeContainerClass
= {"ShapeContainer",
12431 JSCLASS_HAS_RESERVED_SLOTS(1)};
12433 static const size_t SHAPE_CONTAINER_SLOT
= 0;
12435 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
12436 Handle
<NativeObject
*> obj
) {
12437 MOZ_ASSERT(cx
->compartment() != obj
->compartment());
12439 RootedObject
wrapper(cx
);
12441 AutoRealm
ar(cx
, obj
);
12442 wrapper
= NewBuiltinClassInstance(cx
, &shapeContainerClass
);
12446 wrapper
->as
<NativeObject
>().setReservedSlot(
12447 SHAPE_CONTAINER_SLOT
, PrivateGCThingValue(obj
->shape()));
12449 if (!JS_WrapObject(cx
, &wrapper
)) {
12452 MOZ_ASSERT(IsWrapper(wrapper
));
12456 void jit::LoadShapeWrapperContents(MacroAssembler
& masm
, Register obj
,
12457 Register dst
, Label
* failure
) {
12458 masm
.loadPtr(Address(obj
, ProxyObject::offsetOfReservedSlots()), dst
);
12459 Address
privateAddr(dst
,
12460 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
12461 masm
.fallibleUnboxObject(privateAddr
, dst
, failure
);
12462 masm
.unboxNonDouble(
12463 Address(dst
, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT
)), dst
,
12464 JSVAL_TYPE_PRIVATE_GCTHING
);
12467 static bool CanConvertToInt32ForToNumber(const Value
& v
) {
12468 return v
.isInt32() || v
.isBoolean() || v
.isNull();
12471 static Int32OperandId
EmitGuardToInt32ForToNumber(CacheIRWriter
& writer
,
12475 return writer
.guardToInt32(id
);
12478 writer
.guardIsNull(id
);
12479 return writer
.loadInt32Constant(0);
12481 MOZ_ASSERT(v
.isBoolean());
12482 return writer
.guardBooleanToInt32(id
);
12485 static bool CanConvertToDoubleForToNumber(const Value
& v
) {
12486 return v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
12489 static NumberOperandId
EmitGuardToDoubleForToNumber(CacheIRWriter
& writer
,
12492 if (v
.isNumber()) {
12493 return writer
.guardIsNumber(id
);
12495 if (v
.isBoolean()) {
12496 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
12497 return writer
.booleanToNumber(boolId
);
12500 writer
.guardIsNull(id
);
12501 return writer
.loadDoubleConstant(0.0);
12503 MOZ_ASSERT(v
.isUndefined());
12504 writer
.guardIsUndefined(id
);
12505 return writer
.loadDoubleConstant(JS::GenericNaN());
12508 CompareIRGenerator::CompareIRGenerator(JSContext
* cx
, HandleScript script
,
12509 jsbytecode
* pc
, ICState state
, JSOp op
,
12510 HandleValue lhsVal
, HandleValue rhsVal
)
12511 : IRGenerator(cx
, script
, pc
, CacheKind::Compare
, state
),
12516 AttachDecision
CompareIRGenerator::tryAttachString(ValOperandId lhsId
,
12517 ValOperandId rhsId
) {
12518 if (!lhsVal_
.isString() || !rhsVal_
.isString()) {
12519 return AttachDecision::NoAction
;
12522 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
12523 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
12524 writer
.compareStringResult(op_
, lhsStrId
, rhsStrId
);
12525 writer
.returnFromIC();
12527 trackAttached("Compare.String");
12528 return AttachDecision::Attach
;
12531 AttachDecision
CompareIRGenerator::tryAttachObject(ValOperandId lhsId
,
12532 ValOperandId rhsId
) {
12533 MOZ_ASSERT(IsEqualityOp(op_
));
12535 if (!lhsVal_
.isObject() || !rhsVal_
.isObject()) {
12536 return AttachDecision::NoAction
;
12539 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
12540 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
12541 writer
.compareObjectResult(op_
, lhsObjId
, rhsObjId
);
12542 writer
.returnFromIC();
12544 trackAttached("Compare.Object");
12545 return AttachDecision::Attach
;
12548 AttachDecision
CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId
,
12549 ValOperandId rhsId
) {
12550 MOZ_ASSERT(IsEqualityOp(op_
));
12552 if (!lhsVal_
.isSymbol() || !rhsVal_
.isSymbol()) {
12553 return AttachDecision::NoAction
;
12556 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
12557 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
12558 writer
.compareSymbolResult(op_
, lhsSymId
, rhsSymId
);
12559 writer
.returnFromIC();
12561 trackAttached("Compare.Symbol");
12562 return AttachDecision::Attach
;
12565 AttachDecision
CompareIRGenerator::tryAttachStrictDifferentTypes(
12566 ValOperandId lhsId
, ValOperandId rhsId
) {
12567 MOZ_ASSERT(IsEqualityOp(op_
));
12569 if (op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
) {
12570 return AttachDecision::NoAction
;
12573 // Probably can't hit some of these.
12574 if (SameType(lhsVal_
, rhsVal_
) ||
12575 (lhsVal_
.isNumber() && rhsVal_
.isNumber())) {
12576 return AttachDecision::NoAction
;
12580 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
12581 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
12582 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
12584 // Now that we've passed the guard, we know differing types, so return the
12586 writer
.loadBooleanResult(op_
== JSOp::StrictNe
? true : false);
12587 writer
.returnFromIC();
12589 trackAttached("Compare.StrictDifferentTypes");
12590 return AttachDecision::Attach
;
12593 AttachDecision
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId
,
12594 ValOperandId rhsId
) {
12595 if (!CanConvertToInt32ForToNumber(lhsVal_
) ||
12596 !CanConvertToInt32ForToNumber(rhsVal_
)) {
12597 return AttachDecision::NoAction
;
12600 // Strictly different types should have been handed by
12601 // tryAttachStrictDifferentTypes.
12602 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12603 lhsVal_
.type() == rhsVal_
.type());
12605 // Should have been handled by tryAttachAnyNullUndefined.
12606 MOZ_ASSERT_IF(lhsVal_
.isNull() || rhsVal_
.isNull(), !IsEqualityOp(op_
));
12608 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12609 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12611 writer
.compareInt32Result(op_
, lhsIntId
, rhsIntId
);
12612 writer
.returnFromIC();
12614 trackAttached("Compare.Int32");
12615 return AttachDecision::Attach
;
12618 AttachDecision
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId
,
12619 ValOperandId rhsId
) {
12620 if (!CanConvertToDoubleForToNumber(lhsVal_
) ||
12621 !CanConvertToDoubleForToNumber(rhsVal_
)) {
12622 return AttachDecision::NoAction
;
12625 // Strictly different types should have been handed by
12626 // tryAttachStrictDifferentTypes.
12627 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12628 lhsVal_
.type() == rhsVal_
.type() ||
12629 (lhsVal_
.isNumber() && rhsVal_
.isNumber()));
12631 // Should have been handled by tryAttachAnyNullUndefined.
12632 MOZ_ASSERT_IF(lhsVal_
.isNullOrUndefined() || rhsVal_
.isNullOrUndefined(),
12633 !IsEqualityOp(op_
));
12635 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12636 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12637 writer
.compareDoubleResult(op_
, lhs
, rhs
);
12638 writer
.returnFromIC();
12640 trackAttached("Compare.Number");
12641 return AttachDecision::Attach
;
12644 AttachDecision
CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId
,
12645 ValOperandId rhsId
) {
12646 if (!lhsVal_
.isBigInt() || !rhsVal_
.isBigInt()) {
12647 return AttachDecision::NoAction
;
12650 BigIntOperandId lhs
= writer
.guardToBigInt(lhsId
);
12651 BigIntOperandId rhs
= writer
.guardToBigInt(rhsId
);
12653 writer
.compareBigIntResult(op_
, lhs
, rhs
);
12654 writer
.returnFromIC();
12656 trackAttached("Compare.BigInt");
12657 return AttachDecision::Attach
;
12660 AttachDecision
CompareIRGenerator::tryAttachAnyNullUndefined(
12661 ValOperandId lhsId
, ValOperandId rhsId
) {
12662 MOZ_ASSERT(IsEqualityOp(op_
));
12664 // Either RHS or LHS needs to be null/undefined.
12665 if (!lhsVal_
.isNullOrUndefined() && !rhsVal_
.isNullOrUndefined()) {
12666 return AttachDecision::NoAction
;
12669 // We assume that the side with null/undefined is usually constant, in
12670 // code like `if (x === undefined) { x = {}; }`.
12671 // That is why we don't attach when both sides are undefined/null,
12672 // because we would basically need to decide by chance which side is
12673 // the likely constant.
12674 // The actual generated code however handles null/undefined of course.
12675 if (lhsVal_
.isNullOrUndefined() && rhsVal_
.isNullOrUndefined()) {
12676 return AttachDecision::NoAction
;
12679 if (rhsVal_
.isNullOrUndefined()) {
12680 if (rhsVal_
.isNull()) {
12681 writer
.guardIsNull(rhsId
);
12682 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, lhsId
);
12683 trackAttached("Compare.AnyNull");
12685 writer
.guardIsUndefined(rhsId
);
12686 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, lhsId
);
12687 trackAttached("Compare.AnyUndefined");
12690 if (lhsVal_
.isNull()) {
12691 writer
.guardIsNull(lhsId
);
12692 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, rhsId
);
12693 trackAttached("Compare.NullAny");
12695 writer
.guardIsUndefined(lhsId
);
12696 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, rhsId
);
12697 trackAttached("Compare.UndefinedAny");
12701 writer
.returnFromIC();
12702 return AttachDecision::Attach
;
12705 // Handle {null/undefined} x {null,undefined} equality comparisons
12706 AttachDecision
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId
,
12707 ValOperandId rhsId
) {
12708 if (!lhsVal_
.isNullOrUndefined() || !rhsVal_
.isNullOrUndefined()) {
12709 return AttachDecision::NoAction
;
12712 if (op_
== JSOp::Eq
|| op_
== JSOp::Ne
) {
12713 writer
.guardIsNullOrUndefined(lhsId
);
12714 writer
.guardIsNullOrUndefined(rhsId
);
12715 // Sloppy equality means we actually only care about the op:
12716 writer
.loadBooleanResult(op_
== JSOp::Eq
);
12717 trackAttached("Compare.SloppyNullUndefined");
12719 // Strict equality only hits this branch, and only in the
12720 // undef {!,=}== undef and null {!,=}== null cases.
12721 // The other cases should have hit tryAttachStrictDifferentTypes.
12722 MOZ_ASSERT(lhsVal_
.isNull() == rhsVal_
.isNull());
12723 lhsVal_
.isNull() ? writer
.guardIsNull(lhsId
)
12724 : writer
.guardIsUndefined(lhsId
);
12725 rhsVal_
.isNull() ? writer
.guardIsNull(rhsId
)
12726 : writer
.guardIsUndefined(rhsId
);
12727 writer
.loadBooleanResult(op_
== JSOp::StrictEq
);
12728 trackAttached("Compare.StrictNullUndefinedEquality");
12731 writer
.returnFromIC();
12732 return AttachDecision::Attach
;
12735 AttachDecision
CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId
,
12736 ValOperandId rhsId
) {
12737 // Ensure String x {Number, Boolean, Null, Undefined}
12738 if (!(lhsVal_
.isString() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12739 !(rhsVal_
.isString() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12740 return AttachDecision::NoAction
;
12743 // Case should have been handled by tryAttachStrictDifferentTypes
12744 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12746 auto createGuards
= [&](const Value
& v
, ValOperandId vId
) {
12747 if (v
.isString()) {
12748 StringOperandId strId
= writer
.guardToString(vId
);
12749 return writer
.guardStringToNumber(strId
);
12751 return EmitGuardToDoubleForToNumber(writer
, vId
, v
);
12754 NumberOperandId lhsGuardedId
= createGuards(lhsVal_
, lhsId
);
12755 NumberOperandId rhsGuardedId
= createGuards(rhsVal_
, rhsId
);
12756 writer
.compareDoubleResult(op_
, lhsGuardedId
, rhsGuardedId
);
12757 writer
.returnFromIC();
12759 trackAttached("Compare.StringNumber");
12760 return AttachDecision::Attach
;
12763 AttachDecision
CompareIRGenerator::tryAttachPrimitiveSymbol(
12764 ValOperandId lhsId
, ValOperandId rhsId
) {
12765 MOZ_ASSERT(IsEqualityOp(op_
));
12767 // The set of primitive cases we want to handle here (excluding null,
12768 // undefined, and symbol)
12769 auto isPrimitive
= [](const Value
& x
) {
12770 return x
.isString() || x
.isBoolean() || x
.isNumber() || x
.isBigInt();
12773 // Ensure Symbol x {String, Bool, Number, BigInt}.
12774 if (!(lhsVal_
.isSymbol() && isPrimitive(rhsVal_
)) &&
12775 !(rhsVal_
.isSymbol() && isPrimitive(lhsVal_
))) {
12776 return AttachDecision::NoAction
;
12779 auto guardPrimitive
= [&](const Value
& v
, ValOperandId id
) {
12780 MOZ_ASSERT(isPrimitive(v
));
12781 if (v
.isNumber()) {
12782 writer
.guardIsNumber(id
);
12785 switch (v
.extractNonDoubleType()) {
12786 case JSVAL_TYPE_STRING
:
12787 writer
.guardToString(id
);
12789 case JSVAL_TYPE_BOOLEAN
:
12790 writer
.guardToBoolean(id
);
12792 case JSVAL_TYPE_BIGINT
:
12793 writer
.guardToBigInt(id
);
12796 MOZ_CRASH("unexpected type");
12801 if (lhsVal_
.isSymbol()) {
12802 writer
.guardToSymbol(lhsId
);
12803 guardPrimitive(rhsVal_
, rhsId
);
12805 guardPrimitive(lhsVal_
, lhsId
);
12806 writer
.guardToSymbol(rhsId
);
12809 // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
12810 // always be false for other compare ops.
12811 writer
.loadBooleanResult(op_
== JSOp::Ne
|| op_
== JSOp::StrictNe
);
12812 writer
.returnFromIC();
12814 trackAttached("Compare.PrimitiveSymbol");
12815 return AttachDecision::Attach
;
12818 AttachDecision
CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId
,
12819 ValOperandId rhsId
) {
12820 // Ensure BigInt x {Int32, Boolean, Null}.
12821 if (!(lhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(rhsVal_
)) &&
12822 !(rhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(lhsVal_
))) {
12823 return AttachDecision::NoAction
;
12826 // Case should have been handled by tryAttachStrictDifferentTypes
12827 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12829 if (lhsVal_
.isBigInt()) {
12830 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12831 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12833 writer
.compareBigIntInt32Result(op_
, bigIntId
, intId
);
12835 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12836 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12838 writer
.compareBigIntInt32Result(ReverseCompareOp(op_
), bigIntId
, intId
);
12840 writer
.returnFromIC();
12842 trackAttached("Compare.BigIntInt32");
12843 return AttachDecision::Attach
;
12846 AttachDecision
CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId
,
12847 ValOperandId rhsId
) {
12848 // Ensure BigInt x {Number, Undefined}.
12849 if (!(lhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12850 !(rhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12851 return AttachDecision::NoAction
;
12854 // Case should have been handled by tryAttachStrictDifferentTypes
12855 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12857 // Case should have been handled by tryAttachBigIntInt32.
12858 MOZ_ASSERT(!CanConvertToInt32ForToNumber(lhsVal_
));
12859 MOZ_ASSERT(!CanConvertToInt32ForToNumber(rhsVal_
));
12861 if (lhsVal_
.isBigInt()) {
12862 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12863 NumberOperandId numId
=
12864 EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12866 writer
.compareBigIntNumberResult(op_
, bigIntId
, numId
);
12868 NumberOperandId numId
=
12869 EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12870 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12872 writer
.compareBigIntNumberResult(ReverseCompareOp(op_
), bigIntId
, numId
);
12874 writer
.returnFromIC();
12876 trackAttached("Compare.BigIntNumber");
12877 return AttachDecision::Attach
;
12880 AttachDecision
CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId
,
12881 ValOperandId rhsId
) {
12882 // Ensure BigInt x String.
12883 if (!(lhsVal_
.isBigInt() && rhsVal_
.isString()) &&
12884 !(rhsVal_
.isBigInt() && lhsVal_
.isString())) {
12885 return AttachDecision::NoAction
;
12888 // Case should have been handled by tryAttachStrictDifferentTypes
12889 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12891 if (lhsVal_
.isBigInt()) {
12892 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
12893 StringOperandId strId
= writer
.guardToString(rhsId
);
12895 writer
.compareBigIntStringResult(op_
, bigIntId
, strId
);
12897 StringOperandId strId
= writer
.guardToString(lhsId
);
12898 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
12900 writer
.compareBigIntStringResult(ReverseCompareOp(op_
), bigIntId
, strId
);
12902 writer
.returnFromIC();
12904 trackAttached("Compare.BigIntString");
12905 return AttachDecision::Attach
;
12908 AttachDecision
CompareIRGenerator::tryAttachStub() {
12909 MOZ_ASSERT(cacheKind_
== CacheKind::Compare
);
12910 MOZ_ASSERT(IsEqualityOp(op_
) || IsRelationalOp(op_
));
12912 AutoAssertNoPendingException
aanpe(cx_
);
12914 constexpr uint8_t lhsIndex
= 0;
12915 constexpr uint8_t rhsIndex
= 1;
12917 ValOperandId
lhsId(writer
.setInputOperandId(lhsIndex
));
12918 ValOperandId
rhsId(writer
.setInputOperandId(rhsIndex
));
12920 // For sloppy equality ops, there are cases this IC does not handle:
12921 // - {Object} x {String, Symbol, Bool, Number, BigInt}.
12923 // For relational comparison ops, these cases aren't handled:
12924 // - Object x {String, Bool, Number, BigInt, Object, Null, Undefined}.
12925 // Note: |Symbol x any| always throws, so it doesn't need to be handled.
12927 // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
12928 // already present.)
12930 if (IsEqualityOp(op_
)) {
12931 TRY_ATTACH(tryAttachObject(lhsId
, rhsId
));
12932 TRY_ATTACH(tryAttachSymbol(lhsId
, rhsId
));
12934 // Handles any (non null or undefined) comparison with null/undefined.
12935 TRY_ATTACH(tryAttachAnyNullUndefined(lhsId
, rhsId
));
12937 // This covers -strict- equality/inequality using a type tag check, so
12938 // catches all different type pairs outside of Numbers, which cannot be
12939 // checked on tags alone.
12940 TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId
, rhsId
));
12942 TRY_ATTACH(tryAttachNullUndefined(lhsId
, rhsId
));
12944 TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId
, rhsId
));
12947 // We want these to be last, to allow us to bypass the
12948 // strictly-different-types cases in the below attachment code
12949 TRY_ATTACH(tryAttachInt32(lhsId
, rhsId
));
12950 TRY_ATTACH(tryAttachNumber(lhsId
, rhsId
));
12951 TRY_ATTACH(tryAttachBigInt(lhsId
, rhsId
));
12952 TRY_ATTACH(tryAttachString(lhsId
, rhsId
));
12954 TRY_ATTACH(tryAttachStringNumber(lhsId
, rhsId
));
12956 TRY_ATTACH(tryAttachBigIntInt32(lhsId
, rhsId
));
12957 TRY_ATTACH(tryAttachBigIntNumber(lhsId
, rhsId
));
12958 TRY_ATTACH(tryAttachBigIntString(lhsId
, rhsId
));
12960 // Strict equality is always supported.
12961 MOZ_ASSERT(!IsStrictEqualityOp(op_
));
12963 // Other operations are unsupported iff at least one operand is an object.
12964 MOZ_ASSERT(lhsVal_
.isObject() || rhsVal_
.isObject());
12966 trackAttached(IRGenerator::NotAttached
);
12967 return AttachDecision::NoAction
;
12970 void CompareIRGenerator::trackAttached(const char* name
) {
12971 stubName_
= name
? name
: "NotAttached";
12972 #ifdef JS_CACHEIR_SPEW
12973 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12974 sp
.valueProperty("lhs", lhsVal_
);
12975 sp
.valueProperty("rhs", rhsVal_
);
12976 sp
.opcodeProperty("op", op_
);
12981 ToBoolIRGenerator::ToBoolIRGenerator(JSContext
* cx
, HandleScript script
,
12982 jsbytecode
* pc
, ICState state
,
12984 : IRGenerator(cx
, script
, pc
, CacheKind::ToBool
, state
), val_(val
) {}
12986 void ToBoolIRGenerator::trackAttached(const char* name
) {
12987 stubName_
= name
? name
: "NotAttached";
12988 #ifdef JS_CACHEIR_SPEW
12989 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12990 sp
.valueProperty("val", val_
);
12995 AttachDecision
ToBoolIRGenerator::tryAttachStub() {
12996 AutoAssertNoPendingException
aanpe(cx_
);
12997 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
12999 TRY_ATTACH(tryAttachBool());
13000 TRY_ATTACH(tryAttachInt32());
13001 TRY_ATTACH(tryAttachNumber());
13002 TRY_ATTACH(tryAttachString());
13003 TRY_ATTACH(tryAttachNullOrUndefined());
13004 TRY_ATTACH(tryAttachObject());
13005 TRY_ATTACH(tryAttachSymbol());
13006 TRY_ATTACH(tryAttachBigInt());
13008 trackAttached(IRGenerator::NotAttached
);
13009 return AttachDecision::NoAction
;
13012 AttachDecision
ToBoolIRGenerator::tryAttachBool() {
13013 if (!val_
.isBoolean()) {
13014 return AttachDecision::NoAction
;
13017 ValOperandId
valId(writer
.setInputOperandId(0));
13018 writer
.guardNonDoubleType(valId
, ValueType::Boolean
);
13019 writer
.loadOperandResult(valId
);
13020 writer
.returnFromIC();
13021 trackAttached("ToBool.Bool");
13022 return AttachDecision::Attach
;
13025 AttachDecision
ToBoolIRGenerator::tryAttachInt32() {
13026 if (!val_
.isInt32()) {
13027 return AttachDecision::NoAction
;
13030 ValOperandId
valId(writer
.setInputOperandId(0));
13031 writer
.guardNonDoubleType(valId
, ValueType::Int32
);
13032 writer
.loadInt32TruthyResult(valId
);
13033 writer
.returnFromIC();
13034 trackAttached("ToBool.Int32");
13035 return AttachDecision::Attach
;
13038 AttachDecision
ToBoolIRGenerator::tryAttachNumber() {
13039 if (!val_
.isNumber()) {
13040 return AttachDecision::NoAction
;
13043 ValOperandId
valId(writer
.setInputOperandId(0));
13044 NumberOperandId numId
= writer
.guardIsNumber(valId
);
13045 writer
.loadDoubleTruthyResult(numId
);
13046 writer
.returnFromIC();
13047 trackAttached("ToBool.Number");
13048 return AttachDecision::Attach
;
13051 AttachDecision
ToBoolIRGenerator::tryAttachSymbol() {
13052 if (!val_
.isSymbol()) {
13053 return AttachDecision::NoAction
;
13056 ValOperandId
valId(writer
.setInputOperandId(0));
13057 writer
.guardNonDoubleType(valId
, ValueType::Symbol
);
13058 writer
.loadBooleanResult(true);
13059 writer
.returnFromIC();
13060 trackAttached("ToBool.Symbol");
13061 return AttachDecision::Attach
;
13064 AttachDecision
ToBoolIRGenerator::tryAttachString() {
13065 if (!val_
.isString()) {
13066 return AttachDecision::NoAction
;
13069 ValOperandId
valId(writer
.setInputOperandId(0));
13070 StringOperandId strId
= writer
.guardToString(valId
);
13071 writer
.loadStringTruthyResult(strId
);
13072 writer
.returnFromIC();
13073 trackAttached("ToBool.String");
13074 return AttachDecision::Attach
;
13077 AttachDecision
ToBoolIRGenerator::tryAttachNullOrUndefined() {
13078 if (!val_
.isNullOrUndefined()) {
13079 return AttachDecision::NoAction
;
13082 ValOperandId
valId(writer
.setInputOperandId(0));
13083 writer
.guardIsNullOrUndefined(valId
);
13084 writer
.loadBooleanResult(false);
13085 writer
.returnFromIC();
13086 trackAttached("ToBool.NullOrUndefined");
13087 return AttachDecision::Attach
;
13090 AttachDecision
ToBoolIRGenerator::tryAttachObject() {
13091 if (!val_
.isObject()) {
13092 return AttachDecision::NoAction
;
13095 ValOperandId
valId(writer
.setInputOperandId(0));
13096 ObjOperandId objId
= writer
.guardToObject(valId
);
13097 writer
.loadObjectTruthyResult(objId
);
13098 writer
.returnFromIC();
13099 trackAttached("ToBool.Object");
13100 return AttachDecision::Attach
;
13103 AttachDecision
ToBoolIRGenerator::tryAttachBigInt() {
13104 if (!val_
.isBigInt()) {
13105 return AttachDecision::NoAction
;
13108 ValOperandId
valId(writer
.setInputOperandId(0));
13109 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13110 writer
.loadBigIntTruthyResult(bigIntId
);
13111 writer
.returnFromIC();
13112 trackAttached("ToBool.BigInt");
13113 return AttachDecision::Attach
;
13116 GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext
* cx
,
13117 HandleScript script
,
13118 jsbytecode
* pc
, ICState state
,
13120 : IRGenerator(cx
, script
, pc
, CacheKind::GetIntrinsic
, state
), val_(val
) {}
13122 void GetIntrinsicIRGenerator::trackAttached(const char* name
) {
13123 stubName_
= name
? name
: "NotAttached";
13124 #ifdef JS_CACHEIR_SPEW
13125 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13126 sp
.valueProperty("val", val_
);
13131 AttachDecision
GetIntrinsicIRGenerator::tryAttachStub() {
13132 AutoAssertNoPendingException
aanpe(cx_
);
13133 writer
.loadValueResult(val_
);
13134 writer
.returnFromIC();
13135 trackAttached("GetIntrinsic");
13136 return AttachDecision::Attach
;
13139 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext
* cx
, HandleScript script
,
13140 jsbytecode
* pc
, ICState state
,
13141 JSOp op
, HandleValue val
,
13143 : IRGenerator(cx
, script
, pc
, CacheKind::UnaryArith
, state
),
13148 void UnaryArithIRGenerator::trackAttached(const char* name
) {
13149 stubName_
= name
? name
: "NotAttached";
13150 #ifdef JS_CACHEIR_SPEW
13151 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13152 sp
.valueProperty("val", val_
);
13153 sp
.valueProperty("res", res_
);
13158 AttachDecision
UnaryArithIRGenerator::tryAttachStub() {
13159 AutoAssertNoPendingException
aanpe(cx_
);
13160 TRY_ATTACH(tryAttachInt32());
13161 TRY_ATTACH(tryAttachNumber());
13162 TRY_ATTACH(tryAttachBitwise());
13163 TRY_ATTACH(tryAttachBigInt());
13164 TRY_ATTACH(tryAttachStringInt32());
13165 TRY_ATTACH(tryAttachStringNumber());
13167 trackAttached(IRGenerator::NotAttached
);
13168 return AttachDecision::NoAction
;
13171 AttachDecision
UnaryArithIRGenerator::tryAttachInt32() {
13172 if (op_
== JSOp::BitNot
) {
13173 return AttachDecision::NoAction
;
13175 if (!CanConvertToInt32ForToNumber(val_
) || !res_
.isInt32()) {
13176 return AttachDecision::NoAction
;
13179 ValOperandId
valId(writer
.setInputOperandId(0));
13181 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, valId
, val_
);
13184 writer
.loadInt32Result(intId
);
13185 trackAttached("UnaryArith.Int32Pos");
13188 writer
.int32NegationResult(intId
);
13189 trackAttached("UnaryArith.Int32Neg");
13192 writer
.int32IncResult(intId
);
13193 trackAttached("UnaryArith.Int32Inc");
13196 writer
.int32DecResult(intId
);
13197 trackAttached("UnaryArith.Int32Dec");
13199 case JSOp::ToNumeric
:
13200 writer
.loadInt32Result(intId
);
13201 trackAttached("UnaryArith.Int32ToNumeric");
13204 MOZ_CRASH("unexpected OP");
13207 writer
.returnFromIC();
13208 return AttachDecision::Attach
;
13211 AttachDecision
UnaryArithIRGenerator::tryAttachNumber() {
13212 if (op_
== JSOp::BitNot
) {
13213 return AttachDecision::NoAction
;
13215 if (!CanConvertToDoubleForToNumber(val_
)) {
13216 return AttachDecision::NoAction
;
13218 MOZ_ASSERT(res_
.isNumber());
13220 ValOperandId
valId(writer
.setInputOperandId(0));
13221 NumberOperandId numId
= EmitGuardToDoubleForToNumber(writer
, valId
, val_
);
13225 writer
.loadDoubleResult(numId
);
13226 trackAttached("UnaryArith.DoublePos");
13229 writer
.doubleNegationResult(numId
);
13230 trackAttached("UnaryArith.DoubleNeg");
13233 writer
.doubleIncResult(numId
);
13234 trackAttached("UnaryArith.DoubleInc");
13237 writer
.doubleDecResult(numId
);
13238 trackAttached("UnaryArith.DoubleDec");
13240 case JSOp::ToNumeric
:
13241 writer
.loadDoubleResult(numId
);
13242 trackAttached("UnaryArith.DoubleToNumeric");
13245 MOZ_CRASH("Unexpected OP");
13248 writer
.returnFromIC();
13249 return AttachDecision::Attach
;
13252 static bool CanTruncateToInt32(const Value
& val
) {
13253 return val
.isNumber() || val
.isBoolean() || val
.isNullOrUndefined() ||
13257 // Convert type into int32 for the bitwise/shift operands.
13258 static Int32OperandId
EmitTruncateToInt32Guard(CacheIRWriter
& writer
,
13260 const Value
& val
) {
13261 MOZ_ASSERT(CanTruncateToInt32(val
));
13262 if (val
.isInt32()) {
13263 return writer
.guardToInt32(id
);
13265 if (val
.isBoolean()) {
13266 return writer
.guardBooleanToInt32(id
);
13268 if (val
.isNullOrUndefined()) {
13269 writer
.guardIsNullOrUndefined(id
);
13270 return writer
.loadInt32Constant(0);
13272 NumberOperandId numId
;
13273 if (val
.isString()) {
13274 StringOperandId strId
= writer
.guardToString(id
);
13275 numId
= writer
.guardStringToNumber(strId
);
13277 MOZ_ASSERT(val
.isDouble());
13278 numId
= writer
.guardIsNumber(id
);
13280 return writer
.truncateDoubleToUInt32(numId
);
13283 AttachDecision
UnaryArithIRGenerator::tryAttachBitwise() {
13284 // Only bitwise operators.
13285 if (op_
!= JSOp::BitNot
) {
13286 return AttachDecision::NoAction
;
13289 // Check guard conditions
13290 if (!CanTruncateToInt32(val_
)) {
13291 return AttachDecision::NoAction
;
13294 // Bitwise operators always produce Int32 values.
13295 MOZ_ASSERT(res_
.isInt32());
13297 ValOperandId
valId(writer
.setInputOperandId(0));
13298 Int32OperandId intId
= EmitTruncateToInt32Guard(writer
, valId
, val_
);
13299 writer
.int32NotResult(intId
);
13300 trackAttached("UnaryArith.BitwiseBitNot");
13302 writer
.returnFromIC();
13303 return AttachDecision::Attach
;
13306 AttachDecision
UnaryArithIRGenerator::tryAttachBigInt() {
13307 if (!val_
.isBigInt()) {
13308 return AttachDecision::NoAction
;
13310 MOZ_ASSERT(res_
.isBigInt());
13312 MOZ_ASSERT(op_
!= JSOp::Pos
,
13313 "Applying the unary + operator on BigInt values throws an error");
13315 ValOperandId
valId(writer
.setInputOperandId(0));
13316 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13319 writer
.bigIntNotResult(bigIntId
);
13320 trackAttached("UnaryArith.BigIntNot");
13323 writer
.bigIntNegationResult(bigIntId
);
13324 trackAttached("UnaryArith.BigIntNeg");
13327 writer
.bigIntIncResult(bigIntId
);
13328 trackAttached("UnaryArith.BigIntInc");
13331 writer
.bigIntDecResult(bigIntId
);
13332 trackAttached("UnaryArith.BigIntDec");
13334 case JSOp::ToNumeric
:
13335 writer
.loadBigIntResult(bigIntId
);
13336 trackAttached("UnaryArith.BigIntToNumeric");
13339 MOZ_CRASH("Unexpected OP");
13342 writer
.returnFromIC();
13343 return AttachDecision::Attach
;
13346 AttachDecision
UnaryArithIRGenerator::tryAttachStringInt32() {
13347 if (!val_
.isString()) {
13348 return AttachDecision::NoAction
;
13350 MOZ_ASSERT(res_
.isNumber());
13352 // Case should have been handled by tryAttachBitwise.
13353 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13355 if (!res_
.isInt32()) {
13356 return AttachDecision::NoAction
;
13359 ValOperandId
valId(writer
.setInputOperandId(0));
13360 StringOperandId stringId
= writer
.guardToString(valId
);
13361 Int32OperandId intId
= writer
.guardStringToInt32(stringId
);
13365 writer
.loadInt32Result(intId
);
13366 trackAttached("UnaryArith.StringInt32Pos");
13369 writer
.int32NegationResult(intId
);
13370 trackAttached("UnaryArith.StringInt32Neg");
13373 writer
.int32IncResult(intId
);
13374 trackAttached("UnaryArith.StringInt32Inc");
13377 writer
.int32DecResult(intId
);
13378 trackAttached("UnaryArith.StringInt32Dec");
13380 case JSOp::ToNumeric
:
13381 writer
.loadInt32Result(intId
);
13382 trackAttached("UnaryArith.StringInt32ToNumeric");
13385 MOZ_CRASH("Unexpected OP");
13388 writer
.returnFromIC();
13389 return AttachDecision::Attach
;
13392 AttachDecision
UnaryArithIRGenerator::tryAttachStringNumber() {
13393 if (!val_
.isString()) {
13394 return AttachDecision::NoAction
;
13396 MOZ_ASSERT(res_
.isNumber());
13398 // Case should have been handled by tryAttachBitwise.
13399 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13401 ValOperandId
valId(writer
.setInputOperandId(0));
13402 StringOperandId stringId
= writer
.guardToString(valId
);
13403 NumberOperandId numId
= writer
.guardStringToNumber(stringId
);
13405 Int32OperandId truncatedId
;
13408 writer
.loadDoubleResult(numId
);
13409 trackAttached("UnaryArith.StringNumberPos");
13412 writer
.doubleNegationResult(numId
);
13413 trackAttached("UnaryArith.StringNumberNeg");
13416 writer
.doubleIncResult(numId
);
13417 trackAttached("UnaryArith.StringNumberInc");
13420 writer
.doubleDecResult(numId
);
13421 trackAttached("UnaryArith.StringNumberDec");
13423 case JSOp::ToNumeric
:
13424 writer
.loadDoubleResult(numId
);
13425 trackAttached("UnaryArith.StringNumberToNumeric");
13428 MOZ_CRASH("Unexpected OP");
13431 writer
.returnFromIC();
13432 return AttachDecision::Attach
;
13435 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext
* cx
,
13436 HandleScript script
,
13440 : IRGenerator(cx
, script
, pc
, CacheKind::ToPropertyKey
, state
), val_(val
) {}
13442 void ToPropertyKeyIRGenerator::trackAttached(const char* name
) {
13443 stubName_
= name
? name
: "NotAttached";
13444 #ifdef JS_CACHEIR_SPEW
13445 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13446 sp
.valueProperty("val", val_
);
13451 AttachDecision
ToPropertyKeyIRGenerator::tryAttachStub() {
13452 AutoAssertNoPendingException
aanpe(cx_
);
13453 TRY_ATTACH(tryAttachInt32());
13454 TRY_ATTACH(tryAttachNumber());
13455 TRY_ATTACH(tryAttachString());
13456 TRY_ATTACH(tryAttachSymbol());
13458 trackAttached(IRGenerator::NotAttached
);
13459 return AttachDecision::NoAction
;
13462 AttachDecision
ToPropertyKeyIRGenerator::tryAttachInt32() {
13463 if (!val_
.isInt32()) {
13464 return AttachDecision::NoAction
;
13467 ValOperandId
valId(writer
.setInputOperandId(0));
13469 Int32OperandId intId
= writer
.guardToInt32(valId
);
13470 writer
.loadInt32Result(intId
);
13471 writer
.returnFromIC();
13473 trackAttached("ToPropertyKey.Int32");
13474 return AttachDecision::Attach
;
13477 AttachDecision
ToPropertyKeyIRGenerator::tryAttachNumber() {
13478 if (!val_
.isNumber()) {
13479 return AttachDecision::NoAction
;
13482 // We allow negative zero here because ToPropertyKey(-0.0) is 0.
13484 if (!mozilla::NumberEqualsInt32(val_
.toNumber(), &unused
)) {
13485 return AttachDecision::NoAction
;
13488 ValOperandId
valId(writer
.setInputOperandId(0));
13490 Int32OperandId intId
= writer
.guardToInt32Index(valId
);
13491 writer
.loadInt32Result(intId
);
13492 writer
.returnFromIC();
13494 trackAttached("ToPropertyKey.Number");
13495 return AttachDecision::Attach
;
13498 AttachDecision
ToPropertyKeyIRGenerator::tryAttachString() {
13499 if (!val_
.isString()) {
13500 return AttachDecision::NoAction
;
13503 ValOperandId
valId(writer
.setInputOperandId(0));
13505 StringOperandId strId
= writer
.guardToString(valId
);
13506 writer
.loadStringResult(strId
);
13507 writer
.returnFromIC();
13509 trackAttached("ToPropertyKey.String");
13510 return AttachDecision::Attach
;
13513 AttachDecision
ToPropertyKeyIRGenerator::tryAttachSymbol() {
13514 if (!val_
.isSymbol()) {
13515 return AttachDecision::NoAction
;
13518 ValOperandId
valId(writer
.setInputOperandId(0));
13520 SymbolOperandId strId
= writer
.guardToSymbol(valId
);
13521 writer
.loadSymbolResult(strId
);
13522 writer
.returnFromIC();
13524 trackAttached("ToPropertyKey.Symbol");
13525 return AttachDecision::Attach
;
13528 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext
* cx
,
13529 HandleScript script
,
13530 jsbytecode
* pc
, ICState state
,
13531 JSOp op
, HandleValue lhs
,
13532 HandleValue rhs
, HandleValue res
)
13533 : IRGenerator(cx
, script
, pc
, CacheKind::BinaryArith
, state
),
13539 void BinaryArithIRGenerator::trackAttached(const char* name
) {
13540 stubName_
= name
? name
: "NotAttached";
13541 #ifdef JS_CACHEIR_SPEW
13542 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13543 sp
.opcodeProperty("op", op_
);
13544 sp
.valueProperty("rhs", rhs_
);
13545 sp
.valueProperty("lhs", lhs_
);
13550 AttachDecision
BinaryArithIRGenerator::tryAttachStub() {
13551 AutoAssertNoPendingException
aanpe(cx_
);
13552 // Arithmetic operations with Int32 operands
13553 TRY_ATTACH(tryAttachInt32());
13555 // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
13557 TRY_ATTACH(tryAttachBitwise());
13559 // Arithmetic operations with Double operands. This needs to come after
13560 // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
13561 // more specialized Int32 IC if it is possible.
13562 TRY_ATTACH(tryAttachDouble());
13564 // String x {String,Number,Boolean,Null,Undefined}
13565 TRY_ATTACH(tryAttachStringConcat());
13568 TRY_ATTACH(tryAttachStringObjectConcat());
13570 // Arithmetic operations or bitwise operations with BigInt operands
13571 TRY_ATTACH(tryAttachBigInt());
13573 // Arithmetic operations (without addition) with String x Int32.
13574 TRY_ATTACH(tryAttachStringInt32Arith());
13576 // Arithmetic operations (without addition) with String x Number. This needs
13577 // to come after tryAttachStringInt32Arith, as the guards overlap, and we'd
13578 // prefer to attach the more specialized Int32 IC if it is possible.
13579 TRY_ATTACH(tryAttachStringNumberArith());
13581 trackAttached(IRGenerator::NotAttached
);
13582 return AttachDecision::NoAction
;
13585 AttachDecision
BinaryArithIRGenerator::tryAttachBitwise() {
13586 // Only bit-wise and shifts.
13587 if (op_
!= JSOp::BitOr
&& op_
!= JSOp::BitXor
&& op_
!= JSOp::BitAnd
&&
13588 op_
!= JSOp::Lsh
&& op_
!= JSOp::Rsh
&& op_
!= JSOp::Ursh
) {
13589 return AttachDecision::NoAction
;
13592 // Check guard conditions
13593 if (!CanTruncateToInt32(lhs_
) || !CanTruncateToInt32(rhs_
)) {
13594 return AttachDecision::NoAction
;
13597 // All ops, with the exception of Ursh, produce Int32 values.
13598 MOZ_ASSERT_IF(op_
!= JSOp::Ursh
, res_
.isInt32());
13600 ValOperandId
lhsId(writer
.setInputOperandId(0));
13601 ValOperandId
rhsId(writer
.setInputOperandId(1));
13603 Int32OperandId lhsIntId
= EmitTruncateToInt32Guard(writer
, lhsId
, lhs_
);
13604 Int32OperandId rhsIntId
= EmitTruncateToInt32Guard(writer
, rhsId
, rhs_
);
13608 writer
.int32BitOrResult(lhsIntId
, rhsIntId
);
13609 trackAttached("BinaryArith.BitwiseBitOr");
13612 writer
.int32BitXorResult(lhsIntId
, rhsIntId
);
13613 trackAttached("BinaryArith.BitwiseBitXor");
13616 writer
.int32BitAndResult(lhsIntId
, rhsIntId
);
13617 trackAttached("BinaryArith.BitwiseBitAnd");
13620 writer
.int32LeftShiftResult(lhsIntId
, rhsIntId
);
13621 trackAttached("BinaryArith.BitwiseLeftShift");
13624 writer
.int32RightShiftResult(lhsIntId
, rhsIntId
);
13625 trackAttached("BinaryArith.BitwiseRightShift");
13628 writer
.int32URightShiftResult(lhsIntId
, rhsIntId
, res_
.isDouble());
13629 trackAttached("BinaryArith.BitwiseUnsignedRightShift");
13632 MOZ_CRASH("Unhandled op in tryAttachBitwise");
13635 writer
.returnFromIC();
13636 return AttachDecision::Attach
;
13639 AttachDecision
BinaryArithIRGenerator::tryAttachDouble() {
13640 // Check valid opcodes
13641 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13642 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13643 return AttachDecision::NoAction
;
13646 // Check guard conditions.
13647 if (!CanConvertToDoubleForToNumber(lhs_
) ||
13648 !CanConvertToDoubleForToNumber(rhs_
)) {
13649 return AttachDecision::NoAction
;
13652 ValOperandId
lhsId(writer
.setInputOperandId(0));
13653 ValOperandId
rhsId(writer
.setInputOperandId(1));
13655 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhs_
);
13656 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhs_
);
13660 writer
.doubleAddResult(lhs
, rhs
);
13661 trackAttached("BinaryArith.DoubleAdd");
13664 writer
.doubleSubResult(lhs
, rhs
);
13665 trackAttached("BinaryArith.DoubleSub");
13668 writer
.doubleMulResult(lhs
, rhs
);
13669 trackAttached("BinaryArith.DoubleMul");
13672 writer
.doubleDivResult(lhs
, rhs
);
13673 trackAttached("BinaryArith.DoubleDiv");
13676 writer
.doubleModResult(lhs
, rhs
);
13677 trackAttached("BinaryArith.DoubleMod");
13680 writer
.doublePowResult(lhs
, rhs
);
13681 trackAttached("BinaryArith.DoublePow");
13684 MOZ_CRASH("Unhandled Op");
13686 writer
.returnFromIC();
13687 return AttachDecision::Attach
;
13690 AttachDecision
BinaryArithIRGenerator::tryAttachInt32() {
13691 // Check guard conditions.
13692 if (!CanConvertToInt32ForToNumber(lhs_
) ||
13693 !CanConvertToInt32ForToNumber(rhs_
)) {
13694 return AttachDecision::NoAction
;
13697 // These ICs will failure() if result can't be encoded in an Int32:
13698 // If sample result is not Int32, we should avoid IC.
13699 if (!res_
.isInt32()) {
13700 return AttachDecision::NoAction
;
13703 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13704 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13705 return AttachDecision::NoAction
;
13708 if (op_
== JSOp::Pow
&& !CanAttachInt32Pow(lhs_
, rhs_
)) {
13709 return AttachDecision::NoAction
;
13712 ValOperandId
lhsId(writer
.setInputOperandId(0));
13713 ValOperandId
rhsId(writer
.setInputOperandId(1));
13715 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhs_
);
13716 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhs_
);
13720 writer
.int32AddResult(lhsIntId
, rhsIntId
);
13721 trackAttached("BinaryArith.Int32Add");
13724 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13725 trackAttached("BinaryArith.Int32Sub");
13728 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13729 trackAttached("BinaryArith.Int32Mul");
13732 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13733 trackAttached("BinaryArith.Int32Div");
13736 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13737 trackAttached("BinaryArith.Int32Mod");
13740 writer
.int32PowResult(lhsIntId
, rhsIntId
);
13741 trackAttached("BinaryArith.Int32Pow");
13744 MOZ_CRASH("Unhandled op in tryAttachInt32");
13747 writer
.returnFromIC();
13748 return AttachDecision::Attach
;
13751 AttachDecision
BinaryArithIRGenerator::tryAttachStringConcat() {
13753 if (op_
!= JSOp::Add
) {
13754 return AttachDecision::NoAction
;
13757 // One side must be a string, the other side a primitive value we can easily
13758 // convert to a string.
13759 if (!(lhs_
.isString() && CanConvertToString(rhs_
)) &&
13760 !(CanConvertToString(lhs_
) && rhs_
.isString())) {
13761 return AttachDecision::NoAction
;
13764 ValOperandId
lhsId(writer
.setInputOperandId(0));
13765 ValOperandId
rhsId(writer
.setInputOperandId(1));
13767 StringOperandId lhsStrId
= emitToStringGuard(lhsId
, lhs_
);
13768 StringOperandId rhsStrId
= emitToStringGuard(rhsId
, rhs_
);
13770 writer
.callStringConcatResult(lhsStrId
, rhsStrId
);
13772 writer
.returnFromIC();
13773 trackAttached("BinaryArith.StringConcat");
13774 return AttachDecision::Attach
;
13777 AttachDecision
BinaryArithIRGenerator::tryAttachStringObjectConcat() {
13779 if (op_
!= JSOp::Add
) {
13780 return AttachDecision::NoAction
;
13784 if (!(lhs_
.isObject() && rhs_
.isString()) &&
13785 !(lhs_
.isString() && rhs_
.isObject()))
13786 return AttachDecision::NoAction
;
13788 ValOperandId
lhsId(writer
.setInputOperandId(0));
13789 ValOperandId
rhsId(writer
.setInputOperandId(1));
13791 // This guard is actually overly tight, as the runtime
13792 // helper can handle lhs or rhs being a string, so long
13793 // as the other is an object.
13794 if (lhs_
.isString()) {
13795 writer
.guardToString(lhsId
);
13796 writer
.guardToObject(rhsId
);
13798 writer
.guardToObject(lhsId
);
13799 writer
.guardToString(rhsId
);
13802 writer
.callStringObjectConcatResult(lhsId
, rhsId
);
13804 writer
.returnFromIC();
13805 trackAttached("BinaryArith.StringObjectConcat");
13806 return AttachDecision::Attach
;
13809 AttachDecision
BinaryArithIRGenerator::tryAttachBigInt() {
13811 if (!lhs_
.isBigInt() || !rhs_
.isBigInt()) {
13812 return AttachDecision::NoAction
;
13822 // Arithmetic operations.
13830 // Bitwise operations.
13834 return AttachDecision::NoAction
;
13837 ValOperandId
lhsId(writer
.setInputOperandId(0));
13838 ValOperandId
rhsId(writer
.setInputOperandId(1));
13840 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
13841 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
13845 writer
.bigIntAddResult(lhsBigIntId
, rhsBigIntId
);
13846 trackAttached("BinaryArith.BigIntAdd");
13849 writer
.bigIntSubResult(lhsBigIntId
, rhsBigIntId
);
13850 trackAttached("BinaryArith.BigIntSub");
13853 writer
.bigIntMulResult(lhsBigIntId
, rhsBigIntId
);
13854 trackAttached("BinaryArith.BigIntMul");
13857 writer
.bigIntDivResult(lhsBigIntId
, rhsBigIntId
);
13858 trackAttached("BinaryArith.BigIntDiv");
13861 writer
.bigIntModResult(lhsBigIntId
, rhsBigIntId
);
13862 trackAttached("BinaryArith.BigIntMod");
13865 writer
.bigIntPowResult(lhsBigIntId
, rhsBigIntId
);
13866 trackAttached("BinaryArith.BigIntPow");
13869 writer
.bigIntBitOrResult(lhsBigIntId
, rhsBigIntId
);
13870 trackAttached("BinaryArith.BigIntBitOr");
13873 writer
.bigIntBitXorResult(lhsBigIntId
, rhsBigIntId
);
13874 trackAttached("BinaryArith.BigIntBitXor");
13877 writer
.bigIntBitAndResult(lhsBigIntId
, rhsBigIntId
);
13878 trackAttached("BinaryArith.BigIntBitAnd");
13881 writer
.bigIntLeftShiftResult(lhsBigIntId
, rhsBigIntId
);
13882 trackAttached("BinaryArith.BigIntLeftShift");
13885 writer
.bigIntRightShiftResult(lhsBigIntId
, rhsBigIntId
);
13886 trackAttached("BinaryArith.BigIntRightShift");
13889 MOZ_CRASH("Unhandled op in tryAttachBigInt");
13892 writer
.returnFromIC();
13893 return AttachDecision::Attach
;
13896 AttachDecision
BinaryArithIRGenerator::tryAttachStringInt32Arith() {
13897 // Check for either int32 x string or string x int32.
13898 if (!(lhs_
.isInt32() && rhs_
.isString()) &&
13899 !(lhs_
.isString() && rhs_
.isInt32())) {
13900 return AttachDecision::NoAction
;
13903 // The created ICs will fail if the result can't be encoded as as int32.
13904 // Thus skip this IC, if the sample result is not an int32.
13905 if (!res_
.isInt32()) {
13906 return AttachDecision::NoAction
;
13909 // Must _not_ support Add, because it would be string concatenation instead.
13910 // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
13911 // reject that as well.
13912 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13913 op_
!= JSOp::Mod
) {
13914 return AttachDecision::NoAction
;
13917 // The string operand must be convertable to an int32 value.
13918 JSString
* str
= lhs_
.isString() ? lhs_
.toString() : rhs_
.toString();
13921 if (!StringToNumber(cx_
, str
, &num
)) {
13922 cx_
->recoverFromOutOfMemory();
13923 return AttachDecision::NoAction
;
13927 if (!mozilla::NumberIsInt32(num
, &unused
)) {
13928 return AttachDecision::NoAction
;
13931 ValOperandId
lhsId(writer
.setInputOperandId(0));
13932 ValOperandId
rhsId(writer
.setInputOperandId(1));
13934 auto guardToInt32
= [&](ValOperandId id
, const Value
& v
) {
13936 return writer
.guardToInt32(id
);
13939 MOZ_ASSERT(v
.isString());
13940 StringOperandId strId
= writer
.guardToString(id
);
13941 return writer
.guardStringToInt32(strId
);
13944 Int32OperandId lhsIntId
= guardToInt32(lhsId
, lhs_
);
13945 Int32OperandId rhsIntId
= guardToInt32(rhsId
, rhs_
);
13949 writer
.int32SubResult(lhsIntId
, rhsIntId
);
13950 trackAttached("BinaryArith.StringInt32Sub");
13953 writer
.int32MulResult(lhsIntId
, rhsIntId
);
13954 trackAttached("BinaryArith.StringInt32Mul");
13957 writer
.int32DivResult(lhsIntId
, rhsIntId
);
13958 trackAttached("BinaryArith.StringInt32Div");
13961 writer
.int32ModResult(lhsIntId
, rhsIntId
);
13962 trackAttached("BinaryArith.StringInt32Mod");
13965 MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
13968 writer
.returnFromIC();
13969 return AttachDecision::Attach
;
13972 AttachDecision
BinaryArithIRGenerator::tryAttachStringNumberArith() {
13973 // Check for either number x string or string x number.
13974 if (!(lhs_
.isNumber() && rhs_
.isString()) &&
13975 !(lhs_
.isString() && rhs_
.isNumber())) {
13976 return AttachDecision::NoAction
;
13979 // Must _not_ support Add, because it would be string concatenation instead.
13980 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
13981 op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13982 return AttachDecision::NoAction
;
13985 ValOperandId
lhsId(writer
.setInputOperandId(0));
13986 ValOperandId
rhsId(writer
.setInputOperandId(1));
13988 auto guardToNumber
= [&](ValOperandId id
, const Value
& v
) {
13989 if (v
.isNumber()) {
13990 return writer
.guardIsNumber(id
);
13993 MOZ_ASSERT(v
.isString());
13994 StringOperandId strId
= writer
.guardToString(id
);
13995 return writer
.guardStringToNumber(strId
);
13998 NumberOperandId lhsIntId
= guardToNumber(lhsId
, lhs_
);
13999 NumberOperandId rhsIntId
= guardToNumber(rhsId
, rhs_
);
14003 writer
.doubleSubResult(lhsIntId
, rhsIntId
);
14004 trackAttached("BinaryArith.StringNumberSub");
14007 writer
.doubleMulResult(lhsIntId
, rhsIntId
);
14008 trackAttached("BinaryArith.StringNumberMul");
14011 writer
.doubleDivResult(lhsIntId
, rhsIntId
);
14012 trackAttached("BinaryArith.StringNumberDiv");
14015 writer
.doubleModResult(lhsIntId
, rhsIntId
);
14016 trackAttached("BinaryArith.StringNumberMod");
14019 writer
.doublePowResult(lhsIntId
, rhsIntId
);
14020 trackAttached("BinaryArith.StringNumberPow");
14023 MOZ_CRASH("Unhandled op in tryAttachStringNumberArith");
14026 writer
.returnFromIC();
14027 return AttachDecision::Attach
;
14030 NewArrayIRGenerator::NewArrayIRGenerator(JSContext
* cx
, HandleScript script
,
14031 jsbytecode
* pc
, ICState state
, JSOp op
,
14032 HandleObject templateObj
,
14033 BaselineFrame
* frame
)
14034 : IRGenerator(cx
, script
, pc
, CacheKind::NewArray
, state
),
14035 #ifdef JS_CACHEIR_SPEW
14038 templateObject_(templateObj
),
14040 MOZ_ASSERT(templateObject_
);
14043 void NewArrayIRGenerator::trackAttached(const char* name
) {
14044 stubName_
= name
? name
: "NotAttached";
14045 #ifdef JS_CACHEIR_SPEW
14046 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14047 sp
.opcodeProperty("op", op_
);
14052 // Allocation sites are usually created during baseline compilation, but we also
14053 // need to create them when an IC stub is added to a baseline compiled script
14054 // and when trial inlining.
14055 static gc::AllocSite
* MaybeCreateAllocSite(jsbytecode
* pc
,
14056 BaselineFrame
* frame
) {
14057 MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc
)));
14059 JSScript
* outerScript
= frame
->outerScript();
14060 bool hasBaselineScript
= outerScript
->hasBaselineScript();
14061 bool isInlined
= frame
->icScript()->isInlined();
14063 if (!hasBaselineScript
&& !isInlined
) {
14064 MOZ_ASSERT(frame
->runningInInterpreter());
14065 return outerScript
->zone()->unknownAllocSite(JS::TraceKind::Object
);
14068 uint32_t pcOffset
= frame
->script()->pcToOffset(pc
);
14069 return frame
->icScript()->getOrCreateAllocSite(outerScript
, pcOffset
);
14072 AttachDecision
NewArrayIRGenerator::tryAttachArrayObject() {
14073 ArrayObject
* arrayObj
= &templateObject_
->as
<ArrayObject
>();
14075 MOZ_ASSERT(arrayObj
->numUsedFixedSlots() == 0);
14076 MOZ_ASSERT(arrayObj
->numDynamicSlots() == 0);
14077 MOZ_ASSERT(!arrayObj
->isSharedMemory());
14079 // The macro assembler only supports creating arrays with fixed elements.
14080 if (arrayObj
->hasDynamicElements()) {
14081 return AttachDecision::NoAction
;
14084 // Stub doesn't support metadata builder
14085 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
14086 return AttachDecision::NoAction
;
14089 writer
.guardNoAllocationMetadataBuilder(
14090 cx_
->realm()->addressOfMetadataBuilder());
14092 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
14094 return AttachDecision::NoAction
;
14097 Shape
* shape
= arrayObj
->shape();
14098 uint32_t length
= arrayObj
->length();
14100 writer
.newArrayObjectResult(length
, shape
, site
);
14102 writer
.returnFromIC();
14104 trackAttached("NewArray.Object");
14105 return AttachDecision::Attach
;
14108 AttachDecision
NewArrayIRGenerator::tryAttachStub() {
14109 AutoAssertNoPendingException
aanpe(cx_
);
14111 TRY_ATTACH(tryAttachArrayObject());
14113 trackAttached(IRGenerator::NotAttached
);
14114 return AttachDecision::NoAction
;
14117 NewObjectIRGenerator::NewObjectIRGenerator(JSContext
* cx
, HandleScript script
,
14118 jsbytecode
* pc
, ICState state
,
14119 JSOp op
, HandleObject templateObj
,
14120 BaselineFrame
* frame
)
14121 : IRGenerator(cx
, script
, pc
, CacheKind::NewObject
, state
),
14122 #ifdef JS_CACHEIR_SPEW
14125 templateObject_(templateObj
),
14127 MOZ_ASSERT(templateObject_
);
14130 void NewObjectIRGenerator::trackAttached(const char* name
) {
14131 stubName_
= name
? name
: "NotAttached";
14132 #ifdef JS_CACHEIR_SPEW
14133 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14134 sp
.opcodeProperty("op", op_
);
14139 AttachDecision
NewObjectIRGenerator::tryAttachPlainObject() {
14140 // Don't optimize allocations with too many dynamic slots. We use an unrolled
14141 // loop when initializing slots and this avoids generating too much code.
14142 static const uint32_t MaxDynamicSlotsToOptimize
= 64;
14144 NativeObject
* nativeObj
= &templateObject_
->as
<NativeObject
>();
14145 MOZ_ASSERT(nativeObj
->is
<PlainObject
>());
14147 // Stub doesn't support metadata builder
14148 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
14149 return AttachDecision::NoAction
;
14152 if (nativeObj
->numDynamicSlots() > MaxDynamicSlotsToOptimize
) {
14153 return AttachDecision::NoAction
;
14156 MOZ_ASSERT(!nativeObj
->hasDynamicElements());
14157 MOZ_ASSERT(!nativeObj
->isSharedMemory());
14159 gc::AllocSite
* site
= MaybeCreateAllocSite(pc_
, frame_
);
14161 return AttachDecision::NoAction
;
14164 uint32_t numFixedSlots
= nativeObj
->numUsedFixedSlots();
14165 uint32_t numDynamicSlots
= nativeObj
->numDynamicSlots();
14166 gc::AllocKind allocKind
= nativeObj
->allocKindForTenure();
14167 Shape
* shape
= nativeObj
->shape();
14169 writer
.guardNoAllocationMetadataBuilder(
14170 cx_
->realm()->addressOfMetadataBuilder());
14171 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
, shape
,
14174 writer
.returnFromIC();
14176 trackAttached("NewObject.PlainObject");
14177 return AttachDecision::Attach
;
14180 AttachDecision
NewObjectIRGenerator::tryAttachStub() {
14181 AutoAssertNoPendingException
aanpe(cx_
);
14183 TRY_ATTACH(tryAttachPlainObject());
14185 trackAttached(IRGenerator::NotAttached
);
14186 return AttachDecision::NoAction
;
14189 CloseIterIRGenerator::CloseIterIRGenerator(JSContext
* cx
, HandleScript script
,
14190 jsbytecode
* pc
, ICState state
,
14192 CompletionKind kind
)
14193 : IRGenerator(cx
, script
, pc
, CacheKind::CloseIter
, state
),
14197 void CloseIterIRGenerator::trackAttached(const char* name
) {
14198 #ifdef JS_CACHEIR_SPEW
14199 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14200 sp
.valueProperty("iter", ObjectValue(*iter_
));
14205 AttachDecision
CloseIterIRGenerator::tryAttachNoReturnMethod() {
14206 Maybe
<PropertyInfo
> prop
;
14207 NativeObject
* holder
= nullptr;
14209 // If we can guard that the iterator does not have a |return| method,
14210 // then this CloseIter is a no-op.
14211 NativeGetPropKind kind
= CanAttachNativeGetProp(
14212 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
14213 if (kind
!= NativeGetPropKind::Missing
) {
14214 return AttachDecision::NoAction
;
14216 MOZ_ASSERT(!holder
);
14218 ObjOperandId
objId(writer
.setInputOperandId(0));
14220 EmitMissingPropGuard(writer
, &iter_
->as
<NativeObject
>(), objId
);
14222 // There is no return method, so we don't have to do anything.
14223 writer
.returnFromIC();
14225 trackAttached("CloseIter.NoReturn");
14226 return AttachDecision::Attach
;
14229 AttachDecision
CloseIterIRGenerator::tryAttachScriptedReturn() {
14230 Maybe
<PropertyInfo
> prop
;
14231 NativeObject
* holder
= nullptr;
14233 NativeGetPropKind kind
= CanAttachNativeGetProp(
14234 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
14235 if (kind
!= NativeGetPropKind::Slot
) {
14236 return AttachDecision::NoAction
;
14238 MOZ_ASSERT(holder
);
14239 MOZ_ASSERT(prop
->isDataProperty());
14241 size_t slot
= prop
->slot();
14242 Value calleeVal
= holder
->getSlot(slot
);
14243 if (!calleeVal
.isObject() || !calleeVal
.toObject().is
<JSFunction
>()) {
14244 return AttachDecision::NoAction
;
14247 JSFunction
* callee
= &calleeVal
.toObject().as
<JSFunction
>();
14248 if (!callee
->hasJitEntry()) {
14249 return AttachDecision::NoAction
;
14251 if (callee
->isClassConstructor()) {
14252 return AttachDecision::NoAction
;
14255 // We don't support cross-realm |return|.
14256 if (cx_
->realm() != callee
->realm()) {
14257 return AttachDecision::NoAction
;
14260 ObjOperandId
objId(writer
.setInputOperandId(0));
14262 ObjOperandId holderId
=
14263 EmitReadSlotGuard(writer
, &iter_
->as
<NativeObject
>(), holder
, objId
);
14265 ValOperandId calleeValId
= EmitLoadSlot(writer
, holder
, holderId
, slot
);
14266 ObjOperandId calleeId
= writer
.guardToObject(calleeValId
);
14267 emitCalleeGuard(calleeId
, callee
);
14269 writer
.closeIterScriptedResult(objId
, calleeId
, kind_
, callee
->nargs());
14271 writer
.returnFromIC();
14272 trackAttached("CloseIter.ScriptedReturn");
14274 return AttachDecision::Attach
;
14277 AttachDecision
CloseIterIRGenerator::tryAttachStub() {
14278 AutoAssertNoPendingException
aanpe(cx_
);
14280 TRY_ATTACH(tryAttachNoReturnMethod());
14281 TRY_ATTACH(tryAttachScriptedReturn());
14283 trackAttached(IRGenerator::NotAttached
);
14284 return AttachDecision::NoAction
;
14287 OptimizeGetIteratorIRGenerator::OptimizeGetIteratorIRGenerator(
14288 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
14290 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeGetIterator
, state
),
14293 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachStub() {
14294 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeGetIterator
);
14296 AutoAssertNoPendingException
aanpe(cx_
);
14298 TRY_ATTACH(tryAttachArray());
14299 TRY_ATTACH(tryAttachNotOptimizable());
14301 MOZ_CRASH("Failed to attach unoptimizable case.");
14304 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachArray() {
14305 if (!isFirstStub_
) {
14306 return AttachDecision::NoAction
;
14309 // The value must be a packed array.
14310 if (!val_
.isObject()) {
14311 return AttachDecision::NoAction
;
14313 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
14314 if (!IsPackedArray(obj
)) {
14315 return AttachDecision::NoAction
;
14318 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
14320 Rooted
<NativeObject
*> arrProto(cx_
);
14321 uint32_t arrProtoIterSlot
;
14322 Rooted
<JSFunction
*> iterFun(cx_
);
14323 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
14324 return AttachDecision::NoAction
;
14327 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
14328 &arrProtoIterSlot
, &iterFun
)) {
14329 // Fuse should be popped.
14331 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14332 return AttachDecision::NoAction
;
14335 // %ArrayIteratorPrototype%.next must not be modified and
14336 // %ArrayIteratorPrototype%.return must not be present.
14337 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
14339 Rooted
<JSFunction
*> nextFun(cx_
);
14340 if (!IsArrayIteratorPrototypeOptimizable(
14341 cx_
, AllowIteratorReturn::No
, &arrayIteratorProto
, &slot
, &nextFun
)) {
14342 // Fuse should be popped.
14344 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14345 return AttachDecision::NoAction
;
14348 ValOperandId
valId(writer
.setInputOperandId(0));
14349 ObjOperandId objId
= writer
.guardToObject(valId
);
14351 // Guard the object is a packed array with Array.prototype as proto.
14352 MOZ_ASSERT(obj
->is
<ArrayObject
>());
14353 writer
.guardShape(objId
, obj
->shape());
14354 writer
.guardArrayIsPacked(objId
);
14355 bool intact
= obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact();
14357 // If the fuse isn't intact but we've still passed all these dynamic checks
14358 // then we can attach a version of the IC that dynamically checks to ensure
14359 // the required invariants still hold.
14361 // As an example of how this could be the case, consider an assignment
14363 // Array.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]
14365 // This assignment pops the fuse, however we can still use the dynamic check
14366 // version of this IC, as the actual -value- is still correct.
14367 bool useDynamicCheck
= !intact
|| !JS::Prefs::destructuring_fuse();
14368 if (useDynamicCheck
) {
14369 // Guard on Array.prototype[@@iterator].
14370 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
14371 ObjOperandId iterId
= writer
.loadObject(iterFun
);
14372 writer
.guardShape(arrProtoId
, arrProto
->shape());
14373 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
,
14376 // Guard on %ArrayIteratorPrototype%.next.
14377 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
14378 ObjOperandId nextId
= writer
.loadObject(nextFun
);
14379 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
14380 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, slot
);
14382 // Guard on the prototype chain to ensure no "return" method is present.
14383 ShapeGuardProtoChain(writer
, arrayIteratorProto
, iterProtoId
);
14385 // Guard on Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next.
14386 // This fuse also ensures the prototype chain for Array Iterator is
14387 // maintained and that no return method is added.
14388 writer
.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse
);
14391 writer
.loadBooleanResult(true);
14392 writer
.returnFromIC();
14394 if (useDynamicCheck
) {
14395 trackAttached("OptimizeGetIterator.Array.Dynamic");
14397 trackAttached("OptimizeGetIterator.Array.Fuse");
14399 return AttachDecision::Attach
;
14402 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachNotOptimizable() {
14403 ValOperandId
valId(writer
.setInputOperandId(0));
14405 writer
.loadBooleanResult(false);
14406 writer
.returnFromIC();
14408 trackAttached("OptimizeGetIterator.NotOptimizable");
14409 return AttachDecision::Attach
;
14412 void OptimizeGetIteratorIRGenerator::trackAttached(const char* name
) {
14413 stubName_
= name
? name
: "NotAttached";
14415 #ifdef JS_CACHEIR_SPEW
14416 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14417 sp
.valueProperty("val", val_
);
14422 #ifdef JS_SIMULATOR
14423 bool js::jit::CallAnyNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
14424 CallArgs args
= CallArgsFromVp(argc
, vp
);
14425 JSObject
* calleeObj
= &args
.callee();
14427 MOZ_ASSERT(calleeObj
->is
<JSFunction
>());
14428 auto* calleeFunc
= &calleeObj
->as
<JSFunction
>();
14429 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
14431 JSNative native
= calleeFunc
->native();
14432 return native(cx
, args
.length(), args
.base());
14435 const void* js::jit::RedirectedCallAnyNative() {
14436 // The simulator requires native calls to be redirected to a
14437 // special swi instruction. If we are calling an arbitrary native
14438 // function, we can't wrap the real target ahead of time, so we
14439 // call a wrapper function (CallAnyNative) that calls the target
14440 // itself, and redirect that wrapper.
14441 JSNative target
= CallAnyNative
;
14442 void* rawPtr
= JS_FUNC_TO_DATA_PTR(void*, target
);
14443 void* redirected
= Simulator::RedirectNativeFunction(rawPtr
, Args_General3
);