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/CheckedInt.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/FloatingPoint.h"
17 #include "builtin/DataViewObject.h"
18 #include "builtin/MapObject.h"
19 #include "builtin/ModuleObject.h"
20 #include "builtin/Object.h"
21 #include "jit/BaselineIC.h"
22 #include "jit/CacheIRCloner.h"
23 #include "jit/CacheIRCompiler.h"
24 #include "jit/CacheIRGenerator.h"
25 #include "jit/CacheIRSpewer.h"
26 #include "jit/CacheIRWriter.h"
27 #include "jit/InlinableNatives.h"
28 #include "jit/JitContext.h"
29 #include "jit/JitZone.h"
30 #include "js/experimental/JitInfo.h" // JSJitInfo
31 #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
32 #include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy, js::ToWindowIfWindowProxy
33 #include "js/friend/XrayJitInfo.h" // js::jit::GetXrayJitInfo, JS::XrayJitInfo
34 #include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis
35 #include "js/Prefs.h" // JS::Prefs
36 #include "js/RegExpFlags.h" // JS::RegExpFlags
37 #include "js/ScalarType.h" // js::Scalar::Type
38 #include "js/Utility.h" // JS::AutoEnterOOMUnsafeRegion
39 #include "js/Wrapper.h"
40 #include "proxy/DOMProxy.h" // js::GetDOMProxyHandlerFamily
41 #include "proxy/ScriptedProxyHandler.h"
42 #include "util/DifferentialTesting.h"
43 #include "util/Unicode.h"
44 #include "vm/ArrayBufferObject.h"
45 #include "vm/BoundFunctionObject.h"
46 #include "vm/BytecodeUtil.h"
47 #include "vm/Compartment.h"
48 #include "vm/Iteration.h"
49 #include "vm/PlainObject.h" // js::PlainObject
50 #include "vm/ProxyObject.h"
51 #include "vm/RegExpObject.h"
52 #include "vm/SelfHosting.h"
53 #include "vm/ThrowMsgKind.h" // ThrowCondition
54 #include "vm/TypeofEqOperand.h" // TypeofEqOperand
55 #include "vm/Watchtower.h"
56 #include "wasm/WasmInstance.h"
58 #include "jit/BaselineFrame-inl.h"
59 #include "jit/MacroAssembler-inl.h"
60 #include "vm/ArrayBufferObject-inl.h"
61 #include "vm/BytecodeUtil-inl.h"
62 #include "vm/EnvironmentObject-inl.h"
63 #include "vm/JSContext-inl.h"
64 #include "vm/JSFunction-inl.h"
65 #include "vm/JSObject-inl.h"
66 #include "vm/JSScript-inl.h"
67 #include "vm/List-inl.h"
68 #include "vm/NativeObject-inl.h"
69 #include "vm/PlainObject-inl.h"
70 #include "vm/StringObject-inl.h"
71 #include "wasm/WasmInstance-inl.h"
74 using namespace js::jit
;
76 using mozilla::DebugOnly
;
79 using JS::DOMProxyShadowsResult
;
80 using JS::ExpandoAndGeneration
;
82 const char* const js::jit::CacheKindNames
[] = {
83 #define DEFINE_KIND(kind) #kind,
84 CACHE_IR_KINDS(DEFINE_KIND
)
88 const char* const js::jit::CacheIROpNames
[] = {
89 #define OPNAME(op, ...) #op,
94 const CacheIROpInfo
js::jit::CacheIROpInfos
[] = {
95 #define OPINFO(op, len, transpile, ...) {len, transpile},
100 const uint32_t js::jit::CacheIROpHealth
[] = {
101 #define OPHEALTH(op, len, transpile, health) health,
102 CACHE_IR_OPS(OPHEALTH
)
106 size_t js::jit::NumInputsForCacheKind(CacheKind kind
) {
108 case CacheKind::NewArray
:
109 case CacheKind::NewObject
:
110 case CacheKind::GetIntrinsic
:
112 case CacheKind::GetProp
:
113 case CacheKind::TypeOf
:
114 case CacheKind::TypeOfEq
:
115 case CacheKind::ToPropertyKey
:
116 case CacheKind::GetIterator
:
117 case CacheKind::ToBool
:
118 case CacheKind::UnaryArith
:
119 case CacheKind::GetName
:
120 case CacheKind::BindName
:
121 case CacheKind::Call
:
122 case CacheKind::OptimizeSpreadCall
:
123 case CacheKind::CloseIter
:
124 case CacheKind::OptimizeGetIterator
:
126 case CacheKind::Compare
:
127 case CacheKind::GetElem
:
128 case CacheKind::GetPropSuper
:
129 case CacheKind::SetProp
:
131 case CacheKind::HasOwn
:
132 case CacheKind::CheckPrivateField
:
133 case CacheKind::InstanceOf
:
134 case CacheKind::BinaryArith
:
136 case CacheKind::GetElemSuper
:
137 case CacheKind::SetElem
:
140 MOZ_CRASH("Invalid kind");
144 void CacheIRWriter::assertSameCompartment(JSObject
* obj
) {
145 MOZ_ASSERT(cx_
->compartment() == obj
->compartment());
147 void CacheIRWriter::assertSameZone(Shape
* shape
) {
148 MOZ_ASSERT(cx_
->zone() == shape
->zone());
152 StubField
CacheIRWriter::readStubField(uint32_t offset
,
153 StubField::Type type
) const {
155 size_t currentOffset
= 0;
157 // If we've seen an offset earlier than this before, we know we can start the
158 // search there at least, otherwise, we start the search from the beginning.
159 if (lastOffset_
< offset
) {
160 currentOffset
= lastOffset_
;
164 while (currentOffset
!= offset
) {
165 currentOffset
+= StubField::sizeInBytes(stubFields_
[index
].type());
167 MOZ_ASSERT(index
< stubFields_
.length());
170 MOZ_ASSERT(stubFields_
[index
].type() == type
);
172 lastOffset_
= currentOffset
;
175 return stubFields_
[index
];
178 CacheIRCloner::CacheIRCloner(ICCacheIRStub
* stub
)
179 : stubInfo_(stub
->stubInfo()), stubData_(stub
->stubDataStart()) {}
181 void CacheIRCloner::cloneOp(CacheOp op
, CacheIRReader
& reader
,
182 CacheIRWriter
& writer
) {
184 #define DEFINE_OP(op, ...) \
186 clone##op(reader, writer); \
188 CACHE_IR_OPS(DEFINE_OP
)
191 MOZ_CRASH("Invalid op");
195 uintptr_t CacheIRCloner::readStubWord(uint32_t offset
) {
196 return stubInfo_
->getStubRawWord(stubData_
, offset
);
198 int64_t CacheIRCloner::readStubInt64(uint32_t offset
) {
199 return stubInfo_
->getStubRawInt64(stubData_
, offset
);
202 Shape
* CacheIRCloner::getShapeField(uint32_t stubOffset
) {
203 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
205 Shape
* CacheIRCloner::getWeakShapeField(uint32_t stubOffset
) {
206 // No barrier is required to clone a weak pointer.
207 return reinterpret_cast<Shape
*>(readStubWord(stubOffset
));
209 GetterSetter
* CacheIRCloner::getWeakGetterSetterField(uint32_t stubOffset
) {
210 // No barrier is required to clone a weak pointer.
211 return reinterpret_cast<GetterSetter
*>(readStubWord(stubOffset
));
213 JSObject
* CacheIRCloner::getObjectField(uint32_t stubOffset
) {
214 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
216 JSObject
* CacheIRCloner::getWeakObjectField(uint32_t stubOffset
) {
217 // No barrier is required to clone a weak pointer.
218 return reinterpret_cast<JSObject
*>(readStubWord(stubOffset
));
220 JSString
* CacheIRCloner::getStringField(uint32_t stubOffset
) {
221 return reinterpret_cast<JSString
*>(readStubWord(stubOffset
));
223 JSAtom
* CacheIRCloner::getAtomField(uint32_t stubOffset
) {
224 return reinterpret_cast<JSAtom
*>(readStubWord(stubOffset
));
226 JS::Symbol
* CacheIRCloner::getSymbolField(uint32_t stubOffset
) {
227 return reinterpret_cast<JS::Symbol
*>(readStubWord(stubOffset
));
229 BaseScript
* CacheIRCloner::getWeakBaseScriptField(uint32_t stubOffset
) {
230 // No barrier is required to clone a weak pointer.
231 return reinterpret_cast<BaseScript
*>(readStubWord(stubOffset
));
233 JitCode
* CacheIRCloner::getJitCodeField(uint32_t stubOffset
) {
234 return reinterpret_cast<JitCode
*>(readStubWord(stubOffset
));
236 uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset
) {
237 return uint32_t(reinterpret_cast<uintptr_t>(readStubWord(stubOffset
)));
239 const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset
) {
240 return reinterpret_cast<const void*>(readStubWord(stubOffset
));
242 uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset
) {
243 return static_cast<uint64_t>(readStubInt64(stubOffset
));
245 gc::AllocSite
* CacheIRCloner::getAllocSiteField(uint32_t stubOffset
) {
246 return reinterpret_cast<gc::AllocSite
*>(readStubWord(stubOffset
));
249 jsid
CacheIRCloner::getIdField(uint32_t stubOffset
) {
250 return jsid::fromRawBits(readStubWord(stubOffset
));
252 const Value
CacheIRCloner::getValueField(uint32_t stubOffset
) {
253 return Value::fromRawBits(uint64_t(readStubInt64(stubOffset
)));
255 double CacheIRCloner::getDoubleField(uint32_t stubOffset
) {
256 uint64_t bits
= uint64_t(readStubInt64(stubOffset
));
257 return mozilla::BitwiseCast
<double>(bits
);
260 IRGenerator::IRGenerator(JSContext
* cx
, HandleScript script
, jsbytecode
* pc
,
261 CacheKind cacheKind
, ICState state
,
262 BaselineFrame
* maybeFrame
)
267 maybeFrame_(maybeFrame
),
268 cacheKind_(cacheKind
),
270 isFirstStub_(state
.newStubIsFirstStub()),
271 numOptimizedStubs_(state
.numOptimizedStubs()) {}
273 // Allocation sites are usually created during baseline compilation, but we also
274 // need to create them when an IC stub is added to a baseline compiled script
275 // and when trial inlining.
276 gc::AllocSite
* IRGenerator::maybeCreateAllocSite() {
277 MOZ_ASSERT(BytecodeOpCanHaveAllocSite(JSOp(*pc_
)));
279 BaselineFrame
* frame
= maybeFrame_
;
282 JSScript
* outerScript
= frame
->outerScript();
283 bool hasBaselineScript
= outerScript
->hasBaselineScript();
284 bool isInlined
= frame
->icScript()->isInlined();
285 if (!hasBaselineScript
&& !isInlined
) {
286 MOZ_ASSERT(frame
->runningInInterpreter());
287 return outerScript
->zone()->unknownAllocSite(JS::TraceKind::Object
);
290 uint32_t pcOffset
= frame
->script()->pcToOffset(pc_
);
291 return frame
->icScript()->getOrCreateAllocSite(outerScript
, pcOffset
);
294 GetPropIRGenerator::GetPropIRGenerator(JSContext
* cx
, HandleScript script
,
295 jsbytecode
* pc
, ICState state
,
296 CacheKind cacheKind
, HandleValue val
,
298 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
300 static void EmitLoadSlotResult(CacheIRWriter
& writer
, ObjOperandId holderId
,
301 NativeObject
* holder
, PropertyInfo prop
) {
302 if (holder
->isFixedSlot(prop
.slot())) {
303 writer
.loadFixedSlotResult(holderId
,
304 NativeObject::getFixedSlotOffset(prop
.slot()));
306 size_t dynamicSlotOffset
=
307 holder
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
308 writer
.loadDynamicSlotResult(holderId
, dynamicSlotOffset
);
315 // DOM proxies are proxies that are used to implement various DOM objects like
316 // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
317 // object that stores extra properties added to the object. The following
318 // CacheIR instructions are only used with DOM proxies:
320 // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
321 // returns either an UndefinedValue (no expando), ObjectValue (the expando
322 // object), or PrivateValue(ExpandoAndGeneration*).
324 // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
325 // slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
326 // generation, then returns expandoAndGeneration->expando. This Value is
327 // either an UndefinedValue or ObjectValue.
329 // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
330 // expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
331 // returns the expandoAndGeneration->expando Value.
333 // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
334 // guards it's either UndefinedValue or an object with the expected shape.
336 enum class ProxyStubType
{
344 static bool IsCacheableDOMProxy(ProxyObject
* obj
) {
345 const BaseProxyHandler
* handler
= obj
->handler();
346 if (handler
->family() != GetDOMProxyHandlerFamily()) {
350 // Some DOM proxies have dynamic prototypes. We can't really cache those very
352 return obj
->hasStaticPrototype();
355 static ProxyStubType
GetProxyStubType(JSContext
* cx
, HandleObject obj
,
357 if (!obj
->is
<ProxyObject
>()) {
358 return ProxyStubType::None
;
360 auto proxy
= obj
.as
<ProxyObject
>();
362 if (!IsCacheableDOMProxy(proxy
)) {
363 return ProxyStubType::Generic
;
366 // Private fields are defined on a separate expando object.
367 if (id
.isPrivateName()) {
368 return ProxyStubType::Generic
;
371 DOMProxyShadowsResult shadows
= GetDOMProxyShadowsCheck()(cx
, proxy
, id
);
372 if (shadows
== DOMProxyShadowsResult::ShadowCheckFailed
) {
373 cx
->clearPendingException();
374 return ProxyStubType::None
;
377 if (DOMProxyIsShadowing(shadows
)) {
378 if (shadows
== DOMProxyShadowsResult::ShadowsViaDirectExpando
||
379 shadows
== DOMProxyShadowsResult::ShadowsViaIndirectExpando
) {
380 return ProxyStubType::DOMExpando
;
382 return ProxyStubType::DOMShadowed
;
385 MOZ_ASSERT(shadows
== DOMProxyShadowsResult::DoesntShadow
||
386 shadows
== DOMProxyShadowsResult::DoesntShadowUnique
);
387 return ProxyStubType::DOMUnshadowed
;
390 static bool ValueToNameOrSymbolId(JSContext
* cx
, HandleValue idVal
,
391 MutableHandleId id
, bool* nameOrSymbol
) {
392 *nameOrSymbol
= false;
394 if (!idVal
.isString() && !idVal
.isSymbol() && !idVal
.isUndefined() &&
399 if (!PrimitiveValueToId
<CanGC
>(cx
, idVal
, id
)) {
403 if (!id
.isAtom() && !id
.isSymbol()) {
404 id
.set(JS::PropertyKey::Void());
408 if (id
.isAtom() && id
.toAtom()->isIndex()) {
409 id
.set(JS::PropertyKey::Void());
413 *nameOrSymbol
= true;
417 AttachDecision
GetPropIRGenerator::tryAttachStub() {
418 AutoAssertNoPendingException
aanpe(cx_
);
420 ValOperandId
valId(writer
.setInputOperandId(0));
421 if (cacheKind_
!= CacheKind::GetProp
) {
422 MOZ_ASSERT_IF(cacheKind_
== CacheKind::GetPropSuper
,
423 getSuperReceiverValueId().id() == 1);
424 MOZ_ASSERT_IF(cacheKind_
!= CacheKind::GetPropSuper
,
425 getElemKeyValueId().id() == 1);
426 writer
.setInputOperandId(1);
428 if (cacheKind_
== CacheKind::GetElemSuper
) {
429 MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
430 writer
.setInputOperandId(2);
435 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
436 cx_
->clearPendingException();
437 return AttachDecision::NoAction
;
440 // |super.prop| getter calls use a |this| value that differs from lookup
442 ValOperandId receiverId
= isSuper() ? getSuperReceiverValueId() : valId
;
444 if (val_
.isObject()) {
445 RootedObject
obj(cx_
, &val_
.toObject());
446 ObjOperandId objId
= writer
.guardToObject(valId
);
448 TRY_ATTACH(tryAttachObjectLength(obj
, objId
, id
));
449 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, id
));
450 TRY_ATTACH(tryAttachDataView(obj
, objId
, id
));
451 TRY_ATTACH(tryAttachArrayBufferMaybeShared(obj
, objId
, id
));
452 TRY_ATTACH(tryAttachRegExp(obj
, objId
, id
));
453 TRY_ATTACH(tryAttachMap(obj
, objId
, id
));
454 TRY_ATTACH(tryAttachSet(obj
, objId
, id
));
455 TRY_ATTACH(tryAttachNative(obj
, objId
, id
, receiverId
));
456 TRY_ATTACH(tryAttachModuleNamespace(obj
, objId
, id
));
457 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
));
458 TRY_ATTACH(tryAttachCrossCompartmentWrapper(obj
, objId
, id
));
460 tryAttachXrayCrossCompartmentWrapper(obj
, objId
, id
, receiverId
));
461 TRY_ATTACH(tryAttachFunction(obj
, objId
, id
));
462 TRY_ATTACH(tryAttachArgumentsObjectIterator(obj
, objId
, id
));
463 TRY_ATTACH(tryAttachArgumentsObjectCallee(obj
, objId
, id
));
464 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, receiverId
));
466 if (!isSuper() && mode_
== ICState::Mode::Megamorphic
&&
467 JSOp(*pc_
) != JSOp::GetBoundName
) {
468 attachMegamorphicNativeSlotPermissive(objId
, id
);
469 return AttachDecision::Attach
;
472 trackAttached(IRGenerator::NotAttached
);
473 return AttachDecision::NoAction
;
476 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
477 cacheKind_
== CacheKind::GetElemSuper
);
479 TRY_ATTACH(tryAttachProxyElement(obj
, objId
));
480 TRY_ATTACH(tryAttachTypedArrayElement(obj
, objId
));
483 Int32OperandId indexId
;
484 if (maybeGuardInt32Index(idVal_
, getElemKeyValueId(), &index
, &indexId
)) {
485 TRY_ATTACH(tryAttachDenseElement(obj
, objId
, index
, indexId
));
486 TRY_ATTACH(tryAttachDenseElementHole(obj
, objId
, index
, indexId
));
487 TRY_ATTACH(tryAttachSparseElement(obj
, objId
, index
, indexId
));
488 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, index
, indexId
));
489 TRY_ATTACH(tryAttachArgumentsObjectArgHole(obj
, objId
, index
, indexId
));
491 tryAttachGenericElement(obj
, objId
, index
, indexId
, receiverId
));
493 trackAttached(IRGenerator::NotAttached
);
494 return AttachDecision::NoAction
;
497 trackAttached(IRGenerator::NotAttached
);
498 return AttachDecision::NoAction
;
502 TRY_ATTACH(tryAttachPrimitive(valId
, id
));
503 TRY_ATTACH(tryAttachStringLength(valId
, id
));
505 trackAttached(IRGenerator::NotAttached
);
506 return AttachDecision::NoAction
;
509 if (idVal_
.isInt32()) {
510 ValOperandId indexId
= getElemKeyValueId();
511 TRY_ATTACH(tryAttachStringChar(valId
, indexId
));
513 trackAttached(IRGenerator::NotAttached
);
514 return AttachDecision::NoAction
;
517 trackAttached(IRGenerator::NotAttached
);
518 return AttachDecision::NoAction
;
522 // Any property lookups performed when trying to attach ICs must be pure, i.e.
523 // must use LookupPropertyPure() or similar functions. Pure lookups are
524 // guaranteed to never modify the prototype chain. This ensures that the holder
525 // object can always be found on the prototype chain.
526 static bool IsCacheableProtoChain(NativeObject
* obj
, NativeObject
* holder
) {
527 while (obj
!= holder
) {
528 JSObject
* proto
= obj
->staticPrototype();
529 if (!proto
|| !proto
->is
<NativeObject
>()) {
532 obj
= &proto
->as
<NativeObject
>();
538 static bool IsCacheableGetPropSlot(NativeObject
* obj
, NativeObject
* holder
,
540 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
542 return prop
.isDataProperty();
545 enum class NativeGetPropKind
{
553 static NativeGetPropKind
IsCacheableGetPropCall(NativeObject
* obj
,
554 NativeObject
* holder
,
556 jsbytecode
* pc
= nullptr) {
557 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
559 if (pc
&& JSOp(*pc
) == JSOp::GetBoundName
) {
560 return NativeGetPropKind::None
;
563 if (!prop
.isAccessorProperty()) {
564 return NativeGetPropKind::None
;
567 JSObject
* getterObject
= holder
->getGetter(prop
);
568 if (!getterObject
|| !getterObject
->is
<JSFunction
>()) {
569 return NativeGetPropKind::None
;
572 JSFunction
& getter
= getterObject
->as
<JSFunction
>();
574 if (getter
.isClassConstructor()) {
575 return NativeGetPropKind::None
;
578 // Scripted functions and natives with JIT entry can use the scripted path.
579 if (getter
.hasJitEntry()) {
580 return NativeGetPropKind::ScriptedGetter
;
583 MOZ_ASSERT(getter
.isNativeWithoutJitEntry());
584 return NativeGetPropKind::NativeGetter
;
587 static bool CheckHasNoSuchOwnProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
588 if (!obj
->is
<NativeObject
>()) {
591 // Don't handle objects with resolve hooks.
592 if (ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
)) {
595 if (obj
->as
<NativeObject
>().contains(cx
, id
)) {
601 static bool CheckHasNoSuchProperty(JSContext
* cx
, JSObject
* obj
, jsid id
) {
602 JSObject
* curObj
= obj
;
604 if (!CheckHasNoSuchOwnProperty(cx
, curObj
, id
)) {
608 curObj
= curObj
->staticPrototype();
614 static bool IsCacheableNoProperty(JSContext
* cx
, NativeObject
* obj
,
615 NativeObject
* holder
, jsid id
,
619 // If we're doing a name lookup, we have to throw a ReferenceError.
620 if (JSOp(*pc
) == JSOp::GetBoundName
) {
624 return CheckHasNoSuchProperty(cx
, obj
, id
);
627 static NativeGetPropKind
CanAttachNativeGetProp(JSContext
* cx
, JSObject
* obj
,
629 NativeObject
** holder
,
630 Maybe
<PropertyInfo
>* propInfo
,
632 MOZ_ASSERT(id
.isString() || id
.isSymbol());
633 MOZ_ASSERT(!*holder
);
635 // The lookup needs to be universally pure, otherwise we risk calling hooks
636 // out of turn. We don't mind doing this even when purity isn't required,
637 // because we only miss out on shape hashification, which is only a temporary
638 // perf cost. The limits were arbitrarily set, anyways.
639 NativeObject
* baseHolder
= nullptr;
641 if (!LookupPropertyPure(cx
, obj
, id
, &baseHolder
, &prop
)) {
642 return NativeGetPropKind::None
;
644 auto* nobj
= &obj
->as
<NativeObject
>();
646 if (prop
.isNativeProperty()) {
647 MOZ_ASSERT(baseHolder
);
648 *holder
= baseHolder
;
649 *propInfo
= mozilla::Some(prop
.propertyInfo());
651 if (IsCacheableGetPropSlot(nobj
, *holder
, propInfo
->ref())) {
652 return NativeGetPropKind::Slot
;
655 return IsCacheableGetPropCall(nobj
, *holder
, propInfo
->ref(), pc
);
658 if (!prop
.isFound()) {
659 if (IsCacheableNoProperty(cx
, nobj
, *holder
, id
, pc
)) {
660 return NativeGetPropKind::Missing
;
664 return NativeGetPropKind::None
;
667 static void GuardReceiverProto(CacheIRWriter
& writer
, NativeObject
* obj
,
668 ObjOperandId objId
) {
669 // Note: we guard on the actual prototype and not on the shape because this is
670 // used for sparse elements where we expect shape changes.
672 if (JSObject
* proto
= obj
->staticPrototype()) {
673 writer
.guardProto(objId
, proto
);
675 writer
.guardNullProto(objId
);
679 // Guard that a given object has same class and same OwnProperties (excluding
680 // dense elements and dynamic properties).
681 static void TestMatchingNativeReceiver(CacheIRWriter
& writer
, NativeObject
* obj
,
682 ObjOperandId objId
) {
683 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
686 // Similar to |TestMatchingNativeReceiver|, but specialized for ProxyObject.
687 static void TestMatchingProxyReceiver(CacheIRWriter
& writer
, ProxyObject
* obj
,
688 ObjOperandId objId
) {
689 writer
.guardShapeForClass(objId
, obj
->shape());
692 static void GeneratePrototypeGuards(CacheIRWriter
& writer
, JSObject
* obj
,
693 NativeObject
* holder
, ObjOperandId objId
) {
694 // Assuming target property is on |holder|, generate appropriate guards to
695 // ensure |holder| is still on the prototype chain of |obj| and we haven't
696 // introduced any shadowing definitions.
698 // For each item in the proto chain before holder, we must ensure that
699 // [[GetPrototypeOf]] still has the expected result, and that
700 // [[GetOwnProperty]] has no definition of the target property.
703 // [SMDOC] Shape Teleporting Optimization
704 // --------------------------------------
706 // Starting with the assumption (and guideline to developers) that mutating
707 // prototypes is an uncommon and fair-to-penalize operation we move cost
708 // from the access side to the mutation side.
710 // Consider the following proto chain, with B defining a property 'x':
712 // D -> C -> B{x: 3} -> A -> null
714 // When accessing |D.x| we refer to D as the "receiver", and B as the
715 // "holder". To optimize this access we need to ensure that neither D nor C
716 // has since defined a shadowing property 'x'. Since C is a prototype that
717 // we assume is rarely mutated we would like to avoid checking each time if
718 // new properties are added. To do this we require that whenever C starts
719 // shadowing a property on its proto chain, we invalidate (and opt out of) the
720 // teleporting optimization by setting the InvalidatedTeleporting flag on the
721 // object we're shadowing, triggering a shape change of that object. As a
722 // result, checking the shape of D and B is sufficient. Note that we do not
723 // care if the shape or properties of A change since the lookup of 'x' will
726 // The second condition we must verify is that the prototype chain was not
727 // mutated. The same mechanism as above is used. When the prototype link is
728 // changed, we generate a new shape for the object. If the object whose
729 // link we are mutating is itself a prototype, we regenerate shapes down
730 // the chain by setting the InvalidatedTeleporting flag on them. This means
731 // the same two shape checks as above are sufficient.
733 // Once the InvalidatedTeleporting flag is set, it means the shape will no
734 // longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
735 // In this case we can no longer apply the optimization.
738 // - ReshapeForProtoMutation
739 // - ReshapeForShadowedProp
742 MOZ_ASSERT(obj
!= holder
);
744 // Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
745 // unchanged so peel off the receiver.
746 JSObject
* pobj
= obj
->staticPrototype();
747 MOZ_ASSERT(pobj
->isUsedAsPrototype());
749 // If teleporting is supported for this holder, we are done.
750 if (!holder
->hasInvalidatedTeleporting()) {
754 // If already at the holder, no further proto checks are needed.
755 if (pobj
== holder
) {
759 // Synchronize pobj and protoId.
760 MOZ_ASSERT(pobj
== obj
->staticPrototype());
761 ObjOperandId protoId
= writer
.loadProto(objId
);
763 // Shape guard each prototype object between receiver and holder. This guards
764 // against both proto changes and shadowing properties.
765 while (pobj
!= holder
) {
766 writer
.guardShape(protoId
, pobj
->shape());
768 pobj
= pobj
->staticPrototype();
769 protoId
= writer
.loadProto(protoId
);
773 static void GeneratePrototypeHoleGuards(CacheIRWriter
& writer
,
774 NativeObject
* obj
, ObjOperandId objId
,
775 bool alwaysGuardFirstProto
) {
776 if (alwaysGuardFirstProto
) {
777 GuardReceiverProto(writer
, obj
, objId
);
780 JSObject
* pobj
= obj
->staticPrototype();
782 ObjOperandId protoId
= writer
.loadObject(pobj
);
784 // Make sure the shape matches, to ensure the proto is unchanged and to
785 // avoid non-dense elements or anything else that is being checked by
786 // CanAttachDenseElementHole.
787 MOZ_ASSERT(pobj
->is
<NativeObject
>());
788 writer
.guardShape(protoId
, pobj
->shape());
790 // Also make sure there are no dense elements.
791 writer
.guardNoDenseElements(protoId
);
793 pobj
= pobj
->staticPrototype();
797 // Similar to |TestMatchingReceiver|, but for the holder object (when it
798 // differs from the receiver). The holder may also be the expando of the
799 // receiver if it exists.
800 static void TestMatchingHolder(CacheIRWriter
& writer
, NativeObject
* obj
,
801 ObjOperandId objId
) {
802 // The GeneratePrototypeGuards + TestMatchingHolder checks only support
803 // prototype chains composed of NativeObject (excluding the receiver
805 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
808 enum class IsCrossCompartment
{ No
, Yes
};
810 // Emit a shape guard for all objects on the proto chain. This does NOT include
811 // the receiver; callers must ensure the receiver's proto is the first proto by
812 // either emitting a shape guard or a prototype guard for |objId|.
814 // Note: this relies on shape implying proto.
815 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
816 static void ShapeGuardProtoChain(CacheIRWriter
& writer
, NativeObject
* obj
,
817 ObjOperandId objId
) {
819 static const uint32_t MAX_CACHED_LOADS
= 4;
820 ObjOperandId receiverObjId
= objId
;
823 JSObject
* proto
= obj
->staticPrototype();
828 obj
= &proto
->as
<NativeObject
>();
830 // After guarding the shape of an object, we can safely bake that
831 // object's proto into the stub data. Compared to LoadProto, this
832 // takes one load instead of three (object -> shape -> baseshape
833 // -> proto). We cap the depth to avoid bloating the size of the
834 // stub data. To avoid compartment mismatch, we skip this optimization
835 // in the cross-compartment case.
836 if (depth
< MAX_CACHED_LOADS
&&
837 MaybeCrossCompartment
== IsCrossCompartment::No
) {
838 objId
= writer
.loadProtoObject(obj
, receiverObjId
);
840 objId
= writer
.loadProto(objId
);
844 writer
.guardShape(objId
, obj
->shape());
848 // For cross compartment guards we shape-guard the prototype chain to avoid
849 // referencing the holder object.
851 // This peels off the first layer because it's guarded against obj == holder.
853 // Returns the holder's OperandId.
854 static ObjOperandId
ShapeGuardProtoChainForCrossCompartmentHolder(
855 CacheIRWriter
& writer
, NativeObject
* obj
, ObjOperandId objId
,
856 NativeObject
* holder
) {
857 MOZ_ASSERT(obj
!= holder
);
860 MOZ_ASSERT(obj
->staticPrototype());
861 obj
= &obj
->staticPrototype()->as
<NativeObject
>();
863 objId
= writer
.loadProto(objId
);
865 TestMatchingHolder(writer
, obj
, objId
);
868 writer
.guardShapeForOwnProperties(objId
, obj
->shape());
872 // Emit guards for reading a data property on |holder|. Returns the holder's
874 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
875 static ObjOperandId
EmitReadSlotGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
876 NativeObject
* holder
,
877 ObjOperandId objId
) {
879 TestMatchingNativeReceiver(writer
, obj
, objId
);
885 if (MaybeCrossCompartment
== IsCrossCompartment::Yes
) {
886 // Guard proto chain integrity.
887 // We use a variant of guards that avoid baking in any cross-compartment
889 return ShapeGuardProtoChainForCrossCompartmentHolder(writer
, obj
, objId
,
893 // Guard proto chain integrity.
894 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
896 // Guard on the holder's shape.
897 ObjOperandId holderId
= writer
.loadObject(holder
);
898 TestMatchingHolder(writer
, holder
, holderId
);
902 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
903 static void EmitMissingPropGuard(CacheIRWriter
& writer
, NativeObject
* obj
,
904 ObjOperandId objId
) {
905 TestMatchingNativeReceiver(writer
, obj
, objId
);
907 // The property does not exist. Guard on everything in the prototype
908 // chain. This is guaranteed to see only Native objects because of
909 // CanAttachNativeGetProp().
910 ShapeGuardProtoChain
<MaybeCrossCompartment
>(writer
, obj
, objId
);
913 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
914 static void EmitReadSlotResult(CacheIRWriter
& writer
, NativeObject
* obj
,
915 NativeObject
* holder
, PropertyInfo prop
,
916 ObjOperandId objId
) {
919 ObjOperandId holderId
=
920 EmitReadSlotGuard
<MaybeCrossCompartment
>(writer
, obj
, holder
, objId
);
922 MOZ_ASSERT(holderId
.valid());
923 EmitLoadSlotResult(writer
, holderId
, holder
, prop
);
926 template <IsCrossCompartment MaybeCrossCompartment
= IsCrossCompartment::No
>
927 static void EmitMissingPropResult(CacheIRWriter
& writer
, NativeObject
* obj
,
928 ObjOperandId objId
) {
929 EmitMissingPropGuard
<MaybeCrossCompartment
>(writer
, obj
, objId
);
930 writer
.loadUndefinedResult();
933 static void EmitCallGetterResultNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
934 NativeGetPropKind kind
,
936 NativeObject
* holder
,
938 ValOperandId receiverId
) {
939 MOZ_ASSERT(IsCacheableGetPropCall(obj
, holder
, prop
) == kind
);
941 JSFunction
* target
= &holder
->getGetter(prop
)->as
<JSFunction
>();
942 bool sameRealm
= cx
->realm() == target
->realm();
945 case NativeGetPropKind::NativeGetter
: {
946 writer
.callNativeGetterResult(receiverId
, target
, sameRealm
);
947 writer
.returnFromIC();
950 case NativeGetPropKind::ScriptedGetter
: {
951 writer
.callScriptedGetterResult(receiverId
, target
, sameRealm
);
952 writer
.returnFromIC();
956 // CanAttachNativeGetProp guarantees that the getter is either a native or
957 // a scripted function.
958 MOZ_ASSERT_UNREACHABLE("Can't attach getter");
963 // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
965 static void EmitGuardGetterSetterSlot(CacheIRWriter
& writer
,
966 NativeObject
* holder
, PropertyInfo prop
,
967 ObjOperandId holderId
,
968 bool holderIsConstant
= false) {
969 // If the holder is guaranteed to be the same object, and it never had a
970 // slot holding a GetterSetter mutated or deleted, its Shape will change when
971 // that does happen so we don't need to guard on the GetterSetter.
972 if (holderIsConstant
&& !holder
->hadGetterSetterChange()) {
976 size_t slot
= prop
.slot();
977 Value slotVal
= holder
->getSlot(slot
);
978 MOZ_ASSERT(slotVal
.isPrivateGCThing());
980 if (holder
->isFixedSlot(slot
)) {
981 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
982 writer
.guardFixedSlotValue(holderId
, offset
, slotVal
);
984 size_t offset
= holder
->dynamicSlotIndex(slot
) * sizeof(Value
);
985 writer
.guardDynamicSlotValue(holderId
, offset
, slotVal
);
989 static void EmitCallGetterResultGuards(CacheIRWriter
& writer
, NativeObject
* obj
,
990 NativeObject
* holder
, HandleId id
,
991 PropertyInfo prop
, ObjOperandId objId
,
992 ICState::Mode mode
) {
993 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
994 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
995 // require outerizing).
997 MOZ_ASSERT(holder
->containsPure(id
, prop
));
999 if (mode
== ICState::Mode::Specialized
|| IsWindow(obj
)) {
1000 TestMatchingNativeReceiver(writer
, obj
, objId
);
1002 if (obj
!= holder
) {
1003 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
1005 // Guard on the holder's shape.
1006 ObjOperandId holderId
= writer
.loadObject(holder
);
1007 TestMatchingHolder(writer
, holder
, holderId
);
1009 EmitGuardGetterSetterSlot(writer
, holder
, prop
, holderId
,
1010 /* holderIsConstant = */ true);
1012 EmitGuardGetterSetterSlot(writer
, holder
, prop
, objId
);
1015 GetterSetter
* gs
= holder
->getGetterSetter(prop
);
1016 writer
.guardHasGetterSetter(objId
, id
, gs
);
1020 static void EmitCallGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
1021 NativeGetPropKind kind
, NativeObject
* obj
,
1022 NativeObject
* holder
, HandleId id
,
1023 PropertyInfo prop
, ObjOperandId objId
,
1024 ValOperandId receiverId
, ICState::Mode mode
) {
1025 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
, mode
);
1026 EmitCallGetterResultNoGuards(cx
, writer
, kind
, obj
, holder
, prop
, receiverId
);
1029 static bool CanAttachDOMCall(JSContext
* cx
, JSJitInfo::OpType type
,
1030 JSObject
* obj
, JSFunction
* fun
,
1031 ICState::Mode mode
) {
1032 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
||
1033 type
== JSJitInfo::Method
);
1035 if (mode
!= ICState::Mode::Specialized
) {
1039 if (!fun
->hasJitInfo()) {
1043 if (cx
->realm() != fun
->realm()) {
1047 const JSJitInfo
* jitInfo
= fun
->jitInfo();
1048 if (jitInfo
->type() != type
) {
1052 MOZ_ASSERT_IF(IsWindow(obj
), !jitInfo
->needsOuterizedThisObject());
1054 const JSClass
* clasp
= obj
->getClass();
1055 if (!clasp
->isDOMClass()) {
1059 if (type
!= JSJitInfo::Method
&& clasp
->isProxyObject()) {
1063 // Ion codegen expects DOM_OBJECT_SLOT to be a fixed slot in LoadDOMPrivate.
1064 // It can be a dynamic slot if we transplanted this reflector object with a
1066 if (obj
->is
<NativeObject
>() && obj
->as
<NativeObject
>().numFixedSlots() == 0) {
1070 // Tell the analysis the |DOMInstanceClassHasProtoAtDepth| hook can't GC.
1071 JS::AutoSuppressGCAnalysis nogc
;
1073 DOMInstanceClassHasProtoAtDepth instanceChecker
=
1074 cx
->runtime()->DOMcallbacks
->instanceClassMatchesProto
;
1075 return instanceChecker(clasp
, jitInfo
->protoID
, jitInfo
->depth
);
1078 static bool CanAttachDOMGetterSetter(JSContext
* cx
, JSJitInfo::OpType type
,
1079 NativeObject
* obj
, NativeObject
* holder
,
1080 PropertyInfo prop
, ICState::Mode mode
) {
1081 MOZ_ASSERT(type
== JSJitInfo::Getter
|| type
== JSJitInfo::Setter
);
1083 JSObject
* accessor
= type
== JSJitInfo::Getter
? holder
->getGetter(prop
)
1084 : holder
->getSetter(prop
);
1085 JSFunction
* fun
= &accessor
->as
<JSFunction
>();
1087 return CanAttachDOMCall(cx
, type
, obj
, fun
, mode
);
1090 static void EmitCallDOMGetterResultNoGuards(CacheIRWriter
& writer
,
1091 NativeObject
* holder
,
1093 ObjOperandId objId
) {
1094 JSFunction
* getter
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1095 writer
.callDOMGetterResult(objId
, getter
->jitInfo());
1096 writer
.returnFromIC();
1099 static void EmitCallDOMGetterResult(JSContext
* cx
, CacheIRWriter
& writer
,
1100 NativeObject
* obj
, NativeObject
* holder
,
1101 HandleId id
, PropertyInfo prop
,
1102 ObjOperandId objId
) {
1103 // Note: this relies on EmitCallGetterResultGuards emitting a shape guard
1104 // for specialized stubs.
1105 // The shape guard ensures the receiver's Class is valid for this DOM getter.
1106 EmitCallGetterResultGuards(writer
, obj
, holder
, id
, prop
, objId
,
1107 ICState::Mode::Specialized
);
1108 EmitCallDOMGetterResultNoGuards(writer
, holder
, prop
, objId
);
1111 static ValOperandId
EmitLoadSlot(CacheIRWriter
& writer
, NativeObject
* holder
,
1112 ObjOperandId holderId
, uint32_t slot
) {
1113 if (holder
->isFixedSlot(slot
)) {
1114 return writer
.loadFixedSlot(holderId
,
1115 NativeObject::getFixedSlotOffset(slot
));
1117 size_t dynamicSlotIndex
= holder
->dynamicSlotIndex(slot
);
1118 return writer
.loadDynamicSlot(holderId
, dynamicSlotIndex
);
1121 void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId
,
1123 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1125 // We don't support GetBoundName because environment objects have
1126 // lookupProperty hooks and GetBoundName is usually not megamorphic.
1127 MOZ_ASSERT(JSOp(*pc_
) != JSOp::GetBoundName
);
1129 if (cacheKind_
== CacheKind::GetProp
||
1130 cacheKind_
== CacheKind::GetPropSuper
) {
1131 writer
.megamorphicLoadSlotResult(objId
, id
);
1133 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
1134 cacheKind_
== CacheKind::GetElemSuper
);
1135 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1137 writer
.returnFromIC();
1139 trackAttached("GetProp.MegamorphicNativeSlot");
1142 void GetPropIRGenerator::attachMegamorphicNativeSlotPermissive(
1143 ObjOperandId objId
, jsid id
) {
1144 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1146 // We don't support GetBoundName because environment objects have
1147 // lookupProperty hooks and GetBoundName is usually not megamorphic.
1148 MOZ_ASSERT(JSOp(*pc_
) != JSOp::GetBoundName
);
1149 // It is not worth the complexity to support super here because we'd have
1150 // to plumb the receiver through everywhere, so we just skip it.
1151 MOZ_ASSERT(!isSuper());
1153 if (cacheKind_
== CacheKind::GetProp
) {
1154 writer
.megamorphicLoadSlotPermissiveResult(objId
, id
);
1156 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
1157 writer
.megamorphicLoadSlotByValuePermissiveResult(objId
,
1158 getElemKeyValueId());
1160 writer
.returnFromIC();
1162 trackAttached("GetProp.MegamorphicNativeSlotPermissive");
1165 AttachDecision
GetPropIRGenerator::tryAttachNative(HandleObject obj
,
1168 ValOperandId receiverId
) {
1169 Maybe
<PropertyInfo
> prop
;
1170 NativeObject
* holder
= nullptr;
1172 NativeGetPropKind kind
=
1173 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
1175 case NativeGetPropKind::None
:
1176 return AttachDecision::NoAction
;
1177 case NativeGetPropKind::Missing
:
1178 case NativeGetPropKind::Slot
: {
1179 auto* nobj
= &obj
->as
<NativeObject
>();
1181 if (mode_
== ICState::Mode::Megamorphic
&&
1182 JSOp(*pc_
) != JSOp::GetBoundName
) {
1183 attachMegamorphicNativeSlot(objId
, id
);
1184 return AttachDecision::Attach
;
1187 maybeEmitIdGuard(id
);
1188 if (kind
== NativeGetPropKind::Slot
) {
1189 EmitReadSlotResult(writer
, nobj
, holder
, *prop
, objId
);
1190 writer
.returnFromIC();
1191 trackAttached("GetProp.NativeSlot");
1193 EmitMissingPropResult(writer
, nobj
, objId
);
1194 writer
.returnFromIC();
1195 trackAttached("GetProp.Missing");
1197 return AttachDecision::Attach
;
1199 case NativeGetPropKind::ScriptedGetter
:
1200 case NativeGetPropKind::NativeGetter
: {
1201 auto* nobj
= &obj
->as
<NativeObject
>();
1202 MOZ_ASSERT(!IsWindow(nobj
));
1204 // If we're in megamorphic mode, we assume that a specialized
1205 // getter call is just going to end up failing later, so we let this
1206 // get handled further down the chain by
1207 // attachMegamorphicNativeSlotPermissive
1208 if (!isSuper() && mode_
== ICState::Mode::Megamorphic
) {
1209 return AttachDecision::NoAction
;
1212 maybeEmitIdGuard(id
);
1214 if (!isSuper() && CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, nobj
,
1215 holder
, *prop
, mode_
)) {
1216 EmitCallDOMGetterResult(cx_
, writer
, nobj
, holder
, id
, *prop
, objId
);
1218 trackAttached("GetProp.DOMGetter");
1219 return AttachDecision::Attach
;
1222 EmitCallGetterResult(cx_
, writer
, kind
, nobj
, holder
, id
, *prop
, objId
,
1225 trackAttached("GetProp.NativeGetter");
1226 return AttachDecision::Attach
;
1230 MOZ_CRASH("Bad NativeGetPropKind");
1233 // Returns whether obj is a WindowProxy wrapping the script's global.
1234 static bool IsWindowProxyForScriptGlobal(JSScript
* script
, JSObject
* obj
) {
1235 if (!IsWindowProxy(obj
)) {
1239 MOZ_ASSERT(obj
->getClass() ==
1240 script
->runtimeFromMainThread()->maybeWindowProxyClass());
1242 JSObject
* window
= ToWindowIfWindowProxy(obj
);
1244 // Ion relies on the WindowProxy's group changing (and the group getting
1245 // marked as having unknown properties) on navigation. If we ever stop
1246 // transplanting same-compartment WindowProxies, this assert will fail and we
1247 // need to fix that code.
1248 MOZ_ASSERT(window
== &obj
->nonCCWGlobal());
1250 // This must be a WindowProxy for a global in this compartment. Else it would
1251 // be a cross-compartment wrapper and IsWindowProxy returns false for
1253 MOZ_ASSERT(script
->compartment() == obj
->compartment());
1255 // Only optimize lookups on the WindowProxy for the current global. Other
1256 // WindowProxies in the compartment may require security checks (based on
1257 // mutable document.domain). See bug 1516775.
1258 return window
== &script
->global();
1261 // Guards objId is a WindowProxy for windowObj. Returns the window's operand id.
1262 static ObjOperandId
GuardAndLoadWindowProxyWindow(CacheIRWriter
& writer
,
1264 GlobalObject
* windowObj
) {
1265 writer
.guardClass(objId
, GuardClassKind::WindowProxy
);
1266 ObjOperandId windowObjId
= writer
.loadWrapperTarget(objId
,
1267 /*fallible = */ false);
1268 writer
.guardSpecificObject(windowObjId
, windowObj
);
1272 // Whether a getter/setter on the global should have the WindowProxy as |this|
1273 // value instead of the Window (the global object). This always returns true for
1274 // scripted functions.
1275 static bool GetterNeedsWindowProxyThis(NativeObject
* holder
,
1276 PropertyInfo prop
) {
1277 JSFunction
* callee
= &holder
->getGetter(prop
)->as
<JSFunction
>();
1278 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1280 static bool SetterNeedsWindowProxyThis(NativeObject
* holder
,
1281 PropertyInfo prop
) {
1282 JSFunction
* callee
= &holder
->getSetter(prop
)->as
<JSFunction
>();
1283 return !callee
->hasJitInfo() || callee
->jitInfo()->needsOuterizedThisObject();
1286 AttachDecision
GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
1289 // Attach a stub when the receiver is a WindowProxy and we can do the lookup
1290 // on the Window (the global object).
1292 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
1293 return AttachDecision::NoAction
;
1296 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1298 if (mode_
== ICState::Mode::Megamorphic
) {
1299 return AttachDecision::NoAction
;
1302 // Now try to do the lookup on the Window (the current global).
1303 GlobalObject
* windowObj
= cx_
->global();
1304 NativeObject
* holder
= nullptr;
1305 Maybe
<PropertyInfo
> prop
;
1306 NativeGetPropKind kind
=
1307 CanAttachNativeGetProp(cx_
, windowObj
, id
, &holder
, &prop
, pc_
);
1309 case NativeGetPropKind::None
:
1310 return AttachDecision::NoAction
;
1312 case NativeGetPropKind::Slot
: {
1313 maybeEmitIdGuard(id
);
1314 ObjOperandId windowObjId
=
1315 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1316 EmitReadSlotResult(writer
, windowObj
, holder
, *prop
, windowObjId
);
1317 writer
.returnFromIC();
1319 trackAttached("GetProp.WindowProxySlot");
1320 return AttachDecision::Attach
;
1323 case NativeGetPropKind::Missing
: {
1324 maybeEmitIdGuard(id
);
1325 ObjOperandId windowObjId
=
1326 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1327 EmitMissingPropResult(writer
, windowObj
, windowObjId
);
1328 writer
.returnFromIC();
1330 trackAttached("GetProp.WindowProxyMissing");
1331 return AttachDecision::Attach
;
1334 case NativeGetPropKind::NativeGetter
:
1335 case NativeGetPropKind::ScriptedGetter
: {
1336 // If a |super| access, it is not worth the complexity to attach an IC.
1338 return AttachDecision::NoAction
;
1341 bool needsWindowProxy
= GetterNeedsWindowProxyThis(holder
, *prop
);
1343 // Guard the incoming object is a WindowProxy and inline a getter call
1344 // based on the Window object.
1345 maybeEmitIdGuard(id
);
1346 ObjOperandId windowObjId
=
1347 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
1349 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, windowObj
, holder
,
1351 MOZ_ASSERT(!needsWindowProxy
);
1352 EmitCallDOMGetterResult(cx_
, writer
, windowObj
, holder
, id
, *prop
,
1354 trackAttached("GetProp.WindowProxyDOMGetter");
1356 ValOperandId receiverId
=
1357 writer
.boxObject(needsWindowProxy
? objId
: windowObjId
);
1358 EmitCallGetterResult(cx_
, writer
, kind
, windowObj
, holder
, id
, *prop
,
1359 windowObjId
, receiverId
, mode_
);
1360 trackAttached("GetProp.WindowProxyGetter");
1363 return AttachDecision::Attach
;
1367 MOZ_CRASH("Unreachable");
1370 AttachDecision
GetPropIRGenerator::tryAttachCrossCompartmentWrapper(
1371 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
1372 // We can only optimize this very wrapper-handler, because others might
1373 // have a security policy.
1374 if (!IsWrapper(obj
) ||
1375 Wrapper::wrapperHandler(obj
) != &CrossCompartmentWrapper::singleton
) {
1376 return AttachDecision::NoAction
;
1379 // If we're megamorphic prefer a generic proxy stub that handles a lot more
1381 if (mode_
== ICState::Mode::Megamorphic
) {
1382 return AttachDecision::NoAction
;
1385 RootedObject
unwrapped(cx_
, Wrapper::wrappedObject(obj
));
1386 MOZ_ASSERT(unwrapped
== UnwrapOneCheckedStatic(obj
));
1387 MOZ_ASSERT(!IsCrossCompartmentWrapper(unwrapped
),
1388 "CCWs must not wrap other CCWs");
1390 // If we allowed different zones we would have to wrap strings.
1391 if (unwrapped
->compartment()->zone() != cx_
->compartment()->zone()) {
1392 return AttachDecision::NoAction
;
1395 // Take the unwrapped object's global, and wrap in a
1396 // this-compartment wrapper. This is what will be stored in the IC
1397 // keep the compartment alive.
1398 RootedObject
wrappedTargetGlobal(cx_
, &unwrapped
->nonCCWGlobal());
1399 if (!cx_
->compartment()->wrap(cx_
, &wrappedTargetGlobal
)) {
1400 cx_
->clearPendingException();
1401 return AttachDecision::NoAction
;
1404 NativeObject
* holder
= nullptr;
1405 Maybe
<PropertyInfo
> prop
;
1407 // Enter realm of target to prevent failing compartment assertions when doing
1410 AutoRealm
ar(cx_
, unwrapped
);
1412 NativeGetPropKind kind
=
1413 CanAttachNativeGetProp(cx_
, unwrapped
, id
, &holder
, &prop
, pc_
);
1414 if (kind
!= NativeGetPropKind::Slot
&& kind
!= NativeGetPropKind::Missing
) {
1415 return AttachDecision::NoAction
;
1418 auto* unwrappedNative
= &unwrapped
->as
<NativeObject
>();
1420 maybeEmitIdGuard(id
);
1421 writer
.guardIsProxy(objId
);
1422 writer
.guardHasProxyHandler(objId
, Wrapper::wrapperHandler(obj
));
1424 // Load the object wrapped by the CCW
1425 ObjOperandId wrapperTargetId
=
1426 writer
.loadWrapperTarget(objId
, /*fallible = */ false);
1428 // If the compartment of the wrapped object is different we should fail.
1429 writer
.guardCompartment(wrapperTargetId
, wrappedTargetGlobal
,
1430 unwrappedNative
->compartment());
1432 ObjOperandId unwrappedId
= wrapperTargetId
;
1434 EmitReadSlotResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
, holder
,
1435 *prop
, unwrappedId
);
1436 writer
.wrapResult();
1437 writer
.returnFromIC();
1438 trackAttached("GetProp.CCWSlot");
1440 EmitMissingPropResult
<IsCrossCompartment::Yes
>(writer
, unwrappedNative
,
1442 writer
.returnFromIC();
1443 trackAttached("GetProp.CCWMissing");
1445 return AttachDecision::Attach
;
1448 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
1449 Handle
<NativeObject
*> obj
);
1451 static bool GetXrayExpandoShapeWrapper(JSContext
* cx
, HandleObject xray
,
1452 MutableHandleObject wrapper
) {
1453 Value v
= GetProxyReservedSlot(xray
, GetXrayJitInfo()->xrayHolderSlot
);
1455 NativeObject
* holder
= &v
.toObject().as
<NativeObject
>();
1456 v
= holder
->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot
);
1458 Rooted
<NativeObject
*> expando(
1459 cx
, &UncheckedUnwrap(&v
.toObject())->as
<NativeObject
>());
1460 wrapper
.set(NewWrapperWithObjectShape(cx
, expando
));
1461 return wrapper
!= nullptr;
1464 wrapper
.set(nullptr);
1468 AttachDecision
GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(
1469 HandleObject obj
, ObjOperandId objId
, HandleId id
,
1470 ValOperandId receiverId
) {
1471 if (!obj
->is
<ProxyObject
>()) {
1472 return AttachDecision::NoAction
;
1475 JS::XrayJitInfo
* info
= GetXrayJitInfo();
1476 if (!info
|| !info
->isCrossCompartmentXray(GetProxyHandler(obj
))) {
1477 return AttachDecision::NoAction
;
1480 if (!info
->compartmentHasExclusiveExpandos(obj
)) {
1481 return AttachDecision::NoAction
;
1484 RootedObject
target(cx_
, UncheckedUnwrap(obj
));
1486 RootedObject
expandoShapeWrapper(cx_
);
1487 if (!GetXrayExpandoShapeWrapper(cx_
, obj
, &expandoShapeWrapper
)) {
1488 cx_
->recoverFromOutOfMemory();
1489 return AttachDecision::NoAction
;
1492 // Look for a getter we can call on the xray or its prototype chain.
1493 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx_
);
1494 RootedObject
holder(cx_
, obj
);
1495 RootedObjectVector
prototypes(cx_
);
1496 RootedObjectVector
prototypeExpandoShapeWrappers(cx_
);
1498 if (!GetOwnPropertyDescriptor(cx_
, holder
, id
, &desc
)) {
1499 cx_
->clearPendingException();
1500 return AttachDecision::NoAction
;
1502 if (desc
.isSome()) {
1505 if (!GetPrototype(cx_
, holder
, &holder
)) {
1506 cx_
->clearPendingException();
1507 return AttachDecision::NoAction
;
1509 if (!holder
|| !holder
->is
<ProxyObject
>() ||
1510 !info
->isCrossCompartmentXray(GetProxyHandler(holder
))) {
1511 return AttachDecision::NoAction
;
1513 RootedObject
prototypeExpandoShapeWrapper(cx_
);
1514 if (!GetXrayExpandoShapeWrapper(cx_
, holder
,
1515 &prototypeExpandoShapeWrapper
) ||
1516 !prototypes
.append(holder
) ||
1517 !prototypeExpandoShapeWrappers
.append(prototypeExpandoShapeWrapper
)) {
1518 cx_
->recoverFromOutOfMemory();
1519 return AttachDecision::NoAction
;
1522 if (!desc
->isAccessorDescriptor()) {
1523 return AttachDecision::NoAction
;
1526 RootedObject
getter(cx_
, desc
->getter());
1527 if (!getter
|| !getter
->is
<JSFunction
>() ||
1528 !getter
->as
<JSFunction
>().isNativeWithoutJitEntry()) {
1529 return AttachDecision::NoAction
;
1532 maybeEmitIdGuard(id
);
1533 writer
.guardIsProxy(objId
);
1534 writer
.guardHasProxyHandler(objId
, GetProxyHandler(obj
));
1536 // Load the object wrapped by the CCW
1537 ObjOperandId wrapperTargetId
=
1538 writer
.loadWrapperTarget(objId
, /*fallible = */ false);
1540 // Test the wrapped object's class. The properties held by xrays or their
1541 // prototypes will be invariant for objects of a given class, except for
1542 // changes due to xray expandos or xray prototype mutations.
1543 writer
.guardAnyClass(wrapperTargetId
, target
->getClass());
1545 // Make sure the expandos on the xray and its prototype chain match up with
1546 // what we expect. The expando shape needs to be consistent, to ensure it
1547 // has not had any shadowing properties added, and the expando cannot have
1548 // any custom prototype (xray prototypes are stable otherwise).
1550 // We can only do this for xrays with exclusive access to their expandos
1551 // (as we checked earlier), which store a pointer to their expando
1552 // directly. Xrays in other compartments may share their expandos with each
1553 // other and a VM call is needed just to find the expando.
1554 if (expandoShapeWrapper
) {
1555 writer
.guardXrayExpandoShapeAndDefaultProto(objId
, expandoShapeWrapper
);
1557 writer
.guardXrayNoExpando(objId
);
1559 for (size_t i
= 0; i
< prototypes
.length(); i
++) {
1560 JSObject
* proto
= prototypes
[i
];
1561 ObjOperandId protoId
= writer
.loadObject(proto
);
1562 if (JSObject
* protoShapeWrapper
= prototypeExpandoShapeWrappers
[i
]) {
1563 writer
.guardXrayExpandoShapeAndDefaultProto(protoId
, protoShapeWrapper
);
1565 writer
.guardXrayNoExpando(protoId
);
1569 bool sameRealm
= cx_
->realm() == getter
->as
<JSFunction
>().realm();
1570 writer
.callNativeGetterResult(receiverId
, &getter
->as
<JSFunction
>(),
1572 writer
.returnFromIC();
1574 trackAttached("GetProp.XrayCCW");
1575 return AttachDecision::Attach
;
1579 AttachDecision
GetPropIRGenerator::tryAttachScriptedProxy(
1580 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1581 if (cacheKind_
!= CacheKind::GetProp
&& cacheKind_
!= CacheKind::GetElem
) {
1582 return AttachDecision::NoAction
;
1584 if (cacheKind_
== CacheKind::GetElem
) {
1585 if (!idVal_
.isString() && !idVal_
.isInt32() && !idVal_
.isSymbol()) {
1586 return AttachDecision::NoAction
;
1590 JSObject
* handlerObj
= ScriptedProxyHandler::handlerObject(obj
);
1592 return AttachDecision::NoAction
;
1595 NativeObject
* trapHolder
= nullptr;
1596 Maybe
<PropertyInfo
> trapProp
;
1597 // We call with pc_ even though that's not the actual corresponding pc. It
1598 // should, however, be fine, because it's just used to check if this is a
1599 // GetBoundName, which it's not.
1600 NativeGetPropKind trapKind
= CanAttachNativeGetProp(
1601 cx_
, handlerObj
, NameToId(cx_
->names().get
), &trapHolder
, &trapProp
, pc_
);
1603 if (trapKind
!= NativeGetPropKind::Missing
&&
1604 trapKind
!= NativeGetPropKind::Slot
) {
1605 return AttachDecision::NoAction
;
1608 if (trapKind
!= NativeGetPropKind::Missing
) {
1609 uint32_t trapSlot
= trapProp
->slot();
1610 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1611 if (!trapVal
.isObject()) {
1612 return AttachDecision::NoAction
;
1615 JSObject
* trapObj
= &trapVal
.toObject();
1616 if (!trapObj
->is
<JSFunction
>()) {
1617 return AttachDecision::NoAction
;
1620 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1621 if (trapFn
->isClassConstructor()) {
1622 return AttachDecision::NoAction
;
1625 if (!trapFn
->hasJitEntry()) {
1626 return AttachDecision::NoAction
;
1629 if (cx_
->realm() != trapFn
->realm()) {
1630 return AttachDecision::NoAction
;
1634 NativeObject
* nHandlerObj
= &handlerObj
->as
<NativeObject
>();
1635 JSObject
* targetObj
= obj
->target();
1636 MOZ_ASSERT(targetObj
, "Guaranteed by the scripted Proxy constructor");
1638 // We just require that the target is a NativeObject to make our lives
1639 // easier. There's too much nonsense we might have to handle otherwise and
1640 // we're not set up to recursively call GetPropIRGenerator::tryAttachStub
1641 // for the target object.
1642 if (!targetObj
->is
<NativeObject
>()) {
1643 return AttachDecision::NoAction
;
1646 writer
.guardIsProxy(objId
);
1647 writer
.guardHasProxyHandler(objId
, &ScriptedProxyHandler::singleton
);
1648 ObjOperandId handlerObjId
= writer
.loadScriptedProxyHandler(objId
);
1649 ObjOperandId targetObjId
=
1650 writer
.loadWrapperTarget(objId
, /*fallible =*/true);
1652 writer
.guardIsNativeObject(targetObjId
);
1654 if (trapKind
== NativeGetPropKind::Missing
) {
1655 EmitMissingPropGuard(writer
, nHandlerObj
, handlerObjId
);
1656 if (cacheKind_
== CacheKind::GetProp
) {
1657 writer
.megamorphicLoadSlotResult(targetObjId
, id
);
1659 writer
.megamorphicLoadSlotByValueResult(objId
, getElemKeyValueId());
1662 uint32_t trapSlot
= trapProp
->slot();
1663 const Value
& trapVal
= trapHolder
->getSlot(trapSlot
);
1664 JSObject
* trapObj
= &trapVal
.toObject();
1665 JSFunction
* trapFn
= &trapObj
->as
<JSFunction
>();
1666 ObjOperandId trapHolderId
=
1667 EmitReadSlotGuard(writer
, nHandlerObj
, trapHolder
, handlerObjId
);
1669 ValOperandId fnValId
=
1670 EmitLoadSlot(writer
, trapHolder
, trapHolderId
, trapSlot
);
1671 ObjOperandId fnObjId
= writer
.guardToObject(fnValId
);
1672 emitCalleeGuard(fnObjId
, trapFn
);
1673 ValOperandId targetValId
= writer
.boxObject(targetObjId
);
1674 if (cacheKind_
== CacheKind::GetProp
) {
1675 writer
.callScriptedProxyGetResult(targetValId
, objId
, handlerObjId
,
1676 fnObjId
, trapFn
, id
);
1678 ValOperandId idId
= getElemKeyValueId();
1679 ValOperandId stringIdId
= writer
.idToStringOrSymbol(idId
);
1680 writer
.callScriptedProxyGetByValueResult(targetValId
, objId
, handlerObjId
,
1681 stringIdId
, fnObjId
, trapFn
);
1684 writer
.returnFromIC();
1686 trackAttached("GetScriptedProxy");
1687 return AttachDecision::Attach
;
1691 AttachDecision
GetPropIRGenerator::tryAttachGenericProxy(
1692 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1693 bool handleDOMProxies
) {
1694 writer
.guardIsProxy(objId
);
1696 if (!handleDOMProxies
) {
1697 // Ensure that the incoming object is not a DOM proxy, so that we can get to
1698 // the specialized stubs
1699 writer
.guardIsNotDOMProxy(objId
);
1702 if (cacheKind_
== CacheKind::GetProp
|| mode_
== ICState::Mode::Specialized
) {
1703 MOZ_ASSERT(!isSuper());
1704 maybeEmitIdGuard(id
);
1705 writer
.proxyGetResult(objId
, id
);
1707 // Attach a stub that handles every id.
1708 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
1709 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
1710 MOZ_ASSERT(!isSuper());
1711 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
1714 writer
.returnFromIC();
1716 trackAttached("GetProp.GenericProxy");
1717 return AttachDecision::Attach
;
1720 static bool ValueIsInt64Index(const Value
& val
, int64_t* index
) {
1721 // Try to convert the Value to a TypedArray index or DataView offset.
1723 if (val
.isInt32()) {
1724 *index
= val
.toInt32();
1728 if (val
.isDouble()) {
1729 // Use NumberEqualsInt64 because ToPropertyKey(-0) is 0.
1730 return mozilla::NumberEqualsInt64(val
.toDouble(), index
);
1736 IntPtrOperandId
IRGenerator::guardToIntPtrIndex(const Value
& index
,
1737 ValOperandId indexId
,
1741 MOZ_ASSERT_IF(!supportOOB
, ValueIsInt64Index(index
, &indexInt64
));
1744 if (index
.isInt32()) {
1745 Int32OperandId int32IndexId
= writer
.guardToInt32(indexId
);
1746 return writer
.int32ToIntPtr(int32IndexId
);
1749 MOZ_ASSERT(index
.isNumber());
1750 NumberOperandId numberIndexId
= writer
.guardIsNumber(indexId
);
1751 return writer
.guardNumberToIntPtrIndex(numberIndexId
, supportOOB
);
1754 ObjOperandId
IRGenerator::guardDOMProxyExpandoObjectAndShape(
1755 ProxyObject
* obj
, ObjOperandId objId
, const Value
& expandoVal
,
1756 NativeObject
* expandoObj
) {
1757 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1759 TestMatchingProxyReceiver(writer
, obj
, objId
);
1761 // Shape determines Class, so now it must be a DOM proxy.
1762 ValOperandId expandoValId
;
1763 if (expandoVal
.isObject()) {
1764 expandoValId
= writer
.loadDOMExpandoValue(objId
);
1766 expandoValId
= writer
.loadDOMExpandoValueIgnoreGeneration(objId
);
1769 // Guard the expando is an object and shape guard.
1770 ObjOperandId expandoObjId
= writer
.guardToObject(expandoValId
);
1771 TestMatchingHolder(writer
, expandoObj
, expandoObjId
);
1772 return expandoObjId
;
1775 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyExpando(
1776 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1777 ValOperandId receiverId
) {
1778 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1780 Value expandoVal
= GetProxyPrivate(obj
);
1781 JSObject
* expandoObj
;
1782 if (expandoVal
.isObject()) {
1783 expandoObj
= &expandoVal
.toObject();
1785 MOZ_ASSERT(!expandoVal
.isUndefined(),
1786 "How did a missing expando manage to shadow things?");
1787 auto expandoAndGeneration
=
1788 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1789 MOZ_ASSERT(expandoAndGeneration
);
1790 expandoObj
= &expandoAndGeneration
->expando
.toObject();
1793 // Try to do the lookup on the expando object.
1794 NativeObject
* holder
= nullptr;
1795 Maybe
<PropertyInfo
> prop
;
1796 NativeGetPropKind kind
=
1797 CanAttachNativeGetProp(cx_
, expandoObj
, id
, &holder
, &prop
, pc_
);
1798 if (kind
== NativeGetPropKind::None
) {
1799 return AttachDecision::NoAction
;
1802 return AttachDecision::NoAction
;
1804 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
1806 MOZ_ASSERT(holder
== nativeExpandoObj
);
1808 maybeEmitIdGuard(id
);
1809 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
1810 obj
, objId
, expandoVal
, nativeExpandoObj
);
1812 if (kind
== NativeGetPropKind::Slot
) {
1813 // Load from the expando's slots.
1814 EmitLoadSlotResult(writer
, expandoObjId
, nativeExpandoObj
, *prop
);
1815 writer
.returnFromIC();
1817 // Call the getter. Note that we pass objId, the DOM proxy, as |this|
1818 // and not the expando object.
1819 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1820 kind
== NativeGetPropKind::ScriptedGetter
);
1821 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
1822 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeExpandoObj
,
1823 nativeExpandoObj
, *prop
, receiverId
);
1826 trackAttached("GetProp.DOMProxyExpando");
1827 return AttachDecision::Attach
;
1830 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyShadowed(
1831 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
) {
1832 MOZ_ASSERT(!isSuper());
1833 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1835 maybeEmitIdGuard(id
);
1836 TestMatchingProxyReceiver(writer
, obj
, objId
);
1837 writer
.proxyGetResult(objId
, id
);
1838 writer
.returnFromIC();
1840 trackAttached("GetProp.DOMProxyShadowed");
1841 return AttachDecision::Attach
;
1844 // Emit CacheIR to guard the DOM proxy doesn't shadow |id|. There are two types
1847 // (a) DOM proxies marked LegacyOverrideBuiltIns in WebIDL, for example
1848 // HTMLDocument or HTMLFormElement. These proxies look up properties in this
1851 // (1) The expando object.
1852 // (2) The proxy's named-property handler.
1853 // (3) The prototype chain.
1855 // To optimize properties on the prototype chain, we have to guard that (1)
1856 // and (2) don't shadow (3). We handle (1) by either emitting a shape guard
1857 // for the expando object or by guarding the proxy has no expando object. To
1858 // efficiently handle (2), the proxy must have an ExpandoAndGeneration*
1859 // stored as PrivateValue. We guard on its generation field to ensure the
1860 // set of names hasn't changed.
1862 // Missing properties can be optimized in a similar way by emitting shape
1863 // guards for the prototype chain.
1865 // (b) Other DOM proxies. These proxies look up properties in this
1868 // (1) The expando object.
1869 // (2) The prototype chain.
1870 // (3) The proxy's named-property handler.
1872 // To optimize properties on the prototype chain, we only have to guard the
1873 // expando object doesn't shadow it.
1875 // Missing properties can't be optimized in this case because we don't have
1876 // an efficient way to guard against the proxy handler shadowing the
1877 // property (there's no ExpandoAndGeneration*).
1880 // * DOMProxyShadows in DOMJSProxyHandler.cpp
1881 // * https://webidl.spec.whatwg.org/#dfn-named-property-visibility (the Note at
1884 // Callers are expected to have already guarded on the shape of the
1885 // object, which guarantees the object is a DOM proxy.
1886 static void CheckDOMProxyDoesNotShadow(CacheIRWriter
& writer
, ProxyObject
* obj
,
1887 jsid id
, ObjOperandId objId
,
1888 bool* canOptimizeMissing
) {
1889 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1891 Value expandoVal
= GetProxyPrivate(obj
);
1893 ValOperandId expandoId
;
1894 if (!expandoVal
.isObject() && !expandoVal
.isUndefined()) {
1896 auto expandoAndGeneration
=
1897 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
1898 uint64_t generation
= expandoAndGeneration
->generation
;
1899 expandoId
= writer
.loadDOMExpandoValueGuardGeneration(
1900 objId
, expandoAndGeneration
, generation
);
1901 expandoVal
= expandoAndGeneration
->expando
;
1902 *canOptimizeMissing
= true;
1905 expandoId
= writer
.loadDOMExpandoValue(objId
);
1906 *canOptimizeMissing
= false;
1909 if (expandoVal
.isUndefined()) {
1910 // Guard there's no expando object.
1911 writer
.guardNonDoubleType(expandoId
, ValueType::Undefined
);
1912 } else if (expandoVal
.isObject()) {
1913 // Guard the proxy either has no expando object or, if it has one, that
1914 // the shape matches the current expando object.
1915 NativeObject
& expandoObj
= expandoVal
.toObject().as
<NativeObject
>();
1916 MOZ_ASSERT(!expandoObj
.containsPure(id
));
1917 writer
.guardDOMExpandoMissingOrGuardShape(expandoId
, expandoObj
.shape());
1919 MOZ_CRASH("Invalid expando value");
1923 AttachDecision
GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
1924 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
1925 ValOperandId receiverId
) {
1926 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
1928 JSObject
* protoObj
= obj
->staticPrototype();
1930 return AttachDecision::NoAction
;
1933 NativeObject
* holder
= nullptr;
1934 Maybe
<PropertyInfo
> prop
;
1935 NativeGetPropKind kind
=
1936 CanAttachNativeGetProp(cx_
, protoObj
, id
, &holder
, &prop
, pc_
);
1937 if (kind
== NativeGetPropKind::None
) {
1938 return AttachDecision::NoAction
;
1940 auto* nativeProtoObj
= &protoObj
->as
<NativeObject
>();
1942 maybeEmitIdGuard(id
);
1944 // Guard that our proxy (expando) object hasn't started shadowing this
1946 TestMatchingProxyReceiver(writer
, obj
, objId
);
1947 bool canOptimizeMissing
= false;
1948 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
1951 // Found the property on the prototype chain. Treat it like a native
1953 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
1955 // Guard on the holder of the property.
1956 ObjOperandId holderId
= writer
.loadObject(holder
);
1957 TestMatchingHolder(writer
, holder
, holderId
);
1959 if (kind
== NativeGetPropKind::Slot
) {
1960 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
1961 writer
.returnFromIC();
1963 // EmitCallGetterResultNoGuards expects |obj| to be the object the
1964 // property is on to do some checks. Since we actually looked at
1965 // checkObj, and no extra guards will be generated, we can just
1966 // pass that instead.
1967 MOZ_ASSERT(kind
== NativeGetPropKind::NativeGetter
||
1968 kind
== NativeGetPropKind::ScriptedGetter
);
1969 MOZ_ASSERT(!isSuper());
1970 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
1971 /* holderIsConstant = */ true);
1972 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, nativeProtoObj
, holder
,
1976 // Property was not found on the prototype chain.
1977 MOZ_ASSERT(kind
== NativeGetPropKind::Missing
);
1978 if (canOptimizeMissing
) {
1979 // We already guarded on the proxy's shape, so now shape guard the proto
1981 ObjOperandId protoId
= writer
.loadObject(nativeProtoObj
);
1982 EmitMissingPropResult(writer
, nativeProtoObj
, protoId
);
1984 MOZ_ASSERT(!isSuper());
1985 writer
.proxyGetResult(objId
, id
);
1987 writer
.returnFromIC();
1990 trackAttached("GetProp.DOMProxyUnshadowed");
1991 return AttachDecision::Attach
;
1994 AttachDecision
GetPropIRGenerator::tryAttachProxy(HandleObject obj
,
1997 ValOperandId receiverId
) {
1998 // The proxy stubs don't currently support |super| access.
2000 return AttachDecision::NoAction
;
2003 // Always try to attach scripted proxy get even if we're megamorphic.
2004 // In Speedometer 3 we'll often run into cases where we're megamorphic
2005 // overall, but monomorphic for the proxy case. This is because there
2006 // are functions which lazily turn various differently-shaped objects
2007 // into proxies. So the un-proxified objects are megamorphic, but the
2008 // proxy handlers are actually monomorphic. There is room for a bit
2009 // more sophistication here, but this should do for now.
2010 if (!obj
->is
<ProxyObject
>()) {
2011 return AttachDecision::NoAction
;
2013 auto proxy
= obj
.as
<ProxyObject
>();
2015 if (proxy
->handler()->isScripted()) {
2016 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, id
));
2020 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
2021 if (type
== ProxyStubType::None
) {
2022 return AttachDecision::NoAction
;
2025 if (mode_
== ICState::Mode::Megamorphic
) {
2026 return tryAttachGenericProxy(proxy
, objId
, id
,
2027 /* handleDOMProxies = */ true);
2031 case ProxyStubType::None
:
2033 case ProxyStubType::DOMExpando
:
2034 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, receiverId
));
2035 [[fallthrough
]]; // Fall through to the generic shadowed case.
2036 case ProxyStubType::DOMShadowed
:
2037 return tryAttachDOMProxyShadowed(proxy
, objId
, id
);
2038 case ProxyStubType::DOMUnshadowed
:
2039 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, receiverId
));
2040 return tryAttachGenericProxy(proxy
, objId
, id
,
2041 /* handleDOMProxies = */ true);
2042 case ProxyStubType::Generic
:
2043 return tryAttachGenericProxy(proxy
, objId
, id
,
2044 /* handleDOMProxies = */ false);
2047 MOZ_CRASH("Unexpected ProxyStubType");
2050 const JSClass
* js::jit::ClassFor(GuardClassKind kind
) {
2052 case GuardClassKind::Array
:
2053 return &ArrayObject::class_
;
2054 case GuardClassKind::PlainObject
:
2055 return &PlainObject::class_
;
2056 case GuardClassKind::FixedLengthArrayBuffer
:
2057 return &FixedLengthArrayBufferObject::class_
;
2058 case GuardClassKind::ResizableArrayBuffer
:
2059 return &ResizableArrayBufferObject::class_
;
2060 case GuardClassKind::FixedLengthSharedArrayBuffer
:
2061 return &FixedLengthSharedArrayBufferObject::class_
;
2062 case GuardClassKind::GrowableSharedArrayBuffer
:
2063 return &GrowableSharedArrayBufferObject::class_
;
2064 case GuardClassKind::FixedLengthDataView
:
2065 return &FixedLengthDataViewObject::class_
;
2066 case GuardClassKind::ResizableDataView
:
2067 return &ResizableDataViewObject::class_
;
2068 case GuardClassKind::MappedArguments
:
2069 return &MappedArgumentsObject::class_
;
2070 case GuardClassKind::UnmappedArguments
:
2071 return &UnmappedArgumentsObject::class_
;
2072 case GuardClassKind::WindowProxy
:
2073 // Caller needs to handle this case, see
2074 // JSRuntime::maybeWindowProxyClass().
2076 case GuardClassKind::JSFunction
:
2077 // Caller needs to handle this case. Can be either |js::FunctionClass| or
2078 // |js::ExtendedFunctionClass|.
2080 case GuardClassKind::BoundFunction
:
2081 return &BoundFunctionObject::class_
;
2082 case GuardClassKind::Set
:
2083 return &SetObject::class_
;
2084 case GuardClassKind::Map
:
2085 return &MapObject::class_
;
2087 MOZ_CRASH("unexpected kind");
2090 // Guards the class of an object. Because shape implies class, and a shape guard
2091 // is faster than a class guard, if this is our first time attaching a stub, we
2092 // instead generate a shape guard.
2093 void IRGenerator::emitOptimisticClassGuard(ObjOperandId objId
, JSObject
* obj
,
2094 GuardClassKind kind
) {
2097 case GuardClassKind::Array
:
2098 case GuardClassKind::PlainObject
:
2099 case GuardClassKind::FixedLengthArrayBuffer
:
2100 case GuardClassKind::ResizableArrayBuffer
:
2101 case GuardClassKind::FixedLengthSharedArrayBuffer
:
2102 case GuardClassKind::GrowableSharedArrayBuffer
:
2103 case GuardClassKind::FixedLengthDataView
:
2104 case GuardClassKind::ResizableDataView
:
2105 case GuardClassKind::Set
:
2106 case GuardClassKind::Map
:
2107 MOZ_ASSERT(obj
->hasClass(ClassFor(kind
)));
2110 case GuardClassKind::MappedArguments
:
2111 case GuardClassKind::UnmappedArguments
:
2112 case GuardClassKind::JSFunction
:
2113 case GuardClassKind::BoundFunction
:
2114 case GuardClassKind::WindowProxy
:
2115 // Arguments, functions, and the global object have
2116 // less consistent shapes.
2117 MOZ_CRASH("GuardClassKind not supported");
2122 writer
.guardShapeForClass(objId
, obj
->shape());
2124 writer
.guardClass(objId
, kind
);
2128 static void AssertArgumentsCustomDataProp(ArgumentsObject
* obj
,
2131 // The property must still be a custom data property if it has been resolved.
2132 // If this assertion fails, we're probably missing a call to mark this
2133 // property overridden.
2134 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(key
);
2135 MOZ_ASSERT_IF(prop
, prop
->isCustomDataProperty());
2139 AttachDecision
GetPropIRGenerator::tryAttachObjectLength(HandleObject obj
,
2142 if (!id
.isAtom(cx_
->names().length
)) {
2143 return AttachDecision::NoAction
;
2146 if (obj
->is
<ArrayObject
>()) {
2147 if (obj
->as
<ArrayObject
>().length() > INT32_MAX
) {
2148 return AttachDecision::NoAction
;
2151 maybeEmitIdGuard(id
);
2152 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
2153 writer
.loadInt32ArrayLengthResult(objId
);
2154 writer
.returnFromIC();
2156 trackAttached("GetProp.ArrayLength");
2157 return AttachDecision::Attach
;
2160 if (obj
->is
<ArgumentsObject
>() &&
2161 !obj
->as
<ArgumentsObject
>().hasOverriddenLength()) {
2162 AssertArgumentsCustomDataProp(&obj
->as
<ArgumentsObject
>(), id
);
2163 maybeEmitIdGuard(id
);
2164 if (obj
->is
<MappedArgumentsObject
>()) {
2165 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2167 MOZ_ASSERT(obj
->is
<UnmappedArgumentsObject
>());
2168 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2170 writer
.loadArgumentsObjectLengthResult(objId
);
2171 writer
.returnFromIC();
2173 trackAttached("GetProp.ArgumentsObjectLength");
2174 return AttachDecision::Attach
;
2177 return AttachDecision::NoAction
;
2180 AttachDecision
GetPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
2183 if (!obj
->is
<TypedArrayObject
>()) {
2184 return AttachDecision::NoAction
;
2187 if (mode_
!= ICState::Mode::Specialized
) {
2188 return AttachDecision::NoAction
;
2191 // Receiver should be the object.
2193 return AttachDecision::NoAction
;
2196 bool isLength
= id
.isAtom(cx_
->names().length
);
2197 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2198 if (!isLength
&& !isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2199 return AttachDecision::NoAction
;
2202 NativeObject
* holder
= nullptr;
2203 Maybe
<PropertyInfo
> prop
;
2204 NativeGetPropKind kind
=
2205 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2206 if (kind
!= NativeGetPropKind::NativeGetter
) {
2207 return AttachDecision::NoAction
;
2210 JSFunction
& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2212 if (!TypedArrayObject::isOriginalLengthGetter(fun
.native())) {
2213 return AttachDecision::NoAction
;
2215 } else if (isByteOffset
) {
2216 if (!TypedArrayObject::isOriginalByteOffsetGetter(fun
.native())) {
2217 return AttachDecision::NoAction
;
2220 if (!TypedArrayObject::isOriginalByteLengthGetter(fun
.native())) {
2221 return AttachDecision::NoAction
;
2225 auto* tarr
= &obj
->as
<TypedArrayObject
>();
2227 maybeEmitIdGuard(id
);
2228 // Emit all the normal guards for calling this native, but specialize
2229 // callNativeGetterResult.
2230 EmitCallGetterResultGuards(writer
, tarr
, holder
, id
, *prop
, objId
, mode_
);
2232 size_t length
= tarr
->length().valueOr(0);
2233 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
2234 if (length
<= INT32_MAX
) {
2235 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2237 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2240 if (length
<= INT32_MAX
) {
2241 writer
.resizableTypedArrayLengthInt32Result(objId
);
2243 writer
.resizableTypedArrayLengthDoubleResult(objId
);
2246 trackAttached("GetProp.TypedArrayLength");
2247 } else if (isByteOffset
) {
2248 // byteOffset doesn't need to use different code paths for fixed-length and
2249 // resizable TypedArrays.
2250 size_t byteOffset
= tarr
->byteOffset().valueOr(0);
2251 if (byteOffset
<= INT32_MAX
) {
2252 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2254 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2256 trackAttached("GetProp.TypedArrayByteOffset");
2258 size_t byteLength
= tarr
->byteLength().valueOr(0);
2259 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
2260 if (byteLength
<= INT32_MAX
) {
2261 writer
.typedArrayByteLengthInt32Result(objId
);
2263 writer
.typedArrayByteLengthDoubleResult(objId
);
2266 if (byteLength
<= INT32_MAX
) {
2267 writer
.resizableTypedArrayByteLengthInt32Result(objId
);
2269 writer
.resizableTypedArrayByteLengthDoubleResult(objId
);
2272 trackAttached("GetProp.TypedArrayByteLength");
2274 writer
.returnFromIC();
2276 return AttachDecision::Attach
;
2279 AttachDecision
GetPropIRGenerator::tryAttachDataView(HandleObject obj
,
2282 if (!obj
->is
<DataViewObject
>()) {
2283 return AttachDecision::NoAction
;
2285 auto* dv
= &obj
->as
<DataViewObject
>();
2287 if (mode_
!= ICState::Mode::Specialized
) {
2288 return AttachDecision::NoAction
;
2291 // Receiver should be the object.
2293 return AttachDecision::NoAction
;
2296 bool isByteOffset
= id
.isAtom(cx_
->names().byteOffset
);
2297 if (!isByteOffset
&& !id
.isAtom(cx_
->names().byteLength
)) {
2298 return AttachDecision::NoAction
;
2301 // byteOffset and byteLength both throw when the ArrayBuffer is detached.
2302 if (dv
->hasDetachedBuffer()) {
2303 return AttachDecision::NoAction
;
2306 // byteOffset and byteLength both throw when the ArrayBuffer is out-of-bounds.
2307 if (dv
->is
<ResizableDataViewObject
>() &&
2308 dv
->as
<ResizableDataViewObject
>().isOutOfBounds()) {
2309 return AttachDecision::NoAction
;
2312 NativeObject
* holder
= nullptr;
2313 Maybe
<PropertyInfo
> prop
;
2314 NativeGetPropKind kind
=
2315 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2316 if (kind
!= NativeGetPropKind::NativeGetter
) {
2317 return AttachDecision::NoAction
;
2320 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2322 if (!DataViewObject::isOriginalByteOffsetGetter(fun
.native())) {
2323 return AttachDecision::NoAction
;
2326 if (!DataViewObject::isOriginalByteLengthGetter(fun
.native())) {
2327 return AttachDecision::NoAction
;
2331 maybeEmitIdGuard(id
);
2332 // Emit all the normal guards for calling this native, but specialize
2333 // callNativeGetterResult.
2334 EmitCallGetterResultGuards(writer
, dv
, holder
, id
, *prop
, objId
, mode_
);
2335 writer
.guardHasAttachedArrayBuffer(objId
);
2336 if (dv
->is
<ResizableDataViewObject
>()) {
2337 writer
.guardResizableArrayBufferViewInBounds(objId
);
2340 // byteOffset doesn't need to use different code paths for fixed-length and
2341 // resizable DataViews.
2342 size_t byteOffset
= dv
->byteOffset().valueOr(0);
2343 if (byteOffset
<= INT32_MAX
) {
2344 writer
.arrayBufferViewByteOffsetInt32Result(objId
);
2346 writer
.arrayBufferViewByteOffsetDoubleResult(objId
);
2348 trackAttached("GetProp.DataViewByteOffset");
2350 size_t byteLength
= dv
->byteLength().valueOr(0);
2351 if (dv
->is
<FixedLengthDataViewObject
>()) {
2352 if (byteLength
<= INT32_MAX
) {
2353 writer
.loadArrayBufferViewLengthInt32Result(objId
);
2355 writer
.loadArrayBufferViewLengthDoubleResult(objId
);
2358 if (byteLength
<= INT32_MAX
) {
2359 writer
.resizableDataViewByteLengthInt32Result(objId
);
2361 writer
.resizableDataViewByteLengthDoubleResult(objId
);
2364 trackAttached("GetProp.DataViewByteLength");
2366 writer
.returnFromIC();
2368 return AttachDecision::Attach
;
2371 AttachDecision
GetPropIRGenerator::tryAttachArrayBufferMaybeShared(
2372 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2373 if (!obj
->is
<ArrayBufferObjectMaybeShared
>()) {
2374 return AttachDecision::NoAction
;
2376 auto* buf
= &obj
->as
<ArrayBufferObjectMaybeShared
>();
2378 if (mode_
!= ICState::Mode::Specialized
) {
2379 return AttachDecision::NoAction
;
2382 // Receiver should be the object.
2384 return AttachDecision::NoAction
;
2387 if (!id
.isAtom(cx_
->names().byteLength
)) {
2388 return AttachDecision::NoAction
;
2391 NativeObject
* holder
= nullptr;
2392 Maybe
<PropertyInfo
> prop
;
2393 NativeGetPropKind kind
=
2394 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2395 if (kind
!= NativeGetPropKind::NativeGetter
) {
2396 return AttachDecision::NoAction
;
2399 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2400 if (buf
->is
<ArrayBufferObject
>()) {
2401 if (!ArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2402 return AttachDecision::NoAction
;
2405 if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun
.native())) {
2406 return AttachDecision::NoAction
;
2410 maybeEmitIdGuard(id
);
2411 // Emit all the normal guards for calling this native, but specialize
2412 // callNativeGetterResult.
2413 EmitCallGetterResultGuards(writer
, buf
, holder
, id
, *prop
, objId
, mode_
);
2414 if (!buf
->is
<GrowableSharedArrayBufferObject
>()) {
2415 if (buf
->byteLength() <= INT32_MAX
) {
2416 writer
.loadArrayBufferByteLengthInt32Result(objId
);
2418 writer
.loadArrayBufferByteLengthDoubleResult(objId
);
2421 if (buf
->byteLength() <= INT32_MAX
) {
2422 writer
.growableSharedArrayBufferByteLengthInt32Result(objId
);
2424 writer
.growableSharedArrayBufferByteLengthDoubleResult(objId
);
2427 writer
.returnFromIC();
2429 trackAttached("GetProp.ArrayBufferMaybeSharedByteLength");
2430 return AttachDecision::Attach
;
2433 AttachDecision
GetPropIRGenerator::tryAttachRegExp(HandleObject obj
,
2436 if (!obj
->is
<RegExpObject
>()) {
2437 return AttachDecision::NoAction
;
2439 auto* regExp
= &obj
->as
<RegExpObject
>();
2441 if (mode_
!= ICState::Mode::Specialized
) {
2442 return AttachDecision::NoAction
;
2445 // Receiver should be the object.
2447 return AttachDecision::NoAction
;
2450 NativeObject
* holder
= nullptr;
2451 Maybe
<PropertyInfo
> prop
;
2452 NativeGetPropKind kind
=
2453 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2454 if (kind
!= NativeGetPropKind::NativeGetter
) {
2455 return AttachDecision::NoAction
;
2458 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2459 JS::RegExpFlags flags
= JS::RegExpFlag::NoFlags
;
2460 if (!RegExpObject::isOriginalFlagGetter(fun
.native(), &flags
)) {
2461 return AttachDecision::NoAction
;
2464 maybeEmitIdGuard(id
);
2465 // Emit all the normal guards for calling this native, but specialize
2466 // callNativeGetterResult.
2467 EmitCallGetterResultGuards(writer
, regExp
, holder
, id
, *prop
, objId
, mode_
);
2469 writer
.regExpFlagResult(objId
, flags
.value());
2470 writer
.returnFromIC();
2472 trackAttached("GetProp.RegExpFlag");
2473 return AttachDecision::Attach
;
2476 AttachDecision
GetPropIRGenerator::tryAttachMap(HandleObject obj
,
2479 if (!obj
->is
<MapObject
>()) {
2480 return AttachDecision::NoAction
;
2482 auto* mapObj
= &obj
->as
<MapObject
>();
2484 if (mode_
!= ICState::Mode::Specialized
) {
2485 return AttachDecision::NoAction
;
2488 // Receiver should be the object.
2490 return AttachDecision::NoAction
;
2493 if (!id
.isAtom(cx_
->names().size
)) {
2494 return AttachDecision::NoAction
;
2497 NativeObject
* holder
= nullptr;
2498 Maybe
<PropertyInfo
> prop
;
2499 NativeGetPropKind kind
=
2500 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2501 if (kind
!= NativeGetPropKind::NativeGetter
) {
2502 return AttachDecision::NoAction
;
2505 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2506 if (!MapObject::isOriginalSizeGetter(fun
.native())) {
2507 return AttachDecision::NoAction
;
2510 maybeEmitIdGuard(id
);
2512 // Emit all the normal guards for calling this native, but specialize
2513 // callNativeGetterResult.
2514 EmitCallGetterResultGuards(writer
, mapObj
, holder
, id
, *prop
, objId
, mode_
);
2516 writer
.mapSizeResult(objId
);
2517 writer
.returnFromIC();
2519 trackAttached("GetProp.MapSize");
2520 return AttachDecision::Attach
;
2523 AttachDecision
GetPropIRGenerator::tryAttachSet(HandleObject obj
,
2526 if (!obj
->is
<SetObject
>()) {
2527 return AttachDecision::NoAction
;
2529 auto* setObj
= &obj
->as
<SetObject
>();
2531 if (mode_
!= ICState::Mode::Specialized
) {
2532 return AttachDecision::NoAction
;
2535 // Receiver should be the object.
2537 return AttachDecision::NoAction
;
2540 if (!id
.isAtom(cx_
->names().size
)) {
2541 return AttachDecision::NoAction
;
2544 NativeObject
* holder
= nullptr;
2545 Maybe
<PropertyInfo
> prop
;
2546 NativeGetPropKind kind
=
2547 CanAttachNativeGetProp(cx_
, obj
, id
, &holder
, &prop
, pc_
);
2548 if (kind
!= NativeGetPropKind::NativeGetter
) {
2549 return AttachDecision::NoAction
;
2552 auto& fun
= holder
->getGetter(*prop
)->as
<JSFunction
>();
2553 if (!SetObject::isOriginalSizeGetter(fun
.native())) {
2554 return AttachDecision::NoAction
;
2557 maybeEmitIdGuard(id
);
2559 // Emit all the normal guards for calling this native, but specialize
2560 // callNativeGetterResult.
2561 EmitCallGetterResultGuards(writer
, setObj
, holder
, id
, *prop
, objId
, mode_
);
2563 writer
.setSizeResult(objId
);
2564 writer
.returnFromIC();
2566 trackAttached("GetProp.SetSize");
2567 return AttachDecision::Attach
;
2570 AttachDecision
GetPropIRGenerator::tryAttachFunction(HandleObject obj
,
2573 // Function properties are lazily resolved so they might not be defined yet.
2574 // And we might end up in a situation where we always have a fresh function
2575 // object during the IC generation.
2576 if (!obj
->is
<JSFunction
>()) {
2577 return AttachDecision::NoAction
;
2580 bool isLength
= id
.isAtom(cx_
->names().length
);
2581 if (!isLength
&& !id
.isAtom(cx_
->names().name
)) {
2582 return AttachDecision::NoAction
;
2585 NativeObject
* holder
= nullptr;
2586 PropertyResult prop
;
2587 // If this property exists already, don't attach the stub.
2588 if (LookupPropertyPure(cx_
, obj
, id
, &holder
, &prop
)) {
2589 return AttachDecision::NoAction
;
2592 JSFunction
* fun
= &obj
->as
<JSFunction
>();
2595 // length was probably deleted from the function.
2596 if (fun
->hasResolvedLength()) {
2597 return AttachDecision::NoAction
;
2600 // Lazy functions don't store the length.
2601 if (!fun
->hasBytecode()) {
2602 return AttachDecision::NoAction
;
2605 // name was probably deleted from the function.
2606 if (fun
->hasResolvedName()) {
2607 return AttachDecision::NoAction
;
2611 maybeEmitIdGuard(id
);
2612 writer
.guardClass(objId
, GuardClassKind::JSFunction
);
2614 writer
.loadFunctionLengthResult(objId
);
2615 writer
.returnFromIC();
2616 trackAttached("GetProp.FunctionLength");
2618 writer
.loadFunctionNameResult(objId
);
2619 writer
.returnFromIC();
2620 trackAttached("GetProp.FunctionName");
2622 return AttachDecision::Attach
;
2625 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectIterator(
2626 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
2627 if (!obj
->is
<ArgumentsObject
>()) {
2628 return AttachDecision::NoAction
;
2631 if (!id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
2632 return AttachDecision::NoAction
;
2635 Handle
<ArgumentsObject
*> args
= obj
.as
<ArgumentsObject
>();
2636 if (args
->hasOverriddenIterator()) {
2637 return AttachDecision::NoAction
;
2640 AssertArgumentsCustomDataProp(args
, id
);
2642 RootedValue
iterator(cx_
);
2643 if (!ArgumentsObject::getArgumentsIterator(cx_
, &iterator
)) {
2644 cx_
->recoverFromOutOfMemory();
2645 return AttachDecision::NoAction
;
2647 MOZ_ASSERT(iterator
.isObject());
2649 maybeEmitIdGuard(id
);
2650 if (args
->is
<MappedArgumentsObject
>()) {
2651 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2653 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2654 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2656 uint32_t flags
= ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
;
2657 writer
.guardArgumentsObjectFlags(objId
, flags
);
2659 ObjOperandId iterId
= writer
.loadObject(&iterator
.toObject());
2660 writer
.loadObjectResult(iterId
);
2661 writer
.returnFromIC();
2663 trackAttached("GetProp.ArgumentsObjectIterator");
2664 return AttachDecision::Attach
;
2667 AttachDecision
GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj
,
2670 if (!obj
->is
<ModuleNamespaceObject
>()) {
2671 return AttachDecision::NoAction
;
2674 auto* ns
= &obj
->as
<ModuleNamespaceObject
>();
2675 ModuleEnvironmentObject
* env
= nullptr;
2676 Maybe
<PropertyInfo
> prop
;
2677 if (!ns
->bindings().lookup(id
, &env
, &prop
)) {
2678 return AttachDecision::NoAction
;
2681 // Don't emit a stub until the target binding has been initialized.
2682 if (env
->getSlot(prop
->slot()).isMagic(JS_UNINITIALIZED_LEXICAL
)) {
2683 return AttachDecision::NoAction
;
2686 // Check for the specific namespace object.
2687 maybeEmitIdGuard(id
);
2688 writer
.guardSpecificObject(objId
, ns
);
2690 ObjOperandId envId
= writer
.loadObject(env
);
2691 EmitLoadSlotResult(writer
, envId
, env
, *prop
);
2692 writer
.returnFromIC();
2694 trackAttached("GetProp.ModuleNamespace");
2695 return AttachDecision::Attach
;
2698 AttachDecision
GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId
,
2700 MOZ_ASSERT(!isSuper(), "SuperBase is guaranteed to be an object");
2702 JSProtoKey protoKey
;
2703 switch (val_
.type()) {
2704 case ValueType::String
:
2705 if (id
.isAtom(cx_
->names().length
)) {
2706 // String length is special-cased, see js::GetProperty.
2707 return AttachDecision::NoAction
;
2709 protoKey
= JSProto_String
;
2711 case ValueType::Int32
:
2712 case ValueType::Double
:
2713 protoKey
= JSProto_Number
;
2715 case ValueType::Boolean
:
2716 protoKey
= JSProto_Boolean
;
2718 case ValueType::Symbol
:
2719 protoKey
= JSProto_Symbol
;
2721 case ValueType::BigInt
:
2722 protoKey
= JSProto_BigInt
;
2724 case ValueType::Null
:
2725 case ValueType::Undefined
:
2726 case ValueType::Magic
:
2727 return AttachDecision::NoAction
;
2728 #ifdef ENABLE_RECORD_TUPLE
2729 case ValueType::ExtendedPrimitive
:
2731 case ValueType::Object
:
2732 case ValueType::PrivateGCThing
:
2733 MOZ_CRASH("unexpected type");
2736 JSObject
* proto
= GlobalObject::getOrCreatePrototype(cx_
, protoKey
);
2738 cx_
->recoverFromOutOfMemory();
2739 return AttachDecision::NoAction
;
2742 NativeObject
* holder
= nullptr;
2743 Maybe
<PropertyInfo
> prop
;
2744 NativeGetPropKind kind
=
2745 CanAttachNativeGetProp(cx_
, proto
, id
, &holder
, &prop
, pc_
);
2747 case NativeGetPropKind::None
:
2748 return AttachDecision::NoAction
;
2749 case NativeGetPropKind::Missing
:
2750 case NativeGetPropKind::Slot
: {
2751 auto* nproto
= &proto
->as
<NativeObject
>();
2753 if (val_
.isNumber()) {
2754 writer
.guardIsNumber(valId
);
2756 writer
.guardNonDoubleType(valId
, val_
.type());
2758 maybeEmitIdGuard(id
);
2760 ObjOperandId protoId
= writer
.loadObject(nproto
);
2761 if (kind
== NativeGetPropKind::Slot
) {
2762 EmitReadSlotResult(writer
, nproto
, holder
, *prop
, protoId
);
2763 writer
.returnFromIC();
2764 trackAttached("GetProp.PrimitiveSlot");
2766 EmitMissingPropResult(writer
, nproto
, protoId
);
2767 writer
.returnFromIC();
2768 trackAttached("GetProp.PrimitiveMissing");
2770 return AttachDecision::Attach
;
2772 case NativeGetPropKind::ScriptedGetter
:
2773 case NativeGetPropKind::NativeGetter
: {
2774 auto* nproto
= &proto
->as
<NativeObject
>();
2776 if (val_
.isNumber()) {
2777 writer
.guardIsNumber(valId
);
2779 writer
.guardNonDoubleType(valId
, val_
.type());
2781 maybeEmitIdGuard(id
);
2783 ObjOperandId protoId
= writer
.loadObject(nproto
);
2784 EmitCallGetterResult(cx_
, writer
, kind
, nproto
, holder
, id
, *prop
,
2785 protoId
, valId
, mode_
);
2787 trackAttached("GetProp.PrimitiveGetter");
2788 return AttachDecision::Attach
;
2792 MOZ_CRASH("Bad NativeGetPropKind");
2795 AttachDecision
GetPropIRGenerator::tryAttachStringLength(ValOperandId valId
,
2797 if (!val_
.isString() || !id
.isAtom(cx_
->names().length
)) {
2798 return AttachDecision::NoAction
;
2801 StringOperandId strId
= writer
.guardToString(valId
);
2802 maybeEmitIdGuard(id
);
2803 writer
.loadStringLengthResult(strId
);
2804 writer
.returnFromIC();
2806 trackAttached("GetProp.StringLength");
2807 return AttachDecision::Attach
;
2810 enum class AttachStringChar
{ No
, Yes
, Linearize
, OutOfBounds
};
2812 static AttachStringChar
CanAttachStringChar(const Value
& val
,
2815 if (!val
.isString() || !idVal
.isInt32()) {
2816 return AttachStringChar::No
;
2819 JSString
* str
= val
.toString();
2820 int32_t index
= idVal
.toInt32();
2822 if (index
< 0 && kind
== StringChar::At
) {
2823 static_assert(JSString::MAX_LENGTH
<= INT32_MAX
,
2824 "string length fits in int32");
2825 index
+= int32_t(str
->length());
2828 if (index
< 0 || size_t(index
) >= str
->length()) {
2829 return AttachStringChar::OutOfBounds
;
2832 // This follows JSString::getChar and MacroAssembler::loadStringChar.
2833 if (str
->isRope()) {
2834 JSRope
* rope
= &str
->asRope();
2835 if (size_t(index
) < rope
->leftChild()->length()) {
2836 str
= rope
->leftChild();
2838 // MacroAssembler::loadStringChar doesn't support surrogate pairs which
2839 // are split between the left and right child of a rope.
2840 if (kind
== StringChar::CodePointAt
&&
2841 size_t(index
) + 1 == str
->length() && str
->isLinear()) {
2842 // Linearize the string when the last character of the left child is a
2843 // a lead surrogate.
2844 char16_t ch
= str
->asLinear().latin1OrTwoByteChar(index
);
2845 if (unicode::IsLeadSurrogate(ch
)) {
2846 return AttachStringChar::Linearize
;
2850 str
= rope
->rightChild();
2854 if (!str
->isLinear()) {
2855 return AttachStringChar::Linearize
;
2858 return AttachStringChar::Yes
;
2861 AttachDecision
GetPropIRGenerator::tryAttachStringChar(ValOperandId valId
,
2862 ValOperandId indexId
) {
2863 MOZ_ASSERT(idVal_
.isInt32());
2865 auto attach
= CanAttachStringChar(val_
, idVal_
, StringChar::CharAt
);
2866 if (attach
== AttachStringChar::No
) {
2867 return AttachDecision::NoAction
;
2870 // Can't attach for out-of-bounds access without guarding that indexed
2871 // properties aren't present along the prototype chain of |String.prototype|.
2872 if (attach
== AttachStringChar::OutOfBounds
) {
2873 return AttachDecision::NoAction
;
2876 StringOperandId strId
= writer
.guardToString(valId
);
2877 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
2878 if (attach
== AttachStringChar::Linearize
) {
2879 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
2881 writer
.loadStringCharResult(strId
, int32IndexId
, /* handleOOB = */ false);
2882 writer
.returnFromIC();
2884 trackAttached("GetProp.StringChar");
2885 return AttachDecision::Attach
;
2888 static bool ClassCanHaveExtraProperties(const JSClass
* clasp
) {
2889 return clasp
->getResolve() || clasp
->getOpsLookupProperty() ||
2890 clasp
->getOpsGetProperty() || IsTypedArrayClass(clasp
);
2893 enum class OwnProperty
: bool { No
, Yes
};
2894 enum class AllowIndexedReceiver
: bool { No
, Yes
};
2895 enum class AllowExtraReceiverProperties
: bool { No
, Yes
};
2897 static bool CanAttachDenseElementHole(
2898 NativeObject
* obj
, OwnProperty ownProp
,
2899 AllowIndexedReceiver allowIndexedReceiver
= AllowIndexedReceiver::No
,
2900 AllowExtraReceiverProperties allowExtraReceiverProperties
=
2901 AllowExtraReceiverProperties::No
) {
2902 // Make sure the objects on the prototype don't have any indexed properties
2903 // or that such properties can't appear without a shape change.
2904 // Otherwise returning undefined for holes would obviously be incorrect,
2905 // because we would have to lookup a property on the prototype instead.
2907 // The first two checks are also relevant to the receiver object.
2908 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
2911 allowIndexedReceiver
= AllowIndexedReceiver::No
;
2913 if (allowExtraReceiverProperties
== AllowExtraReceiverProperties::No
&&
2914 ClassCanHaveExtraProperties(obj
->getClass())) {
2917 allowExtraReceiverProperties
= AllowExtraReceiverProperties::No
;
2919 // Don't need to check prototype for OwnProperty checks
2920 if (ownProp
== OwnProperty::Yes
) {
2924 JSObject
* proto
= obj
->staticPrototype();
2929 if (!proto
->is
<NativeObject
>()) {
2933 // Make sure objects on the prototype don't have dense elements.
2934 if (proto
->as
<NativeObject
>().getDenseInitializedLength() != 0) {
2938 obj
= &proto
->as
<NativeObject
>();
2944 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArg(
2945 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2946 Int32OperandId indexId
) {
2947 if (!obj
->is
<ArgumentsObject
>()) {
2948 return AttachDecision::NoAction
;
2950 auto* args
= &obj
->as
<ArgumentsObject
>();
2952 // No elements must have been overridden or deleted.
2953 if (args
->hasOverriddenElement()) {
2954 return AttachDecision::NoAction
;
2958 if (index
>= args
->initialLength()) {
2959 return AttachDecision::NoAction
;
2962 AssertArgumentsCustomDataProp(args
, PropertyKey::Int(index
));
2964 // And finally also check that the argument isn't forwarded.
2965 if (args
->argIsForwarded(index
)) {
2966 return AttachDecision::NoAction
;
2969 if (args
->is
<MappedArgumentsObject
>()) {
2970 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
2972 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
2973 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
2976 writer
.loadArgumentsObjectArgResult(objId
, indexId
);
2977 writer
.returnFromIC();
2979 trackAttached("GetProp.ArgumentsObjectArg");
2980 return AttachDecision::Attach
;
2983 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectArgHole(
2984 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
2985 Int32OperandId indexId
) {
2986 if (!obj
->is
<ArgumentsObject
>()) {
2987 return AttachDecision::NoAction
;
2989 auto* args
= &obj
->as
<ArgumentsObject
>();
2991 // No elements must have been overridden or deleted.
2992 if (args
->hasOverriddenElement()) {
2993 return AttachDecision::NoAction
;
2996 // And also check that the argument isn't forwarded.
2997 if (index
< args
->initialLength() && args
->argIsForwarded(index
)) {
2998 return AttachDecision::NoAction
;
3001 if (!CanAttachDenseElementHole(args
, OwnProperty::No
,
3002 AllowIndexedReceiver::Yes
,
3003 AllowExtraReceiverProperties::Yes
)) {
3004 return AttachDecision::NoAction
;
3007 // We don't need to guard on the shape, because we check if any element is
3008 // overridden. Elements are marked as overridden iff any element is defined,
3009 // irrespective of whether the element is in-bounds or out-of-bounds. So when
3010 // that flag isn't set, we can guarantee that the arguments object doesn't
3011 // have any additional own elements.
3013 if (args
->is
<MappedArgumentsObject
>()) {
3014 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3016 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
3017 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
3020 GeneratePrototypeHoleGuards(writer
, args
, objId
,
3021 /* alwaysGuardFirstProto = */ true);
3023 writer
.loadArgumentsObjectArgHoleResult(objId
, indexId
);
3024 writer
.returnFromIC();
3026 trackAttached("GetProp.ArgumentsObjectArgHole");
3027 return AttachDecision::Attach
;
3030 AttachDecision
GetPropIRGenerator::tryAttachArgumentsObjectCallee(
3031 HandleObject obj
, ObjOperandId objId
, HandleId id
) {
3032 // Only mapped arguments objects have a `callee` property.
3033 if (!obj
->is
<MappedArgumentsObject
>()) {
3034 return AttachDecision::NoAction
;
3037 if (!id
.isAtom(cx_
->names().callee
)) {
3038 return AttachDecision::NoAction
;
3041 // The callee must not have been overridden or deleted.
3042 MappedArgumentsObject
* args
= &obj
->as
<MappedArgumentsObject
>();
3043 if (args
->hasOverriddenCallee()) {
3044 return AttachDecision::NoAction
;
3047 AssertArgumentsCustomDataProp(args
, id
);
3049 maybeEmitIdGuard(id
);
3050 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3052 uint32_t flags
= ArgumentsObject::CALLEE_OVERRIDDEN_BIT
;
3053 writer
.guardArgumentsObjectFlags(objId
, flags
);
3055 writer
.loadFixedSlotResult(objId
,
3056 MappedArgumentsObject::getCalleeSlotOffset());
3057 writer
.returnFromIC();
3059 trackAttached("GetProp.ArgumentsObjectCallee");
3060 return AttachDecision::Attach
;
3063 AttachDecision
GetPropIRGenerator::tryAttachDenseElement(
3064 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3065 Int32OperandId indexId
) {
3066 if (!obj
->is
<NativeObject
>()) {
3067 return AttachDecision::NoAction
;
3070 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3071 if (!nobj
->containsDenseElement(index
)) {
3072 return AttachDecision::NoAction
;
3075 if (mode_
== ICState::Mode::Megamorphic
) {
3076 writer
.guardIsNativeObject(objId
);
3078 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3080 writer
.loadDenseElementResult(objId
, indexId
);
3081 writer
.returnFromIC();
3083 trackAttached("GetProp.DenseElement");
3084 return AttachDecision::Attach
;
3087 AttachDecision
GetPropIRGenerator::tryAttachDenseElementHole(
3088 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3089 Int32OperandId indexId
) {
3090 if (!obj
->is
<NativeObject
>()) {
3091 return AttachDecision::NoAction
;
3094 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3095 if (nobj
->containsDenseElement(index
)) {
3096 return AttachDecision::NoAction
;
3098 if (!CanAttachDenseElementHole(nobj
, OwnProperty::No
)) {
3099 return AttachDecision::NoAction
;
3102 // Guard on the shape, to prevent non-dense elements from appearing.
3103 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3104 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3105 /* alwaysGuardFirstProto = */ false);
3106 writer
.loadDenseElementHoleResult(objId
, indexId
);
3107 writer
.returnFromIC();
3109 trackAttached("GetProp.DenseElementHole");
3110 return AttachDecision::Attach
;
3113 AttachDecision
GetPropIRGenerator::tryAttachSparseElement(
3114 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3115 Int32OperandId indexId
) {
3116 if (!obj
->is
<NativeObject
>()) {
3117 return AttachDecision::NoAction
;
3119 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3121 // Stub doesn't handle negative indices.
3122 if (index
> INT32_MAX
) {
3123 return AttachDecision::NoAction
;
3126 // The object must have sparse elements.
3127 if (!nobj
->isIndexed()) {
3128 return AttachDecision::NoAction
;
3131 // The index must not be for a dense element.
3132 if (nobj
->containsDenseElement(index
)) {
3133 return AttachDecision::NoAction
;
3136 // Only handle ArrayObject and PlainObject in this stub.
3137 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
3138 return AttachDecision::NoAction
;
3141 // GetSparseElementHelper assumes that the target and the receiver
3144 return AttachDecision::NoAction
;
3147 // Here, we ensure that the prototype chain does not define any sparse
3148 // indexed properties on the shape lineage. This allows us to guard on
3149 // the shapes up the prototype chain to ensure that no indexed properties
3150 // exist outside of the dense elements.
3152 // The `GeneratePrototypeHoleGuards` call below will guard on the shapes,
3153 // as well as ensure that no prototypes contain dense elements, allowing
3154 // us to perform a pure shape-search for out-of-bounds integer-indexed
3155 // properties on the receiver object.
3156 if (PrototypeMayHaveIndexedProperties(nobj
)) {
3157 return AttachDecision::NoAction
;
3160 // Ensure that obj is an ArrayObject or PlainObject.
3161 if (nobj
->is
<ArrayObject
>()) {
3162 writer
.guardClass(objId
, GuardClassKind::Array
);
3164 MOZ_ASSERT(nobj
->is
<PlainObject
>());
3165 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
3168 // The helper we are going to call only applies to non-dense elements.
3169 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3171 // Ensures we are able to efficiently able to map to an integral jsid.
3172 writer
.guardInt32IsNonNegative(indexId
);
3174 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
3175 // The helper function also ensures that the index does not appear within the
3176 // dense element set of the prototypes.
3177 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3178 /* alwaysGuardFirstProto = */ true);
3180 // At this point, we are guaranteed that the indexed property will not
3181 // be found on one of the prototypes. We are assured that we only have
3182 // to check that the receiving object has the property.
3184 writer
.callGetSparseElementResult(objId
, indexId
);
3185 writer
.returnFromIC();
3187 trackAttached("GetProp.SparseElement");
3188 return AttachDecision::Attach
;
3191 // For Uint32Array we let the stub return an Int32 if we have not seen a
3192 // double, to allow better codegen in Warp while avoiding bailout loops.
3193 static bool ForceDoubleForUint32Array(TypedArrayObject
* tarr
, uint64_t index
) {
3194 MOZ_ASSERT(index
< tarr
->length().valueOr(0));
3196 if (tarr
->type() != Scalar::Type::Uint32
) {
3197 // Return value is only relevant for Uint32Array.
3202 MOZ_ALWAYS_TRUE(tarr
->getElementPure(index
, &res
));
3203 MOZ_ASSERT(res
.isNumber());
3204 return res
.isDouble();
3207 static ArrayBufferViewKind
ToArrayBufferViewKind(const TypedArrayObject
* obj
) {
3208 if (obj
->is
<FixedLengthTypedArrayObject
>()) {
3209 return ArrayBufferViewKind::FixedLength
;
3212 MOZ_ASSERT(obj
->is
<ResizableTypedArrayObject
>());
3213 return ArrayBufferViewKind::Resizable
;
3216 static ArrayBufferViewKind
ToArrayBufferViewKind(const DataViewObject
* obj
) {
3217 if (obj
->is
<FixedLengthDataViewObject
>()) {
3218 return ArrayBufferViewKind::FixedLength
;
3221 MOZ_ASSERT(obj
->is
<ResizableDataViewObject
>());
3222 return ArrayBufferViewKind::Resizable
;
3225 AttachDecision
GetPropIRGenerator::tryAttachTypedArrayElement(
3226 HandleObject obj
, ObjOperandId objId
) {
3227 if (!obj
->is
<TypedArrayObject
>()) {
3228 return AttachDecision::NoAction
;
3231 if (!idVal_
.isNumber()) {
3232 return AttachDecision::NoAction
;
3235 auto* tarr
= &obj
->as
<TypedArrayObject
>();
3237 bool handleOOB
= false;
3239 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
3240 uint64_t(indexInt64
) >= tarr
->length().valueOr(0)) {
3244 // If the number is not representable as an integer the result will be
3245 // |undefined| so we leave |forceDoubleForUint32| as false.
3246 bool forceDoubleForUint32
= false;
3248 uint64_t index
= uint64_t(indexInt64
);
3249 forceDoubleForUint32
= ForceDoubleForUint32Array(tarr
, index
);
3252 writer
.guardShapeForClass(objId
, tarr
->shape());
3254 ValOperandId keyId
= getElemKeyValueId();
3255 IntPtrOperandId intPtrIndexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
3257 auto viewKind
= ToArrayBufferViewKind(tarr
);
3258 writer
.loadTypedArrayElementResult(objId
, intPtrIndexId
, tarr
->type(),
3259 handleOOB
, forceDoubleForUint32
, viewKind
);
3260 writer
.returnFromIC();
3262 trackAttached("GetProp.TypedElement");
3263 return AttachDecision::Attach
;
3266 AttachDecision
GetPropIRGenerator::tryAttachGenericElement(
3267 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
3268 Int32OperandId indexId
, ValOperandId receiverId
) {
3269 if (!obj
->is
<NativeObject
>()) {
3270 return AttachDecision::NoAction
;
3273 #ifdef JS_CODEGEN_X86
3275 // There aren't enough registers available on x86.
3276 return AttachDecision::NoAction
;
3280 // To allow other types to attach in the non-megamorphic case we test the
3281 // specific matching native receiver; however, once megamorphic we can attach
3283 if (mode_
== ICState::Mode::Megamorphic
) {
3284 writer
.guardIsNativeObject(objId
);
3286 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3287 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3289 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
3291 writer
.callNativeGetElementSuperResult(objId
, indexId
, receiverId
);
3293 writer
.callNativeGetElementResult(objId
, indexId
);
3295 writer
.returnFromIC();
3297 trackAttached(mode_
== ICState::Mode::Megamorphic
3298 ? "GenericElementMegamorphic"
3299 : "GenericElement");
3300 return AttachDecision::Attach
;
3303 AttachDecision
GetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
3304 ObjOperandId objId
) {
3305 if (!obj
->is
<ProxyObject
>()) {
3306 return AttachDecision::NoAction
;
3309 // The proxy stubs don't currently support |super| access.
3311 return AttachDecision::NoAction
;
3315 auto proxy
= obj
.as
<ProxyObject
>();
3316 if (proxy
->handler()->isScripted()) {
3317 TRY_ATTACH(tryAttachScriptedProxy(proxy
, objId
, JS::VoidHandlePropertyKey
));
3321 writer
.guardIsProxy(objId
);
3323 // We are not guarding against DOM proxies here, because there is no other
3324 // specialized DOM IC we could attach.
3325 // We could call maybeEmitIdGuard here and then emit ProxyGetResult,
3326 // but for GetElem we prefer to attach a stub that can handle any Value
3327 // so we don't attach a new stub for every id.
3328 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
);
3329 MOZ_ASSERT(!isSuper());
3330 writer
.proxyGetByValueResult(objId
, getElemKeyValueId());
3331 writer
.returnFromIC();
3333 trackAttached("GetProp.ProxyElement");
3334 return AttachDecision::Attach
;
3337 void GetPropIRGenerator::trackAttached(const char* name
) {
3338 stubName_
= name
? name
: "NotAttached";
3339 #ifdef JS_CACHEIR_SPEW
3340 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3341 sp
.valueProperty("base", val_
);
3342 sp
.valueProperty("property", idVal_
);
3347 void IRGenerator::emitIdGuard(ValOperandId valId
, const Value
& idVal
, jsid id
) {
3348 if (id
.isSymbol()) {
3349 MOZ_ASSERT(idVal
.toSymbol() == id
.toSymbol());
3350 SymbolOperandId symId
= writer
.guardToSymbol(valId
);
3351 writer
.guardSpecificSymbol(symId
, id
.toSymbol());
3353 MOZ_ASSERT(id
.isAtom());
3354 if (idVal
.isUndefined()) {
3355 MOZ_ASSERT(id
.isAtom(cx_
->names().undefined
));
3356 writer
.guardIsUndefined(valId
);
3357 } else if (idVal
.isNull()) {
3358 MOZ_ASSERT(id
.isAtom(cx_
->names().null
));
3359 writer
.guardIsNull(valId
);
3361 MOZ_ASSERT(idVal
.isString());
3362 StringOperandId strId
= writer
.guardToString(valId
);
3363 writer
.guardSpecificAtom(strId
, id
.toAtom());
3368 void GetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3369 if (cacheKind_
== CacheKind::GetProp
||
3370 cacheKind_
== CacheKind::GetPropSuper
) {
3371 // Constant PropertyName, no guards necessary.
3372 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3376 MOZ_ASSERT(cacheKind_
== CacheKind::GetElem
||
3377 cacheKind_
== CacheKind::GetElemSuper
);
3378 emitIdGuard(getElemKeyValueId(), idVal_
, id
);
3381 void SetPropIRGenerator::maybeEmitIdGuard(jsid id
) {
3382 if (cacheKind_
== CacheKind::SetProp
) {
3383 // Constant PropertyName, no guards necessary.
3384 MOZ_ASSERT(&idVal_
.toString()->asAtom() == id
.toAtom());
3388 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
3389 emitIdGuard(setElemKeyValueId(), idVal_
, id
);
3392 GetNameIRGenerator::GetNameIRGenerator(JSContext
* cx
, HandleScript script
,
3393 jsbytecode
* pc
, ICState state
,
3395 Handle
<PropertyName
*> name
)
3396 : IRGenerator(cx
, script
, pc
, CacheKind::GetName
, state
),
3400 AttachDecision
GetNameIRGenerator::tryAttachStub() {
3401 MOZ_ASSERT(cacheKind_
== CacheKind::GetName
);
3403 AutoAssertNoPendingException
aanpe(cx_
);
3405 ObjOperandId
envId(writer
.setInputOperandId(0));
3406 RootedId
id(cx_
, NameToId(name_
));
3408 TRY_ATTACH(tryAttachGlobalNameValue(envId
, id
));
3409 TRY_ATTACH(tryAttachGlobalNameGetter(envId
, id
));
3410 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3412 trackAttached(IRGenerator::NotAttached
);
3413 return AttachDecision::NoAction
;
3416 static bool CanAttachGlobalName(JSContext
* cx
,
3417 GlobalLexicalEnvironmentObject
* globalLexical
,
3418 PropertyKey id
, NativeObject
** holder
,
3419 Maybe
<PropertyInfo
>* prop
) {
3420 // The property must be found, and it must be found as a normal data property.
3421 NativeObject
* current
= globalLexical
;
3423 *prop
= current
->lookup(cx
, id
);
3424 if (prop
->isSome()) {
3428 if (current
== globalLexical
) {
3429 current
= &globalLexical
->global();
3431 // In the browser the global prototype chain should be immutable.
3432 if (!current
->staticPrototypeIsImmutable()) {
3436 JSObject
* proto
= current
->staticPrototype();
3437 if (!proto
|| !proto
->is
<NativeObject
>()) {
3441 current
= &proto
->as
<NativeObject
>();
3449 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId
,
3451 if (!IsGlobalOp(JSOp(*pc_
))) {
3452 return AttachDecision::NoAction
;
3454 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3456 auto* globalLexical
= &env_
->as
<GlobalLexicalEnvironmentObject
>();
3458 NativeObject
* holder
= nullptr;
3459 Maybe
<PropertyInfo
> prop
;
3460 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3461 return AttachDecision::NoAction
;
3464 // The property must be found, and it must be found as a normal data property.
3465 if (!prop
->isDataProperty()) {
3466 return AttachDecision::NoAction
;
3469 // This might still be an uninitialized lexical.
3470 if (holder
->getSlot(prop
->slot()).isMagic()) {
3471 return AttachDecision::NoAction
;
3474 if (holder
== globalLexical
) {
3475 // There is no need to guard on the shape. Lexical bindings are
3476 // non-configurable, and this stub cannot be shared across globals.
3477 size_t dynamicSlotOffset
=
3478 holder
->dynamicSlotIndex(prop
->slot()) * sizeof(Value
);
3479 writer
.loadDynamicSlotResult(objId
, dynamicSlotOffset
);
3480 } else if (holder
== &globalLexical
->global()) {
3481 MOZ_ASSERT(globalLexical
->global().isGenerationCountedGlobal());
3482 writer
.guardGlobalGeneration(
3483 globalLexical
->global().generationCount(),
3484 globalLexical
->global().addressOfGenerationCount());
3485 ObjOperandId holderId
= writer
.loadObject(holder
);
3487 writer
.assertPropertyLookup(holderId
, id
, prop
->slot());
3489 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3491 // Check the prototype chain from the global to the holder
3492 // prototype. Ignore the global lexical scope as it doesn't figure
3493 // into the prototype chain. We guard on the global lexical
3494 // scope's shape independently.
3495 if (!IsCacheableGetPropSlot(&globalLexical
->global(), holder
, *prop
)) {
3496 return AttachDecision::NoAction
;
3499 // Shape guard for global lexical.
3500 writer
.guardShape(objId
, globalLexical
->shape());
3502 // Guard on the shape of the GlobalObject.
3503 ObjOperandId globalId
= writer
.loadObject(&globalLexical
->global());
3504 writer
.guardShape(globalId
, globalLexical
->global().shape());
3506 // Shape guard holder.
3507 ObjOperandId holderId
= writer
.loadObject(holder
);
3508 writer
.guardShape(holderId
, holder
->shape());
3510 EmitLoadSlotResult(writer
, holderId
, holder
, *prop
);
3513 writer
.returnFromIC();
3515 trackAttached("GetName.GlobalNameValue");
3516 return AttachDecision::Attach
;
3519 AttachDecision
GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId
,
3521 if (!IsGlobalOp(JSOp(*pc_
))) {
3522 return AttachDecision::NoAction
;
3524 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3526 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3527 env_
.as
<GlobalLexicalEnvironmentObject
>();
3528 MOZ_ASSERT(globalLexical
->isGlobal());
3530 NativeObject
* holder
= nullptr;
3531 Maybe
<PropertyInfo
> prop
;
3532 if (!CanAttachGlobalName(cx_
, globalLexical
, id
, &holder
, &prop
)) {
3533 return AttachDecision::NoAction
;
3536 if (holder
== globalLexical
) {
3537 return AttachDecision::NoAction
;
3540 GlobalObject
* global
= &globalLexical
->global();
3542 NativeGetPropKind kind
= IsCacheableGetPropCall(global
, holder
, *prop
, pc_
);
3543 if (kind
!= NativeGetPropKind::NativeGetter
&&
3544 kind
!= NativeGetPropKind::ScriptedGetter
) {
3545 return AttachDecision::NoAction
;
3548 bool needsWindowProxy
=
3549 IsWindow(global
) && GetterNeedsWindowProxyThis(holder
, *prop
);
3551 // Shape guard for global lexical.
3552 writer
.guardShape(objId
, globalLexical
->shape());
3554 // Guard on the shape of the GlobalObject.
3555 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3556 writer
.guardShape(globalId
, global
->shape());
3558 if (holder
!= global
) {
3559 // Shape guard holder.
3560 ObjOperandId holderId
= writer
.loadObject(holder
);
3561 writer
.guardShape(holderId
, holder
->shape());
3562 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
3563 /* holderIsConstant = */ true);
3565 // Note: pass true for |holderIsConstant| because the holder must be the
3566 // current global object.
3567 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, globalId
,
3568 /* holderIsConstant = */ true);
3571 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Getter
, global
, holder
, *prop
,
3573 // The global shape guard above ensures the instance JSClass is correct.
3574 MOZ_ASSERT(!needsWindowProxy
);
3575 EmitCallDOMGetterResultNoGuards(writer
, holder
, *prop
, globalId
);
3576 trackAttached("GetName.GlobalNameDOMGetter");
3578 ObjOperandId receiverObjId
;
3579 if (needsWindowProxy
) {
3580 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
3581 receiverObjId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
3583 receiverObjId
= globalId
;
3585 ValOperandId receiverId
= writer
.boxObject(receiverObjId
);
3586 EmitCallGetterResultNoGuards(cx_
, writer
, kind
, global
, holder
, *prop
,
3588 trackAttached("GetName.GlobalNameGetter");
3591 return AttachDecision::Attach
;
3594 static bool NeedEnvironmentShapeGuard(JSContext
* cx
, JSObject
* envObj
) {
3595 // We can skip a guard on the call object if the script's bindings are
3596 // guaranteed to be immutable (and thus cannot introduce shadowing variables).
3597 // If the function is a relazified self-hosted function it has no BaseScript
3598 // and we pessimistically create the guard.
3599 if (envObj
->is
<CallObject
>()) {
3600 auto* callObj
= &envObj
->as
<CallObject
>();
3601 JSFunction
* fun
= &callObj
->callee();
3602 return !fun
->hasBaseScript() ||
3603 fun
->baseScript()->funHasExtensibleScope() ||
3604 DebugEnvironments::hasDebugEnvironment(cx
, *callObj
);
3607 // Similar to the call object case, we can also skip a guard if the lexical
3608 // environment's bindings are immutable.
3609 if (envObj
->is
<LexicalEnvironmentObject
>()) {
3610 return envObj
->as
<LexicalEnvironmentObject
>().isExtensible();
3613 // Use a shape guard for all other environment objects.
3617 AttachDecision
GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3619 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3620 return AttachDecision::NoAction
;
3623 JSObject
* env
= env_
;
3624 Maybe
<PropertyInfo
> prop
;
3625 NativeObject
* holder
= nullptr;
3628 if (env
->is
<GlobalObject
>()) {
3629 prop
= env
->as
<GlobalObject
>().lookup(cx_
, id
);
3630 if (prop
.isSome()) {
3633 return AttachDecision::NoAction
;
3636 if (!env
->is
<EnvironmentObject
>() || env
->is
<WithEnvironmentObject
>()) {
3637 return AttachDecision::NoAction
;
3640 // Check for an 'own' property on the env. There is no need to
3641 // check the prototype as non-with scopes do not inherit properties
3642 // from any prototype.
3643 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3644 if (prop
.isSome()) {
3648 env
= env
->enclosingEnvironment();
3651 holder
= &env
->as
<NativeObject
>();
3652 if (!IsCacheableGetPropSlot(holder
, holder
, *prop
)) {
3653 return AttachDecision::NoAction
;
3655 if (holder
->getSlot(prop
->slot()).isMagic()) {
3656 MOZ_ASSERT(holder
->is
<EnvironmentObject
>());
3657 return AttachDecision::NoAction
;
3660 ObjOperandId lastObjId
= objId
;
3663 if (NeedEnvironmentShapeGuard(cx_
, env
)) {
3664 writer
.guardShape(lastObjId
, env
->shape());
3667 if (env
== holder
) {
3671 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3672 env
= env
->enclosingEnvironment();
3675 ValOperandId resId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3676 if (holder
->is
<EnvironmentObject
>()) {
3677 writer
.guardIsNotUninitializedLexical(resId
);
3679 writer
.loadOperandResult(resId
);
3680 writer
.returnFromIC();
3682 trackAttached("GetName.EnvironmentName");
3683 return AttachDecision::Attach
;
3686 void GetNameIRGenerator::trackAttached(const char* name
) {
3687 stubName_
= name
? name
: "NotAttached";
3688 #ifdef JS_CACHEIR_SPEW
3689 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3690 sp
.valueProperty("base", ObjectValue(*env_
));
3691 sp
.valueProperty("property", StringValue(name_
));
3696 BindNameIRGenerator::BindNameIRGenerator(JSContext
* cx
, HandleScript script
,
3697 jsbytecode
* pc
, ICState state
,
3699 Handle
<PropertyName
*> name
)
3700 : IRGenerator(cx
, script
, pc
, CacheKind::BindName
, state
),
3704 AttachDecision
BindNameIRGenerator::tryAttachStub() {
3705 MOZ_ASSERT(cacheKind_
== CacheKind::BindName
);
3707 AutoAssertNoPendingException
aanpe(cx_
);
3709 ObjOperandId
envId(writer
.setInputOperandId(0));
3710 RootedId
id(cx_
, NameToId(name_
));
3712 TRY_ATTACH(tryAttachGlobalName(envId
, id
));
3713 TRY_ATTACH(tryAttachEnvironmentName(envId
, id
));
3715 trackAttached(IRGenerator::NotAttached
);
3716 return AttachDecision::NoAction
;
3719 AttachDecision
BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId
,
3721 if (!IsGlobalOp(JSOp(*pc_
))) {
3722 return AttachDecision::NoAction
;
3724 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
3726 Handle
<GlobalLexicalEnvironmentObject
*> globalLexical
=
3727 env_
.as
<GlobalLexicalEnvironmentObject
>();
3728 MOZ_ASSERT(globalLexical
->isGlobal());
3730 JSObject
* result
= nullptr;
3731 if (Maybe
<PropertyInfo
> prop
= globalLexical
->lookup(cx_
, id
)) {
3732 // If this is an uninitialized lexical or a const, we need to return a
3733 // RuntimeLexicalErrorObject.
3734 if (globalLexical
->getSlot(prop
->slot()).isMagic() || !prop
->writable()) {
3735 return AttachDecision::NoAction
;
3737 result
= globalLexical
;
3739 result
= &globalLexical
->global();
3742 if (result
== globalLexical
) {
3743 // Lexical bindings are non-configurable so we can just return the
3745 writer
.loadObjectResult(objId
);
3747 // If the property exists on the global and is non-configurable, it cannot
3748 // be shadowed by the lexical scope so we can just return the global without
3750 Maybe
<PropertyInfo
> prop
= result
->as
<GlobalObject
>().lookup(cx_
, id
);
3751 if (prop
.isNothing() || prop
->configurable()) {
3752 writer
.guardShape(objId
, globalLexical
->shape());
3754 ObjOperandId globalId
= writer
.loadEnclosingEnvironment(objId
);
3755 writer
.loadObjectResult(globalId
);
3757 writer
.returnFromIC();
3759 trackAttached("BindName.GlobalName");
3760 return AttachDecision::Attach
;
3763 AttachDecision
BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId
,
3765 if (IsGlobalOp(JSOp(*pc_
)) || script_
->hasNonSyntacticScope()) {
3766 return AttachDecision::NoAction
;
3769 // JSOp::BindUnqualifiedName when writing to a dynamic environment binding.
3770 // JSOp::BindName when reading from a dynamic environment binding.
3771 bool unqualifiedLookup
= JSOp(*pc_
) == JSOp::BindUnqualifiedName
;
3773 JSObject
* env
= env_
;
3774 Maybe
<PropertyInfo
> prop
;
3776 // Stop when we've reached the global object.
3777 if (env
->is
<GlobalObject
>()) {
3781 if (!env
->is
<EnvironmentObject
>() || env
->is
<WithEnvironmentObject
>()) {
3782 return AttachDecision::NoAction
;
3785 // When we reach an unqualified variables object (like the global) we
3786 // have to stop looking and return that object.
3787 if (unqualifiedLookup
&& env
->isUnqualifiedVarObj()) {
3791 // Check for an 'own' property on the env. There is no need to
3792 // check the prototype as non-with scopes do not inherit properties
3793 // from any prototype.
3794 prop
= env
->as
<NativeObject
>().lookup(cx_
, id
);
3795 if (prop
.isSome()) {
3799 env
= env
->enclosingEnvironment();
3802 // If this is an uninitialized lexical or a const, we need to return a
3803 // RuntimeLexicalErrorObject.
3804 auto* holder
= &env
->as
<NativeObject
>();
3805 if (prop
.isSome() && holder
->is
<EnvironmentObject
>()) {
3806 // Uninitialized lexical binding.
3807 if (holder
->getSlot(prop
->slot()).isMagic()) {
3808 return AttachDecision::NoAction
;
3811 // Attempt to write to a const binding.
3812 if (unqualifiedLookup
&& !prop
->writable()) {
3813 return AttachDecision::NoAction
;
3817 ObjOperandId lastObjId
= objId
;
3820 if (NeedEnvironmentShapeGuard(cx_
, env
) && !env
->is
<GlobalObject
>()) {
3821 writer
.guardShape(lastObjId
, env
->shape());
3824 if (env
== holder
) {
3828 lastObjId
= writer
.loadEnclosingEnvironment(lastObjId
);
3829 env
= env
->enclosingEnvironment();
3832 if (prop
.isSome() && holder
->is
<EnvironmentObject
>()) {
3833 ValOperandId valId
= EmitLoadSlot(writer
, holder
, lastObjId
, prop
->slot());
3834 writer
.guardIsNotUninitializedLexical(valId
);
3837 writer
.loadObjectResult(lastObjId
);
3838 writer
.returnFromIC();
3840 trackAttached("BindName.EnvironmentName");
3841 return AttachDecision::Attach
;
3844 void BindNameIRGenerator::trackAttached(const char* name
) {
3845 stubName_
= name
? name
: "NotAttached";
3846 #ifdef JS_CACHEIR_SPEW
3847 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
3848 sp
.valueProperty("base", ObjectValue(*env_
));
3849 sp
.valueProperty("property", StringValue(name_
));
3854 HasPropIRGenerator::HasPropIRGenerator(JSContext
* cx
, HandleScript script
,
3855 jsbytecode
* pc
, ICState state
,
3856 CacheKind cacheKind
, HandleValue idVal
,
3858 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {}
3860 AttachDecision
HasPropIRGenerator::tryAttachDense(HandleObject obj
,
3863 Int32OperandId indexId
) {
3864 if (!obj
->is
<NativeObject
>()) {
3865 return AttachDecision::NoAction
;
3868 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3869 if (!nobj
->containsDenseElement(index
)) {
3870 return AttachDecision::NoAction
;
3873 if (mode_
== ICState::Mode::Megamorphic
) {
3874 writer
.guardIsNativeObject(objId
);
3876 // Guard shape to ensure object class is NativeObject.
3877 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3879 writer
.loadDenseElementExistsResult(objId
, indexId
);
3880 writer
.returnFromIC();
3882 trackAttached("HasProp.Dense");
3883 return AttachDecision::Attach
;
3886 AttachDecision
HasPropIRGenerator::tryAttachDenseHole(HandleObject obj
,
3889 Int32OperandId indexId
) {
3890 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3891 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3893 if (!obj
->is
<NativeObject
>()) {
3894 return AttachDecision::NoAction
;
3897 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
3898 if (nobj
->containsDenseElement(index
)) {
3899 return AttachDecision::NoAction
;
3901 if (!CanAttachDenseElementHole(nobj
, ownProp
)) {
3902 return AttachDecision::NoAction
;
3905 // Guard shape to ensure class is NativeObject and to prevent non-dense
3906 // elements being added. Also ensures prototype doesn't change if dynamic
3907 // checks aren't emitted.
3908 TestMatchingNativeReceiver(writer
, nobj
, objId
);
3910 // Generate prototype guards if needed. This includes monitoring that
3911 // properties were not added in the chain.
3913 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3914 /* alwaysGuardFirstProto = */ false);
3917 writer
.loadDenseElementHoleExistsResult(objId
, indexId
);
3918 writer
.returnFromIC();
3920 trackAttached("HasProp.DenseHole");
3921 return AttachDecision::Attach
;
3924 AttachDecision
HasPropIRGenerator::tryAttachSparse(HandleObject obj
,
3926 Int32OperandId indexId
) {
3927 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3928 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3930 if (!obj
->is
<NativeObject
>()) {
3931 return AttachDecision::NoAction
;
3933 auto* nobj
= &obj
->as
<NativeObject
>();
3935 if (!nobj
->isIndexed()) {
3936 return AttachDecision::NoAction
;
3938 if (!CanAttachDenseElementHole(nobj
, ownProp
, AllowIndexedReceiver::Yes
)) {
3939 return AttachDecision::NoAction
;
3942 // Guard that this is a native object.
3943 writer
.guardIsNativeObject(objId
);
3945 // Generate prototype guards if needed. This includes monitoring that
3946 // properties were not added in the chain.
3948 GeneratePrototypeHoleGuards(writer
, nobj
, objId
,
3949 /* alwaysGuardFirstProto = */ true);
3952 // Because of the prototype guard we know that the prototype chain
3953 // does not include any dense or sparse (i.e indexed) properties.
3954 writer
.callObjectHasSparseElementResult(objId
, indexId
);
3955 writer
.returnFromIC();
3957 trackAttached("HasProp.Sparse");
3958 return AttachDecision::Attach
;
3961 AttachDecision
HasPropIRGenerator::tryAttachArgumentsObjectArg(
3962 HandleObject obj
, ObjOperandId objId
, Int32OperandId indexId
) {
3963 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
3964 OwnProperty ownProp
= hasOwn
? OwnProperty::Yes
: OwnProperty::No
;
3966 if (!obj
->is
<ArgumentsObject
>()) {
3967 return AttachDecision::NoAction
;
3969 auto* args
= &obj
->as
<ArgumentsObject
>();
3971 // No elements must have been overridden or deleted.
3972 if (args
->hasOverriddenElement()) {
3973 return AttachDecision::NoAction
;
3976 if (!CanAttachDenseElementHole(args
, ownProp
, AllowIndexedReceiver::Yes
,
3977 AllowExtraReceiverProperties::Yes
)) {
3978 return AttachDecision::NoAction
;
3981 if (args
->is
<MappedArgumentsObject
>()) {
3982 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
3984 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
3985 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
3989 GeneratePrototypeHoleGuards(writer
, args
, objId
,
3990 /* alwaysGuardFirstProto = */ true);
3993 writer
.loadArgumentsObjectArgExistsResult(objId
, indexId
);
3994 writer
.returnFromIC();
3996 trackAttached("HasProp.ArgumentsObjectArg");
3997 return AttachDecision::Attach
;
4000 AttachDecision
HasPropIRGenerator::tryAttachNamedProp(HandleObject obj
,
4003 ValOperandId keyId
) {
4004 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4006 Rooted
<NativeObject
*> holder(cx_
);
4007 PropertyResult prop
;
4010 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
4011 return AttachDecision::NoAction
;
4014 holder
.set(&obj
->as
<NativeObject
>());
4016 NativeObject
* nHolder
= nullptr;
4017 if (!LookupPropertyPure(cx_
, obj
, key
, &nHolder
, &prop
)) {
4018 return AttachDecision::NoAction
;
4020 holder
.set(nHolder
);
4022 if (prop
.isNotFound()) {
4023 return AttachDecision::NoAction
;
4026 TRY_ATTACH(tryAttachSmallObjectVariableKey(obj
, objId
, key
, keyId
));
4027 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
4028 TRY_ATTACH(tryAttachNative(&obj
->as
<NativeObject
>(), objId
, key
, keyId
, prop
,
4031 return AttachDecision::NoAction
;
4034 AttachDecision
HasPropIRGenerator::tryAttachSmallObjectVariableKey(
4035 HandleObject obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
4036 MOZ_ASSERT(obj
->is
<NativeObject
>());
4038 if (cacheKind_
!= CacheKind::HasOwn
) {
4039 return AttachDecision::NoAction
;
4042 if (mode_
!= ICState::Mode::Megamorphic
) {
4043 return AttachDecision::NoAction
;
4046 if (numOptimizedStubs_
!= 0) {
4047 return AttachDecision::NoAction
;
4050 if (!key
.isString()) {
4051 return AttachDecision::NoAction
;
4054 if (!obj
->as
<NativeObject
>().hasEmptyElements()) {
4055 return AttachDecision::NoAction
;
4058 if (obj
->getClass()->getResolve()) {
4059 return AttachDecision::NoAction
;
4062 if (!obj
->shape()->isShared()) {
4063 return AttachDecision::NoAction
;
4066 static constexpr size_t SMALL_OBJECT_SIZE
= 5;
4068 if (obj
->shape()->asShared().slotSpan() > SMALL_OBJECT_SIZE
) {
4069 return AttachDecision::NoAction
;
4072 Rooted
<ListObject
*> keyListObj(cx_
, ListObject::create(cx_
));
4074 cx_
->recoverFromOutOfMemory();
4075 return AttachDecision::NoAction
;
4078 for (SharedShapePropertyIter
<CanGC
> iter(cx_
, &obj
->shape()->asShared());
4079 !iter
.done(); iter
++) {
4080 if (!iter
->key().isAtom()) {
4081 return AttachDecision::NoAction
;
4084 if (keyListObj
->length() == SMALL_OBJECT_SIZE
) {
4085 return AttachDecision::NoAction
;
4088 RootedValue
key(cx_
, StringValue(iter
->key().toAtom()));
4089 if (!keyListObj
->append(cx_
, key
)) {
4090 cx_
->recoverFromOutOfMemory();
4091 return AttachDecision::NoAction
;
4095 writer
.guardShape(objId
, obj
->shape());
4096 writer
.guardNoDenseElements(objId
);
4097 StringOperandId keyStrId
= writer
.guardToString(keyId
);
4098 StringOperandId keyAtomId
= writer
.stringToAtom(keyStrId
);
4099 writer
.smallObjectVariableKeyHasOwnResult(keyAtomId
, keyListObj
,
4101 writer
.returnFromIC();
4102 trackAttached("HasProp.SmallObjectVariableKey");
4103 return AttachDecision::Attach
;
4106 AttachDecision
HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId
,
4107 ValOperandId keyId
) {
4108 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4110 if (mode_
!= ICState::Mode::Megamorphic
) {
4111 return AttachDecision::NoAction
;
4114 writer
.megamorphicHasPropResult(objId
, keyId
, hasOwn
);
4115 writer
.returnFromIC();
4116 trackAttached("HasProp.Megamorphic");
4117 return AttachDecision::Attach
;
4120 AttachDecision
HasPropIRGenerator::tryAttachNative(NativeObject
* obj
,
4121 ObjOperandId objId
, jsid key
,
4123 PropertyResult prop
,
4124 NativeObject
* holder
) {
4125 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4127 if (!prop
.isNativeProperty()) {
4128 return AttachDecision::NoAction
;
4131 emitIdGuard(keyId
, idVal_
, key
);
4132 EmitReadSlotGuard(writer
, obj
, holder
, objId
);
4133 writer
.loadBooleanResult(true);
4134 writer
.returnFromIC();
4136 trackAttached("HasProp.Native");
4137 return AttachDecision::Attach
;
4140 static void EmitGuardTypedArray(CacheIRWriter
& writer
, TypedArrayObject
* obj
,
4141 ObjOperandId objId
) {
4142 if (obj
->is
<FixedLengthTypedArrayObject
>()) {
4143 writer
.guardIsFixedLengthTypedArray(objId
);
4145 writer
.guardIsResizableTypedArray(objId
);
4149 AttachDecision
HasPropIRGenerator::tryAttachTypedArray(HandleObject obj
,
4151 ValOperandId keyId
) {
4152 if (!obj
->is
<TypedArrayObject
>()) {
4153 return AttachDecision::NoAction
;
4157 if (!ValueIsInt64Index(idVal_
, &index
)) {
4158 return AttachDecision::NoAction
;
4161 auto* tarr
= &obj
->as
<TypedArrayObject
>();
4162 EmitGuardTypedArray(writer
, tarr
, objId
);
4164 IntPtrOperandId intPtrIndexId
=
4165 guardToIntPtrIndex(idVal_
, keyId
, /* supportOOB = */ true);
4167 auto viewKind
= ToArrayBufferViewKind(tarr
);
4168 writer
.loadTypedArrayElementExistsResult(objId
, intPtrIndexId
, viewKind
);
4169 writer
.returnFromIC();
4171 trackAttached("HasProp.TypedArrayObject");
4172 return AttachDecision::Attach
;
4175 AttachDecision
HasPropIRGenerator::tryAttachSlotDoesNotExist(
4176 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
) {
4177 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4179 emitIdGuard(keyId
, idVal_
, key
);
4181 TestMatchingNativeReceiver(writer
, obj
, objId
);
4183 EmitMissingPropGuard(writer
, obj
, objId
);
4185 writer
.loadBooleanResult(false);
4186 writer
.returnFromIC();
4188 trackAttached("HasProp.DoesNotExist");
4189 return AttachDecision::Attach
;
4192 AttachDecision
HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj
,
4195 ValOperandId keyId
) {
4196 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4198 // Check that property doesn't exist on |obj| or it's prototype chain. These
4199 // checks allow NativeObjects with a NativeObject prototype chain. They return
4200 // NoAction if unknown such as resolve hooks or proxies.
4202 if (!CheckHasNoSuchOwnProperty(cx_
, obj
, key
)) {
4203 return AttachDecision::NoAction
;
4206 if (!CheckHasNoSuchProperty(cx_
, obj
, key
)) {
4207 return AttachDecision::NoAction
;
4211 TRY_ATTACH(tryAttachSmallObjectVariableKey(obj
, objId
, key
, keyId
));
4212 TRY_ATTACH(tryAttachMegamorphic(objId
, keyId
));
4214 tryAttachSlotDoesNotExist(&obj
->as
<NativeObject
>(), objId
, key
, keyId
));
4216 return AttachDecision::NoAction
;
4219 AttachDecision
HasPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
4221 ValOperandId keyId
) {
4222 bool hasOwn
= (cacheKind_
== CacheKind::HasOwn
);
4224 if (!obj
->is
<ProxyObject
>()) {
4225 return AttachDecision::NoAction
;
4228 writer
.guardIsProxy(objId
);
4229 writer
.proxyHasPropResult(objId
, keyId
, hasOwn
);
4230 writer
.returnFromIC();
4232 trackAttached("HasProp.ProxyElement");
4233 return AttachDecision::Attach
;
4236 AttachDecision
HasPropIRGenerator::tryAttachStub() {
4237 MOZ_ASSERT(cacheKind_
== CacheKind::In
|| cacheKind_
== CacheKind::HasOwn
);
4239 AutoAssertNoPendingException
aanpe(cx_
);
4241 // NOTE: Argument order is PROPERTY, OBJECT
4242 ValOperandId
keyId(writer
.setInputOperandId(0));
4243 ValOperandId
valId(writer
.setInputOperandId(1));
4245 if (!val_
.isObject()) {
4246 trackAttached(IRGenerator::NotAttached
);
4247 return AttachDecision::NoAction
;
4249 RootedObject
obj(cx_
, &val_
.toObject());
4250 ObjOperandId objId
= writer
.guardToObject(valId
);
4253 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, keyId
));
4257 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4258 cx_
->clearPendingException();
4259 return AttachDecision::NoAction
;
4263 TRY_ATTACH(tryAttachNamedProp(obj
, objId
, id
, keyId
));
4264 TRY_ATTACH(tryAttachDoesNotExist(obj
, objId
, id
, keyId
));
4266 trackAttached(IRGenerator::NotAttached
);
4267 return AttachDecision::NoAction
;
4270 TRY_ATTACH(tryAttachTypedArray(obj
, objId
, keyId
));
4273 Int32OperandId indexId
;
4274 if (maybeGuardInt32Index(idVal_
, keyId
, &index
, &indexId
)) {
4275 TRY_ATTACH(tryAttachDense(obj
, objId
, index
, indexId
));
4276 TRY_ATTACH(tryAttachDenseHole(obj
, objId
, index
, indexId
));
4277 TRY_ATTACH(tryAttachSparse(obj
, objId
, indexId
));
4278 TRY_ATTACH(tryAttachArgumentsObjectArg(obj
, objId
, indexId
));
4280 trackAttached(IRGenerator::NotAttached
);
4281 return AttachDecision::NoAction
;
4284 trackAttached(IRGenerator::NotAttached
);
4285 return AttachDecision::NoAction
;
4288 void HasPropIRGenerator::trackAttached(const char* name
) {
4289 stubName_
= name
? name
: "NotAttached";
4290 #ifdef JS_CACHEIR_SPEW
4291 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4292 sp
.valueProperty("base", val_
);
4293 sp
.valueProperty("property", idVal_
);
4298 CheckPrivateFieldIRGenerator::CheckPrivateFieldIRGenerator(
4299 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
4300 CacheKind cacheKind
, HandleValue idVal
, HandleValue val
)
4301 : IRGenerator(cx
, script
, pc
, cacheKind
, state
), val_(val
), idVal_(idVal
) {
4302 MOZ_ASSERT(idVal
.isSymbol() && idVal
.toSymbol()->isPrivateName());
4305 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachStub() {
4306 AutoAssertNoPendingException
aanpe(cx_
);
4308 ValOperandId
valId(writer
.setInputOperandId(0));
4309 ValOperandId
keyId(writer
.setInputOperandId(1));
4311 if (!val_
.isObject()) {
4312 trackAttached(IRGenerator::NotAttached
);
4313 return AttachDecision::NoAction
;
4315 JSObject
* obj
= &val_
.toObject();
4316 ObjOperandId objId
= writer
.guardToObject(valId
);
4317 PropertyKey key
= PropertyKey::Symbol(idVal_
.toSymbol());
4319 ThrowCondition condition
;
4320 ThrowMsgKind msgKind
;
4321 GetCheckPrivateFieldOperands(pc_
, &condition
, &msgKind
);
4323 PropertyResult prop
;
4324 if (!LookupOwnPropertyPure(cx_
, obj
, key
, &prop
)) {
4325 return AttachDecision::NoAction
;
4328 if (CheckPrivateFieldWillThrow(condition
, prop
.isFound())) {
4329 // Don't attach a stub if the operation will throw.
4330 return AttachDecision::NoAction
;
4333 auto* nobj
= &obj
->as
<NativeObject
>();
4335 TRY_ATTACH(tryAttachNative(nobj
, objId
, key
, keyId
, prop
));
4337 return AttachDecision::NoAction
;
4340 AttachDecision
CheckPrivateFieldIRGenerator::tryAttachNative(
4341 NativeObject
* obj
, ObjOperandId objId
, jsid key
, ValOperandId keyId
,
4342 PropertyResult prop
) {
4343 MOZ_ASSERT(prop
.isNativeProperty() || prop
.isNotFound());
4345 emitIdGuard(keyId
, idVal_
, key
);
4346 TestMatchingNativeReceiver(writer
, obj
, objId
);
4347 writer
.loadBooleanResult(prop
.isFound());
4348 writer
.returnFromIC();
4350 trackAttached("CheckPrivateField.Native");
4351 return AttachDecision::Attach
;
4354 void CheckPrivateFieldIRGenerator::trackAttached(const char* name
) {
4355 stubName_
= name
? name
: "NotAttached";
4356 #ifdef JS_CACHEIR_SPEW
4357 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4358 sp
.valueProperty("base", val_
);
4359 sp
.valueProperty("property", idVal_
);
4364 bool IRGenerator::maybeGuardInt32Index(const Value
& index
, ValOperandId indexId
,
4365 uint32_t* int32Index
,
4366 Int32OperandId
* int32IndexId
) {
4367 if (index
.isNumber()) {
4368 int32_t indexSigned
;
4369 if (index
.isInt32()) {
4370 indexSigned
= index
.toInt32();
4372 // We allow negative zero here.
4373 if (!mozilla::NumberEqualsInt32(index
.toDouble(), &indexSigned
)) {
4378 if (indexSigned
< 0) {
4382 *int32Index
= uint32_t(indexSigned
);
4383 *int32IndexId
= writer
.guardToInt32Index(indexId
);
4387 if (index
.isString()) {
4388 int32_t indexSigned
= GetIndexFromString(index
.toString());
4389 if (indexSigned
< 0) {
4393 StringOperandId strId
= writer
.guardToString(indexId
);
4394 *int32Index
= uint32_t(indexSigned
);
4395 *int32IndexId
= writer
.guardStringToIndex(strId
);
4402 SetPropIRGenerator::SetPropIRGenerator(JSContext
* cx
, HandleScript script
,
4403 jsbytecode
* pc
, CacheKind cacheKind
,
4404 ICState state
, HandleValue lhsVal
,
4405 HandleValue idVal
, HandleValue rhsVal
)
4406 : IRGenerator(cx
, script
, pc
, cacheKind
, state
),
4411 AttachDecision
SetPropIRGenerator::tryAttachStub() {
4412 AutoAssertNoPendingException
aanpe(cx_
);
4414 ValOperandId
objValId(writer
.setInputOperandId(0));
4415 ValOperandId rhsValId
;
4416 if (cacheKind_
== CacheKind::SetProp
) {
4417 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
4419 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4420 MOZ_ASSERT(setElemKeyValueId().id() == 1);
4421 writer
.setInputOperandId(1);
4422 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
4427 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
4428 cx_
->clearPendingException();
4429 return AttachDecision::NoAction
;
4432 if (lhsVal_
.isObject()) {
4433 RootedObject
obj(cx_
, &lhsVal_
.toObject());
4435 ObjOperandId objId
= writer
.guardToObject(objValId
);
4436 if (IsPropertySetOp(JSOp(*pc_
))) {
4437 TRY_ATTACH(tryAttachMegamorphicSetElement(obj
, objId
, rhsValId
));
4440 TRY_ATTACH(tryAttachNativeSetSlot(obj
, objId
, id
, rhsValId
));
4441 if (IsPropertySetOp(JSOp(*pc_
))) {
4442 TRY_ATTACH(tryAttachSetArrayLength(obj
, objId
, id
, rhsValId
));
4443 TRY_ATTACH(tryAttachSetter(obj
, objId
, id
, rhsValId
));
4444 TRY_ATTACH(tryAttachWindowProxy(obj
, objId
, id
, rhsValId
));
4445 TRY_ATTACH(tryAttachProxy(obj
, objId
, id
, rhsValId
));
4446 TRY_ATTACH(tryAttachMegamorphicSetSlot(obj
, objId
, id
, rhsValId
));
4448 if (canAttachAddSlotStub(obj
, id
)) {
4449 deferType_
= DeferType::AddSlot
;
4450 return AttachDecision::Deferred
;
4452 return AttachDecision::NoAction
;
4455 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
4457 if (IsPropertySetOp(JSOp(*pc_
))) {
4458 TRY_ATTACH(tryAttachProxyElement(obj
, objId
, rhsValId
));
4461 TRY_ATTACH(tryAttachSetTypedArrayElement(obj
, objId
, rhsValId
));
4464 Int32OperandId indexId
;
4465 if (maybeGuardInt32Index(idVal_
, setElemKeyValueId(), &index
, &indexId
)) {
4467 tryAttachSetDenseElement(obj
, objId
, index
, indexId
, rhsValId
));
4469 tryAttachSetDenseElementHole(obj
, objId
, index
, indexId
, rhsValId
));
4470 TRY_ATTACH(tryAttachAddOrUpdateSparseElement(obj
, objId
, index
, indexId
,
4472 return AttachDecision::NoAction
;
4475 return AttachDecision::NoAction
;
4478 static void EmitStoreSlotAndReturn(CacheIRWriter
& writer
, ObjOperandId objId
,
4479 NativeObject
* nobj
, PropertyInfo prop
,
4480 ValOperandId rhsId
) {
4481 if (nobj
->isFixedSlot(prop
.slot())) {
4482 size_t offset
= NativeObject::getFixedSlotOffset(prop
.slot());
4483 writer
.storeFixedSlot(objId
, offset
, rhsId
);
4485 size_t offset
= nobj
->dynamicSlotIndex(prop
.slot()) * sizeof(Value
);
4486 writer
.storeDynamicSlot(objId
, offset
, rhsId
);
4488 writer
.returnFromIC();
4491 static Maybe
<PropertyInfo
> LookupShapeForSetSlot(JSOp op
, NativeObject
* obj
,
4493 Maybe
<PropertyInfo
> prop
= obj
->lookupPure(id
);
4494 if (prop
.isNothing() || !prop
->isDataProperty() || !prop
->writable()) {
4495 return mozilla::Nothing();
4498 // If this is a property init operation, the property's attributes may have to
4499 // be changed too, so make sure the current flags match.
4500 if (IsPropertyInitOp(op
)) {
4501 // Don't support locked init operations.
4502 if (IsLockedInitOp(op
)) {
4503 return mozilla::Nothing();
4506 // Can't redefine a non-configurable property.
4507 if (!prop
->configurable()) {
4508 return mozilla::Nothing();
4511 // Make sure the enumerable flag matches the init operation.
4512 if (IsHiddenInitOp(op
) == prop
->enumerable()) {
4513 return mozilla::Nothing();
4520 static bool CanAttachNativeSetSlot(JSOp op
, JSObject
* obj
, PropertyKey id
,
4521 Maybe
<PropertyInfo
>* prop
) {
4522 if (!obj
->is
<NativeObject
>()) {
4526 if (Watchtower::watchesPropertyModification(&obj
->as
<NativeObject
>())) {
4530 *prop
= LookupShapeForSetSlot(op
, &obj
->as
<NativeObject
>(), id
);
4531 return prop
->isSome();
4534 // There is no need to guard on the shape. Global lexical bindings are
4535 // non-configurable and can not be shadowed.
4536 static bool IsGlobalLexicalSetGName(JSOp op
, NativeObject
* obj
,
4537 PropertyInfo prop
) {
4538 // Ensure that the env can't change.
4539 if (op
!= JSOp::SetGName
&& op
!= JSOp::StrictSetGName
) {
4543 if (!obj
->is
<GlobalLexicalEnvironmentObject
>()) {
4547 // Uninitialized let bindings use a RuntimeLexicalErrorObject.
4548 MOZ_ASSERT(!obj
->getSlot(prop
.slot()).isMagic());
4549 MOZ_ASSERT(prop
.writable());
4550 MOZ_ASSERT(!prop
.configurable());
4554 AttachDecision
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj
,
4557 ValOperandId rhsId
) {
4558 Maybe
<PropertyInfo
> prop
;
4559 if (!CanAttachNativeSetSlot(JSOp(*pc_
), obj
, id
, &prop
)) {
4560 return AttachDecision::NoAction
;
4563 if (mode_
== ICState::Mode::Megamorphic
&& cacheKind_
== CacheKind::SetProp
&&
4564 IsPropertySetOp(JSOp(*pc_
))) {
4565 return AttachDecision::NoAction
;
4568 maybeEmitIdGuard(id
);
4570 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4571 if (!IsGlobalLexicalSetGName(JSOp(*pc_
), nobj
, *prop
)) {
4572 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4574 EmitStoreSlotAndReturn(writer
, objId
, nobj
, *prop
, rhsId
);
4576 trackAttached("SetProp.NativeSlot");
4577 return AttachDecision::Attach
;
4580 static bool ValueCanConvertToNumeric(Scalar::Type type
, const Value
& val
) {
4581 if (Scalar::isBigIntType(type
)) {
4582 return val
.isBigInt();
4584 return val
.isNumber() || val
.isNullOrUndefined() || val
.isBoolean() ||
4588 OperandId
IRGenerator::emitNumericGuard(ValOperandId valId
, const Value
& v
,
4589 Scalar::Type type
) {
4590 MOZ_ASSERT(ValueCanConvertToNumeric(type
, v
));
4595 case Scalar::Uint16
:
4597 case Scalar::Uint32
: {
4599 return writer
.guardToInt32ModUint32(valId
);
4601 if (v
.isNullOrUndefined()) {
4602 writer
.guardIsNullOrUndefined(valId
);
4603 return writer
.loadInt32Constant(0);
4605 if (v
.isBoolean()) {
4606 return writer
.guardBooleanToInt32(valId
);
4608 MOZ_ASSERT(v
.isString());
4609 StringOperandId strId
= writer
.guardToString(valId
);
4610 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4611 return writer
.truncateDoubleToUInt32(numId
);
4614 case Scalar::Float16
:
4615 case Scalar::Float32
:
4616 case Scalar::Float64
: {
4618 return writer
.guardIsNumber(valId
);
4621 writer
.guardIsNull(valId
);
4622 return writer
.loadDoubleConstant(0.0);
4624 if (v
.isUndefined()) {
4625 writer
.guardIsUndefined(valId
);
4626 return writer
.loadDoubleConstant(JS::GenericNaN());
4628 if (v
.isBoolean()) {
4629 BooleanOperandId boolId
= writer
.guardToBoolean(valId
);
4630 return writer
.booleanToNumber(boolId
);
4632 MOZ_ASSERT(v
.isString());
4633 StringOperandId strId
= writer
.guardToString(valId
);
4634 return writer
.guardStringToNumber(strId
);
4637 case Scalar::Uint8Clamped
: {
4639 return writer
.guardToUint8Clamped(valId
);
4641 if (v
.isNullOrUndefined()) {
4642 writer
.guardIsNullOrUndefined(valId
);
4643 return writer
.loadInt32Constant(0);
4645 if (v
.isBoolean()) {
4646 return writer
.guardBooleanToInt32(valId
);
4648 MOZ_ASSERT(v
.isString());
4649 StringOperandId strId
= writer
.guardToString(valId
);
4650 NumberOperandId numId
= writer
.guardStringToNumber(strId
);
4651 return writer
.doubleToUint8Clamped(numId
);
4654 case Scalar::BigInt64
:
4655 case Scalar::BigUint64
:
4656 MOZ_ASSERT(v
.isBigInt());
4657 return writer
.guardToBigInt(valId
);
4659 case Scalar::MaxTypedArrayViewType
:
4661 case Scalar::Simd128
:
4664 MOZ_CRASH("Unsupported TypedArray type");
4667 void SetPropIRGenerator::trackAttached(const char* name
) {
4668 stubName_
= name
? name
: "NotAttached";
4669 #ifdef JS_CACHEIR_SPEW
4670 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
4671 sp
.opcodeProperty("op", JSOp(*pc_
));
4672 sp
.valueProperty("base", lhsVal_
);
4673 sp
.valueProperty("property", idVal_
);
4674 sp
.valueProperty("value", rhsVal_
);
4679 static bool IsCacheableSetPropCallNative(NativeObject
* obj
,
4680 NativeObject
* holder
,
4681 PropertyInfo prop
) {
4682 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4684 if (!prop
.isAccessorProperty()) {
4688 JSObject
* setterObject
= holder
->getSetter(prop
);
4689 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4693 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4694 if (!setter
.isNativeWithoutJitEntry()) {
4698 if (setter
.isClassConstructor()) {
4705 static bool IsCacheableSetPropCallScripted(NativeObject
* obj
,
4706 NativeObject
* holder
,
4707 PropertyInfo prop
) {
4708 MOZ_ASSERT(IsCacheableProtoChain(obj
, holder
));
4710 if (!prop
.isAccessorProperty()) {
4714 JSObject
* setterObject
= holder
->getSetter(prop
);
4715 if (!setterObject
|| !setterObject
->is
<JSFunction
>()) {
4719 JSFunction
& setter
= setterObject
->as
<JSFunction
>();
4720 if (setter
.isClassConstructor()) {
4724 // Scripted functions and natives with JIT entry can use the scripted path.
4725 return setter
.hasJitEntry();
4728 static bool CanAttachSetter(JSContext
* cx
, jsbytecode
* pc
, JSObject
* obj
,
4729 PropertyKey id
, NativeObject
** holder
,
4730 Maybe
<PropertyInfo
>* propInfo
) {
4731 // Don't attach a setter stub for ops like JSOp::InitElem.
4732 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
4734 PropertyResult prop
;
4735 if (!LookupPropertyPure(cx
, obj
, id
, holder
, &prop
)) {
4738 auto* nobj
= &obj
->as
<NativeObject
>();
4740 if (!prop
.isNativeProperty()) {
4744 if (!IsCacheableSetPropCallScripted(nobj
, *holder
, prop
.propertyInfo()) &&
4745 !IsCacheableSetPropCallNative(nobj
, *holder
, prop
.propertyInfo())) {
4749 *propInfo
= mozilla::Some(prop
.propertyInfo());
4753 static void EmitCallSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4754 NativeObject
* obj
, NativeObject
* holder
,
4755 PropertyInfo prop
, ObjOperandId receiverId
,
4756 ValOperandId rhsId
) {
4757 JSFunction
* target
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4758 bool sameRealm
= cx
->realm() == target
->realm();
4760 if (target
->isNativeWithoutJitEntry()) {
4761 MOZ_ASSERT(IsCacheableSetPropCallNative(obj
, holder
, prop
));
4762 writer
.callNativeSetter(receiverId
, target
, rhsId
, sameRealm
);
4763 writer
.returnFromIC();
4767 MOZ_ASSERT(IsCacheableSetPropCallScripted(obj
, holder
, prop
));
4768 writer
.callScriptedSetter(receiverId
, target
, rhsId
, sameRealm
);
4769 writer
.returnFromIC();
4772 static void EmitCallDOMSetterNoGuards(JSContext
* cx
, CacheIRWriter
& writer
,
4773 NativeObject
* holder
, PropertyInfo prop
,
4774 ObjOperandId objId
, ValOperandId rhsId
) {
4775 JSFunction
* setter
= &holder
->getSetter(prop
)->as
<JSFunction
>();
4776 MOZ_ASSERT(cx
->realm() == setter
->realm());
4778 writer
.callDOMSetter(objId
, setter
->jitInfo(), rhsId
);
4779 writer
.returnFromIC();
4782 AttachDecision
SetPropIRGenerator::tryAttachSetter(HandleObject obj
,
4785 ValOperandId rhsId
) {
4786 // Don't attach a setter stub for ops like JSOp::InitElem.
4787 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4789 NativeObject
* holder
= nullptr;
4790 Maybe
<PropertyInfo
> prop
;
4791 if (!CanAttachSetter(cx_
, pc_
, obj
, id
, &holder
, &prop
)) {
4792 return AttachDecision::NoAction
;
4794 auto* nobj
= &obj
->as
<NativeObject
>();
4796 bool needsWindowProxy
=
4797 IsWindow(nobj
) && SetterNeedsWindowProxyThis(holder
, *prop
);
4799 maybeEmitIdGuard(id
);
4801 // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
4802 // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
4803 // require outerizing).
4804 if (mode_
== ICState::Mode::Specialized
|| IsWindow(nobj
)) {
4805 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4807 if (nobj
!= holder
) {
4808 GeneratePrototypeGuards(writer
, nobj
, holder
, objId
);
4810 // Guard on the holder's shape.
4811 ObjOperandId holderId
= writer
.loadObject(holder
);
4812 TestMatchingHolder(writer
, holder
, holderId
);
4814 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
4815 /* holderIsConstant = */ true);
4817 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, objId
);
4820 GetterSetter
* gs
= holder
->getGetterSetter(*prop
);
4821 writer
.guardHasGetterSetter(objId
, id
, gs
);
4824 if (CanAttachDOMGetterSetter(cx_
, JSJitInfo::Setter
, nobj
, holder
, *prop
,
4826 MOZ_ASSERT(!needsWindowProxy
);
4827 EmitCallDOMSetterNoGuards(cx_
, writer
, holder
, *prop
, objId
, rhsId
);
4829 trackAttached("SetProp.DOMSetter");
4830 return AttachDecision::Attach
;
4833 ObjOperandId receiverId
;
4834 if (needsWindowProxy
) {
4835 MOZ_ASSERT(cx_
->global()->maybeWindowProxy());
4836 receiverId
= writer
.loadObject(cx_
->global()->maybeWindowProxy());
4840 EmitCallSetterNoGuards(cx_
, writer
, nobj
, holder
, *prop
, receiverId
, rhsId
);
4842 trackAttached("SetProp.Setter");
4843 return AttachDecision::Attach
;
4846 AttachDecision
SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj
,
4849 ValOperandId rhsId
) {
4850 // Don't attach an array length stub for ops like JSOp::InitElem.
4851 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
4853 if (!obj
->is
<ArrayObject
>() || !id
.isAtom(cx_
->names().length
) ||
4854 !obj
->as
<ArrayObject
>().lengthIsWritable()) {
4855 return AttachDecision::NoAction
;
4858 maybeEmitIdGuard(id
);
4859 emitOptimisticClassGuard(objId
, obj
, GuardClassKind::Array
);
4860 writer
.callSetArrayLength(objId
, IsStrictSetPC(pc_
), rhsId
);
4861 writer
.returnFromIC();
4863 trackAttached("SetProp.ArrayLength");
4864 return AttachDecision::Attach
;
4867 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElement(
4868 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4869 Int32OperandId indexId
, ValOperandId rhsId
) {
4870 if (!obj
->is
<NativeObject
>()) {
4871 return AttachDecision::NoAction
;
4874 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4875 if (!nobj
->containsDenseElement(index
) || nobj
->denseElementsAreFrozen()) {
4876 return AttachDecision::NoAction
;
4879 // Setting holes requires extra code for marking the elements non-packed.
4880 MOZ_ASSERT(!rhsVal_
.isMagic(JS_ELEMENTS_HOLE
));
4882 JSOp op
= JSOp(*pc_
);
4884 // We don't currently emit locked init for any indexed properties.
4885 MOZ_ASSERT(!IsLockedInitOp(op
));
4887 // We don't currently emit hidden init for any existing indexed properties.
4888 MOZ_ASSERT(!IsHiddenInitOp(op
));
4890 // Don't optimize InitElem (DefineProperty) on non-extensible objects: when
4891 // the elements are sealed, we have to throw an exception. Note that we have
4892 // to check !isExtensible instead of denseElementsAreSealed because sealing
4893 // a (non-extensible) object does not necessarily trigger a Shape change.
4894 if (IsPropertyInitOp(op
) && !nobj
->isExtensible()) {
4895 return AttachDecision::NoAction
;
4898 TestMatchingNativeReceiver(writer
, nobj
, objId
);
4900 writer
.storeDenseElement(objId
, indexId
, rhsId
);
4901 writer
.returnFromIC();
4903 trackAttached("SetProp.DenseElement");
4904 return AttachDecision::Attach
;
4907 static bool CanAttachAddElement(NativeObject
* obj
, bool isInit
,
4908 AllowIndexedReceiver allowIndexedReceiver
) {
4909 // Make sure the receiver doesn't have any indexed properties and that such
4910 // properties can't appear without a shape change.
4911 if (allowIndexedReceiver
== AllowIndexedReceiver::No
&& obj
->isIndexed()) {
4916 // This check is also relevant for the receiver object.
4917 const JSClass
* clasp
= obj
->getClass();
4918 if (clasp
!= &ArrayObject::class_
&&
4919 (clasp
->getAddProperty() || clasp
->getResolve() ||
4920 clasp
->getOpsLookupProperty() || clasp
->getOpsSetProperty())) {
4924 // If we're initializing a property instead of setting one, the objects
4925 // on the prototype are not relevant.
4930 JSObject
* proto
= obj
->staticPrototype();
4935 if (!proto
->is
<NativeObject
>()) {
4939 NativeObject
* nproto
= &proto
->as
<NativeObject
>();
4940 if (nproto
->isIndexed()) {
4944 // We have to make sure the proto has no non-writable (frozen) elements
4945 // because we're not allowed to shadow them.
4946 if (nproto
->denseElementsAreFrozen() &&
4947 nproto
->getDenseInitializedLength() > 0) {
4957 AttachDecision
SetPropIRGenerator::tryAttachSetDenseElementHole(
4958 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
4959 Int32OperandId indexId
, ValOperandId rhsId
) {
4960 if (!obj
->is
<NativeObject
>()) {
4961 return AttachDecision::NoAction
;
4964 // Setting holes requires extra code for marking the elements non-packed.
4965 if (rhsVal_
.isMagic(JS_ELEMENTS_HOLE
)) {
4966 return AttachDecision::NoAction
;
4969 JSOp op
= JSOp(*pc_
);
4970 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
4972 // We don't currently emit locked init for any indexed properties.
4973 MOZ_ASSERT(!IsLockedInitOp(op
));
4975 // Hidden init can be emitted for absent indexed properties.
4976 if (IsHiddenInitOp(op
)) {
4977 MOZ_ASSERT(op
== JSOp::InitHiddenElem
);
4978 return AttachDecision::NoAction
;
4981 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
4982 if (!nobj
->isExtensible()) {
4983 return AttachDecision::NoAction
;
4986 MOZ_ASSERT(!nobj
->denseElementsAreFrozen(),
4987 "Extensible objects should not have frozen elements");
4989 uint32_t initLength
= nobj
->getDenseInitializedLength();
4991 // Optimize if we're adding an element at initLength or writing to a hole.
4993 // In the case where index > initLength, we need noteHasDenseAdd to be called
4994 // to ensure Ion is aware that writes have occurred to-out-of-bound indexes
4997 // TODO(post-Warp): noteHasDenseAdd (nee: noteArrayWriteHole) no longer exists
4998 bool isAdd
= index
== initLength
;
4999 bool isHoleInBounds
=
5000 index
< initLength
&& !nobj
->containsDenseElement(index
);
5001 if (!isAdd
&& !isHoleInBounds
) {
5002 return AttachDecision::NoAction
;
5005 // Can't add new elements to arrays with non-writable length.
5006 if (isAdd
&& nobj
->is
<ArrayObject
>() &&
5007 !nobj
->as
<ArrayObject
>().lengthIsWritable()) {
5008 return AttachDecision::NoAction
;
5011 // Typed arrays don't have dense elements.
5012 if (nobj
->is
<TypedArrayObject
>()) {
5013 return AttachDecision::NoAction
;
5016 // Check for other indexed properties or class hooks.
5017 if (!CanAttachAddElement(nobj
, IsPropertyInitOp(op
),
5018 AllowIndexedReceiver::No
)) {
5019 return AttachDecision::NoAction
;
5022 TestMatchingNativeReceiver(writer
, nobj
, objId
);
5024 // Also shape guard the proto chain, unless this is an InitElem.
5025 if (IsPropertySetOp(op
)) {
5026 ShapeGuardProtoChain(writer
, nobj
, objId
);
5029 writer
.storeDenseElementHole(objId
, indexId
, rhsId
, isAdd
);
5030 writer
.returnFromIC();
5032 trackAttached(isAdd
? "AddDenseElement" : "StoreDenseElementHole");
5033 return AttachDecision::Attach
;
5036 // Add an IC for adding or updating a sparse element.
5037 AttachDecision
SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(
5038 HandleObject obj
, ObjOperandId objId
, uint32_t index
,
5039 Int32OperandId indexId
, ValOperandId rhsId
) {
5040 JSOp op
= JSOp(*pc_
);
5041 MOZ_ASSERT(IsPropertySetOp(op
) || IsPropertyInitOp(op
));
5043 if (op
!= JSOp::SetElem
&& op
!= JSOp::StrictSetElem
) {
5044 return AttachDecision::NoAction
;
5047 if (!obj
->is
<NativeObject
>()) {
5048 return AttachDecision::NoAction
;
5050 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
5052 // We cannot attach a stub to a non-extensible object
5053 if (!nobj
->isExtensible()) {
5054 return AttachDecision::NoAction
;
5057 // Stub doesn't handle negative indices.
5058 if (index
> INT32_MAX
) {
5059 return AttachDecision::NoAction
;
5062 // The index must not be for a dense element.
5063 if (nobj
->containsDenseElement(index
)) {
5064 return AttachDecision::NoAction
;
5067 // Only handle ArrayObject and PlainObject in this stub.
5068 if (!nobj
->is
<ArrayObject
>() && !nobj
->is
<PlainObject
>()) {
5069 return AttachDecision::NoAction
;
5072 // Don't attach if we're adding to an array with non-writable length.
5073 if (nobj
->is
<ArrayObject
>()) {
5074 ArrayObject
* aobj
= &nobj
->as
<ArrayObject
>();
5075 bool isAdd
= (index
>= aobj
->length());
5076 if (isAdd
&& !aobj
->lengthIsWritable()) {
5077 return AttachDecision::NoAction
;
5081 // Check for class hooks or indexed properties on the prototype chain that
5082 // we're not allowed to shadow.
5083 if (!CanAttachAddElement(nobj
, /* isInit = */ false,
5084 AllowIndexedReceiver::Yes
)) {
5085 return AttachDecision::NoAction
;
5088 // Ensure that obj is an ArrayObject or PlainObject.
5089 if (nobj
->is
<ArrayObject
>()) {
5090 writer
.guardClass(objId
, GuardClassKind::Array
);
5092 MOZ_ASSERT(nobj
->is
<PlainObject
>());
5093 writer
.guardClass(objId
, GuardClassKind::PlainObject
);
5096 // The helper we are going to call only applies to non-dense elements.
5097 writer
.guardIndexIsNotDenseElement(objId
, indexId
);
5099 // Guard extensible: We may be trying to add a new element, and so we'd best
5100 // be able to do so safely.
5101 writer
.guardIsExtensible(objId
);
5103 // Ensures we are able to efficiently able to map to an integral jsid.
5104 writer
.guardInt32IsNonNegative(indexId
);
5106 // Shape guard the prototype chain to avoid shadowing indexes from appearing.
5107 // Guard the prototype of the receiver explicitly, because the receiver's
5108 // shape is not being guarded as a proxy for that.
5109 GuardReceiverProto(writer
, nobj
, objId
);
5111 // Dense elements may appear on the prototype chain (and prototypes may
5112 // have a different notion of which elements are dense), but they can
5113 // only be data properties, so our specialized Set handler is ok to bind
5115 if (IsPropertySetOp(op
)) {
5116 ShapeGuardProtoChain(writer
, nobj
, objId
);
5119 // Ensure that if we're adding an element to the object, the object's
5120 // length is writable.
5121 if (nobj
->is
<ArrayObject
>()) {
5122 writer
.guardIndexIsValidUpdateOrAdd(objId
, indexId
);
5125 writer
.callAddOrUpdateSparseElementHelper(
5126 objId
, indexId
, rhsId
,
5127 /* strict = */ op
== JSOp::StrictSetElem
);
5128 writer
.returnFromIC();
5130 trackAttached("SetProp.AddOrUpdateSparseElement");
5131 return AttachDecision::Attach
;
5134 AttachDecision
SetPropIRGenerator::tryAttachSetTypedArrayElement(
5135 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5136 if (!obj
->is
<TypedArrayObject
>()) {
5137 return AttachDecision::NoAction
;
5139 if (!idVal_
.isNumber()) {
5140 return AttachDecision::NoAction
;
5143 auto* tarr
= &obj
->as
<TypedArrayObject
>();
5144 Scalar::Type elementType
= tarr
->type();
5146 // Don't attach if the input type doesn't match the guard added below.
5147 if (!ValueCanConvertToNumeric(elementType
, rhsVal_
)) {
5148 return AttachDecision::NoAction
;
5151 bool handleOOB
= false;
5153 if (!ValueIsInt64Index(idVal_
, &indexInt64
) || indexInt64
< 0 ||
5154 uint64_t(indexInt64
) >= tarr
->length().valueOr(0)) {
5158 JSOp op
= JSOp(*pc_
);
5160 // The only expected property init operation is InitElem.
5161 MOZ_ASSERT_IF(IsPropertyInitOp(op
), op
== JSOp::InitElem
);
5163 // InitElem (DefineProperty) has to throw an exception on out-of-bounds.
5164 if (handleOOB
&& IsPropertyInitOp(op
)) {
5165 return AttachDecision::NoAction
;
5168 writer
.guardShapeForClass(objId
, tarr
->shape());
5170 OperandId rhsValId
= emitNumericGuard(rhsId
, rhsVal_
, elementType
);
5172 ValOperandId keyId
= setElemKeyValueId();
5173 IntPtrOperandId indexId
= guardToIntPtrIndex(idVal_
, keyId
, handleOOB
);
5175 auto viewKind
= ToArrayBufferViewKind(tarr
);
5176 writer
.storeTypedArrayElement(objId
, elementType
, indexId
, rhsValId
,
5177 handleOOB
, viewKind
);
5178 writer
.returnFromIC();
5180 trackAttached(handleOOB
? "SetTypedElementOOB" : "SetTypedElement");
5181 return AttachDecision::Attach
;
5184 AttachDecision
SetPropIRGenerator::tryAttachGenericProxy(
5185 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5186 ValOperandId rhsId
, bool handleDOMProxies
) {
5187 // Don't attach a proxy stub for ops like JSOp::InitElem.
5188 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5190 writer
.guardIsProxy(objId
);
5192 if (!handleDOMProxies
) {
5193 // Ensure that the incoming object is not a DOM proxy, so that we can
5194 // get to the specialized stubs. If handleDOMProxies is true, we were
5195 // unable to attach a specialized DOM stub, so we just handle all
5197 writer
.guardIsNotDOMProxy(objId
);
5200 if (cacheKind_
== CacheKind::SetProp
|| mode_
== ICState::Mode::Specialized
) {
5201 maybeEmitIdGuard(id
);
5202 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5204 // Attach a stub that handles every id.
5205 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5206 MOZ_ASSERT(mode_
== ICState::Mode::Megamorphic
);
5207 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
,
5208 IsStrictSetPC(pc_
));
5211 writer
.returnFromIC();
5213 trackAttached("SetProp.GenericProxy");
5214 return AttachDecision::Attach
;
5217 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyShadowed(
5218 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5219 ValOperandId rhsId
) {
5220 // Don't attach a proxy stub for ops like JSOp::InitElem.
5221 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5223 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5225 maybeEmitIdGuard(id
);
5226 TestMatchingProxyReceiver(writer
, obj
, objId
);
5227 writer
.proxySet(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5228 writer
.returnFromIC();
5230 trackAttached("SetProp.DOMProxyShadowed");
5231 return AttachDecision::Attach
;
5234 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
5235 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5236 ValOperandId rhsId
) {
5237 // Don't attach a proxy stub for ops like JSOp::InitElem.
5238 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5240 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5242 JSObject
* proto
= obj
->staticPrototype();
5244 return AttachDecision::NoAction
;
5247 NativeObject
* holder
= nullptr;
5248 Maybe
<PropertyInfo
> prop
;
5249 if (!CanAttachSetter(cx_
, pc_
, proto
, id
, &holder
, &prop
)) {
5250 return AttachDecision::NoAction
;
5252 auto* nproto
= &proto
->as
<NativeObject
>();
5254 maybeEmitIdGuard(id
);
5256 // Guard that our proxy (expando) object hasn't started shadowing this
5258 TestMatchingProxyReceiver(writer
, obj
, objId
);
5259 bool canOptimizeMissing
= false;
5260 CheckDOMProxyDoesNotShadow(writer
, obj
, id
, objId
, &canOptimizeMissing
);
5262 GeneratePrototypeGuards(writer
, obj
, holder
, objId
);
5264 // Guard on the holder of the property.
5265 ObjOperandId holderId
= writer
.loadObject(holder
);
5266 TestMatchingHolder(writer
, holder
, holderId
);
5268 EmitGuardGetterSetterSlot(writer
, holder
, *prop
, holderId
,
5269 /* holderIsConstant = */ true);
5271 // EmitCallSetterNoGuards expects |obj| to be the object the property is
5272 // on to do some checks. Since we actually looked at proto, and no extra
5273 // guards will be generated, we can just pass that instead.
5274 EmitCallSetterNoGuards(cx_
, writer
, nproto
, holder
, *prop
, objId
, rhsId
);
5276 trackAttached("SetProp.DOMProxyUnshadowed");
5277 return AttachDecision::Attach
;
5280 AttachDecision
SetPropIRGenerator::tryAttachDOMProxyExpando(
5281 Handle
<ProxyObject
*> obj
, ObjOperandId objId
, HandleId id
,
5282 ValOperandId rhsId
) {
5283 // Don't attach a proxy stub for ops like JSOp::InitElem.
5284 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5286 MOZ_ASSERT(IsCacheableDOMProxy(obj
));
5288 Value expandoVal
= GetProxyPrivate(obj
);
5289 JSObject
* expandoObj
;
5290 if (expandoVal
.isObject()) {
5291 expandoObj
= &expandoVal
.toObject();
5293 MOZ_ASSERT(!expandoVal
.isUndefined(),
5294 "How did a missing expando manage to shadow things?");
5295 auto expandoAndGeneration
=
5296 static_cast<ExpandoAndGeneration
*>(expandoVal
.toPrivate());
5297 MOZ_ASSERT(expandoAndGeneration
);
5298 expandoObj
= &expandoAndGeneration
->expando
.toObject();
5301 Maybe
<PropertyInfo
> prop
;
5302 if (CanAttachNativeSetSlot(JSOp(*pc_
), expandoObj
, id
, &prop
)) {
5303 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5305 maybeEmitIdGuard(id
);
5306 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5307 obj
, objId
, expandoVal
, nativeExpandoObj
);
5309 EmitStoreSlotAndReturn(writer
, expandoObjId
, nativeExpandoObj
, *prop
,
5311 trackAttached("SetProp.DOMProxyExpandoSlot");
5312 return AttachDecision::Attach
;
5315 NativeObject
* holder
= nullptr;
5316 if (CanAttachSetter(cx_
, pc_
, expandoObj
, id
, &holder
, &prop
)) {
5317 auto* nativeExpandoObj
= &expandoObj
->as
<NativeObject
>();
5319 // Call the setter. Note that we pass objId, the DOM proxy, as |this|
5320 // and not the expando object.
5321 maybeEmitIdGuard(id
);
5322 ObjOperandId expandoObjId
= guardDOMProxyExpandoObjectAndShape(
5323 obj
, objId
, expandoVal
, nativeExpandoObj
);
5325 MOZ_ASSERT(holder
== nativeExpandoObj
);
5326 EmitGuardGetterSetterSlot(writer
, nativeExpandoObj
, *prop
, expandoObjId
);
5327 EmitCallSetterNoGuards(cx_
, writer
, nativeExpandoObj
, nativeExpandoObj
,
5328 *prop
, objId
, rhsId
);
5329 trackAttached("SetProp.DOMProxyExpandoSetter");
5330 return AttachDecision::Attach
;
5333 return AttachDecision::NoAction
;
5336 AttachDecision
SetPropIRGenerator::tryAttachProxy(HandleObject obj
,
5339 ValOperandId rhsId
) {
5340 // Don't attach a proxy stub for ops like JSOp::InitElem.
5341 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5343 ProxyStubType type
= GetProxyStubType(cx_
, obj
, id
);
5344 if (type
== ProxyStubType::None
) {
5345 return AttachDecision::NoAction
;
5347 auto proxy
= obj
.as
<ProxyObject
>();
5349 if (mode_
== ICState::Mode::Megamorphic
) {
5350 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5351 /* handleDOMProxies = */ true);
5355 case ProxyStubType::None
:
5357 case ProxyStubType::DOMExpando
:
5358 TRY_ATTACH(tryAttachDOMProxyExpando(proxy
, objId
, id
, rhsId
));
5359 [[fallthrough
]]; // Fall through to the generic shadowed case.
5360 case ProxyStubType::DOMShadowed
:
5361 return tryAttachDOMProxyShadowed(proxy
, objId
, id
, rhsId
);
5362 case ProxyStubType::DOMUnshadowed
:
5363 TRY_ATTACH(tryAttachDOMProxyUnshadowed(proxy
, objId
, id
, rhsId
));
5364 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5365 /* handleDOMProxies = */ true);
5366 case ProxyStubType::Generic
:
5367 return tryAttachGenericProxy(proxy
, objId
, id
, rhsId
,
5368 /* handleDOMProxies = */ false);
5371 MOZ_CRASH("Unexpected ProxyStubType");
5374 AttachDecision
SetPropIRGenerator::tryAttachProxyElement(HandleObject obj
,
5376 ValOperandId rhsId
) {
5377 // Don't attach a proxy stub for ops like JSOp::InitElem.
5378 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5380 if (!obj
->is
<ProxyObject
>()) {
5381 return AttachDecision::NoAction
;
5384 writer
.guardIsProxy(objId
);
5386 // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
5387 // proxies here as we don't have specialized DOM stubs for this.
5388 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5389 writer
.proxySetByValue(objId
, setElemKeyValueId(), rhsId
, IsStrictSetPC(pc_
));
5390 writer
.returnFromIC();
5392 trackAttached("SetProp.ProxyElement");
5393 return AttachDecision::Attach
;
5396 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetElement(
5397 HandleObject obj
, ObjOperandId objId
, ValOperandId rhsId
) {
5398 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5400 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetElem
) {
5401 return AttachDecision::NoAction
;
5404 // The generic proxy stubs are faster.
5405 if (obj
->is
<ProxyObject
>()) {
5406 return AttachDecision::NoAction
;
5409 writer
.megamorphicSetElement(objId
, setElemKeyValueId(), rhsId
,
5410 IsStrictSetPC(pc_
));
5411 writer
.returnFromIC();
5413 trackAttached("SetProp.MegamorphicSetElement");
5414 return AttachDecision::Attach
;
5417 AttachDecision
SetPropIRGenerator::tryAttachMegamorphicSetSlot(
5418 HandleObject obj
, ObjOperandId objId
, HandleId id
, ValOperandId rhsId
) {
5419 if (mode_
!= ICState::Mode::Megamorphic
|| cacheKind_
!= CacheKind::SetProp
) {
5420 return AttachDecision::NoAction
;
5423 writer
.megamorphicStoreSlot(objId
, id
, rhsId
, IsStrictSetPC(pc_
));
5424 writer
.returnFromIC();
5425 trackAttached("SetProp.MegamorphicNativeSlot");
5426 return AttachDecision::Attach
;
5429 AttachDecision
SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj
,
5432 ValOperandId rhsId
) {
5433 // Don't attach a window proxy stub for ops like JSOp::InitElem.
5434 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_
)));
5436 // Attach a stub when the receiver is a WindowProxy and we can do the set
5437 // on the Window (the global object).
5439 if (!IsWindowProxyForScriptGlobal(script_
, obj
)) {
5440 return AttachDecision::NoAction
;
5443 // If we're megamorphic prefer a generic proxy stub that handles a lot more
5445 if (mode_
== ICState::Mode::Megamorphic
) {
5446 return AttachDecision::NoAction
;
5449 // Now try to do the set on the Window (the current global).
5450 GlobalObject
* windowObj
= cx_
->global();
5452 Maybe
<PropertyInfo
> prop
;
5453 if (!CanAttachNativeSetSlot(JSOp(*pc_
), windowObj
, id
, &prop
)) {
5454 return AttachDecision::NoAction
;
5457 maybeEmitIdGuard(id
);
5459 ObjOperandId windowObjId
=
5460 GuardAndLoadWindowProxyWindow(writer
, objId
, windowObj
);
5461 writer
.guardShape(windowObjId
, windowObj
->shape());
5463 EmitStoreSlotAndReturn(writer
, windowObjId
, windowObj
, *prop
, rhsId
);
5465 trackAttached("SetProp.WindowProxySlot");
5466 return AttachDecision::Attach
;
5469 // Detect if |id| refers to the 'prototype' property of a function object. This
5470 // property is special-cased in canAttachAddSlotStub().
5471 static bool IsFunctionPrototype(const JSAtomState
& names
, JSObject
* obj
,
5473 return obj
->is
<JSFunction
>() && id
.isAtom(names
.prototype
);
5476 bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj
, HandleId id
) {
5477 if (!obj
->is
<NativeObject
>()) {
5480 auto* nobj
= &obj
->as
<NativeObject
>();
5482 // Special-case JSFunction resolve hook to allow redefining the 'prototype'
5483 // property without triggering lazy expansion of property and object
5485 if (IsFunctionPrototype(cx_
->names(), nobj
, id
)) {
5486 MOZ_ASSERT(ClassMayResolveId(cx_
->names(), nobj
->getClass(), id
, nobj
));
5488 // We're only interested in functions that have a builtin .prototype
5489 // property (needsPrototypeProperty). The stub will guard on this because
5490 // the builtin .prototype property is non-configurable/non-enumerable and it
5491 // would be wrong to add a property with those attributes to a function that
5492 // doesn't have a builtin .prototype.
5494 // Inlining needsPrototypeProperty in JIT code is complicated so we use
5495 // isNonBuiltinConstructor as a stronger condition that's easier to check
5497 JSFunction
* fun
= &nobj
->as
<JSFunction
>();
5498 if (!fun
->isNonBuiltinConstructor()) {
5501 MOZ_ASSERT(fun
->needsPrototypeProperty());
5503 // If property exists this isn't an "add".
5504 if (fun
->lookupPure(id
)) {
5508 // Normal Case: If property exists this isn't an "add"
5509 PropertyResult prop
;
5510 if (!LookupOwnPropertyPure(cx_
, nobj
, id
, &prop
)) {
5513 if (prop
.isFound()) {
5518 // For now we don't optimize Watchtower-monitored objects.
5519 if (Watchtower::watchesPropertyAdd(nobj
)) {
5523 // Object must be extensible, or we must be initializing a private
5525 bool canAddNewProperty
= nobj
->isExtensible() || id
.isPrivateName();
5526 if (!canAddNewProperty
) {
5530 JSOp op
= JSOp(*pc_
);
5531 if (IsPropertyInitOp(op
)) {
5535 MOZ_ASSERT(IsPropertySetOp(op
));
5537 // Walk up the object prototype chain and ensure that all prototypes are
5538 // native, and that all prototypes have no setter defined on the property.
5539 for (JSObject
* proto
= nobj
->staticPrototype(); proto
;
5540 proto
= proto
->staticPrototype()) {
5541 if (!proto
->is
<NativeObject
>()) {
5545 // If prototype defines this property in a non-plain way, don't optimize.
5546 Maybe
<PropertyInfo
> protoProp
= proto
->as
<NativeObject
>().lookup(cx_
, id
);
5547 if (protoProp
.isSome() && !protoProp
->isDataProperty()) {
5551 // Otherwise, if there's no such property, watch out for a resolve hook
5552 // that would need to be invoked and thus prevent inlining of property
5553 // addition. Allow the JSFunction resolve hook as it only defines plain
5554 // data properties and we don't need to invoke it for objects on the
5556 if (ClassMayResolveId(cx_
->names(), proto
->getClass(), id
, proto
) &&
5557 !proto
->is
<JSFunction
>()) {
5565 static PropertyFlags
SetPropertyFlags(JSOp op
, bool isFunctionPrototype
) {
5566 // Locked properties are non-writable, non-enumerable, and non-configurable.
5567 if (IsLockedInitOp(op
)) {
5571 // Hidden properties are writable, non-enumerable, and configurable.
5572 if (IsHiddenInitOp(op
)) {
5574 PropertyFlag::Writable
,
5575 PropertyFlag::Configurable
,
5579 // This is a special case to overwrite an unresolved function.prototype
5580 // property. The initial property flags of this property are writable,
5581 // non-enumerable, and non-configurable. See canAttachAddSlotStub.
5582 if (isFunctionPrototype
) {
5584 PropertyFlag::Writable
,
5588 // Other properties are writable, enumerable, and configurable.
5589 return PropertyFlags::defaultDataPropFlags
;
5592 AttachDecision
SetPropIRGenerator::tryAttachAddSlotStub(
5593 Handle
<Shape
*> oldShape
) {
5594 ValOperandId
objValId(writer
.setInputOperandId(0));
5595 ValOperandId rhsValId
;
5596 if (cacheKind_
== CacheKind::SetProp
) {
5597 rhsValId
= ValOperandId(writer
.setInputOperandId(1));
5599 MOZ_ASSERT(cacheKind_
== CacheKind::SetElem
);
5600 MOZ_ASSERT(setElemKeyValueId().id() == 1);
5601 writer
.setInputOperandId(1);
5602 rhsValId
= ValOperandId(writer
.setInputOperandId(2));
5607 if (!ValueToNameOrSymbolId(cx_
, idVal_
, &id
, &nameOrSymbol
)) {
5608 cx_
->clearPendingException();
5609 return AttachDecision::NoAction
;
5612 if (!lhsVal_
.isObject() || !nameOrSymbol
) {
5613 return AttachDecision::NoAction
;
5616 JSObject
* obj
= &lhsVal_
.toObject();
5618 PropertyResult prop
;
5619 if (!LookupOwnPropertyPure(cx_
, obj
, id
, &prop
)) {
5620 return AttachDecision::NoAction
;
5622 if (prop
.isNotFound()) {
5623 return AttachDecision::NoAction
;
5626 if (!obj
->is
<NativeObject
>()) {
5627 return AttachDecision::NoAction
;
5629 auto* nobj
= &obj
->as
<NativeObject
>();
5631 PropertyInfo propInfo
= prop
.propertyInfo();
5632 NativeObject
* holder
= nobj
;
5634 if (holder
->inDictionaryMode()) {
5635 return AttachDecision::NoAction
;
5638 SharedShape
* oldSharedShape
= &oldShape
->asShared();
5640 // The property must be the last added property of the object.
5641 SharedShape
* newShape
= holder
->sharedShape();
5642 MOZ_RELEASE_ASSERT(newShape
->lastProperty() == propInfo
);
5645 // Verify exactly one property was added by comparing the property map
5647 if (oldSharedShape
->propMapLength() == PropMap::Capacity
) {
5648 MOZ_ASSERT(newShape
->propMapLength() == 1);
5650 MOZ_ASSERT(newShape
->propMapLength() ==
5651 oldSharedShape
->propMapLength() + 1);
5655 bool isFunctionPrototype
= IsFunctionPrototype(cx_
->names(), nobj
, id
);
5657 JSOp op
= JSOp(*pc_
);
5658 PropertyFlags flags
= SetPropertyFlags(op
, isFunctionPrototype
);
5660 // Basic property checks.
5661 if (!propInfo
.isDataProperty() || propInfo
.flags() != flags
) {
5662 return AttachDecision::NoAction
;
5665 ObjOperandId objId
= writer
.guardToObject(objValId
);
5666 maybeEmitIdGuard(id
);
5668 // Shape guard the object.
5669 writer
.guardShape(objId
, oldShape
);
5671 // If this is the special function.prototype case, we need to guard the
5672 // function is a non-builtin constructor. See canAttachAddSlotStub.
5673 if (isFunctionPrototype
) {
5674 MOZ_ASSERT(nobj
->as
<JSFunction
>().isNonBuiltinConstructor());
5675 writer
.guardFunctionIsNonBuiltinCtor(objId
);
5678 // Also shape guard the proto chain, unless this is an InitElem.
5679 if (IsPropertySetOp(op
)) {
5680 ShapeGuardProtoChain(writer
, nobj
, objId
);
5683 // If the JSClass has an addProperty hook, we need to call a VM function to
5684 // invoke this hook. Ignore the Array addProperty hook, because it doesn't do
5685 // anything for non-index properties.
5686 DebugOnly
<uint32_t> index
;
5687 MOZ_ASSERT_IF(obj
->is
<ArrayObject
>(), !IdIsIndex(id
, &index
));
5688 bool mustCallAddPropertyHook
=
5689 obj
->getClass()->getAddProperty() && !obj
->is
<ArrayObject
>();
5691 if (mustCallAddPropertyHook
) {
5692 writer
.addSlotAndCallAddPropHook(objId
, rhsValId
, newShape
);
5693 trackAttached("SetProp.AddSlotWithAddPropertyHook");
5694 } else if (holder
->isFixedSlot(propInfo
.slot())) {
5695 size_t offset
= NativeObject::getFixedSlotOffset(propInfo
.slot());
5696 writer
.addAndStoreFixedSlot(objId
, offset
, rhsValId
, newShape
);
5697 trackAttached("SetProp.AddSlotFixed");
5699 size_t offset
= holder
->dynamicSlotIndex(propInfo
.slot()) * sizeof(Value
);
5700 uint32_t numOldSlots
= NativeObject::calculateDynamicSlots(oldSharedShape
);
5701 uint32_t numNewSlots
= holder
->numDynamicSlots();
5702 if (numOldSlots
== numNewSlots
) {
5703 writer
.addAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
);
5704 trackAttached("SetProp.AddSlotDynamic");
5706 MOZ_ASSERT(numNewSlots
> numOldSlots
);
5707 writer
.allocateAndStoreDynamicSlot(objId
, offset
, rhsValId
, newShape
,
5709 trackAttached("SetProp.AllocateSlot");
5712 writer
.returnFromIC();
5714 return AttachDecision::Attach
;
5717 InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext
* cx
, HandleScript script
,
5718 jsbytecode
* pc
, ICState state
,
5719 HandleValue lhs
, HandleObject rhs
)
5720 : IRGenerator(cx
, script
, pc
, CacheKind::InstanceOf
, state
),
5724 AttachDecision
InstanceOfIRGenerator::tryAttachStub() {
5725 MOZ_ASSERT(cacheKind_
== CacheKind::InstanceOf
);
5726 AutoAssertNoPendingException
aanpe(cx_
);
5728 // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared
5730 if (!rhsObj_
->is
<JSFunction
>()) {
5731 trackAttached(IRGenerator::NotAttached
);
5732 return AttachDecision::NoAction
;
5735 HandleFunction fun
= rhsObj_
.as
<JSFunction
>();
5737 // Look up the @@hasInstance property, and check that Function.__proto__ is
5738 // the property holder, and that no object further down the prototype chain
5739 // (including this function) has shadowed it; together with the fact that
5740 // Function.__proto__[@@hasInstance] is immutable, this ensures that the
5741 // hasInstance hook will not change without the need to guard on the actual
5743 PropertyResult hasInstanceProp
;
5744 NativeObject
* hasInstanceHolder
= nullptr;
5745 jsid hasInstanceID
= PropertyKey::Symbol(cx_
->wellKnownSymbols().hasInstance
);
5746 if (!LookupPropertyPure(cx_
, fun
, hasInstanceID
, &hasInstanceHolder
,
5747 &hasInstanceProp
) ||
5748 !hasInstanceProp
.isNativeProperty()) {
5749 trackAttached(IRGenerator::NotAttached
);
5750 return AttachDecision::NoAction
;
5753 JSObject
& funProto
= cx_
->global()->getPrototype(JSProto_Function
);
5754 if (hasInstanceHolder
!= &funProto
) {
5755 trackAttached(IRGenerator::NotAttached
);
5756 return AttachDecision::NoAction
;
5759 // If the above succeeded, then these should be true about @@hasInstance,
5760 // because the property on Function.__proto__ is an immutable data property:
5761 MOZ_ASSERT(hasInstanceProp
.propertyInfo().isDataProperty());
5762 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().configurable());
5763 MOZ_ASSERT(!hasInstanceProp
.propertyInfo().writable());
5765 MOZ_ASSERT(IsCacheableProtoChain(fun
, hasInstanceHolder
));
5767 // Ensure that the function's prototype slot is the same.
5768 Maybe
<PropertyInfo
> prop
= fun
->lookupPure(cx_
->names().prototype
);
5769 if (prop
.isNothing() || !prop
->isDataProperty()) {
5770 trackAttached(IRGenerator::NotAttached
);
5771 return AttachDecision::NoAction
;
5774 uint32_t slot
= prop
->slot();
5775 MOZ_ASSERT(slot
>= fun
->numFixedSlots(), "Stub code relies on this");
5776 if (!fun
->getSlot(slot
).isObject()) {
5777 trackAttached(IRGenerator::NotAttached
);
5778 return AttachDecision::NoAction
;
5782 ValOperandId
lhs(writer
.setInputOperandId(0));
5783 ValOperandId
rhs(writer
.setInputOperandId(1));
5785 ObjOperandId rhsId
= writer
.guardToObject(rhs
);
5786 writer
.guardShape(rhsId
, fun
->shape());
5788 // Ensure that the shapes up the prototype chain for the RHS remain the same
5789 // so that @@hasInstance is not shadowed by some intermediate prototype
5791 if (hasInstanceHolder
!= fun
) {
5792 GeneratePrototypeGuards(writer
, fun
, hasInstanceHolder
, rhsId
);
5793 ObjOperandId holderId
= writer
.loadObject(hasInstanceHolder
);
5794 TestMatchingHolder(writer
, hasInstanceHolder
, holderId
);
5797 // Load the .prototype value and ensure it's an object.
5798 ValOperandId protoValId
=
5799 writer
.loadDynamicSlot(rhsId
, slot
- fun
->numFixedSlots());
5800 ObjOperandId protoId
= writer
.guardToObject(protoValId
);
5802 // Needn't guard LHS is object, because the actual stub can handle that
5803 // and correctly return false.
5804 writer
.loadInstanceOfObjectResult(lhs
, protoId
);
5805 writer
.returnFromIC();
5806 trackAttached("InstanceOf");
5807 return AttachDecision::Attach
;
5810 void InstanceOfIRGenerator::trackAttached(const char* name
) {
5811 stubName_
= name
? name
: "NotAttached";
5812 #ifdef JS_CACHEIR_SPEW
5813 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5814 sp
.valueProperty("lhs", lhsVal_
);
5815 sp
.valueProperty("rhs", ObjectValue(*rhsObj_
));
5818 // Silence Clang -Wunused-private-field warning.
5823 TypeOfIRGenerator::TypeOfIRGenerator(JSContext
* cx
, HandleScript script
,
5824 jsbytecode
* pc
, ICState state
,
5826 : IRGenerator(cx
, script
, pc
, CacheKind::TypeOf
, state
), val_(value
) {}
5828 void TypeOfIRGenerator::trackAttached(const char* name
) {
5829 stubName_
= name
? name
: "NotAttached";
5830 #ifdef JS_CACHEIR_SPEW
5831 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5832 sp
.valueProperty("val", val_
);
5837 AttachDecision
TypeOfIRGenerator::tryAttachStub() {
5838 MOZ_ASSERT(cacheKind_
== CacheKind::TypeOf
);
5840 AutoAssertNoPendingException
aanpe(cx_
);
5842 ValOperandId
valId(writer
.setInputOperandId(0));
5844 TRY_ATTACH(tryAttachPrimitive(valId
));
5845 TRY_ATTACH(tryAttachObject(valId
));
5847 MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOf");
5848 return AttachDecision::NoAction
;
5851 AttachDecision
TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId
) {
5852 if (!val_
.isPrimitive()) {
5853 return AttachDecision::NoAction
;
5856 // Note: we don't use GuardIsNumber for int32 values because it's less
5857 // efficient in Warp (unboxing to double instead of int32).
5858 if (val_
.isDouble()) {
5859 writer
.guardIsNumber(valId
);
5861 writer
.guardNonDoubleType(valId
, val_
.type());
5864 writer
.loadConstantStringResult(
5865 TypeName(js::TypeOfValue(val_
), cx_
->names()));
5866 writer
.returnFromIC();
5867 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5868 trackAttached("TypeOf.Primitive");
5869 return AttachDecision::Attach
;
5872 AttachDecision
TypeOfIRGenerator::tryAttachObject(ValOperandId valId
) {
5873 if (!val_
.isObject()) {
5874 return AttachDecision::NoAction
;
5877 ObjOperandId objId
= writer
.guardToObject(valId
);
5878 writer
.loadTypeOfObjectResult(objId
);
5879 writer
.returnFromIC();
5880 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5881 trackAttached("TypeOf.Object");
5882 return AttachDecision::Attach
;
5885 TypeOfEqIRGenerator::TypeOfEqIRGenerator(JSContext
* cx
, HandleScript script
,
5886 jsbytecode
* pc
, ICState state
,
5887 HandleValue value
, JSType type
,
5889 : IRGenerator(cx
, script
, pc
, CacheKind::TypeOfEq
, state
),
5892 compareOp_(compareOp
) {}
5894 void TypeOfEqIRGenerator::trackAttached(const char* name
) {
5895 stubName_
= name
? name
: "NotAttached";
5896 #ifdef JS_CACHEIR_SPEW
5897 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
5898 sp
.valueProperty("val", val_
);
5899 sp
.jstypeProperty("type", type_
);
5900 sp
.opcodeProperty("compareOp", compareOp_
);
5905 AttachDecision
TypeOfEqIRGenerator::tryAttachStub() {
5906 MOZ_ASSERT(cacheKind_
== CacheKind::TypeOfEq
);
5908 AutoAssertNoPendingException
aanpe(cx_
);
5910 ValOperandId
valId(writer
.setInputOperandId(0));
5912 TRY_ATTACH(tryAttachPrimitive(valId
));
5913 TRY_ATTACH(tryAttachObject(valId
));
5915 MOZ_ASSERT_UNREACHABLE("Failed to attach TypeOfEq");
5916 return AttachDecision::NoAction
;
5919 AttachDecision
TypeOfEqIRGenerator::tryAttachPrimitive(ValOperandId valId
) {
5920 if (!val_
.isPrimitive()) {
5921 return AttachDecision::NoAction
;
5924 // Note: we don't use GuardIsNumber for int32 values because it's less
5925 // efficient in Warp (unboxing to double instead of int32).
5926 if (val_
.isDouble()) {
5927 writer
.guardIsNumber(valId
);
5929 writer
.guardNonDoubleType(valId
, val_
.type());
5932 bool result
= js::TypeOfValue(val_
) == type_
;
5933 if (compareOp_
== JSOp::Ne
) {
5936 writer
.loadBooleanResult(result
);
5937 writer
.returnFromIC();
5938 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5939 trackAttached("TypeOfEq.Primitive");
5940 return AttachDecision::Attach
;
5943 AttachDecision
TypeOfEqIRGenerator::tryAttachObject(ValOperandId valId
) {
5944 if (!val_
.isObject()) {
5945 return AttachDecision::NoAction
;
5948 ObjOperandId objId
= writer
.guardToObject(valId
);
5949 writer
.loadTypeOfEqObjectResult(objId
, TypeofEqOperand(type_
, compareOp_
));
5950 writer
.returnFromIC();
5951 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
5952 trackAttached("TypeOfEq.Object");
5953 return AttachDecision::Attach
;
5956 GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext
* cx
,
5957 HandleScript script
,
5958 jsbytecode
* pc
, ICState state
,
5960 : IRGenerator(cx
, script
, pc
, CacheKind::GetIterator
, state
), val_(value
) {}
5962 AttachDecision
GetIteratorIRGenerator::tryAttachStub() {
5963 MOZ_ASSERT(cacheKind_
== CacheKind::GetIterator
);
5965 AutoAssertNoPendingException
aanpe(cx_
);
5967 ValOperandId
valId(writer
.setInputOperandId(0));
5969 TRY_ATTACH(tryAttachObject(valId
));
5970 TRY_ATTACH(tryAttachNullOrUndefined(valId
));
5971 TRY_ATTACH(tryAttachGeneric(valId
));
5973 trackAttached(IRGenerator::NotAttached
);
5974 return AttachDecision::NoAction
;
5977 AttachDecision
GetIteratorIRGenerator::tryAttachObject(ValOperandId valId
) {
5978 if (!val_
.isObject()) {
5979 return AttachDecision::NoAction
;
5982 MOZ_ASSERT(val_
.toObject().compartment() == cx_
->compartment());
5984 ObjOperandId objId
= writer
.guardToObject(valId
);
5985 writer
.objectToIteratorResult(objId
, cx_
->compartment()->enumeratorsAddr());
5986 writer
.returnFromIC();
5988 trackAttached("GetIterator.Object");
5989 return AttachDecision::Attach
;
5992 AttachDecision
GetIteratorIRGenerator::tryAttachNullOrUndefined(
5993 ValOperandId valId
) {
5994 MOZ_ASSERT(JSOp(*pc_
) == JSOp::Iter
);
5996 // For null/undefined we can simply return the empty iterator singleton. This
5997 // works because this iterator is unlinked and immutable.
5999 if (!val_
.isNullOrUndefined()) {
6000 return AttachDecision::NoAction
;
6003 PropertyIteratorObject
* emptyIter
=
6004 GlobalObject::getOrCreateEmptyIterator(cx_
);
6006 cx_
->recoverFromOutOfMemory();
6007 return AttachDecision::NoAction
;
6010 writer
.guardIsNullOrUndefined(valId
);
6012 ObjOperandId iterId
= writer
.loadObject(emptyIter
);
6013 writer
.loadObjectResult(iterId
);
6014 writer
.returnFromIC();
6016 trackAttached("GetIterator.NullOrUndefined");
6017 return AttachDecision::Attach
;
6020 AttachDecision
GetIteratorIRGenerator::tryAttachGeneric(ValOperandId valId
) {
6021 writer
.valueToIteratorResult(valId
);
6022 writer
.returnFromIC();
6024 trackAttached("GetIterator.Generic");
6025 return AttachDecision::Attach
;
6028 void GetIteratorIRGenerator::trackAttached(const char* name
) {
6029 stubName_
= name
? name
: "NotAttached";
6030 #ifdef JS_CACHEIR_SPEW
6031 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
6032 sp
.valueProperty("val", val_
);
6037 OptimizeSpreadCallIRGenerator::OptimizeSpreadCallIRGenerator(
6038 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
6040 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeSpreadCall
, state
),
6043 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachStub() {
6044 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeSpreadCall
);
6046 AutoAssertNoPendingException
aanpe(cx_
);
6048 TRY_ATTACH(tryAttachArray());
6049 TRY_ATTACH(tryAttachArguments());
6050 TRY_ATTACH(tryAttachNotOptimizable());
6052 trackAttached(IRGenerator::NotAttached
);
6053 return AttachDecision::NoAction
;
6056 static bool IsArrayInstanceOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
6057 MutableHandle
<NativeObject
*> arrProto
) {
6058 // Prototype must be Array.prototype.
6059 auto* proto
= cx
->global()->maybeGetArrayPrototype();
6060 if (!proto
|| arr
->staticPrototype() != proto
) {
6063 arrProto
.set(proto
);
6065 // The object must not have an own @@iterator property.
6066 PropertyKey iteratorKey
=
6067 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
6068 return !arr
->lookupPure(iteratorKey
);
6071 static bool IsArrayPrototypeOptimizable(JSContext
* cx
, Handle
<ArrayObject
*> arr
,
6072 Handle
<NativeObject
*> arrProto
,
6074 MutableHandle
<JSFunction
*> iterFun
) {
6075 PropertyKey iteratorKey
=
6076 PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
6077 // Ensure that Array.prototype's @@iterator slot is unchanged.
6078 Maybe
<PropertyInfo
> prop
= arrProto
->lookupPure(iteratorKey
);
6079 if (prop
.isNothing() || !prop
->isDataProperty()) {
6083 *slot
= prop
->slot();
6084 MOZ_ASSERT(arrProto
->numFixedSlots() == 0, "Stub code relies on this");
6086 const Value
& iterVal
= arrProto
->getSlot(*slot
);
6087 if (!iterVal
.isObject() || !iterVal
.toObject().is
<JSFunction
>()) {
6091 iterFun
.set(&iterVal
.toObject().as
<JSFunction
>());
6092 return IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
);
6095 enum class AllowIteratorReturn
: bool {
6099 static bool IsArrayIteratorPrototypeOptimizable(
6100 JSContext
* cx
, AllowIteratorReturn allowReturn
,
6101 MutableHandle
<NativeObject
*> arrIterProto
, uint32_t* slot
,
6102 MutableHandle
<JSFunction
*> nextFun
) {
6103 NativeObject
* proto
= nullptr;
6105 AutoEnterOOMUnsafeRegion oom
;
6106 proto
= GlobalObject::getOrCreateArrayIteratorPrototype(cx
, cx
->global());
6108 oom
.crash("failed to allocate Array iterator prototype");
6111 arrIterProto
.set(proto
);
6113 // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged.
6114 Maybe
<PropertyInfo
> prop
= proto
->lookupPure(cx
->names().next
);
6115 if (prop
.isNothing() || !prop
->isDataProperty()) {
6119 *slot
= prop
->slot();
6120 MOZ_ASSERT(proto
->numFixedSlots() == 0, "Stub code relies on this");
6122 const Value
& nextVal
= proto
->getSlot(*slot
);
6123 if (!nextVal
.isObject() || !nextVal
.toObject().is
<JSFunction
>()) {
6127 nextFun
.set(&nextVal
.toObject().as
<JSFunction
>());
6128 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
6132 if (allowReturn
== AllowIteratorReturn::No
) {
6133 // Ensure that %ArrayIteratorPrototype% doesn't define "return".
6134 if (!CheckHasNoSuchProperty(cx
, proto
, NameToId(cx
->names().return_
))) {
6142 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArray() {
6143 if (!isFirstStub_
) {
6144 return AttachDecision::NoAction
;
6147 // The value must be a packed array.
6148 if (!val_
.isObject()) {
6149 return AttachDecision::NoAction
;
6151 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
6152 if (!IsPackedArray(obj
)) {
6153 return AttachDecision::NoAction
;
6156 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
6158 Rooted
<NativeObject
*> arrProto(cx_
);
6159 uint32_t arrProtoIterSlot
;
6160 Rooted
<JSFunction
*> iterFun(cx_
);
6161 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
6162 return AttachDecision::NoAction
;
6165 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
6166 &arrProtoIterSlot
, &iterFun
)) {
6167 return AttachDecision::NoAction
;
6170 // %ArrayIteratorPrototype%.next must not be modified.
6171 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
6172 uint32_t iterNextSlot
;
6173 Rooted
<JSFunction
*> nextFun(cx_
);
6174 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
6175 &arrayIteratorProto
, &iterNextSlot
,
6177 return AttachDecision::NoAction
;
6180 ValOperandId
valId(writer
.setInputOperandId(0));
6181 ObjOperandId objId
= writer
.guardToObject(valId
);
6183 // Guard the object is a packed array with Array.prototype as proto.
6184 MOZ_ASSERT(obj
->is
<ArrayObject
>());
6185 writer
.guardShape(objId
, obj
->shape());
6186 writer
.guardArrayIsPacked(objId
);
6188 // Guard on Array.prototype[@@iterator].
6189 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
6190 ObjOperandId iterId
= writer
.loadObject(iterFun
);
6191 writer
.guardShape(arrProtoId
, arrProto
->shape());
6192 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
, arrProtoIterSlot
);
6194 // Guard on %ArrayIteratorPrototype%.next.
6195 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
6196 ObjOperandId nextId
= writer
.loadObject(nextFun
);
6197 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
6198 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, iterNextSlot
);
6200 writer
.loadObjectResult(objId
);
6201 writer
.returnFromIC();
6203 trackAttached("OptimizeSpreadCall.Array");
6204 return AttachDecision::Attach
;
6207 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachArguments() {
6208 // The value must be an arguments object.
6209 if (!val_
.isObject()) {
6210 return AttachDecision::NoAction
;
6212 RootedObject
obj(cx_
, &val_
.toObject());
6213 if (!obj
->is
<ArgumentsObject
>()) {
6214 return AttachDecision::NoAction
;
6216 auto args
= obj
.as
<ArgumentsObject
>();
6218 // Ensure neither elements, nor the length, nor the iterator has been
6219 // overridden. Also ensure no args are forwarded to allow reading them
6220 // directly from the frame.
6221 if (args
->hasOverriddenElement() || args
->hasOverriddenLength() ||
6222 args
->hasOverriddenIterator() || args
->anyArgIsForwarded()) {
6223 return AttachDecision::NoAction
;
6226 Rooted
<Shape
*> shape(cx_
, GlobalObject::getArrayShapeWithDefaultProto(cx_
));
6228 cx_
->clearPendingException();
6229 return AttachDecision::NoAction
;
6232 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
6234 Rooted
<JSFunction
*> nextFun(cx_
);
6235 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
6236 &arrayIteratorProto
, &slot
,
6238 return AttachDecision::NoAction
;
6241 ValOperandId
valId(writer
.setInputOperandId(0));
6242 ObjOperandId objId
= writer
.guardToObject(valId
);
6244 if (args
->is
<MappedArgumentsObject
>()) {
6245 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6247 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6248 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6250 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6251 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6252 ArgumentsObject::ITERATOR_OVERRIDDEN_BIT
|
6253 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6254 writer
.guardArgumentsObjectFlags(objId
, flags
);
6256 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
6257 ObjOperandId nextId
= writer
.loadObject(nextFun
);
6259 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
6261 // Ensure that proto[slot] == nextFun.
6262 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
6264 writer
.arrayFromArgumentsObjectResult(objId
, shape
);
6265 writer
.returnFromIC();
6267 trackAttached("OptimizeSpreadCall.Arguments");
6268 return AttachDecision::Attach
;
6271 AttachDecision
OptimizeSpreadCallIRGenerator::tryAttachNotOptimizable() {
6272 ValOperandId
valId(writer
.setInputOperandId(0));
6274 writer
.loadUndefinedResult();
6275 writer
.returnFromIC();
6277 trackAttached("OptimizeSpreadCall.NotOptimizable");
6278 return AttachDecision::Attach
;
6281 void OptimizeSpreadCallIRGenerator::trackAttached(const char* name
) {
6282 stubName_
= name
? name
: "NotAttached";
6283 #ifdef JS_CACHEIR_SPEW
6284 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
6285 sp
.valueProperty("val", val_
);
6290 CallIRGenerator::CallIRGenerator(JSContext
* cx
, HandleScript script
,
6291 jsbytecode
* pc
, JSOp op
, ICState state
,
6292 BaselineFrame
* frame
, uint32_t argc
,
6293 HandleValue callee
, HandleValue thisval
,
6294 HandleValue newTarget
, HandleValueArray args
)
6295 : IRGenerator(cx
, script
, pc
, CacheKind::Call
, state
, frame
),
6300 newTarget_(newTarget
),
6303 void InlinableNativeIRGenerator::emitNativeCalleeGuard() {
6304 // Note: we rely on GuardSpecificFunction to also guard against the same
6305 // native from a different realm.
6306 MOZ_ASSERT(callee_
->isNativeWithoutJitEntry());
6308 ObjOperandId calleeObjId
;
6309 if (flags_
.getArgFormat() == CallFlags::Standard
) {
6310 ValOperandId calleeValId
=
6311 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
6312 calleeObjId
= writer
.guardToObject(calleeValId
);
6313 } else if (flags_
.getArgFormat() == CallFlags::Spread
) {
6314 ValOperandId calleeValId
=
6315 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags_
);
6316 calleeObjId
= writer
.guardToObject(calleeValId
);
6317 } else if (flags_
.getArgFormat() == CallFlags::FunCall
) {
6318 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
6320 Int32OperandId
argcId(0);
6321 calleeObjId
= generator_
.emitFunCallOrApplyGuard(argcId
);
6323 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
6324 MOZ_ASSERT(generator_
.writer
.numOperandIds() > 0, "argcId is initialized");
6326 Int32OperandId
argcId(0);
6327 calleeObjId
= generator_
.emitFunApplyGuard(argcId
);
6330 writer
.guardSpecificFunction(calleeObjId
, callee_
);
6332 // If we're constructing we also need to guard newTarget == callee.
6333 if (flags_
.isConstructing()) {
6334 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
6335 MOZ_ASSERT(&newTarget_
.toObject() == callee_
);
6337 ValOperandId newTargetValId
=
6338 writer
.loadArgumentFixedSlot(ArgumentKind::NewTarget
, argc_
, flags_
);
6339 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
6340 writer
.guardSpecificFunction(newTargetObjId
, callee_
);
6344 ObjOperandId
InlinableNativeIRGenerator::emitLoadArgsArray() {
6345 if (flags_
.getArgFormat() == CallFlags::Spread
) {
6346 return writer
.loadSpreadArgs();
6349 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::FunApplyArray
);
6350 return generator_
.emitFunApplyArgsGuard(flags_
.getArgFormat()).ref();
6353 void IRGenerator::emitCalleeGuard(ObjOperandId calleeId
, JSFunction
* callee
) {
6354 // Guarding on the callee JSFunction* is most efficient, but doesn't work well
6355 // for lambda clones (multiple functions with the same BaseScript). We guard
6356 // on the function's BaseScript if the callee is scripted and this isn't the
6359 // Self-hosted functions are more complicated: top-level functions can be
6360 // relazified using SelfHostedLazyScript and this means they don't have a
6361 // stable BaseScript pointer. These functions are never lambda clones, though,
6362 // so we can just always guard on the JSFunction*. Self-hosted lambdas are
6363 // never relazified so there we use the normal heuristics.
6364 if (isFirstStub_
|| !callee
->hasBaseScript() ||
6365 (callee
->isSelfHostedBuiltin() && !callee
->isLambda())) {
6366 writer
.guardSpecificFunction(calleeId
, callee
);
6368 MOZ_ASSERT_IF(callee
->isSelfHostedBuiltin(),
6369 !callee
->baseScript()->allowRelazify());
6370 writer
.guardClass(calleeId
, GuardClassKind::JSFunction
);
6371 writer
.guardFunctionScript(calleeId
, callee
->baseScript());
6375 ObjOperandId
CallIRGenerator::emitFunCallOrApplyGuard(Int32OperandId argcId
) {
6376 JSFunction
* callee
= &callee_
.toObject().as
<JSFunction
>();
6377 MOZ_ASSERT(callee
->native() == fun_call
|| callee
->native() == fun_apply
);
6379 // Guard that callee is the |fun_call| or |fun_apply| native function.
6380 ValOperandId calleeValId
=
6381 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
);
6382 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
6383 writer
.guardSpecificFunction(calleeObjId
, callee
);
6385 // Guard that |this| is an object.
6386 ValOperandId thisValId
=
6387 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
);
6388 return writer
.guardToObject(thisValId
);
6391 ObjOperandId
CallIRGenerator::emitFunCallGuard(Int32OperandId argcId
) {
6392 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_call
);
6394 return emitFunCallOrApplyGuard(argcId
);
6397 ObjOperandId
CallIRGenerator::emitFunApplyGuard(Int32OperandId argcId
) {
6398 MOZ_ASSERT(callee_
.toObject().as
<JSFunction
>().native() == fun_apply
);
6400 return emitFunCallOrApplyGuard(argcId
);
6403 Maybe
<ObjOperandId
> CallIRGenerator::emitFunApplyArgsGuard(
6404 CallFlags::ArgFormat format
) {
6405 MOZ_ASSERT(argc_
== 2);
6407 ValOperandId argValId
=
6408 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6410 if (format
== CallFlags::FunApplyArgsObj
) {
6411 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6412 if (args_
[1].toObject().is
<MappedArgumentsObject
>()) {
6413 writer
.guardClass(argObjId
, GuardClassKind::MappedArguments
);
6415 MOZ_ASSERT(args_
[1].toObject().is
<UnmappedArgumentsObject
>());
6416 writer
.guardClass(argObjId
, GuardClassKind::UnmappedArguments
);
6418 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6419 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6420 writer
.guardArgumentsObjectFlags(argObjId
, flags
);
6421 return mozilla::Some(argObjId
);
6424 if (format
== CallFlags::FunApplyArray
) {
6425 ObjOperandId argObjId
= writer
.guardToObject(argValId
);
6426 emitOptimisticClassGuard(argObjId
, &args_
[1].toObject(),
6427 GuardClassKind::Array
);
6428 writer
.guardArrayIsPacked(argObjId
);
6429 return mozilla::Some(argObjId
);
6432 MOZ_ASSERT(format
== CallFlags::FunApplyNullUndefined
);
6433 writer
.guardIsNullOrUndefined(argValId
);
6434 return mozilla::Nothing();
6437 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPush() {
6438 // Only optimize on obj.push(val);
6439 if (argc_
!= 1 || !thisval_
.isObject()) {
6440 return AttachDecision::NoAction
;
6443 // Where |obj| is a native array.
6444 JSObject
* thisobj
= &thisval_
.toObject();
6445 if (!thisobj
->is
<ArrayObject
>()) {
6446 return AttachDecision::NoAction
;
6449 auto* thisarray
= &thisobj
->as
<ArrayObject
>();
6451 // Check for other indexed properties or class hooks.
6452 if (!CanAttachAddElement(thisarray
, /* isInit = */ false,
6453 AllowIndexedReceiver::No
)) {
6454 return AttachDecision::NoAction
;
6457 // Can't add new elements to arrays with non-writable length.
6458 if (!thisarray
->lengthIsWritable()) {
6459 return AttachDecision::NoAction
;
6462 // Check that array is extensible.
6463 if (!thisarray
->isExtensible()) {
6464 return AttachDecision::NoAction
;
6467 // Check that the array is completely initialized (no holes).
6468 if (thisarray
->getDenseInitializedLength() != thisarray
->length()) {
6469 return AttachDecision::NoAction
;
6472 MOZ_ASSERT(!thisarray
->denseElementsAreFrozen(),
6473 "Extensible arrays should not have frozen elements");
6475 // After this point, we can generate code fine.
6477 // Initialize the input operand.
6478 initializeInputOperand();
6480 // Guard callee is the 'push' native function.
6481 emitNativeCalleeGuard();
6483 // Guard this is an array object.
6484 ValOperandId thisValId
=
6485 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6486 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6488 // Guard that the shape matches.
6489 TestMatchingNativeReceiver(writer
, thisarray
, thisObjId
);
6491 // Guard proto chain shapes.
6492 ShapeGuardProtoChain(writer
, thisarray
, thisObjId
);
6494 // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
6495 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6496 writer
.arrayPush(thisObjId
, argId
);
6498 writer
.returnFromIC();
6500 trackAttached("ArrayPush");
6501 return AttachDecision::Attach
;
6504 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayPopShift(
6505 InlinableNative native
) {
6506 // Expecting no arguments.
6508 return AttachDecision::NoAction
;
6511 // Only optimize if |this| is a packed array.
6512 if (!thisval_
.isObject() || !IsPackedArray(&thisval_
.toObject())) {
6513 return AttachDecision::NoAction
;
6516 // Other conditions:
6518 // * The array length needs to be writable because we're changing it.
6519 // * The array must be extensible. Non-extensible arrays require preserving
6520 // the |initializedLength == capacity| invariant on ObjectElements.
6521 // See NativeObject::shrinkCapacityToInitializedLength.
6522 // This also ensures the elements aren't sealed/frozen.
6523 // * There must not be a for-in iterator for the elements because the IC stub
6524 // does not suppress deleted properties.
6525 ArrayObject
* arr
= &thisval_
.toObject().as
<ArrayObject
>();
6526 if (!arr
->lengthIsWritable() || !arr
->isExtensible() ||
6527 arr
->denseElementsHaveMaybeInIterationFlag()) {
6528 return AttachDecision::NoAction
;
6531 // Initialize the input operand.
6532 initializeInputOperand();
6534 // Guard callee is the 'pop' or 'shift' native function.
6535 emitNativeCalleeGuard();
6537 ValOperandId thisValId
=
6538 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6539 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6540 emitOptimisticClassGuard(objId
, arr
, GuardClassKind::Array
);
6542 if (native
== InlinableNative::ArrayPop
) {
6543 writer
.packedArrayPopResult(objId
);
6545 MOZ_ASSERT(native
== InlinableNative::ArrayShift
);
6546 writer
.packedArrayShiftResult(objId
);
6549 writer
.returnFromIC();
6551 trackAttached("ArrayPopShift");
6552 return AttachDecision::Attach
;
6555 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayJoin() {
6556 // Only handle argc <= 1.
6558 return AttachDecision::NoAction
;
6561 // Only optimize if |this| is an array.
6562 if (!thisval_
.isObject() || !thisval_
.toObject().is
<ArrayObject
>()) {
6563 return AttachDecision::NoAction
;
6566 // The separator argument must be a string, if present.
6567 if (argc_
> 0 && !args_
[0].isString()) {
6568 return AttachDecision::NoAction
;
6571 // IC stub code can handle non-packed array.
6573 // Initialize the input operand.
6574 initializeInputOperand();
6576 // Guard callee is the 'join' native function.
6577 emitNativeCalleeGuard();
6579 // Guard this is an array object.
6580 ValOperandId thisValId
=
6581 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6582 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
6583 emitOptimisticClassGuard(thisObjId
, &thisval_
.toObject(),
6584 GuardClassKind::Array
);
6586 StringOperandId sepId
;
6588 // If argcount is 1, guard that the argument is a string.
6589 ValOperandId argValId
=
6590 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6591 sepId
= writer
.guardToString(argValId
);
6593 sepId
= writer
.loadConstantString(cx_
->names().comma_
);
6597 writer
.arrayJoinResult(thisObjId
, sepId
);
6599 writer
.returnFromIC();
6601 trackAttached("ArrayJoin");
6602 return AttachDecision::Attach
;
6605 AttachDecision
InlinableNativeIRGenerator::tryAttachArraySlice() {
6606 // Only handle argc <= 2.
6608 return AttachDecision::NoAction
;
6611 // Only optimize if |this| is a packed array or an arguments object.
6612 if (!thisval_
.isObject()) {
6613 return AttachDecision::NoAction
;
6616 bool isPackedArray
= IsPackedArray(&thisval_
.toObject());
6617 if (!isPackedArray
) {
6618 if (!thisval_
.toObject().is
<ArgumentsObject
>()) {
6619 return AttachDecision::NoAction
;
6621 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6623 // No elements must have been overridden or deleted.
6624 if (args
->hasOverriddenElement()) {
6625 return AttachDecision::NoAction
;
6628 // The length property mustn't be overridden.
6629 if (args
->hasOverriddenLength()) {
6630 return AttachDecision::NoAction
;
6633 // And finally also check that no argument is forwarded.
6634 if (args
->anyArgIsForwarded()) {
6635 return AttachDecision::NoAction
;
6639 // Arguments for the sliced region must be integers.
6640 if (argc_
> 0 && !args_
[0].isInt32()) {
6641 return AttachDecision::NoAction
;
6643 if (argc_
> 1 && !args_
[1].isInt32()) {
6644 return AttachDecision::NoAction
;
6647 JSObject
* templateObj
= NewDenseFullyAllocatedArray(cx_
, 0, TenuredObject
);
6649 cx_
->recoverFromOutOfMemory();
6650 return AttachDecision::NoAction
;
6653 // Initialize the input operand.
6654 initializeInputOperand();
6656 // Guard callee is the 'slice' native function.
6657 emitNativeCalleeGuard();
6659 ValOperandId thisValId
=
6660 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6661 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6663 if (isPackedArray
) {
6664 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6665 GuardClassKind::Array
);
6667 auto* args
= &thisval_
.toObject().as
<ArgumentsObject
>();
6669 if (args
->is
<MappedArgumentsObject
>()) {
6670 writer
.guardClass(objId
, GuardClassKind::MappedArguments
);
6672 MOZ_ASSERT(args
->is
<UnmappedArgumentsObject
>());
6673 writer
.guardClass(objId
, GuardClassKind::UnmappedArguments
);
6676 uint8_t flags
= ArgumentsObject::ELEMENT_OVERRIDDEN_BIT
|
6677 ArgumentsObject::LENGTH_OVERRIDDEN_BIT
|
6678 ArgumentsObject::FORWARDED_ARGUMENTS_BIT
;
6679 writer
.guardArgumentsObjectFlags(objId
, flags
);
6682 Int32OperandId int32BeginId
;
6684 ValOperandId beginId
=
6685 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6686 int32BeginId
= writer
.guardToInt32(beginId
);
6688 int32BeginId
= writer
.loadInt32Constant(0);
6691 Int32OperandId int32EndId
;
6693 ValOperandId endId
=
6694 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6695 int32EndId
= writer
.guardToInt32(endId
);
6696 } else if (isPackedArray
) {
6697 int32EndId
= writer
.loadInt32ArrayLength(objId
);
6699 int32EndId
= writer
.loadArgumentsObjectLength(objId
);
6702 if (isPackedArray
) {
6703 writer
.packedArraySliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6705 writer
.argumentsSliceResult(templateObj
, objId
, int32BeginId
, int32EndId
);
6707 writer
.returnFromIC();
6709 trackAttached(isPackedArray
? "ArraySlice" : "ArgumentsSlice");
6710 return AttachDecision::Attach
;
6713 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayIsArray() {
6714 // Need a single argument.
6716 return AttachDecision::NoAction
;
6719 // Initialize the input operand.
6720 initializeInputOperand();
6722 // Guard callee is the 'isArray' native function.
6723 emitNativeCalleeGuard();
6725 // Check if the argument is an Array and return result.
6726 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6727 writer
.isArrayResult(argId
);
6728 writer
.returnFromIC();
6730 trackAttached("ArrayIsArray");
6731 return AttachDecision::Attach
;
6734 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewGet(
6735 Scalar::Type type
) {
6736 // Ensure |this| is a DataViewObject.
6737 if (!thisval_
.isObject() || !thisval_
.toObject().is
<DataViewObject
>()) {
6738 return AttachDecision::NoAction
;
6741 // Expected arguments: offset (number), optional littleEndian (boolean).
6742 if (argc_
< 1 || argc_
> 2) {
6743 return AttachDecision::NoAction
;
6745 int64_t offsetInt64
;
6746 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6747 return AttachDecision::NoAction
;
6749 if (argc_
> 1 && !args_
[1].isBoolean()) {
6750 return AttachDecision::NoAction
;
6753 auto* dv
= &thisval_
.toObject().as
<DataViewObject
>();
6755 // Bounds check the offset.
6756 size_t byteLength
= dv
->byteLength().valueOr(0);
6757 if (offsetInt64
< 0 || !DataViewObject::offsetIsInBounds(
6758 Scalar::byteSize(type
), offsetInt64
, byteLength
)) {
6759 return AttachDecision::NoAction
;
6762 // For getUint32 we let the stub return an Int32 if we have not seen a
6763 // double, to allow better codegen in Warp while avoiding bailout loops.
6764 bool forceDoubleForUint32
= false;
6765 if (type
== Scalar::Uint32
) {
6766 bool isLittleEndian
= argc_
> 1 && args_
[1].toBoolean();
6767 uint32_t res
= dv
->read
<uint32_t>(offsetInt64
, byteLength
, isLittleEndian
);
6768 forceDoubleForUint32
= res
>= INT32_MAX
;
6771 // Initialize the input operand.
6772 initializeInputOperand();
6774 // Guard callee is this DataView native function.
6775 emitNativeCalleeGuard();
6777 // Guard |this| is a DataViewObject.
6778 ValOperandId thisValId
=
6779 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6780 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6782 if (dv
->is
<FixedLengthDataViewObject
>()) {
6783 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6784 GuardClassKind::FixedLengthDataView
);
6786 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6787 GuardClassKind::ResizableDataView
);
6790 // Convert offset to intPtr.
6791 ValOperandId offsetId
=
6792 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6793 IntPtrOperandId intPtrOffsetId
=
6794 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6796 BooleanOperandId boolLittleEndianId
;
6798 ValOperandId littleEndianId
=
6799 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6800 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6802 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6805 auto viewKind
= ToArrayBufferViewKind(dv
);
6806 writer
.loadDataViewValueResult(objId
, intPtrOffsetId
, boolLittleEndianId
,
6807 type
, forceDoubleForUint32
, viewKind
);
6809 writer
.returnFromIC();
6811 trackAttached("DataViewGet");
6812 return AttachDecision::Attach
;
6815 AttachDecision
InlinableNativeIRGenerator::tryAttachDataViewSet(
6816 Scalar::Type type
) {
6817 // Ensure |this| is a DataViewObject.
6818 if (!thisval_
.isObject() || !thisval_
.toObject().is
<DataViewObject
>()) {
6819 return AttachDecision::NoAction
;
6822 // Expected arguments: offset (number), value, optional littleEndian (boolean)
6823 if (argc_
< 2 || argc_
> 3) {
6824 return AttachDecision::NoAction
;
6826 int64_t offsetInt64
;
6827 if (!ValueIsInt64Index(args_
[0], &offsetInt64
)) {
6828 return AttachDecision::NoAction
;
6830 if (!ValueCanConvertToNumeric(type
, args_
[1])) {
6831 return AttachDecision::NoAction
;
6833 if (argc_
> 2 && !args_
[2].isBoolean()) {
6834 return AttachDecision::NoAction
;
6837 auto* dv
= &thisval_
.toObject().as
<DataViewObject
>();
6839 // Bounds check the offset.
6840 size_t byteLength
= dv
->byteLength().valueOr(0);
6841 if (offsetInt64
< 0 || !DataViewObject::offsetIsInBounds(
6842 Scalar::byteSize(type
), offsetInt64
, byteLength
)) {
6843 return AttachDecision::NoAction
;
6846 // Initialize the input operand.
6847 initializeInputOperand();
6849 // Guard callee is this DataView native function.
6850 emitNativeCalleeGuard();
6852 // Guard |this| is a DataViewObject.
6853 ValOperandId thisValId
=
6854 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
6855 ObjOperandId objId
= writer
.guardToObject(thisValId
);
6857 if (dv
->is
<FixedLengthDataViewObject
>()) {
6858 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6859 GuardClassKind::FixedLengthDataView
);
6861 emitOptimisticClassGuard(objId
, &thisval_
.toObject(),
6862 GuardClassKind::ResizableDataView
);
6865 // Convert offset to intPtr.
6866 ValOperandId offsetId
=
6867 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6868 IntPtrOperandId intPtrOffsetId
=
6869 guardToIntPtrIndex(args_
[0], offsetId
, /* supportOOB = */ false);
6871 // Convert value to number or BigInt.
6872 ValOperandId valueId
=
6873 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
6874 OperandId numericValueId
= emitNumericGuard(valueId
, args_
[1], type
);
6876 BooleanOperandId boolLittleEndianId
;
6878 ValOperandId littleEndianId
=
6879 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6880 boolLittleEndianId
= writer
.guardToBoolean(littleEndianId
);
6882 boolLittleEndianId
= writer
.loadBooleanConstant(false);
6885 auto viewKind
= ToArrayBufferViewKind(dv
);
6886 writer
.storeDataViewValueResult(objId
, intPtrOffsetId
, numericValueId
,
6887 boolLittleEndianId
, type
, viewKind
);
6889 writer
.returnFromIC();
6891 trackAttached("DataViewSet");
6892 return AttachDecision::Attach
;
6895 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeGetReservedSlot(
6896 InlinableNative native
) {
6897 // Self-hosted code calls this with (object, int32) arguments.
6898 MOZ_ASSERT(argc_
== 2);
6899 MOZ_ASSERT(args_
[0].isObject());
6900 MOZ_ASSERT(args_
[1].isInt32());
6901 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6903 uint32_t slot
= uint32_t(args_
[1].toInt32());
6904 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6905 return AttachDecision::NoAction
;
6907 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6909 // Initialize the input operand.
6910 initializeInputOperand();
6912 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6914 // Guard that the first argument is an object.
6915 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6916 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6918 // BytecodeEmitter::assertSelfHostedUnsafeGetReservedSlot ensures that the
6919 // slot argument is constant. (At least for direct calls)
6922 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
6923 writer
.loadFixedSlotResult(objId
, offset
);
6925 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
6926 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Object
);
6928 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
6929 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::Int32
);
6931 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
6932 writer
.loadFixedSlotTypedResult(objId
, offset
, ValueType::String
);
6935 MOZ_CRASH("unexpected native");
6938 writer
.returnFromIC();
6940 trackAttached("UnsafeGetReservedSlot");
6941 return AttachDecision::Attach
;
6944 AttachDecision
InlinableNativeIRGenerator::tryAttachUnsafeSetReservedSlot() {
6945 // Self-hosted code calls this with (object, int32, value) arguments.
6946 MOZ_ASSERT(argc_
== 3);
6947 MOZ_ASSERT(args_
[0].isObject());
6948 MOZ_ASSERT(args_
[1].isInt32());
6949 MOZ_ASSERT(args_
[1].toInt32() >= 0);
6951 uint32_t slot
= uint32_t(args_
[1].toInt32());
6952 if (slot
>= NativeObject::MAX_FIXED_SLOTS
) {
6953 return AttachDecision::NoAction
;
6955 size_t offset
= NativeObject::getFixedSlotOffset(slot
);
6957 // Initialize the input operand.
6958 initializeInputOperand();
6960 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
6962 // Guard that the first argument is an object.
6963 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6964 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
6966 // BytecodeEmitter::assertSelfHostedUnsafeSetReservedSlot ensures that the
6967 // slot argument is constant. (At least for direct calls)
6969 // Get the value to set.
6970 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
6972 // Set the fixed slot and return undefined.
6973 writer
.storeFixedSlotUndefinedResult(objId
, offset
, valId
);
6975 // This stub always returns undefined.
6976 writer
.returnFromIC();
6978 trackAttached("UnsafeSetReservedSlot");
6979 return AttachDecision::Attach
;
6982 AttachDecision
InlinableNativeIRGenerator::tryAttachIsSuspendedGenerator() {
6983 // The IsSuspendedGenerator intrinsic is only called in
6984 // self-hosted code, so it's safe to assume we have a single
6985 // argument and the callee is our intrinsic.
6987 MOZ_ASSERT(argc_
== 1);
6989 initializeInputOperand();
6991 // Stack layout here is (bottom to top):
6994 // 0: Arg <-- Top of stack.
6995 // We only care about the argument.
6996 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
6998 // Check whether the argument is a suspended generator.
6999 // We don't need guards, because IsSuspendedGenerator returns
7000 // false for values that are not generator objects.
7001 writer
.callIsSuspendedGeneratorResult(valId
);
7002 writer
.returnFromIC();
7004 trackAttached("IsSuspendedGenerator");
7005 return AttachDecision::Attach
;
7008 AttachDecision
InlinableNativeIRGenerator::tryAttachToObject() {
7009 // Self-hosted code calls this with a single argument.
7010 MOZ_ASSERT(argc_
== 1);
7012 // Need a single object argument.
7013 // TODO(Warp): Support all or more conversions to object.
7014 if (!args_
[0].isObject()) {
7015 return AttachDecision::NoAction
;
7018 // Initialize the input operand.
7019 initializeInputOperand();
7021 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7023 // Guard that the argument is an object.
7024 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7025 ObjOperandId objId
= writer
.guardToObject(argId
);
7027 // Return the object.
7028 writer
.loadObjectResult(objId
);
7029 writer
.returnFromIC();
7031 trackAttached("ToObject");
7032 return AttachDecision::Attach
;
7035 AttachDecision
InlinableNativeIRGenerator::tryAttachToInteger() {
7036 // Self-hosted code calls this with a single argument.
7037 MOZ_ASSERT(argc_
== 1);
7039 // Need a single int32 argument.
7040 // TODO(Warp): Support all or more conversions to integer.
7041 // Make sure to update this code correctly if we ever start
7042 // returning non-int32 integers.
7043 if (!args_
[0].isInt32()) {
7044 return AttachDecision::NoAction
;
7047 // Initialize the input operand.
7048 initializeInputOperand();
7050 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7052 // Guard that the argument is an int32.
7053 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7054 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
7056 // Return the int32.
7057 writer
.loadInt32Result(int32Id
);
7058 writer
.returnFromIC();
7060 trackAttached("ToInteger");
7061 return AttachDecision::Attach
;
7064 AttachDecision
InlinableNativeIRGenerator::tryAttachToLength() {
7065 // Self-hosted code calls this with a single argument.
7066 MOZ_ASSERT(argc_
== 1);
7068 // Need a single int32 argument.
7069 if (!args_
[0].isInt32()) {
7070 return AttachDecision::NoAction
;
7073 // Initialize the input operand.
7074 initializeInputOperand();
7076 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7078 // ToLength(int32) is equivalent to max(int32, 0).
7079 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7080 Int32OperandId int32ArgId
= writer
.guardToInt32(argId
);
7081 Int32OperandId zeroId
= writer
.loadInt32Constant(0);
7083 Int32OperandId maxId
= writer
.int32MinMax(isMax
, int32ArgId
, zeroId
);
7084 writer
.loadInt32Result(maxId
);
7085 writer
.returnFromIC();
7087 trackAttached("ToLength");
7088 return AttachDecision::Attach
;
7091 AttachDecision
InlinableNativeIRGenerator::tryAttachIsObject() {
7092 // Self-hosted code calls this with a single argument.
7093 MOZ_ASSERT(argc_
== 1);
7095 // Initialize the input operand.
7096 initializeInputOperand();
7098 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7100 // Type check the argument and return result.
7101 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7102 writer
.isObjectResult(argId
);
7103 writer
.returnFromIC();
7105 trackAttached("IsObject");
7106 return AttachDecision::Attach
;
7109 AttachDecision
InlinableNativeIRGenerator::tryAttachIsPackedArray() {
7110 // Self-hosted code calls this with a single object argument.
7111 MOZ_ASSERT(argc_
== 1);
7112 MOZ_ASSERT(args_
[0].isObject());
7114 // Initialize the input operand.
7115 initializeInputOperand();
7117 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7119 // Check if the argument is packed and return result.
7120 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7121 ObjOperandId objArgId
= writer
.guardToObject(argId
);
7122 writer
.isPackedArrayResult(objArgId
);
7123 writer
.returnFromIC();
7125 trackAttached("IsPackedArray");
7126 return AttachDecision::Attach
;
7129 AttachDecision
InlinableNativeIRGenerator::tryAttachIsCallable() {
7130 // Self-hosted code calls this with a single argument.
7131 MOZ_ASSERT(argc_
== 1);
7133 // Initialize the input operand.
7134 initializeInputOperand();
7136 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7138 // Check if the argument is callable and return result.
7139 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7140 writer
.isCallableResult(argId
);
7141 writer
.returnFromIC();
7143 trackAttached("IsCallable");
7144 return AttachDecision::Attach
;
7147 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructor() {
7148 // Self-hosted code calls this with a single argument.
7149 MOZ_ASSERT(argc_
== 1);
7151 // Need a single object argument.
7152 if (!args_
[0].isObject()) {
7153 return AttachDecision::NoAction
;
7156 // Initialize the input operand.
7157 initializeInputOperand();
7159 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7161 // Guard that the argument is an object.
7162 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7163 ObjOperandId objId
= writer
.guardToObject(argId
);
7165 // Check if the argument is a constructor and return result.
7166 writer
.isConstructorResult(objId
);
7167 writer
.returnFromIC();
7169 trackAttached("IsConstructor");
7170 return AttachDecision::Attach
;
7174 InlinableNativeIRGenerator::tryAttachIsCrossRealmArrayConstructor() {
7175 // Self-hosted code calls this with an object argument.
7176 MOZ_ASSERT(argc_
== 1);
7177 MOZ_ASSERT(args_
[0].isObject());
7179 if (args_
[0].toObject().is
<ProxyObject
>()) {
7180 return AttachDecision::NoAction
;
7183 // Initialize the input operand.
7184 initializeInputOperand();
7186 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7188 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7189 ObjOperandId objId
= writer
.guardToObject(argId
);
7190 writer
.guardIsNotProxy(objId
);
7191 writer
.isCrossRealmArrayConstructorResult(objId
);
7192 writer
.returnFromIC();
7194 trackAttached("IsCrossRealmArrayConstructor");
7195 return AttachDecision::Attach
;
7198 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
7199 InlinableNative native
) {
7200 // Self-hosted code calls this with an object argument.
7201 MOZ_ASSERT(argc_
== 1);
7202 MOZ_ASSERT(args_
[0].isObject());
7204 // Class must match.
7205 const JSClass
* clasp
= InlinableNativeGuardToClass(native
);
7206 if (args_
[0].toObject().getClass() != clasp
) {
7207 return AttachDecision::NoAction
;
7210 // Initialize the input operand.
7211 initializeInputOperand();
7213 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7215 // Guard that the argument is an object.
7216 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7217 ObjOperandId objId
= writer
.guardToObject(argId
);
7219 // Guard that the object has the correct class.
7220 writer
.guardAnyClass(objId
, clasp
);
7222 // Return the object.
7223 writer
.loadObjectResult(objId
);
7224 writer
.returnFromIC();
7226 trackAttached("GuardToClass");
7227 return AttachDecision::Attach
;
7230 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToClass(
7231 GuardClassKind kind
) {
7232 // Self-hosted code calls this with an object argument.
7233 MOZ_ASSERT(argc_
== 1);
7234 MOZ_ASSERT(args_
[0].isObject());
7236 // Class must match.
7237 const JSClass
* clasp
= ClassFor(kind
);
7238 if (args_
[0].toObject().getClass() != clasp
) {
7239 return AttachDecision::NoAction
;
7242 // Initialize the input operand.
7243 initializeInputOperand();
7245 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7247 // Guard that the argument is an object.
7248 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7249 ObjOperandId objId
= writer
.guardToObject(argId
);
7251 // Guard that the object has the correct class.
7252 writer
.guardClass(objId
, kind
);
7254 // Return the object.
7255 writer
.loadObjectResult(objId
);
7256 writer
.returnFromIC();
7258 trackAttached("GuardToClass");
7259 return AttachDecision::Attach
;
7262 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToEitherClass(
7263 GuardClassKind kind1
, GuardClassKind kind2
) {
7264 MOZ_ASSERT(kind1
!= kind2
,
7265 "prefer tryAttachGuardToClass for the same class case");
7267 // Self-hosted code calls this with an object argument.
7268 MOZ_ASSERT(argc_
== 1);
7269 MOZ_ASSERT(args_
[0].isObject());
7271 // Class must match.
7272 const JSClass
* clasp1
= ClassFor(kind1
);
7273 const JSClass
* clasp2
= ClassFor(kind2
);
7274 const JSClass
* objClass
= args_
[0].toObject().getClass();
7275 if (objClass
!= clasp1
&& objClass
!= clasp2
) {
7276 return AttachDecision::NoAction
;
7279 // Initialize the input operand.
7280 initializeInputOperand();
7282 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7284 // Guard that the argument is an object.
7285 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7286 ObjOperandId objId
= writer
.guardToObject(argId
);
7288 // Guard that the object has the correct class.
7289 writer
.guardEitherClass(objId
, kind1
, kind2
);
7291 // Return the object.
7292 writer
.loadObjectResult(objId
);
7293 writer
.returnFromIC();
7295 trackAttached("GuardToEitherClass");
7296 return AttachDecision::Attach
;
7299 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToArrayBuffer() {
7300 return tryAttachGuardToEitherClass(GuardClassKind::FixedLengthArrayBuffer
,
7301 GuardClassKind::ResizableArrayBuffer
);
7304 AttachDecision
InlinableNativeIRGenerator::tryAttachGuardToSharedArrayBuffer() {
7305 return tryAttachGuardToEitherClass(
7306 GuardClassKind::FixedLengthSharedArrayBuffer
,
7307 GuardClassKind::GrowableSharedArrayBuffer
);
7310 AttachDecision
InlinableNativeIRGenerator::tryAttachHasClass(
7311 const JSClass
* clasp
, bool isPossiblyWrapped
) {
7312 // Self-hosted code calls this with an object argument.
7313 MOZ_ASSERT(argc_
== 1);
7314 MOZ_ASSERT(args_
[0].isObject());
7316 // Only optimize when the object isn't a proxy.
7317 if (isPossiblyWrapped
&& args_
[0].toObject().is
<ProxyObject
>()) {
7318 return AttachDecision::NoAction
;
7321 // Initialize the input operand.
7322 initializeInputOperand();
7324 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7326 // Perform the Class check.
7327 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7328 ObjOperandId objId
= writer
.guardToObject(argId
);
7330 if (isPossiblyWrapped
) {
7331 writer
.guardIsNotProxy(objId
);
7334 writer
.hasClassResult(objId
, clasp
);
7335 writer
.returnFromIC();
7337 trackAttached("HasClass");
7338 return AttachDecision::Attach
;
7341 // Returns whether the .lastIndex property is a non-negative int32 value and is
7343 static bool HasOptimizableLastIndexSlot(RegExpObject
* regexp
, JSContext
* cx
) {
7344 auto lastIndexProp
= regexp
->lookupPure(cx
->names().lastIndex
);
7345 MOZ_ASSERT(lastIndexProp
->isDataProperty());
7346 if (!lastIndexProp
->writable()) {
7349 Value lastIndex
= regexp
->getLastIndex();
7350 if (!lastIndex
.isInt32() || lastIndex
.toInt32() < 0) {
7356 // Returns the RegExp stub used by the optimized code path for this intrinsic.
7357 // We store a pointer to this in the IC stub to ensure GC doesn't discard it.
7358 static JitCode
* GetOrCreateRegExpStub(JSContext
* cx
, InlinableNative native
) {
7359 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
7362 // The stubs assume the global has non-null RegExpStatics and match result
7364 if (!GlobalObject::getRegExpStatics(cx
, cx
->global()) ||
7365 !cx
->global()->regExpRealm().getOrCreateMatchResultShape(cx
)) {
7366 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
7367 cx
->clearPendingException();
7372 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
7373 case InlinableNative::IntrinsicRegExpExecForTest
:
7374 code
= cx
->zone()->jitZone()->ensureRegExpExecTestStubExists(cx
);
7376 case InlinableNative::IntrinsicRegExpBuiltinExec
:
7377 case InlinableNative::IntrinsicRegExpExec
:
7378 code
= cx
->zone()->jitZone()->ensureRegExpExecMatchStubExists(cx
);
7380 case InlinableNative::RegExpMatcher
:
7381 code
= cx
->zone()->jitZone()->ensureRegExpMatcherStubExists(cx
);
7383 case InlinableNative::RegExpSearcher
:
7384 code
= cx
->zone()->jitZone()->ensureRegExpSearcherStubExists(cx
);
7387 MOZ_CRASH("Unexpected native");
7390 MOZ_ASSERT(cx
->isThrowingOutOfMemory() || cx
->isThrowingOverRecursed());
7391 cx
->clearPendingException();
7398 static void EmitGuardLastIndexIsNonNegativeInt32(CacheIRWriter
& writer
,
7399 ObjOperandId regExpId
) {
7401 NativeObject::getFixedSlotOffset(RegExpObject::lastIndexSlot());
7402 ValOperandId lastIndexValId
= writer
.loadFixedSlot(regExpId
, offset
);
7403 Int32OperandId lastIndexId
= writer
.guardToInt32(lastIndexValId
);
7404 writer
.guardInt32IsNonNegative(lastIndexId
);
7407 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpBuiltinExec(
7408 InlinableNative native
) {
7409 // Self-hosted code calls this with (regexp, string) arguments.
7410 MOZ_ASSERT(argc_
== 2);
7411 MOZ_ASSERT(args_
[0].isObject());
7412 MOZ_ASSERT(args_
[1].isString());
7414 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7416 return AttachDecision::NoAction
;
7419 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
7420 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
7421 return AttachDecision::NoAction
;
7424 // Initialize the input operand.
7425 initializeInputOperand();
7427 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7429 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7430 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7431 writer
.guardShape(regExpId
, re
->shape());
7432 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7434 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7435 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7437 if (native
== InlinableNative::IntrinsicRegExpBuiltinExecForTest
) {
7438 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7440 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7442 writer
.returnFromIC();
7444 trackAttached("IntrinsicRegExpBuiltinExec");
7445 return AttachDecision::Attach
;
7448 AttachDecision
InlinableNativeIRGenerator::tryAttachIntrinsicRegExpExec(
7449 InlinableNative native
) {
7450 // Self-hosted code calls this with (object, string) arguments.
7451 MOZ_ASSERT(argc_
== 2);
7452 MOZ_ASSERT(args_
[0].isObject());
7453 MOZ_ASSERT(args_
[1].isString());
7455 if (!args_
[0].toObject().is
<RegExpObject
>()) {
7456 return AttachDecision::NoAction
;
7459 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7461 return AttachDecision::NoAction
;
7464 RegExpObject
* re
= &args_
[0].toObject().as
<RegExpObject
>();
7465 if (!HasOptimizableLastIndexSlot(re
, cx_
)) {
7466 return AttachDecision::NoAction
;
7469 // Ensure regexp.exec is the original RegExp.prototype.exec function on the
7471 if (re
->containsPure(cx_
->names().exec
)) {
7472 return AttachDecision::NoAction
;
7474 MOZ_ASSERT(cx_
->global()->maybeGetRegExpPrototype());
7476 &cx_
->global()->maybeGetRegExpPrototype()->as
<NativeObject
>();
7477 if (re
->staticPrototype() != regExpProto
) {
7478 return AttachDecision::NoAction
;
7480 auto execProp
= regExpProto
->as
<NativeObject
>().lookupPure(cx_
->names().exec
);
7481 if (!execProp
|| !execProp
->isDataProperty()) {
7482 return AttachDecision::NoAction
;
7484 // It should be stored in a dynamic slot. We assert this in
7485 // FinishRegExpClassInit.
7486 if (regExpProto
->isFixedSlot(execProp
->slot())) {
7487 return AttachDecision::NoAction
;
7489 Value execVal
= regExpProto
->getSlot(execProp
->slot());
7490 PropertyName
* execName
= cx_
->names().RegExp_prototype_Exec
;
7491 if (!IsSelfHostedFunctionWithName(execVal
, execName
)) {
7492 return AttachDecision::NoAction
;
7494 JSFunction
* execFunction
= &execVal
.toObject().as
<JSFunction
>();
7496 // Initialize the input operand.
7497 initializeInputOperand();
7499 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7501 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7502 ObjOperandId regExpId
= writer
.guardToObject(arg0Id
);
7503 writer
.guardShape(regExpId
, re
->shape());
7504 EmitGuardLastIndexIsNonNegativeInt32(writer
, regExpId
);
7506 // Emit guards for the RegExp.prototype.exec property.
7507 ObjOperandId regExpProtoId
= writer
.loadObject(regExpProto
);
7508 writer
.guardShape(regExpProtoId
, regExpProto
->shape());
7510 regExpProto
->dynamicSlotIndex(execProp
->slot()) * sizeof(Value
);
7511 writer
.guardDynamicSlotValue(regExpProtoId
, offset
,
7512 ObjectValue(*execFunction
));
7514 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7515 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7517 if (native
== InlinableNative::IntrinsicRegExpExecForTest
) {
7518 writer
.regExpBuiltinExecTestResult(regExpId
, inputId
, stub
);
7520 writer
.regExpBuiltinExecMatchResult(regExpId
, inputId
, stub
);
7522 writer
.returnFromIC();
7524 trackAttached("IntrinsicRegExpExec");
7525 return AttachDecision::Attach
;
7528 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpMatcherSearcher(
7529 InlinableNative native
) {
7530 // Self-hosted code calls this with (object, string, number) arguments.
7531 MOZ_ASSERT(argc_
== 3);
7532 MOZ_ASSERT(args_
[0].isObject());
7533 MOZ_ASSERT(args_
[1].isString());
7534 MOZ_ASSERT(args_
[2].isNumber());
7536 // It's not guaranteed that the JITs have typed |lastIndex| as an Int32.
7537 if (!args_
[2].isInt32()) {
7538 return AttachDecision::NoAction
;
7541 JitCode
* stub
= GetOrCreateRegExpStub(cx_
, native
);
7543 return AttachDecision::NoAction
;
7546 // Initialize the input operand.
7547 initializeInputOperand();
7549 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7551 // Guard argument types.
7552 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7553 ObjOperandId reId
= writer
.guardToObject(arg0Id
);
7555 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7556 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7558 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7559 Int32OperandId lastIndexId
= writer
.guardToInt32(arg2Id
);
7562 case InlinableNative::RegExpMatcher
:
7563 writer
.callRegExpMatcherResult(reId
, inputId
, lastIndexId
, stub
);
7564 writer
.returnFromIC();
7565 trackAttached("RegExpMatcher");
7568 case InlinableNative::RegExpSearcher
:
7569 writer
.callRegExpSearcherResult(reId
, inputId
, lastIndexId
, stub
);
7570 writer
.returnFromIC();
7571 trackAttached("RegExpSearcher");
7575 MOZ_CRASH("Unexpected native");
7578 return AttachDecision::Attach
;
7581 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
7582 // Self-hosted code calls this with a string argument that's only used for an
7584 MOZ_ASSERT(argc_
== 1);
7585 MOZ_ASSERT(args_
[0].isString());
7587 // Initialize the input operand.
7588 initializeInputOperand();
7590 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7592 writer
.regExpSearcherLastLimitResult();
7593 writer
.returnFromIC();
7595 trackAttached("RegExpSearcherLastLimit");
7596 return AttachDecision::Attach
;
7599 AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
7600 // Self-hosted code calls this with object and string arguments.
7601 MOZ_ASSERT(argc_
== 2);
7602 MOZ_ASSERT(args_
[0].toObject().is
<RegExpObject
>());
7603 MOZ_ASSERT(args_
[1].isString());
7605 // Initialize the input operand.
7606 initializeInputOperand();
7608 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7610 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7611 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7613 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7614 StringOperandId inputId
= writer
.guardToString(arg1Id
);
7616 writer
.regExpHasCaptureGroupsResult(objId
, inputId
);
7617 writer
.returnFromIC();
7619 trackAttached("RegExpHasCaptureGroups");
7620 return AttachDecision::Attach
;
7624 InlinableNativeIRGenerator::tryAttachRegExpPrototypeOptimizable() {
7625 // Self-hosted code calls this with a single object argument.
7626 MOZ_ASSERT(argc_
== 1);
7627 MOZ_ASSERT(args_
[0].isObject());
7629 // Initialize the input operand.
7630 initializeInputOperand();
7632 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7634 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7635 ObjOperandId protoId
= writer
.guardToObject(arg0Id
);
7637 writer
.regExpPrototypeOptimizableResult(protoId
);
7638 writer
.returnFromIC();
7640 trackAttached("RegExpPrototypeOptimizable");
7641 return AttachDecision::Attach
;
7645 InlinableNativeIRGenerator::tryAttachRegExpInstanceOptimizable() {
7646 // Self-hosted code calls this with two object arguments.
7647 MOZ_ASSERT(argc_
== 2);
7648 MOZ_ASSERT(args_
[0].isObject());
7649 MOZ_ASSERT(args_
[1].isObject());
7651 // Initialize the input operand.
7652 initializeInputOperand();
7654 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7656 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7657 ObjOperandId regexpId
= writer
.guardToObject(arg0Id
);
7659 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7660 ObjOperandId protoId
= writer
.guardToObject(arg1Id
);
7662 writer
.regExpInstanceOptimizableResult(regexpId
, protoId
);
7663 writer
.returnFromIC();
7665 trackAttached("RegExpInstanceOptimizable");
7666 return AttachDecision::Attach
;
7669 AttachDecision
InlinableNativeIRGenerator::tryAttachGetFirstDollarIndex() {
7670 // Self-hosted code calls this with a single string argument.
7671 MOZ_ASSERT(argc_
== 1);
7672 MOZ_ASSERT(args_
[0].isString());
7674 // Initialize the input operand.
7675 initializeInputOperand();
7677 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7679 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7680 StringOperandId strId
= writer
.guardToString(arg0Id
);
7682 writer
.getFirstDollarIndexResult(strId
);
7683 writer
.returnFromIC();
7685 trackAttached("GetFirstDollarIndex");
7686 return AttachDecision::Attach
;
7689 AttachDecision
InlinableNativeIRGenerator::tryAttachSubstringKernel() {
7690 // Self-hosted code calls this with (string, int32, int32) arguments.
7691 MOZ_ASSERT(argc_
== 3);
7692 MOZ_ASSERT(args_
[0].isString());
7693 MOZ_ASSERT(args_
[1].isInt32());
7694 MOZ_ASSERT(args_
[2].isInt32());
7696 // Initialize the input operand.
7697 initializeInputOperand();
7699 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7701 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7702 StringOperandId strId
= writer
.guardToString(arg0Id
);
7704 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7705 Int32OperandId beginId
= writer
.guardToInt32(arg1Id
);
7707 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7708 Int32OperandId lengthId
= writer
.guardToInt32(arg2Id
);
7710 writer
.callSubstringKernelResult(strId
, beginId
, lengthId
);
7711 writer
.returnFromIC();
7713 trackAttached("SubstringKernel");
7714 return AttachDecision::Attach
;
7717 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectHasPrototype() {
7718 // Self-hosted code calls this with (object, object) arguments.
7719 MOZ_ASSERT(argc_
== 2);
7720 MOZ_ASSERT(args_
[0].isObject());
7721 MOZ_ASSERT(args_
[1].isObject());
7723 auto* obj
= &args_
[0].toObject().as
<NativeObject
>();
7724 auto* proto
= &args_
[1].toObject().as
<NativeObject
>();
7726 // Only attach when obj.__proto__ is proto.
7727 if (obj
->staticPrototype() != proto
) {
7728 return AttachDecision::NoAction
;
7731 // Initialize the input operand.
7732 initializeInputOperand();
7734 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7736 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7737 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
7739 writer
.guardProto(objId
, proto
);
7740 writer
.loadBooleanResult(true);
7741 writer
.returnFromIC();
7743 trackAttached("ObjectHasPrototype");
7744 return AttachDecision::Attach
;
7747 static bool CanConvertToString(const Value
& v
) {
7748 return v
.isString() || v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
7751 AttachDecision
InlinableNativeIRGenerator::tryAttachString() {
7752 // Need a single argument that is or can be converted to a string.
7753 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7754 return AttachDecision::NoAction
;
7757 // Initialize the input operand.
7758 initializeInputOperand();
7760 // Guard callee is the 'String' function.
7761 emitNativeCalleeGuard();
7763 // Guard that the argument is a string or can be converted to one.
7764 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7765 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7767 // Return the string.
7768 writer
.loadStringResult(strId
);
7769 writer
.returnFromIC();
7771 trackAttached("String");
7772 return AttachDecision::Attach
;
7775 AttachDecision
InlinableNativeIRGenerator::tryAttachStringConstructor() {
7776 // Need a single argument that is or can be converted to a string.
7777 if (argc_
!= 1 || !CanConvertToString(args_
[0])) {
7778 return AttachDecision::NoAction
;
7781 RootedString
emptyString(cx_
, cx_
->runtime()->emptyString
);
7782 JSObject
* templateObj
= StringObject::create(
7783 cx_
, emptyString
, /* proto = */ nullptr, TenuredObject
);
7785 cx_
->recoverFromOutOfMemory();
7786 return AttachDecision::NoAction
;
7789 // Initialize the input operand.
7790 initializeInputOperand();
7792 // Guard callee is the 'String' function.
7793 emitNativeCalleeGuard();
7795 // Guard on number and convert to string.
7796 ValOperandId argId
=
7797 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
7798 StringOperandId strId
= emitToStringGuard(argId
, args_
[0]);
7800 writer
.newStringObjectResult(templateObj
, strId
);
7801 writer
.returnFromIC();
7803 trackAttached("StringConstructor");
7804 return AttachDecision::Attach
;
7807 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToStringValueOf() {
7808 // Expecting no arguments.
7810 return AttachDecision::NoAction
;
7813 // Ensure |this| is a primitive string value.
7814 if (!thisval_
.isString()) {
7815 return AttachDecision::NoAction
;
7818 // Initialize the input operand.
7819 initializeInputOperand();
7821 // Guard callee is the 'toString' OR 'valueOf' native function.
7822 emitNativeCalleeGuard();
7824 // Guard |this| is a string.
7825 ValOperandId thisValId
=
7826 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7827 StringOperandId strId
= writer
.guardToString(thisValId
);
7829 // Return the string
7830 writer
.loadStringResult(strId
);
7831 writer
.returnFromIC();
7833 trackAttached("StringToStringValueOf");
7834 return AttachDecision::Attach
;
7837 AttachDecision
InlinableNativeIRGenerator::tryAttachStringReplaceString() {
7838 // Self-hosted code calls this with (string, string, string) arguments.
7839 MOZ_ASSERT(argc_
== 3);
7840 MOZ_ASSERT(args_
[0].isString());
7841 MOZ_ASSERT(args_
[1].isString());
7842 MOZ_ASSERT(args_
[2].isString());
7844 // Initialize the input operand.
7845 initializeInputOperand();
7847 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7849 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7850 StringOperandId strId
= writer
.guardToString(arg0Id
);
7852 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7853 StringOperandId patternId
= writer
.guardToString(arg1Id
);
7855 ValOperandId arg2Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
7856 StringOperandId replacementId
= writer
.guardToString(arg2Id
);
7858 writer
.stringReplaceStringResult(strId
, patternId
, replacementId
);
7859 writer
.returnFromIC();
7861 trackAttached("StringReplaceString");
7862 return AttachDecision::Attach
;
7865 AttachDecision
InlinableNativeIRGenerator::tryAttachStringSplitString() {
7866 // Self-hosted code calls this with (string, string) arguments.
7867 MOZ_ASSERT(argc_
== 2);
7868 MOZ_ASSERT(args_
[0].isString());
7869 MOZ_ASSERT(args_
[1].isString());
7871 // Initialize the input operand.
7872 initializeInputOperand();
7874 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
7876 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7877 StringOperandId strId
= writer
.guardToString(arg0Id
);
7879 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
7880 StringOperandId separatorId
= writer
.guardToString(arg1Id
);
7882 writer
.stringSplitStringResult(strId
, separatorId
);
7883 writer
.returnFromIC();
7885 trackAttached("StringSplitString");
7886 return AttachDecision::Attach
;
7889 AttachDecision
InlinableNativeIRGenerator::tryAttachStringChar(
7891 // Need one argument.
7893 return AttachDecision::NoAction
;
7896 auto attach
= CanAttachStringChar(thisval_
, args_
[0], kind
);
7897 if (attach
== AttachStringChar::No
) {
7898 return AttachDecision::NoAction
;
7901 bool handleOOB
= attach
== AttachStringChar::OutOfBounds
;
7903 // Initialize the input operand.
7904 initializeInputOperand();
7906 // Guard callee is the 'charCodeAt', 'codePointAt', 'charAt', or 'at' native
7908 emitNativeCalleeGuard();
7910 // Guard this is a string.
7911 ValOperandId thisValId
=
7912 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
7913 StringOperandId strId
= writer
.guardToString(thisValId
);
7915 // Guard int32 index.
7916 ValOperandId indexId
=
7917 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
7918 Int32OperandId int32IndexId
= writer
.guardToInt32Index(indexId
);
7920 // Handle relative string indices, if necessary.
7921 if (kind
== StringChar::At
) {
7922 int32IndexId
= writer
.toRelativeStringIndex(int32IndexId
, strId
);
7925 // Linearize the string.
7927 // AttachStringChar doesn't have a separate state when OOB access happens on
7928 // a string which needs to be linearized, so just linearize unconditionally
7929 // for out-of-bounds accesses.
7930 if (attach
== AttachStringChar::Linearize
||
7931 attach
== AttachStringChar::OutOfBounds
) {
7933 case StringChar::CharCodeAt
:
7934 case StringChar::CharAt
:
7935 case StringChar::At
:
7936 strId
= writer
.linearizeForCharAccess(strId
, int32IndexId
);
7938 case StringChar::CodePointAt
:
7939 strId
= writer
.linearizeForCodePointAccess(strId
, int32IndexId
);
7944 // Load string char or code.
7946 case StringChar::CharCodeAt
:
7947 writer
.loadStringCharCodeResult(strId
, int32IndexId
, handleOOB
);
7949 case StringChar::CodePointAt
:
7950 writer
.loadStringCodePointResult(strId
, int32IndexId
, handleOOB
);
7952 case StringChar::CharAt
:
7953 writer
.loadStringCharResult(strId
, int32IndexId
, handleOOB
);
7955 case StringChar::At
:
7956 writer
.loadStringAtResult(strId
, int32IndexId
, handleOOB
);
7960 writer
.returnFromIC();
7963 case StringChar::CharCodeAt
:
7964 trackAttached("StringCharCodeAt");
7966 case StringChar::CodePointAt
:
7967 trackAttached("StringCodePointAt");
7969 case StringChar::CharAt
:
7970 trackAttached("StringCharAt");
7972 case StringChar::At
:
7973 trackAttached("StringAt");
7977 return AttachDecision::Attach
;
7980 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharCodeAt() {
7981 return tryAttachStringChar(StringChar::CharCodeAt
);
7984 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCodePointAt() {
7985 return tryAttachStringChar(StringChar::CodePointAt
);
7988 AttachDecision
InlinableNativeIRGenerator::tryAttachStringCharAt() {
7989 return tryAttachStringChar(StringChar::CharAt
);
7992 AttachDecision
InlinableNativeIRGenerator::tryAttachStringAt() {
7993 return tryAttachStringChar(StringChar::At
);
7996 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCharCode() {
7997 // Need one number argument.
7998 if (argc_
!= 1 || !args_
[0].isNumber()) {
7999 return AttachDecision::NoAction
;
8002 // Initialize the input operand.
8003 initializeInputOperand();
8005 // Guard callee is the 'fromCharCode' native function.
8006 emitNativeCalleeGuard();
8008 // Guard int32 argument.
8009 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8010 Int32OperandId codeId
;
8011 if (args_
[0].isInt32()) {
8012 codeId
= writer
.guardToInt32(argId
);
8014 // 'fromCharCode' performs ToUint16 on its input. We can use Uint32
8015 // semantics, because ToUint16(ToUint32(v)) == ToUint16(v).
8016 codeId
= writer
.guardToInt32ModUint32(argId
);
8019 // Return string created from code.
8020 writer
.stringFromCharCodeResult(codeId
);
8021 writer
.returnFromIC();
8023 trackAttached("StringFromCharCode");
8024 return AttachDecision::Attach
;
8027 AttachDecision
InlinableNativeIRGenerator::tryAttachStringFromCodePoint() {
8028 // Need one int32 argument.
8029 if (argc_
!= 1 || !args_
[0].isInt32()) {
8030 return AttachDecision::NoAction
;
8033 // String.fromCodePoint throws for invalid code points.
8034 int32_t codePoint
= args_
[0].toInt32();
8035 if (codePoint
< 0 || codePoint
> int32_t(unicode::NonBMPMax
)) {
8036 return AttachDecision::NoAction
;
8039 // Initialize the input operand.
8040 initializeInputOperand();
8042 // Guard callee is the 'fromCodePoint' native function.
8043 emitNativeCalleeGuard();
8045 // Guard int32 argument.
8046 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8047 Int32OperandId codeId
= writer
.guardToInt32(argId
);
8049 // Return string created from code point.
8050 writer
.stringFromCodePointResult(codeId
);
8051 writer
.returnFromIC();
8053 trackAttached("StringFromCodePoint");
8054 return AttachDecision::Attach
;
8057 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIncludes() {
8058 // Need one string argument.
8059 if (argc_
!= 1 || !args_
[0].isString()) {
8060 return AttachDecision::NoAction
;
8063 // Ensure |this| is a primitive string value.
8064 if (!thisval_
.isString()) {
8065 return AttachDecision::NoAction
;
8068 // Initialize the input operand.
8069 initializeInputOperand();
8071 // Guard callee is the 'includes' native function.
8072 emitNativeCalleeGuard();
8074 // Guard this is a string.
8075 ValOperandId thisValId
=
8076 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8077 StringOperandId strId
= writer
.guardToString(thisValId
);
8079 // Guard string argument.
8080 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8081 StringOperandId searchStrId
= writer
.guardToString(argId
);
8083 writer
.stringIncludesResult(strId
, searchStrId
);
8084 writer
.returnFromIC();
8086 trackAttached("StringIncludes");
8087 return AttachDecision::Attach
;
8090 AttachDecision
InlinableNativeIRGenerator::tryAttachStringIndexOf() {
8091 // Need one string argument.
8092 if (argc_
!= 1 || !args_
[0].isString()) {
8093 return AttachDecision::NoAction
;
8096 // Ensure |this| is a primitive string value.
8097 if (!thisval_
.isString()) {
8098 return AttachDecision::NoAction
;
8101 // Initialize the input operand.
8102 initializeInputOperand();
8104 // Guard callee is the 'indexOf' native function.
8105 emitNativeCalleeGuard();
8107 // Guard this is a string.
8108 ValOperandId thisValId
=
8109 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8110 StringOperandId strId
= writer
.guardToString(thisValId
);
8112 // Guard string argument.
8113 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8114 StringOperandId searchStrId
= writer
.guardToString(argId
);
8116 writer
.stringIndexOfResult(strId
, searchStrId
);
8117 writer
.returnFromIC();
8119 trackAttached("StringIndexOf");
8120 return AttachDecision::Attach
;
8123 AttachDecision
InlinableNativeIRGenerator::tryAttachStringLastIndexOf() {
8124 // Need one string argument.
8125 if (argc_
!= 1 || !args_
[0].isString()) {
8126 return AttachDecision::NoAction
;
8129 // Ensure |this| is a primitive string value.
8130 if (!thisval_
.isString()) {
8131 return AttachDecision::NoAction
;
8134 // Initialize the input operand.
8135 initializeInputOperand();
8137 // Guard callee is the 'lastIndexOf' native function.
8138 emitNativeCalleeGuard();
8140 // Guard this is a string.
8141 ValOperandId thisValId
=
8142 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8143 StringOperandId strId
= writer
.guardToString(thisValId
);
8145 // Guard string argument.
8146 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8147 StringOperandId searchStrId
= writer
.guardToString(argId
);
8149 writer
.stringLastIndexOfResult(strId
, searchStrId
);
8150 writer
.returnFromIC();
8152 trackAttached("StringLastIndexOf");
8153 return AttachDecision::Attach
;
8156 AttachDecision
InlinableNativeIRGenerator::tryAttachStringStartsWith() {
8157 // Need one string argument.
8158 if (argc_
!= 1 || !args_
[0].isString()) {
8159 return AttachDecision::NoAction
;
8162 // Ensure |this| is a primitive string value.
8163 if (!thisval_
.isString()) {
8164 return AttachDecision::NoAction
;
8167 // Initialize the input operand.
8168 initializeInputOperand();
8170 // Guard callee is the 'startsWith' native function.
8171 emitNativeCalleeGuard();
8173 // Guard this is a string.
8174 ValOperandId thisValId
=
8175 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8176 StringOperandId strId
= writer
.guardToString(thisValId
);
8178 // Guard string argument.
8179 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8180 StringOperandId searchStrId
= writer
.guardToString(argId
);
8182 writer
.stringStartsWithResult(strId
, searchStrId
);
8183 writer
.returnFromIC();
8185 trackAttached("StringStartsWith");
8186 return AttachDecision::Attach
;
8189 AttachDecision
InlinableNativeIRGenerator::tryAttachStringEndsWith() {
8190 // Need one string argument.
8191 if (argc_
!= 1 || !args_
[0].isString()) {
8192 return AttachDecision::NoAction
;
8195 // Ensure |this| is a primitive string value.
8196 if (!thisval_
.isString()) {
8197 return AttachDecision::NoAction
;
8200 // Initialize the input operand.
8201 initializeInputOperand();
8203 // Guard callee is the 'endsWith' native function.
8204 emitNativeCalleeGuard();
8206 // Guard this is a string.
8207 ValOperandId thisValId
=
8208 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8209 StringOperandId strId
= writer
.guardToString(thisValId
);
8211 // Guard string argument.
8212 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8213 StringOperandId searchStrId
= writer
.guardToString(argId
);
8215 writer
.stringEndsWithResult(strId
, searchStrId
);
8216 writer
.returnFromIC();
8218 trackAttached("StringEndsWith");
8219 return AttachDecision::Attach
;
8222 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToLowerCase() {
8223 // Expecting no arguments.
8225 return AttachDecision::NoAction
;
8228 // Ensure |this| is a primitive string value.
8229 if (!thisval_
.isString()) {
8230 return AttachDecision::NoAction
;
8233 // Initialize the input operand.
8234 initializeInputOperand();
8236 // Guard callee is the 'toLowerCase' native function.
8237 emitNativeCalleeGuard();
8239 // Guard this is a string.
8240 ValOperandId thisValId
=
8241 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8242 StringOperandId strId
= writer
.guardToString(thisValId
);
8244 // Return string converted to lower-case.
8245 writer
.stringToLowerCaseResult(strId
);
8246 writer
.returnFromIC();
8248 trackAttached("StringToLowerCase");
8249 return AttachDecision::Attach
;
8252 AttachDecision
InlinableNativeIRGenerator::tryAttachStringToUpperCase() {
8253 // Expecting no arguments.
8255 return AttachDecision::NoAction
;
8258 // Ensure |this| is a primitive string value.
8259 if (!thisval_
.isString()) {
8260 return AttachDecision::NoAction
;
8263 // Initialize the input operand.
8264 initializeInputOperand();
8266 // Guard callee is the 'toUpperCase' native function.
8267 emitNativeCalleeGuard();
8269 // Guard this is a string.
8270 ValOperandId thisValId
=
8271 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8272 StringOperandId strId
= writer
.guardToString(thisValId
);
8274 // Return string converted to upper-case.
8275 writer
.stringToUpperCaseResult(strId
);
8276 writer
.returnFromIC();
8278 trackAttached("StringToUpperCase");
8279 return AttachDecision::Attach
;
8282 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrim() {
8283 // Expecting no arguments.
8285 return AttachDecision::NoAction
;
8288 // Ensure |this| is a primitive string value.
8289 if (!thisval_
.isString()) {
8290 return AttachDecision::NoAction
;
8293 // Initialize the input operand.
8294 initializeInputOperand();
8296 // Guard callee is the 'trim' native function.
8297 emitNativeCalleeGuard();
8299 // Guard this is a string.
8300 ValOperandId thisValId
=
8301 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8302 StringOperandId strId
= writer
.guardToString(thisValId
);
8304 writer
.stringTrimResult(strId
);
8305 writer
.returnFromIC();
8307 trackAttached("StringTrim");
8308 return AttachDecision::Attach
;
8311 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimStart() {
8312 // Expecting no arguments.
8314 return AttachDecision::NoAction
;
8317 // Ensure |this| is a primitive string value.
8318 if (!thisval_
.isString()) {
8319 return AttachDecision::NoAction
;
8322 // Initialize the input operand.
8323 initializeInputOperand();
8325 // Guard callee is the 'trimStart' native function.
8326 emitNativeCalleeGuard();
8328 // Guard this is a string.
8329 ValOperandId thisValId
=
8330 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8331 StringOperandId strId
= writer
.guardToString(thisValId
);
8333 writer
.stringTrimStartResult(strId
);
8334 writer
.returnFromIC();
8336 trackAttached("StringTrimStart");
8337 return AttachDecision::Attach
;
8340 AttachDecision
InlinableNativeIRGenerator::tryAttachStringTrimEnd() {
8341 // Expecting no arguments.
8343 return AttachDecision::NoAction
;
8346 // Ensure |this| is a primitive string value.
8347 if (!thisval_
.isString()) {
8348 return AttachDecision::NoAction
;
8351 // Initialize the input operand.
8352 initializeInputOperand();
8354 // Guard callee is the 'trimEnd' native function.
8355 emitNativeCalleeGuard();
8357 // Guard this is a string.
8358 ValOperandId thisValId
=
8359 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
8360 StringOperandId strId
= writer
.guardToString(thisValId
);
8362 writer
.stringTrimEndResult(strId
);
8363 writer
.returnFromIC();
8365 trackAttached("StringTrimEnd");
8366 return AttachDecision::Attach
;
8369 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRandom() {
8370 // Expecting no arguments.
8372 return AttachDecision::NoAction
;
8375 MOZ_ASSERT(cx_
->realm() == callee_
->realm(),
8376 "Shouldn't inline cross-realm Math.random because per-realm RNG");
8378 // Initialize the input operand.
8379 initializeInputOperand();
8381 // Guard callee is the 'random' native function.
8382 emitNativeCalleeGuard();
8384 mozilla::non_crypto::XorShift128PlusRNG
* rng
=
8385 &cx_
->realm()->getOrCreateRandomNumberGenerator();
8386 writer
.mathRandomResult(rng
);
8388 writer
.returnFromIC();
8390 trackAttached("MathRandom");
8391 return AttachDecision::Attach
;
8394 AttachDecision
InlinableNativeIRGenerator::tryAttachMathAbs() {
8395 // Need one argument.
8397 return AttachDecision::NoAction
;
8400 if (!args_
[0].isNumber()) {
8401 return AttachDecision::NoAction
;
8404 // Initialize the input operand.
8405 initializeInputOperand();
8407 // Guard callee is the 'abs' native function.
8408 emitNativeCalleeGuard();
8410 ValOperandId argumentId
=
8411 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8413 // abs(INT_MIN) is a double.
8414 if (args_
[0].isInt32() && args_
[0].toInt32() != INT_MIN
) {
8415 Int32OperandId int32Id
= writer
.guardToInt32(argumentId
);
8416 writer
.mathAbsInt32Result(int32Id
);
8418 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8419 writer
.mathAbsNumberResult(numberId
);
8422 writer
.returnFromIC();
8424 trackAttached("MathAbs");
8425 return AttachDecision::Attach
;
8428 AttachDecision
InlinableNativeIRGenerator::tryAttachMathClz32() {
8429 // Need one (number) argument.
8430 if (argc_
!= 1 || !args_
[0].isNumber()) {
8431 return AttachDecision::NoAction
;
8434 // Initialize the input operand.
8435 initializeInputOperand();
8437 // Guard callee is the 'clz32' native function.
8438 emitNativeCalleeGuard();
8440 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8442 Int32OperandId int32Id
;
8443 if (args_
[0].isInt32()) {
8444 int32Id
= writer
.guardToInt32(argId
);
8446 MOZ_ASSERT(args_
[0].isDouble());
8447 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8448 int32Id
= writer
.truncateDoubleToUInt32(numId
);
8450 writer
.mathClz32Result(int32Id
);
8451 writer
.returnFromIC();
8453 trackAttached("MathClz32");
8454 return AttachDecision::Attach
;
8457 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSign() {
8458 // Need one (number) argument.
8459 if (argc_
!= 1 || !args_
[0].isNumber()) {
8460 return AttachDecision::NoAction
;
8463 // Initialize the input operand.
8464 initializeInputOperand();
8466 // Guard callee is the 'sign' native function.
8467 emitNativeCalleeGuard();
8469 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8471 if (args_
[0].isInt32()) {
8472 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
8473 writer
.mathSignInt32Result(int32Id
);
8475 // Math.sign returns a double only if the input is -0 or NaN so try to
8476 // optimize the common Number => Int32 case.
8477 double d
= math_sign_impl(args_
[0].toDouble());
8479 bool resultIsInt32
= mozilla::NumberIsInt32(d
, &unused
);
8481 NumberOperandId numId
= writer
.guardIsNumber(argId
);
8482 if (resultIsInt32
) {
8483 writer
.mathSignNumberToInt32Result(numId
);
8485 writer
.mathSignNumberResult(numId
);
8489 writer
.returnFromIC();
8491 trackAttached("MathSign");
8492 return AttachDecision::Attach
;
8495 AttachDecision
InlinableNativeIRGenerator::tryAttachMathImul() {
8496 // Need two (number) arguments.
8497 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8498 return AttachDecision::NoAction
;
8501 // Initialize the input operand.
8502 initializeInputOperand();
8504 // Guard callee is the 'imul' native function.
8505 emitNativeCalleeGuard();
8507 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8508 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8510 Int32OperandId int32Arg0Id
, int32Arg1Id
;
8511 if (args_
[0].isInt32() && args_
[1].isInt32()) {
8512 int32Arg0Id
= writer
.guardToInt32(arg0Id
);
8513 int32Arg1Id
= writer
.guardToInt32(arg1Id
);
8515 // Treat both arguments as numbers if at least one of them is non-int32.
8516 NumberOperandId numArg0Id
= writer
.guardIsNumber(arg0Id
);
8517 NumberOperandId numArg1Id
= writer
.guardIsNumber(arg1Id
);
8518 int32Arg0Id
= writer
.truncateDoubleToUInt32(numArg0Id
);
8519 int32Arg1Id
= writer
.truncateDoubleToUInt32(numArg1Id
);
8521 writer
.mathImulResult(int32Arg0Id
, int32Arg1Id
);
8522 writer
.returnFromIC();
8524 trackAttached("MathImul");
8525 return AttachDecision::Attach
;
8528 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFloor() {
8529 // Need one (number) argument.
8530 if (argc_
!= 1 || !args_
[0].isNumber()) {
8531 return AttachDecision::NoAction
;
8534 // Check if the result fits in int32.
8535 double res
= math_floor_impl(args_
[0].toNumber());
8537 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8539 // Initialize the input operand.
8540 initializeInputOperand();
8542 // Guard callee is the 'floor' native function.
8543 emitNativeCalleeGuard();
8545 ValOperandId argumentId
=
8546 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8548 if (args_
[0].isInt32()) {
8549 MOZ_ASSERT(resultIsInt32
);
8551 // Use an indirect truncation to inform the optimizer it needs to preserve
8552 // a bailout when the input can't be represented as an int32, even if the
8553 // final result is fully truncated.
8554 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8555 writer
.indirectTruncateInt32Result(intId
);
8557 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8559 if (resultIsInt32
) {
8560 writer
.mathFloorToInt32Result(numberId
);
8562 writer
.mathFloorNumberResult(numberId
);
8566 writer
.returnFromIC();
8568 trackAttached("MathFloor");
8569 return AttachDecision::Attach
;
8572 AttachDecision
InlinableNativeIRGenerator::tryAttachMathCeil() {
8573 // Need one (number) argument.
8574 if (argc_
!= 1 || !args_
[0].isNumber()) {
8575 return AttachDecision::NoAction
;
8578 // Check if the result fits in int32.
8579 double res
= math_ceil_impl(args_
[0].toNumber());
8581 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8583 // Initialize the input operand.
8584 initializeInputOperand();
8586 // Guard callee is the 'ceil' native function.
8587 emitNativeCalleeGuard();
8589 ValOperandId argumentId
=
8590 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8592 if (args_
[0].isInt32()) {
8593 MOZ_ASSERT(resultIsInt32
);
8595 // Use an indirect truncation to inform the optimizer it needs to preserve
8596 // a bailout when the input can't be represented as an int32, even if the
8597 // final result is fully truncated.
8598 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8599 writer
.indirectTruncateInt32Result(intId
);
8601 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8603 if (resultIsInt32
) {
8604 writer
.mathCeilToInt32Result(numberId
);
8606 writer
.mathCeilNumberResult(numberId
);
8610 writer
.returnFromIC();
8612 trackAttached("MathCeil");
8613 return AttachDecision::Attach
;
8616 AttachDecision
InlinableNativeIRGenerator::tryAttachMathTrunc() {
8617 // Need one (number) argument.
8618 if (argc_
!= 1 || !args_
[0].isNumber()) {
8619 return AttachDecision::NoAction
;
8622 // Check if the result fits in int32.
8623 double res
= math_trunc_impl(args_
[0].toNumber());
8625 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8627 // Initialize the input operand.
8628 initializeInputOperand();
8630 // Guard callee is the 'trunc' native function.
8631 emitNativeCalleeGuard();
8633 ValOperandId argumentId
=
8634 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8636 if (args_
[0].isInt32()) {
8637 MOZ_ASSERT(resultIsInt32
);
8639 // We don't need an indirect truncation barrier here, because Math.trunc
8640 // always truncates, but never rounds its input away from zero.
8641 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8642 writer
.loadInt32Result(intId
);
8644 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8646 if (resultIsInt32
) {
8647 writer
.mathTruncToInt32Result(numberId
);
8649 writer
.mathTruncNumberResult(numberId
);
8653 writer
.returnFromIC();
8655 trackAttached("MathTrunc");
8656 return AttachDecision::Attach
;
8659 AttachDecision
InlinableNativeIRGenerator::tryAttachMathRound() {
8660 // Need one (number) argument.
8661 if (argc_
!= 1 || !args_
[0].isNumber()) {
8662 return AttachDecision::NoAction
;
8665 // Check if the result fits in int32.
8666 double res
= math_round_impl(args_
[0].toNumber());
8668 bool resultIsInt32
= mozilla::NumberIsInt32(res
, &unused
);
8670 // Initialize the input operand.
8671 initializeInputOperand();
8673 // Guard callee is the 'round' native function.
8674 emitNativeCalleeGuard();
8676 ValOperandId argumentId
=
8677 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8679 if (args_
[0].isInt32()) {
8680 MOZ_ASSERT(resultIsInt32
);
8682 // Use an indirect truncation to inform the optimizer it needs to preserve
8683 // a bailout when the input can't be represented as an int32, even if the
8684 // final result is fully truncated.
8685 Int32OperandId intId
= writer
.guardToInt32(argumentId
);
8686 writer
.indirectTruncateInt32Result(intId
);
8688 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8690 if (resultIsInt32
) {
8691 writer
.mathRoundToInt32Result(numberId
);
8693 writer
.mathFunctionNumberResult(numberId
, UnaryMathFunction::Round
);
8697 writer
.returnFromIC();
8699 trackAttached("MathRound");
8700 return AttachDecision::Attach
;
8703 AttachDecision
InlinableNativeIRGenerator::tryAttachMathSqrt() {
8704 // Need one (number) argument.
8705 if (argc_
!= 1 || !args_
[0].isNumber()) {
8706 return AttachDecision::NoAction
;
8709 // Initialize the input operand.
8710 initializeInputOperand();
8712 // Guard callee is the 'sqrt' native function.
8713 emitNativeCalleeGuard();
8715 ValOperandId argumentId
=
8716 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8717 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8718 writer
.mathSqrtNumberResult(numberId
);
8719 writer
.returnFromIC();
8721 trackAttached("MathSqrt");
8722 return AttachDecision::Attach
;
8725 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFRound() {
8726 // Need one (number) argument.
8727 if (argc_
!= 1 || !args_
[0].isNumber()) {
8728 return AttachDecision::NoAction
;
8731 // Initialize the input operand.
8732 initializeInputOperand();
8734 // Guard callee is the 'fround' native function.
8735 emitNativeCalleeGuard();
8737 ValOperandId argumentId
=
8738 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8739 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8740 writer
.mathFRoundNumberResult(numberId
);
8741 writer
.returnFromIC();
8743 trackAttached("MathFRound");
8744 return AttachDecision::Attach
;
8747 AttachDecision
InlinableNativeIRGenerator::tryAttachMathF16Round() {
8748 // Need one (number) argument.
8749 if (argc_
!= 1 || !args_
[0].isNumber()) {
8750 return AttachDecision::NoAction
;
8753 // Initialize the input operand.
8754 initializeInputOperand();
8756 // Guard callee is the 'f16round' native function.
8757 emitNativeCalleeGuard();
8759 ValOperandId argumentId
=
8760 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8761 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
8762 writer
.mathF16RoundNumberResult(numberId
);
8763 writer
.returnFromIC();
8765 trackAttached("MathF16Round");
8766 return AttachDecision::Attach
;
8769 static bool CanAttachInt32Pow(const Value
& baseVal
, const Value
& powerVal
) {
8770 auto valToInt32
= [](const Value
& v
) {
8774 if (v
.isBoolean()) {
8775 return int32_t(v
.toBoolean());
8777 MOZ_ASSERT(v
.isNull());
8780 int32_t base
= valToInt32(baseVal
);
8781 int32_t power
= valToInt32(powerVal
);
8783 // x^y where y < 0 is most of the time not an int32, except when x is 1 or y
8784 // gets large enough. It's hard to determine when exactly y is "large enough",
8785 // so we don't use Int32PowResult when x != 1 and y < 0.
8786 // Note: it's important for this condition to match the code generated by
8787 // MacroAssembler::pow32 to prevent failure loops.
8792 double res
= powi(base
, power
);
8794 return mozilla::NumberIsInt32(res
, &unused
);
8797 AttachDecision
InlinableNativeIRGenerator::tryAttachMathPow() {
8798 // Need two number arguments.
8799 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8800 return AttachDecision::NoAction
;
8803 // Initialize the input operand.
8804 initializeInputOperand();
8806 // Guard callee is the 'pow' function.
8807 emitNativeCalleeGuard();
8809 ValOperandId baseId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8810 ValOperandId exponentId
=
8811 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8813 if (args_
[0].isInt32() && args_
[1].isInt32() &&
8814 CanAttachInt32Pow(args_
[0], args_
[1])) {
8815 Int32OperandId baseInt32Id
= writer
.guardToInt32(baseId
);
8816 Int32OperandId exponentInt32Id
= writer
.guardToInt32(exponentId
);
8817 writer
.int32PowResult(baseInt32Id
, exponentInt32Id
);
8819 NumberOperandId baseNumberId
= writer
.guardIsNumber(baseId
);
8820 NumberOperandId exponentNumberId
= writer
.guardIsNumber(exponentId
);
8821 writer
.doublePowResult(baseNumberId
, exponentNumberId
);
8824 writer
.returnFromIC();
8826 trackAttached("MathPow");
8827 return AttachDecision::Attach
;
8830 AttachDecision
InlinableNativeIRGenerator::tryAttachMathHypot() {
8831 // Only optimize if there are 2-4 arguments.
8832 if (argc_
< 2 || argc_
> 4) {
8833 return AttachDecision::NoAction
;
8836 for (size_t i
= 0; i
< argc_
; i
++) {
8837 if (!args_
[i
].isNumber()) {
8838 return AttachDecision::NoAction
;
8842 // Initialize the input operand.
8843 initializeInputOperand();
8845 // Guard callee is the 'hypot' native function.
8846 emitNativeCalleeGuard();
8848 ValOperandId firstId
=
8849 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8850 ValOperandId secondId
=
8851 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8853 NumberOperandId firstNumId
= writer
.guardIsNumber(firstId
);
8854 NumberOperandId secondNumId
= writer
.guardIsNumber(secondId
);
8856 ValOperandId thirdId
;
8857 ValOperandId fourthId
;
8858 NumberOperandId thirdNumId
;
8859 NumberOperandId fourthNumId
;
8863 writer
.mathHypot2NumberResult(firstNumId
, secondNumId
);
8866 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8867 thirdNumId
= writer
.guardIsNumber(thirdId
);
8868 writer
.mathHypot3NumberResult(firstNumId
, secondNumId
, thirdNumId
);
8871 thirdId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
8872 fourthId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
8873 thirdNumId
= writer
.guardIsNumber(thirdId
);
8874 fourthNumId
= writer
.guardIsNumber(fourthId
);
8875 writer
.mathHypot4NumberResult(firstNumId
, secondNumId
, thirdNumId
,
8879 MOZ_CRASH("Unexpected number of arguments to hypot function.");
8882 writer
.returnFromIC();
8884 trackAttached("MathHypot");
8885 return AttachDecision::Attach
;
8888 AttachDecision
InlinableNativeIRGenerator::tryAttachMathATan2() {
8889 // Requires two numbers as arguments.
8890 if (argc_
!= 2 || !args_
[0].isNumber() || !args_
[1].isNumber()) {
8891 return AttachDecision::NoAction
;
8894 // Initialize the input operand.
8895 initializeInputOperand();
8897 // Guard callee is the 'atan2' native function.
8898 emitNativeCalleeGuard();
8900 ValOperandId yId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8901 ValOperandId xId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
8903 NumberOperandId yNumberId
= writer
.guardIsNumber(yId
);
8904 NumberOperandId xNumberId
= writer
.guardIsNumber(xId
);
8906 writer
.mathAtan2NumberResult(yNumberId
, xNumberId
);
8907 writer
.returnFromIC();
8909 trackAttached("MathAtan2");
8910 return AttachDecision::Attach
;
8913 AttachDecision
InlinableNativeIRGenerator::tryAttachMathMinMax(bool isMax
) {
8914 // For now only optimize if there are 1-4 arguments.
8915 if (argc_
< 1 || argc_
> 4) {
8916 return AttachDecision::NoAction
;
8919 // Ensure all arguments are numbers.
8920 bool allInt32
= true;
8921 for (size_t i
= 0; i
< argc_
; i
++) {
8922 if (!args_
[i
].isNumber()) {
8923 return AttachDecision::NoAction
;
8925 if (!args_
[i
].isInt32()) {
8930 // Initialize the input operand.
8931 initializeInputOperand();
8933 // Guard callee is this Math function.
8934 emitNativeCalleeGuard();
8937 ValOperandId valId
=
8938 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8939 Int32OperandId resId
= writer
.guardToInt32(valId
);
8940 for (size_t i
= 1; i
< argc_
; i
++) {
8941 ValOperandId argId
=
8942 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8943 Int32OperandId argInt32Id
= writer
.guardToInt32(argId
);
8944 resId
= writer
.int32MinMax(isMax
, resId
, argInt32Id
);
8946 writer
.loadInt32Result(resId
);
8948 ValOperandId valId
=
8949 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
8950 NumberOperandId resId
= writer
.guardIsNumber(valId
);
8951 for (size_t i
= 1; i
< argc_
; i
++) {
8952 ValOperandId argId
=
8953 writer
.loadArgumentFixedSlot(ArgumentKindForArgIndex(i
), argc_
);
8954 NumberOperandId argNumId
= writer
.guardIsNumber(argId
);
8955 resId
= writer
.numberMinMax(isMax
, resId
, argNumId
);
8957 writer
.loadDoubleResult(resId
);
8960 writer
.returnFromIC();
8962 trackAttached(isMax
? "MathMax" : "MathMin");
8963 return AttachDecision::Attach
;
8966 AttachDecision
InlinableNativeIRGenerator::tryAttachSpreadMathMinMax(
8968 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Spread
||
8969 flags_
.getArgFormat() == CallFlags::FunApplyArray
);
8971 // The result will be an int32 if there is at least one argument,
8972 // and all the arguments are int32.
8973 bool int32Result
= args_
.length() > 0;
8974 for (size_t i
= 0; i
< args_
.length(); i
++) {
8975 if (!args_
[i
].isNumber()) {
8976 return AttachDecision::NoAction
;
8978 if (!args_
[i
].isInt32()) {
8979 int32Result
= false;
8983 // Initialize the input operand.
8984 initializeInputOperand();
8986 // Guard callee is this Math function.
8987 emitNativeCalleeGuard();
8989 // Load the argument array.
8990 ObjOperandId argsId
= emitLoadArgsArray();
8993 writer
.int32MinMaxArrayResult(argsId
, isMax
);
8995 writer
.numberMinMaxArrayResult(argsId
, isMax
);
8998 writer
.returnFromIC();
9000 trackAttached(isMax
? "MathMaxArray" : "MathMinArray");
9001 return AttachDecision::Attach
;
9004 AttachDecision
InlinableNativeIRGenerator::tryAttachMathFunction(
9005 UnaryMathFunction fun
) {
9006 // Need one argument.
9008 return AttachDecision::NoAction
;
9011 if (!args_
[0].isNumber()) {
9012 return AttachDecision::NoAction
;
9015 if (math_use_fdlibm_for_sin_cos_tan() ||
9016 callee_
->realm()->creationOptions().alwaysUseFdlibm()) {
9018 case UnaryMathFunction::SinNative
:
9019 fun
= UnaryMathFunction::SinFdlibm
;
9021 case UnaryMathFunction::CosNative
:
9022 fun
= UnaryMathFunction::CosFdlibm
;
9024 case UnaryMathFunction::TanNative
:
9025 fun
= UnaryMathFunction::TanFdlibm
;
9032 // Initialize the input operand.
9033 initializeInputOperand();
9035 // Guard callee is this Math function.
9036 emitNativeCalleeGuard();
9038 ValOperandId argumentId
=
9039 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9040 NumberOperandId numberId
= writer
.guardIsNumber(argumentId
);
9041 writer
.mathFunctionNumberResult(numberId
, fun
);
9042 writer
.returnFromIC();
9044 trackAttached("MathFunction");
9045 return AttachDecision::Attach
;
9048 AttachDecision
InlinableNativeIRGenerator::tryAttachNumber() {
9049 // Expect a single string argument.
9050 if (argc_
!= 1 || !args_
[0].isString()) {
9051 return AttachDecision::NoAction
;
9055 if (!StringToNumber(cx_
, args_
[0].toString(), &num
)) {
9056 cx_
->recoverFromOutOfMemory();
9057 return AttachDecision::NoAction
;
9060 // Initialize the input operand.
9061 initializeInputOperand();
9063 // Guard callee is the `Number` function.
9064 emitNativeCalleeGuard();
9066 // Guard that the argument is a string.
9067 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9068 StringOperandId strId
= writer
.guardToString(argId
);
9070 // Return either an Int32 or Double result.
9072 if (mozilla::NumberIsInt32(num
, &unused
)) {
9073 Int32OperandId resultId
= writer
.guardStringToInt32(strId
);
9074 writer
.loadInt32Result(resultId
);
9076 NumberOperandId resultId
= writer
.guardStringToNumber(strId
);
9077 writer
.loadDoubleResult(resultId
);
9079 writer
.returnFromIC();
9081 trackAttached("Number");
9082 return AttachDecision::Attach
;
9085 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberParseInt() {
9086 // Expected arguments: input (string or number), optional radix (int32).
9087 if (argc_
< 1 || argc_
> 2) {
9088 return AttachDecision::NoAction
;
9090 if (!args_
[0].isString() && !args_
[0].isNumber()) {
9091 return AttachDecision::NoAction
;
9093 if (args_
[0].isDouble()) {
9094 double d
= args_
[0].toDouble();
9096 // See num_parseInt for why we have to reject numbers smaller than 1.0e-6.
9097 // Negative numbers in the exclusive range (-1, -0) return -0.
9098 bool canTruncateToInt32
=
9099 (DOUBLE_DECIMAL_IN_SHORTEST_LOW
<= d
&& d
<= double(INT32_MAX
)) ||
9100 (double(INT32_MIN
) <= d
&& d
<= -1.0) || (d
== 0.0);
9101 if (!canTruncateToInt32
) {
9102 return AttachDecision::NoAction
;
9105 if (argc_
> 1 && !args_
[1].isInt32(10)) {
9106 return AttachDecision::NoAction
;
9109 // Initialize the input operand.
9110 initializeInputOperand();
9112 // Guard callee is the 'parseInt' native function.
9113 emitNativeCalleeGuard();
9115 auto guardRadix
= [&]() {
9116 ValOperandId radixId
=
9117 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9118 Int32OperandId intRadixId
= writer
.guardToInt32(radixId
);
9119 writer
.guardSpecificInt32(intRadixId
, 10);
9123 ValOperandId inputId
=
9124 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9126 if (args_
[0].isString()) {
9127 StringOperandId strId
= writer
.guardToString(inputId
);
9129 Int32OperandId intRadixId
;
9131 intRadixId
= guardRadix();
9133 intRadixId
= writer
.loadInt32Constant(0);
9136 writer
.numberParseIntResult(strId
, intRadixId
);
9137 } else if (args_
[0].isInt32()) {
9138 Int32OperandId intId
= writer
.guardToInt32(inputId
);
9142 writer
.loadInt32Result(intId
);
9144 MOZ_ASSERT(args_
[0].isDouble());
9146 NumberOperandId numId
= writer
.guardIsNumber(inputId
);
9150 writer
.doubleParseIntResult(numId
);
9153 writer
.returnFromIC();
9155 trackAttached("NumberParseInt");
9156 return AttachDecision::Attach
;
9159 StringOperandId
IRGenerator::emitToStringGuard(ValOperandId id
,
9161 MOZ_ASSERT(CanConvertToString(v
));
9163 return writer
.guardToString(id
);
9165 if (v
.isBoolean()) {
9166 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
9167 return writer
.booleanToString(boolId
);
9170 writer
.guardIsNull(id
);
9171 return writer
.loadConstantString(cx_
->names().null
);
9173 if (v
.isUndefined()) {
9174 writer
.guardIsUndefined(id
);
9175 return writer
.loadConstantString(cx_
->names().undefined
);
9178 Int32OperandId intId
= writer
.guardToInt32(id
);
9179 return writer
.callInt32ToString(intId
);
9181 // At this point we are creating an IC that will handle
9182 // both Int32 and Double cases.
9183 MOZ_ASSERT(v
.isNumber());
9184 NumberOperandId numId
= writer
.guardIsNumber(id
);
9185 return writer
.callNumberToString(numId
);
9188 AttachDecision
InlinableNativeIRGenerator::tryAttachNumberToString() {
9189 // Expecting no arguments or a single int32 argument.
9191 return AttachDecision::NoAction
;
9193 if (argc_
== 1 && !args_
[0].isInt32()) {
9194 return AttachDecision::NoAction
;
9197 // Ensure |this| is a primitive number value.
9198 if (!thisval_
.isNumber()) {
9199 return AttachDecision::NoAction
;
9202 // No arguments means base 10.
9205 base
= args_
[0].toInt32();
9206 if (base
< 2 || base
> 36) {
9207 return AttachDecision::NoAction
;
9210 // Non-decimal bases currently only support int32 inputs.
9211 if (base
!= 10 && !thisval_
.isInt32()) {
9212 return AttachDecision::NoAction
;
9215 MOZ_ASSERT(2 <= base
&& base
<= 36);
9217 // Initialize the input operand.
9218 initializeInputOperand();
9220 // Guard callee is the 'toString' native function.
9221 emitNativeCalleeGuard();
9223 // Initialize the |this| operand.
9224 ValOperandId thisValId
=
9225 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9227 // Guard on number and convert to string.
9229 // If an explicit base was passed, guard its value.
9231 // Guard the `base` argument is an int32.
9232 ValOperandId baseId
=
9233 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9234 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
9236 // Guard `base` is 10 for decimal toString representation.
9237 writer
.guardSpecificInt32(intBaseId
, 10);
9240 StringOperandId strId
= emitToStringGuard(thisValId
, thisval_
);
9242 // Return the string.
9243 writer
.loadStringResult(strId
);
9245 MOZ_ASSERT(argc_
> 0);
9247 // Guard the |this| value is an int32.
9248 Int32OperandId thisIntId
= writer
.guardToInt32(thisValId
);
9250 // Guard the `base` argument is an int32.
9251 ValOperandId baseId
=
9252 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9253 Int32OperandId intBaseId
= writer
.guardToInt32(baseId
);
9255 // Return the string.
9256 writer
.int32ToStringWithBaseResult(thisIntId
, intBaseId
);
9259 writer
.returnFromIC();
9261 trackAttached("NumberToString");
9262 return AttachDecision::Attach
;
9265 AttachDecision
InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
9266 // Need one argument.
9268 return AttachDecision::NoAction
;
9271 if (!args_
[0].isObject()) {
9272 return AttachDecision::NoAction
;
9275 // Initialize the input operand.
9276 initializeInputOperand();
9278 // Guard callee is the 'getPrototypeOf' native function.
9279 emitNativeCalleeGuard();
9281 ValOperandId argumentId
=
9282 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9283 ObjOperandId objId
= writer
.guardToObject(argumentId
);
9285 writer
.reflectGetPrototypeOfResult(objId
);
9286 writer
.returnFromIC();
9288 trackAttached("ReflectGetPrototypeOf");
9289 return AttachDecision::Attach
;
9292 static bool AtomicsMeetsPreconditions(TypedArrayObject
* typedArray
,
9293 const Value
& index
) {
9294 switch (typedArray
->type()) {
9298 case Scalar::Uint16
:
9300 case Scalar::Uint32
:
9301 case Scalar::BigInt64
:
9302 case Scalar::BigUint64
:
9305 case Scalar::Float16
:
9306 case Scalar::Float32
:
9307 case Scalar::Float64
:
9308 case Scalar::Uint8Clamped
:
9309 // Exclude floating types and Uint8Clamped.
9312 case Scalar::MaxTypedArrayViewType
:
9314 case Scalar::Simd128
:
9315 MOZ_CRASH("Unsupported TypedArray type");
9318 // Bounds check the index argument.
9320 if (!ValueIsInt64Index(index
, &indexInt64
)) {
9323 if (indexInt64
< 0 ||
9324 uint64_t(indexInt64
) >= typedArray
->length().valueOr(0)) {
9331 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
9332 if (!JitSupportsAtomics()) {
9333 return AttachDecision::NoAction
;
9336 // Need four arguments.
9338 return AttachDecision::NoAction
;
9341 // Arguments: typedArray, index (number), expected, replacement.
9342 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9343 return AttachDecision::NoAction
;
9345 if (!args_
[1].isNumber()) {
9346 return AttachDecision::NoAction
;
9349 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9350 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9351 return AttachDecision::NoAction
;
9354 Scalar::Type elementType
= typedArray
->type();
9355 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
9356 return AttachDecision::NoAction
;
9358 if (!ValueCanConvertToNumeric(elementType
, args_
[3])) {
9359 return AttachDecision::NoAction
;
9362 // Initialize the input operand.
9363 initializeInputOperand();
9365 // Guard callee is the `compareExchange` native function.
9366 emitNativeCalleeGuard();
9368 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9369 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9370 writer
.guardShapeForClass(objId
, typedArray
->shape());
9372 // Convert index to intPtr.
9373 ValOperandId indexId
=
9374 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9375 IntPtrOperandId intPtrIndexId
=
9376 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9378 // Convert expected value to int32/BigInt.
9379 ValOperandId expectedId
=
9380 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9381 OperandId numericExpectedId
=
9382 emitNumericGuard(expectedId
, args_
[2], elementType
);
9384 // Convert replacement value to int32/BigInt.
9385 ValOperandId replacementId
=
9386 writer
.loadArgumentFixedSlot(ArgumentKind::Arg3
, argc_
);
9387 OperandId numericReplacementId
=
9388 emitNumericGuard(replacementId
, args_
[3], elementType
);
9390 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9391 writer
.atomicsCompareExchangeResult(objId
, intPtrIndexId
, numericExpectedId
,
9392 numericReplacementId
, typedArray
->type(),
9394 writer
.returnFromIC();
9396 trackAttached("AtomicsCompareExchange");
9397 return AttachDecision::Attach
;
9400 bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
9401 if (!JitSupportsAtomics()) {
9405 // Need three arguments.
9410 // Arguments: typedArray, index (number), value.
9411 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9414 if (!args_
[1].isNumber()) {
9418 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9419 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9422 if (!ValueCanConvertToNumeric(typedArray
->type(), args_
[2])) {
9428 InlinableNativeIRGenerator::AtomicsReadWriteModifyOperands
9429 InlinableNativeIRGenerator::emitAtomicsReadWriteModifyOperands() {
9430 MOZ_ASSERT(canAttachAtomicsReadWriteModify());
9432 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9434 // Initialize the input operand.
9435 initializeInputOperand();
9437 // Guard callee is this Atomics function.
9438 emitNativeCalleeGuard();
9440 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9441 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9442 writer
.guardShapeForClass(objId
, typedArray
->shape());
9444 // Convert index to intPtr.
9445 ValOperandId indexId
=
9446 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9447 IntPtrOperandId intPtrIndexId
=
9448 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9450 // Convert value to int32/BigInt.
9451 ValOperandId valueId
=
9452 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9453 OperandId numericValueId
=
9454 emitNumericGuard(valueId
, args_
[2], typedArray
->type());
9456 return {objId
, intPtrIndexId
, numericValueId
};
9459 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsExchange() {
9460 if (!canAttachAtomicsReadWriteModify()) {
9461 return AttachDecision::NoAction
;
9464 auto [objId
, intPtrIndexId
, numericValueId
] =
9465 emitAtomicsReadWriteModifyOperands();
9467 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9468 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9470 writer
.atomicsExchangeResult(objId
, intPtrIndexId
, numericValueId
,
9471 typedArray
->type(), viewKind
);
9472 writer
.returnFromIC();
9474 trackAttached("AtomicsExchange");
9475 return AttachDecision::Attach
;
9478 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAdd() {
9479 if (!canAttachAtomicsReadWriteModify()) {
9480 return AttachDecision::NoAction
;
9483 auto [objId
, intPtrIndexId
, numericValueId
] =
9484 emitAtomicsReadWriteModifyOperands();
9486 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9487 bool forEffect
= ignoresResult();
9488 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9490 writer
.atomicsAddResult(objId
, intPtrIndexId
, numericValueId
,
9491 typedArray
->type(), forEffect
, viewKind
);
9492 writer
.returnFromIC();
9494 trackAttached("AtomicsAdd");
9495 return AttachDecision::Attach
;
9498 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsSub() {
9499 if (!canAttachAtomicsReadWriteModify()) {
9500 return AttachDecision::NoAction
;
9503 auto [objId
, intPtrIndexId
, numericValueId
] =
9504 emitAtomicsReadWriteModifyOperands();
9506 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9507 bool forEffect
= ignoresResult();
9508 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9510 writer
.atomicsSubResult(objId
, intPtrIndexId
, numericValueId
,
9511 typedArray
->type(), forEffect
, viewKind
);
9512 writer
.returnFromIC();
9514 trackAttached("AtomicsSub");
9515 return AttachDecision::Attach
;
9518 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsAnd() {
9519 if (!canAttachAtomicsReadWriteModify()) {
9520 return AttachDecision::NoAction
;
9523 auto [objId
, intPtrIndexId
, numericValueId
] =
9524 emitAtomicsReadWriteModifyOperands();
9526 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9527 bool forEffect
= ignoresResult();
9528 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9530 writer
.atomicsAndResult(objId
, intPtrIndexId
, numericValueId
,
9531 typedArray
->type(), forEffect
, viewKind
);
9532 writer
.returnFromIC();
9534 trackAttached("AtomicsAnd");
9535 return AttachDecision::Attach
;
9538 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsOr() {
9539 if (!canAttachAtomicsReadWriteModify()) {
9540 return AttachDecision::NoAction
;
9543 auto [objId
, intPtrIndexId
, numericValueId
] =
9544 emitAtomicsReadWriteModifyOperands();
9546 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9547 bool forEffect
= ignoresResult();
9548 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9550 writer
.atomicsOrResult(objId
, intPtrIndexId
, numericValueId
,
9551 typedArray
->type(), forEffect
, viewKind
);
9552 writer
.returnFromIC();
9554 trackAttached("AtomicsOr");
9555 return AttachDecision::Attach
;
9558 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsXor() {
9559 if (!canAttachAtomicsReadWriteModify()) {
9560 return AttachDecision::NoAction
;
9563 auto [objId
, intPtrIndexId
, numericValueId
] =
9564 emitAtomicsReadWriteModifyOperands();
9566 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9567 bool forEffect
= ignoresResult();
9568 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9570 writer
.atomicsXorResult(objId
, intPtrIndexId
, numericValueId
,
9571 typedArray
->type(), forEffect
, viewKind
);
9572 writer
.returnFromIC();
9574 trackAttached("AtomicsXor");
9575 return AttachDecision::Attach
;
9578 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
9579 if (!JitSupportsAtomics()) {
9580 return AttachDecision::NoAction
;
9583 // Need two arguments.
9585 return AttachDecision::NoAction
;
9588 // Arguments: typedArray, index (number).
9589 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9590 return AttachDecision::NoAction
;
9592 if (!args_
[1].isNumber()) {
9593 return AttachDecision::NoAction
;
9596 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9597 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9598 return AttachDecision::NoAction
;
9601 // Initialize the input operand.
9602 initializeInputOperand();
9604 // Guard callee is the `load` native function.
9605 emitNativeCalleeGuard();
9607 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9608 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9609 writer
.guardShapeForClass(objId
, typedArray
->shape());
9611 // Convert index to intPtr.
9612 ValOperandId indexId
=
9613 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9614 IntPtrOperandId intPtrIndexId
=
9615 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9617 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9618 writer
.atomicsLoadResult(objId
, intPtrIndexId
, typedArray
->type(), viewKind
);
9619 writer
.returnFromIC();
9621 trackAttached("AtomicsLoad");
9622 return AttachDecision::Attach
;
9625 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsStore() {
9626 if (!JitSupportsAtomics()) {
9627 return AttachDecision::NoAction
;
9630 // Need three arguments.
9632 return AttachDecision::NoAction
;
9635 // Atomics.store() is annoying because it returns the result of converting the
9636 // value by ToInteger(), not the input value, nor the result of converting the
9637 // value by ToInt32(). It is especially annoying because almost nobody uses
9638 // the result value.
9640 // As an expedient compromise, therefore, we inline only if the result is
9641 // obviously unused or if the argument is already Int32 and thus requires no
9644 // Arguments: typedArray, index (number), value.
9645 if (!args_
[0].isObject() || !args_
[0].toObject().is
<TypedArrayObject
>()) {
9646 return AttachDecision::NoAction
;
9648 if (!args_
[1].isNumber()) {
9649 return AttachDecision::NoAction
;
9652 auto* typedArray
= &args_
[0].toObject().as
<TypedArrayObject
>();
9653 if (!AtomicsMeetsPreconditions(typedArray
, args_
[1])) {
9654 return AttachDecision::NoAction
;
9657 Scalar::Type elementType
= typedArray
->type();
9658 if (!ValueCanConvertToNumeric(elementType
, args_
[2])) {
9659 return AttachDecision::NoAction
;
9662 bool guardIsInt32
= !Scalar::isBigIntType(elementType
) && !ignoresResult();
9664 if (guardIsInt32
&& !args_
[2].isInt32()) {
9665 return AttachDecision::NoAction
;
9668 // Initialize the input operand.
9669 initializeInputOperand();
9671 // Guard callee is the `store` native function.
9672 emitNativeCalleeGuard();
9674 ValOperandId arg0Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9675 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
9676 writer
.guardShapeForClass(objId
, typedArray
->shape());
9678 // Convert index to intPtr.
9679 ValOperandId indexId
=
9680 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9681 IntPtrOperandId intPtrIndexId
=
9682 guardToIntPtrIndex(args_
[1], indexId
, /* supportOOB = */ false);
9684 // Ensure value is int32 or BigInt.
9685 ValOperandId valueId
=
9686 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
);
9687 OperandId numericValueId
;
9689 numericValueId
= writer
.guardToInt32(valueId
);
9691 numericValueId
= emitNumericGuard(valueId
, args_
[2], elementType
);
9694 auto viewKind
= ToArrayBufferViewKind(typedArray
);
9695 writer
.atomicsStoreResult(objId
, intPtrIndexId
, numericValueId
,
9696 typedArray
->type(), viewKind
);
9697 writer
.returnFromIC();
9699 trackAttached("AtomicsStore");
9700 return AttachDecision::Attach
;
9703 AttachDecision
InlinableNativeIRGenerator::tryAttachAtomicsIsLockFree() {
9704 // Need one argument.
9706 return AttachDecision::NoAction
;
9709 if (!args_
[0].isInt32()) {
9710 return AttachDecision::NoAction
;
9713 // Initialize the input operand.
9714 initializeInputOperand();
9716 // Guard callee is the `isLockFree` native function.
9717 emitNativeCalleeGuard();
9719 // Ensure value is int32.
9720 ValOperandId valueId
=
9721 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9722 Int32OperandId int32ValueId
= writer
.guardToInt32(valueId
);
9724 writer
.atomicsIsLockFreeResult(int32ValueId
);
9725 writer
.returnFromIC();
9727 trackAttached("AtomicsIsLockFree");
9728 return AttachDecision::Attach
;
9731 AttachDecision
InlinableNativeIRGenerator::tryAttachBoolean() {
9732 // Need zero or one argument.
9734 return AttachDecision::NoAction
;
9737 // Initialize the input operand.
9738 initializeInputOperand();
9740 // Guard callee is the 'Boolean' native function.
9741 emitNativeCalleeGuard();
9744 writer
.loadBooleanResult(false);
9746 ValOperandId valId
=
9747 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9749 writer
.loadValueTruthyResult(valId
);
9752 writer
.returnFromIC();
9754 trackAttached("Boolean");
9755 return AttachDecision::Attach
;
9758 AttachDecision
InlinableNativeIRGenerator::tryAttachBailout() {
9759 // Expecting no arguments.
9761 return AttachDecision::NoAction
;
9764 // Initialize the input operand.
9765 initializeInputOperand();
9767 // Guard callee is the 'bailout' native function.
9768 emitNativeCalleeGuard();
9771 writer
.loadUndefinedResult();
9772 writer
.returnFromIC();
9774 trackAttached("Bailout");
9775 return AttachDecision::Attach
;
9778 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertFloat32() {
9779 // Expecting two arguments.
9781 return AttachDecision::NoAction
;
9784 // (Fuzzing unsafe) testing function which must be called with a constant
9785 // boolean as its second argument.
9786 bool mustBeFloat32
= args_
[1].toBoolean();
9788 // Initialize the input operand.
9789 initializeInputOperand();
9791 // Guard callee is the 'assertFloat32' native function.
9792 emitNativeCalleeGuard();
9794 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9796 writer
.assertFloat32Result(valId
, mustBeFloat32
);
9797 writer
.returnFromIC();
9799 trackAttached("AssertFloat32");
9800 return AttachDecision::Attach
;
9803 AttachDecision
InlinableNativeIRGenerator::tryAttachAssertRecoveredOnBailout() {
9804 // Expecting two arguments.
9806 return AttachDecision::NoAction
;
9809 // (Fuzzing unsafe) testing function which must be called with a constant
9810 // boolean as its second argument.
9811 bool mustBeRecovered
= args_
[1].toBoolean();
9813 // Initialize the input operand.
9814 initializeInputOperand();
9816 // Guard callee is the 'assertRecoveredOnBailout' native function.
9817 emitNativeCalleeGuard();
9819 ValOperandId valId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9821 writer
.assertRecoveredOnBailoutResult(valId
, mustBeRecovered
);
9822 writer
.returnFromIC();
9824 trackAttached("AssertRecoveredOnBailout");
9825 return AttachDecision::Attach
;
9828 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIs() {
9829 // Need two arguments.
9831 return AttachDecision::NoAction
;
9834 // Initialize the input operand.
9835 initializeInputOperand();
9837 // Guard callee is the `is` native function.
9838 emitNativeCalleeGuard();
9840 ValOperandId lhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9841 ValOperandId rhsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
9843 HandleValue lhs
= args_
[0];
9844 HandleValue rhs
= args_
[1];
9846 if (!isFirstStub()) {
9847 writer
.sameValueResult(lhsId
, rhsId
);
9848 } else if (lhs
.isNumber() && rhs
.isNumber() &&
9849 !(lhs
.isInt32() && rhs
.isInt32())) {
9850 NumberOperandId lhsNumId
= writer
.guardIsNumber(lhsId
);
9851 NumberOperandId rhsNumId
= writer
.guardIsNumber(rhsId
);
9852 writer
.compareDoubleSameValueResult(lhsNumId
, rhsNumId
);
9853 } else if (!SameType(lhs
, rhs
)) {
9854 // Compare tags for strictly different types.
9855 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
9856 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
9857 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
9858 writer
.loadBooleanResult(false);
9860 MOZ_ASSERT(lhs
.type() == rhs
.type());
9861 MOZ_ASSERT(lhs
.type() != JS::ValueType::Double
);
9863 switch (lhs
.type()) {
9864 case JS::ValueType::Int32
: {
9865 Int32OperandId lhsIntId
= writer
.guardToInt32(lhsId
);
9866 Int32OperandId rhsIntId
= writer
.guardToInt32(rhsId
);
9867 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9870 case JS::ValueType::Boolean
: {
9871 Int32OperandId lhsIntId
= writer
.guardBooleanToInt32(lhsId
);
9872 Int32OperandId rhsIntId
= writer
.guardBooleanToInt32(rhsId
);
9873 writer
.compareInt32Result(JSOp::StrictEq
, lhsIntId
, rhsIntId
);
9876 case JS::ValueType::Undefined
: {
9877 writer
.guardIsUndefined(lhsId
);
9878 writer
.guardIsUndefined(rhsId
);
9879 writer
.loadBooleanResult(true);
9882 case JS::ValueType::Null
: {
9883 writer
.guardIsNull(lhsId
);
9884 writer
.guardIsNull(rhsId
);
9885 writer
.loadBooleanResult(true);
9888 case JS::ValueType::String
: {
9889 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
9890 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
9891 writer
.compareStringResult(JSOp::StrictEq
, lhsStrId
, rhsStrId
);
9894 case JS::ValueType::Symbol
: {
9895 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
9896 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
9897 writer
.compareSymbolResult(JSOp::StrictEq
, lhsSymId
, rhsSymId
);
9900 case JS::ValueType::BigInt
: {
9901 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
9902 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
9903 writer
.compareBigIntResult(JSOp::StrictEq
, lhsBigIntId
, rhsBigIntId
);
9906 case JS::ValueType::Object
: {
9907 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
9908 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
9909 writer
.compareObjectResult(JSOp::StrictEq
, lhsObjId
, rhsObjId
);
9913 #ifdef ENABLE_RECORD_TUPLE
9914 case ValueType::ExtendedPrimitive
:
9916 case JS::ValueType::Double
:
9917 case JS::ValueType::Magic
:
9918 case JS::ValueType::PrivateGCThing
:
9919 MOZ_CRASH("Unexpected type");
9923 writer
.returnFromIC();
9925 trackAttached("ObjectIs");
9926 return AttachDecision::Attach
;
9929 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectIsPrototypeOf() {
9930 // Ensure |this| is an object.
9931 if (!thisval_
.isObject()) {
9932 return AttachDecision::NoAction
;
9935 // Need a single argument.
9937 return AttachDecision::NoAction
;
9940 // Initialize the input operand.
9941 initializeInputOperand();
9943 // Guard callee is the `isPrototypeOf` native function.
9944 emitNativeCalleeGuard();
9946 // Guard that |this| is an object.
9947 ValOperandId thisValId
=
9948 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
9949 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
9951 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9953 writer
.loadInstanceOfObjectResult(argId
, thisObjId
);
9954 writer
.returnFromIC();
9956 trackAttached("ObjectIsPrototypeOf");
9957 return AttachDecision::Attach
;
9960 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectKeys() {
9961 // Only handle argc <= 1.
9963 return AttachDecision::NoAction
;
9966 // Do not attach any IC if the argument is not an object.
9967 if (!args_
[0].isObject()) {
9968 return AttachDecision::NoAction
;
9970 // Do not attach any IC if the argument is a Proxy. While implementation could
9971 // work with proxies the goal of this implementation is to provide an
9972 // optimization for calls of `Object.keys(obj)` where there is no side-effect,
9973 // and where the computation of the array of property name can be moved.
9974 const JSClass
* clasp
= args_
[0].toObject().getClass();
9975 if (clasp
->isProxyObject()) {
9976 return AttachDecision::NoAction
;
9979 // Generate cache IR code to attach a new inline cache which will delegate the
9980 // call to Object.keys to the native function.
9981 initializeInputOperand();
9983 // Guard callee is the 'keys' native function.
9984 emitNativeCalleeGuard();
9986 // Implicit: Note `Object.keys` is a property of the `Object` global. The fact
9987 // that we are in this function implies that we already identify the function
9988 // as being the proper one. Thus there should not be any need to validate that
9989 // this is the proper function. (test: ion/object-keys-05)
9991 // Guard `arg0` is an object.
9992 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
9993 ObjOperandId argObjId
= writer
.guardToObject(argId
);
9995 // Guard against proxies.
9996 writer
.guardIsNotProxy(argObjId
);
9998 // Compute the keys array.
9999 writer
.objectKeysResult(argObjId
);
10001 writer
.returnFromIC();
10003 trackAttached("ObjectKeys");
10004 return AttachDecision::Attach
;
10007 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectToString() {
10008 // Expecting no arguments.
10010 return AttachDecision::NoAction
;
10013 // Ensure |this| is an object.
10014 if (!thisval_
.isObject()) {
10015 return AttachDecision::NoAction
;
10018 // Don't attach if the object has @@toStringTag or is a proxy.
10019 if (!ObjectClassToString(cx_
, &thisval_
.toObject())) {
10020 return AttachDecision::NoAction
;
10023 // Initialize the input operand.
10024 initializeInputOperand();
10026 // Guard callee is the 'toString' native function.
10027 emitNativeCalleeGuard();
10029 // Guard that |this| is an object.
10030 ValOperandId thisValId
=
10031 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10032 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
10034 writer
.objectToStringResult(thisObjId
);
10035 writer
.returnFromIC();
10037 trackAttached("ObjectToString");
10038 return AttachDecision::Attach
;
10041 AttachDecision
InlinableNativeIRGenerator::tryAttachBigInt() {
10042 // Need a single argument (Int32).
10043 if (argc_
!= 1 || !args_
[0].isInt32()) {
10044 return AttachDecision::NoAction
;
10047 // Initialize the input operand.
10048 initializeInputOperand();
10050 // Guard callee is the 'BigInt' native function.
10051 emitNativeCalleeGuard();
10053 // Guard that the argument is an Int32.
10054 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10055 Int32OperandId int32Id
= writer
.guardToInt32(argId
);
10057 // Convert Int32 to BigInt.
10058 writer
.int32ToBigIntResult(int32Id
);
10059 writer
.returnFromIC();
10061 trackAttached("BigInt");
10062 return AttachDecision::Attach
;
10065 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsIntN() {
10066 // Need two arguments (Int32, BigInt).
10067 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
10068 return AttachDecision::NoAction
;
10071 // Negative bits throws an error.
10072 if (args_
[0].toInt32() < 0) {
10073 return AttachDecision::NoAction
;
10076 // Initialize the input operand.
10077 initializeInputOperand();
10079 // Guard callee is the 'BigInt.asIntN' native function.
10080 emitNativeCalleeGuard();
10082 // Convert bits to int32.
10083 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10084 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
10086 // Number of bits mustn't be negative.
10087 writer
.guardInt32IsNonNegative(int32BitsId
);
10089 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10090 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
10092 writer
.bigIntAsIntNResult(int32BitsId
, bigIntId
);
10093 writer
.returnFromIC();
10095 trackAttached("BigIntAsIntN");
10096 return AttachDecision::Attach
;
10099 AttachDecision
InlinableNativeIRGenerator::tryAttachBigIntAsUintN() {
10100 // Need two arguments (Int32, BigInt).
10101 if (argc_
!= 2 || !args_
[0].isInt32() || !args_
[1].isBigInt()) {
10102 return AttachDecision::NoAction
;
10105 // Negative bits throws an error.
10106 if (args_
[0].toInt32() < 0) {
10107 return AttachDecision::NoAction
;
10110 // Initialize the input operand.
10111 initializeInputOperand();
10113 // Guard callee is the 'BigInt.asUintN' native function.
10114 emitNativeCalleeGuard();
10116 // Convert bits to int32.
10117 ValOperandId bitsId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10118 Int32OperandId int32BitsId
= writer
.guardToInt32Index(bitsId
);
10120 // Number of bits mustn't be negative.
10121 writer
.guardInt32IsNonNegative(int32BitsId
);
10123 ValOperandId arg1Id
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10124 BigIntOperandId bigIntId
= writer
.guardToBigInt(arg1Id
);
10126 writer
.bigIntAsUintNResult(int32BitsId
, bigIntId
);
10127 writer
.returnFromIC();
10129 trackAttached("BigIntAsUintN");
10130 return AttachDecision::Attach
;
10133 AttachDecision
InlinableNativeIRGenerator::tryAttachSetHas() {
10134 // Ensure |this| is a SetObject.
10135 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
10136 return AttachDecision::NoAction
;
10139 // Need a single argument.
10141 return AttachDecision::NoAction
;
10144 // Initialize the input operand.
10145 initializeInputOperand();
10147 // Guard callee is the 'has' native function.
10148 emitNativeCalleeGuard();
10150 // Guard |this| is a SetObject.
10151 ValOperandId thisValId
=
10152 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10153 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10154 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Set
);
10156 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10158 #ifndef JS_CODEGEN_X86
10159 // Assume the hash key will likely always have the same type when attaching
10160 // the first stub. If the call is polymorphic on the hash key, attach a stub
10161 // which handles any value.
10162 if (isFirstStub()) {
10163 switch (args_
[0].type()) {
10164 case ValueType::Double
:
10165 case ValueType::Int32
:
10166 case ValueType::Boolean
:
10167 case ValueType::Undefined
:
10168 case ValueType::Null
: {
10169 writer
.guardToNonGCThing(argId
);
10170 writer
.setHasNonGCThingResult(objId
, argId
);
10173 case ValueType::String
: {
10174 StringOperandId strId
= writer
.guardToString(argId
);
10175 writer
.setHasStringResult(objId
, strId
);
10178 case ValueType::Symbol
: {
10179 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
10180 writer
.setHasSymbolResult(objId
, symId
);
10183 case ValueType::BigInt
: {
10184 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
10185 writer
.setHasBigIntResult(objId
, bigIntId
);
10188 case ValueType::Object
: {
10189 // Currently only supported on 64-bit platforms.
10190 # ifdef JS_PUNBOX64
10191 ObjOperandId valId
= writer
.guardToObject(argId
);
10192 writer
.setHasObjectResult(objId
, valId
);
10194 writer
.setHasResult(objId
, argId
);
10199 # ifdef ENABLE_RECORD_TUPLE
10200 case ValueType::ExtendedPrimitive
:
10202 case ValueType::Magic
:
10203 case ValueType::PrivateGCThing
:
10204 MOZ_CRASH("Unexpected type");
10207 writer
.setHasResult(objId
, argId
);
10210 // The optimized versions require too many registers on x86.
10211 writer
.setHasResult(objId
, argId
);
10214 writer
.returnFromIC();
10216 trackAttached("SetHas");
10217 return AttachDecision::Attach
;
10220 AttachDecision
InlinableNativeIRGenerator::tryAttachSetSize() {
10221 // Ensure |this| is a SetObject.
10222 if (!thisval_
.isObject() || !thisval_
.toObject().is
<SetObject
>()) {
10223 return AttachDecision::NoAction
;
10226 // Expecting no arguments.
10228 return AttachDecision::NoAction
;
10231 // Initialize the input operand.
10232 initializeInputOperand();
10234 // Guard callee is the 'size' native function.
10235 emitNativeCalleeGuard();
10237 // Guard |this| is a SetObject.
10238 ValOperandId thisValId
=
10239 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10240 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10241 writer
.guardClass(objId
, GuardClassKind::Set
);
10243 writer
.setSizeResult(objId
);
10244 writer
.returnFromIC();
10246 trackAttached("SetSize");
10247 return AttachDecision::Attach
;
10250 AttachDecision
InlinableNativeIRGenerator::tryAttachMapHas() {
10251 // Ensure |this| is a MapObject.
10252 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
10253 return AttachDecision::NoAction
;
10256 // Need a single argument.
10258 return AttachDecision::NoAction
;
10261 // Initialize the input operand.
10262 initializeInputOperand();
10264 // Guard callee is the 'has' native function.
10265 emitNativeCalleeGuard();
10267 // Guard |this| is a MapObject.
10268 ValOperandId thisValId
=
10269 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10270 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10271 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
10273 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10275 #ifndef JS_CODEGEN_X86
10276 // Assume the hash key will likely always have the same type when attaching
10277 // the first stub. If the call is polymorphic on the hash key, attach a stub
10278 // which handles any value.
10279 if (isFirstStub()) {
10280 switch (args_
[0].type()) {
10281 case ValueType::Double
:
10282 case ValueType::Int32
:
10283 case ValueType::Boolean
:
10284 case ValueType::Undefined
:
10285 case ValueType::Null
: {
10286 writer
.guardToNonGCThing(argId
);
10287 writer
.mapHasNonGCThingResult(objId
, argId
);
10290 case ValueType::String
: {
10291 StringOperandId strId
= writer
.guardToString(argId
);
10292 writer
.mapHasStringResult(objId
, strId
);
10295 case ValueType::Symbol
: {
10296 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
10297 writer
.mapHasSymbolResult(objId
, symId
);
10300 case ValueType::BigInt
: {
10301 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
10302 writer
.mapHasBigIntResult(objId
, bigIntId
);
10305 case ValueType::Object
: {
10306 // Currently only supported on 64-bit platforms.
10307 # ifdef JS_PUNBOX64
10308 ObjOperandId valId
= writer
.guardToObject(argId
);
10309 writer
.mapHasObjectResult(objId
, valId
);
10311 writer
.mapHasResult(objId
, argId
);
10316 # ifdef ENABLE_RECORD_TUPLE
10317 case ValueType::ExtendedPrimitive
:
10319 case ValueType::Magic
:
10320 case ValueType::PrivateGCThing
:
10321 MOZ_CRASH("Unexpected type");
10324 writer
.mapHasResult(objId
, argId
);
10327 // The optimized versions require too many registers on x86.
10328 writer
.mapHasResult(objId
, argId
);
10331 writer
.returnFromIC();
10333 trackAttached("MapHas");
10334 return AttachDecision::Attach
;
10337 AttachDecision
InlinableNativeIRGenerator::tryAttachMapGet() {
10338 // Ensure |this| is a MapObject.
10339 if (!thisval_
.isObject() || !thisval_
.toObject().is
<MapObject
>()) {
10340 return AttachDecision::NoAction
;
10343 // Need a single argument.
10345 return AttachDecision::NoAction
;
10348 // Initialize the input operand.
10349 initializeInputOperand();
10351 // Guard callee is the 'get' native function.
10352 emitNativeCalleeGuard();
10354 // Guard |this| is a MapObject.
10355 ValOperandId thisValId
=
10356 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
10357 ObjOperandId objId
= writer
.guardToObject(thisValId
);
10358 emitOptimisticClassGuard(objId
, &thisval_
.toObject(), GuardClassKind::Map
);
10360 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10362 #ifndef JS_CODEGEN_X86
10363 // Assume the hash key will likely always have the same type when attaching
10364 // the first stub. If the call is polymorphic on the hash key, attach a stub
10365 // which handles any value.
10366 if (isFirstStub()) {
10367 switch (args_
[0].type()) {
10368 case ValueType::Double
:
10369 case ValueType::Int32
:
10370 case ValueType::Boolean
:
10371 case ValueType::Undefined
:
10372 case ValueType::Null
: {
10373 writer
.guardToNonGCThing(argId
);
10374 writer
.mapGetNonGCThingResult(objId
, argId
);
10377 case ValueType::String
: {
10378 StringOperandId strId
= writer
.guardToString(argId
);
10379 writer
.mapGetStringResult(objId
, strId
);
10382 case ValueType::Symbol
: {
10383 SymbolOperandId symId
= writer
.guardToSymbol(argId
);
10384 writer
.mapGetSymbolResult(objId
, symId
);
10387 case ValueType::BigInt
: {
10388 BigIntOperandId bigIntId
= writer
.guardToBigInt(argId
);
10389 writer
.mapGetBigIntResult(objId
, bigIntId
);
10392 case ValueType::Object
: {
10393 // Currently only supported on 64-bit platforms.
10394 # ifdef JS_PUNBOX64
10395 ObjOperandId valId
= writer
.guardToObject(argId
);
10396 writer
.mapGetObjectResult(objId
, valId
);
10398 writer
.mapGetResult(objId
, argId
);
10403 # ifdef ENABLE_RECORD_TUPLE
10404 case ValueType::ExtendedPrimitive
:
10406 case ValueType::Magic
:
10407 case ValueType::PrivateGCThing
:
10408 MOZ_CRASH("Unexpected type");
10411 writer
.mapGetResult(objId
, argId
);
10414 // The optimized versions require too many registers on x86.
10415 writer
.mapGetResult(objId
, argId
);
10418 writer
.returnFromIC();
10420 trackAttached("MapGet");
10421 return AttachDecision::Attach
;
10424 AttachDecision
CallIRGenerator::tryAttachFunCall(HandleFunction callee
) {
10425 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
10427 if (callee
->native() != fun_call
) {
10428 return AttachDecision::NoAction
;
10431 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
10432 return AttachDecision::NoAction
;
10434 RootedFunction
target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
10436 bool isScripted
= target
->hasJitEntry();
10437 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
10439 if (target
->isClassConstructor()) {
10440 return AttachDecision::NoAction
;
10442 Int32OperandId
argcId(writer
.setInputOperandId(0));
10444 CallFlags
targetFlags(CallFlags::FunCall
);
10445 if (mode_
== ICState::Mode::Specialized
) {
10446 if (cx_
->realm() == target
->realm()) {
10447 targetFlags
.setIsSameRealm();
10451 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&& argc_
> 0) {
10452 // The stack layout is already in the correct form for calls with at least
10455 // clang-format off
10457 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
10458 // Callee <-- argc+1
10459 // ThisValue <-- argc
10460 // Args: | Arg0 | <-- argc-1
10461 // | Arg1 | <-- argc-2
10465 // When passing |argc-1| as the number of arguments, we get:
10467 // *** STACK LAYOUT (bottom to top) *** *** INDEX ***
10468 // Callee <-- (argc-1)+1 = argc = ThisValue
10469 // ThisValue <-- (argc-1) = argc-1 = Arg0
10470 // Args: | Arg0 | <-- (argc-1)-1 = argc-2 = Arg1
10471 // | Arg1 | <-- (argc-1)-2 = argc-3 = Arg2
10476 // This allows to call |loadArgumentFixedSlot(ArgumentKind::Arg0)| and we
10477 // still load the correct argument index from |ArgumentKind::Arg1|.
10479 // When no arguments are passed, i.e. |argc==0|, we have to replace
10480 // |ArgumentKind::Arg0| with the undefined value. But we don't yet support
10482 HandleValue newTarget
= NullHandleValue
;
10483 HandleValue thisValue
= args_
[0];
10484 HandleValueArray args
=
10485 HandleValueArray::subarray(args_
, 1, args_
.length() - 1);
10487 // Check for specific native-function optimizations.
10488 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
10489 args
, targetFlags
);
10490 TRY_ATTACH(nativeGen
.tryAttachStub());
10493 ObjOperandId thisObjId
= emitFunCallGuard(argcId
);
10495 if (mode_
== ICState::Mode::Specialized
) {
10496 // Ensure that |this| is the expected target function.
10497 emitCalleeGuard(thisObjId
, target
);
10500 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10501 ClampFixedArgc(argc_
));
10503 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
10504 ClampFixedArgc(argc_
));
10507 // Guard that |this| is a function.
10508 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
10510 // Guard that function is not a class constructor.
10511 writer
.guardNotClassConstructor(thisObjId
);
10514 writer
.guardFunctionHasJitEntry(thisObjId
);
10515 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
,
10516 ClampFixedArgc(argc_
));
10518 writer
.guardFunctionHasNoJitEntry(thisObjId
);
10519 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
,
10520 ClampFixedArgc(argc_
));
10524 writer
.returnFromIC();
10527 trackAttached("Scripted fun_call");
10529 trackAttached("Native fun_call");
10532 return AttachDecision::Attach
;
10535 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArray(
10536 bool isPossiblyWrapped
) {
10537 // Self-hosted code calls this with a single object argument.
10538 MOZ_ASSERT(argc_
== 1);
10539 MOZ_ASSERT(args_
[0].isObject());
10541 // Initialize the input operand.
10542 initializeInputOperand();
10544 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10546 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10547 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10548 writer
.isTypedArrayResult(objArgId
, isPossiblyWrapped
);
10549 writer
.returnFromIC();
10551 trackAttached(isPossiblyWrapped
? "IsPossiblyWrappedTypedArray"
10553 return AttachDecision::Attach
;
10556 AttachDecision
InlinableNativeIRGenerator::tryAttachIsTypedArrayConstructor() {
10557 // Self-hosted code calls this with a single object argument.
10558 MOZ_ASSERT(argc_
== 1);
10559 MOZ_ASSERT(args_
[0].isObject());
10561 // Initialize the input operand.
10562 initializeInputOperand();
10564 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10566 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10567 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10568 writer
.isTypedArrayConstructorResult(objArgId
);
10569 writer
.returnFromIC();
10571 trackAttached("IsTypedArrayConstructor");
10572 return AttachDecision::Attach
;
10575 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() {
10576 // Self-hosted code calls this with a single TypedArrayObject argument.
10577 MOZ_ASSERT(argc_
== 1);
10578 MOZ_ASSERT(args_
[0].isObject());
10579 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10581 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10583 // Initialize the input operand.
10584 initializeInputOperand();
10586 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10588 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10589 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10591 EmitGuardTypedArray(writer
, tarr
, objArgId
);
10593 size_t byteOffset
= tarr
->byteOffsetMaybeOutOfBounds();
10594 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
10595 if (byteOffset
<= INT32_MAX
) {
10596 writer
.arrayBufferViewByteOffsetInt32Result(objArgId
);
10598 writer
.arrayBufferViewByteOffsetDoubleResult(objArgId
);
10601 if (byteOffset
<= INT32_MAX
) {
10602 writer
.resizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result(objArgId
);
10604 writer
.resizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult(
10609 writer
.returnFromIC();
10611 trackAttached("IntrinsicTypedArrayByteOffset");
10612 return AttachDecision::Attach
;
10615 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayElementSize() {
10616 // Self-hosted code calls this with a single TypedArrayObject argument.
10617 MOZ_ASSERT(argc_
== 1);
10618 MOZ_ASSERT(args_
[0].isObject());
10619 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10621 // Initialize the input operand.
10622 initializeInputOperand();
10624 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10626 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10627 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10628 writer
.typedArrayElementSizeResult(objArgId
);
10629 writer
.returnFromIC();
10631 trackAttached("TypedArrayElementSize");
10632 return AttachDecision::Attach
;
10635 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayLength(
10636 bool isPossiblyWrapped
, bool allowOutOfBounds
) {
10637 // Self-hosted code calls this with a single, possibly wrapped,
10638 // TypedArrayObject argument.
10639 MOZ_ASSERT(argc_
== 1);
10640 MOZ_ASSERT(args_
[0].isObject());
10642 // Only optimize when the object isn't a wrapper.
10643 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10644 return AttachDecision::NoAction
;
10647 MOZ_ASSERT(args_
[0].toObject().is
<TypedArrayObject
>());
10649 auto* tarr
= &args_
[0].toObject().as
<TypedArrayObject
>();
10651 // Don't optimize when a resizable TypedArray is out-of-bounds and
10652 // out-of-bounds isn't allowed.
10653 auto length
= tarr
->length();
10654 if (length
.isNothing() && !tarr
->hasDetachedBuffer()) {
10655 MOZ_ASSERT(tarr
->is
<ResizableTypedArrayObject
>());
10656 MOZ_ASSERT(tarr
->isOutOfBounds());
10658 if (!allowOutOfBounds
) {
10659 return AttachDecision::NoAction
;
10663 // Initialize the input operand.
10664 initializeInputOperand();
10666 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10668 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10669 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10671 if (isPossiblyWrapped
) {
10672 writer
.guardIsNotProxy(objArgId
);
10675 EmitGuardTypedArray(writer
, tarr
, objArgId
);
10677 if (tarr
->is
<FixedLengthTypedArrayObject
>()) {
10678 if (length
.valueOr(0) <= INT32_MAX
) {
10679 writer
.loadArrayBufferViewLengthInt32Result(objArgId
);
10681 writer
.loadArrayBufferViewLengthDoubleResult(objArgId
);
10684 if (!allowOutOfBounds
) {
10685 writer
.guardResizableArrayBufferViewInBoundsOrDetached(objArgId
);
10688 if (length
.valueOr(0) <= INT32_MAX
) {
10689 writer
.resizableTypedArrayLengthInt32Result(objArgId
);
10691 writer
.resizableTypedArrayLengthDoubleResult(objArgId
);
10694 writer
.returnFromIC();
10696 trackAttached("IntrinsicTypedArrayLength");
10697 return AttachDecision::Attach
;
10700 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayBufferByteLength(
10701 bool isPossiblyWrapped
) {
10702 // Self-hosted code calls this with a single, possibly wrapped,
10703 // ArrayBufferObject argument.
10704 MOZ_ASSERT(argc_
== 1);
10705 MOZ_ASSERT(args_
[0].isObject());
10707 // Only optimize when the object isn't a wrapper.
10708 if (isPossiblyWrapped
&& IsWrapper(&args_
[0].toObject())) {
10709 return AttachDecision::NoAction
;
10712 MOZ_ASSERT(args_
[0].toObject().is
<ArrayBufferObject
>());
10714 auto* buffer
= &args_
[0].toObject().as
<ArrayBufferObject
>();
10716 // Initialize the input operand.
10717 initializeInputOperand();
10719 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10721 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10722 ObjOperandId objArgId
= writer
.guardToObject(argId
);
10724 if (isPossiblyWrapped
) {
10725 writer
.guardIsNotProxy(objArgId
);
10728 if (buffer
->byteLength() <= INT32_MAX
) {
10729 writer
.loadArrayBufferByteLengthInt32Result(objArgId
);
10731 writer
.loadArrayBufferByteLengthDoubleResult(objArgId
);
10733 writer
.returnFromIC();
10735 trackAttached("ArrayBufferByteLength");
10736 return AttachDecision::Attach
;
10739 AttachDecision
InlinableNativeIRGenerator::tryAttachIsConstructing() {
10740 // Self-hosted code calls this with no arguments in function scripts.
10741 MOZ_ASSERT(argc_
== 0);
10742 MOZ_ASSERT(script()->isFunction());
10744 // Initialize the input operand.
10745 initializeInputOperand();
10747 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10749 writer
.frameIsConstructingResult();
10750 writer
.returnFromIC();
10752 trackAttached("IsConstructing");
10753 return AttachDecision::Attach
;
10757 InlinableNativeIRGenerator::tryAttachGetNextMapSetEntryForIterator(bool isMap
) {
10758 // Self-hosted code calls this with two objects.
10759 MOZ_ASSERT(argc_
== 2);
10761 MOZ_ASSERT(args_
[0].toObject().is
<MapIteratorObject
>());
10763 MOZ_ASSERT(args_
[0].toObject().is
<SetIteratorObject
>());
10765 MOZ_ASSERT(args_
[1].toObject().is
<ArrayObject
>());
10767 // Initialize the input operand.
10768 initializeInputOperand();
10770 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10772 ValOperandId iterId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10773 ObjOperandId objIterId
= writer
.guardToObject(iterId
);
10775 ValOperandId resultArrId
=
10776 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
);
10777 ObjOperandId objResultArrId
= writer
.guardToObject(resultArrId
);
10779 writer
.getNextMapSetEntryForIteratorResult(objIterId
, objResultArrId
, isMap
);
10780 writer
.returnFromIC();
10782 trackAttached("GetNextMapSetEntryForIterator");
10783 return AttachDecision::Attach
;
10786 AttachDecision
InlinableNativeIRGenerator::tryAttachNewArrayIterator() {
10787 // Self-hosted code calls this without any arguments
10788 MOZ_ASSERT(argc_
== 0);
10790 JSObject
* templateObj
= NewArrayIteratorTemplate(cx_
);
10791 if (!templateObj
) {
10792 cx_
->recoverFromOutOfMemory();
10793 return AttachDecision::NoAction
;
10796 // Initialize the input operand.
10797 initializeInputOperand();
10799 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10801 writer
.newArrayIteratorResult(templateObj
);
10802 writer
.returnFromIC();
10804 trackAttached("NewArrayIterator");
10805 return AttachDecision::Attach
;
10808 AttachDecision
InlinableNativeIRGenerator::tryAttachNewStringIterator() {
10809 // Self-hosted code calls this without any arguments
10810 MOZ_ASSERT(argc_
== 0);
10812 JSObject
* templateObj
= NewStringIteratorTemplate(cx_
);
10813 if (!templateObj
) {
10814 cx_
->recoverFromOutOfMemory();
10815 return AttachDecision::NoAction
;
10818 // Initialize the input operand.
10819 initializeInputOperand();
10821 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10823 writer
.newStringIteratorResult(templateObj
);
10824 writer
.returnFromIC();
10826 trackAttached("NewStringIterator");
10827 return AttachDecision::Attach
;
10830 AttachDecision
InlinableNativeIRGenerator::tryAttachNewRegExpStringIterator() {
10831 // Self-hosted code calls this without any arguments
10832 MOZ_ASSERT(argc_
== 0);
10834 JSObject
* templateObj
= NewRegExpStringIteratorTemplate(cx_
);
10835 if (!templateObj
) {
10836 cx_
->recoverFromOutOfMemory();
10837 return AttachDecision::NoAction
;
10840 // Initialize the input operand.
10841 initializeInputOperand();
10843 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10845 writer
.newRegExpStringIteratorResult(templateObj
);
10846 writer
.returnFromIC();
10848 trackAttached("NewRegExpStringIterator");
10849 return AttachDecision::Attach
;
10853 InlinableNativeIRGenerator::tryAttachArrayIteratorPrototypeOptimizable() {
10854 // Self-hosted code calls this without any arguments
10855 MOZ_ASSERT(argc_
== 0);
10857 if (!isFirstStub()) {
10858 // Attach only once to prevent slowdowns for polymorphic calls.
10859 return AttachDecision::NoAction
;
10862 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
10864 Rooted
<JSFunction
*> nextFun(cx_
);
10865 if (!IsArrayIteratorPrototypeOptimizable(cx_
, AllowIteratorReturn::Yes
,
10866 &arrayIteratorProto
, &slot
,
10868 return AttachDecision::NoAction
;
10871 // Initialize the input operand.
10872 initializeInputOperand();
10874 // Note: we don't need to call emitNativeCalleeGuard for intrinsics.
10876 ObjOperandId protoId
= writer
.loadObject(arrayIteratorProto
);
10877 ObjOperandId nextId
= writer
.loadObject(nextFun
);
10879 writer
.guardShape(protoId
, arrayIteratorProto
->shape());
10881 // Ensure that proto[slot] == nextFun.
10882 writer
.guardDynamicSlotIsSpecificObject(protoId
, nextId
, slot
);
10883 writer
.loadBooleanResult(true);
10884 writer
.returnFromIC();
10886 trackAttached("ArrayIteratorPrototypeOptimizable");
10887 return AttachDecision::Attach
;
10890 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectCreate() {
10891 // Need a single object-or-null argument.
10892 if (argc_
!= 1 || !args_
[0].isObjectOrNull()) {
10893 return AttachDecision::NoAction
;
10896 if (!isFirstStub()) {
10897 // Attach only once to prevent slowdowns for polymorphic calls.
10898 return AttachDecision::NoAction
;
10901 RootedObject
proto(cx_
, args_
[0].toObjectOrNull());
10902 JSObject
* templateObj
= ObjectCreateImpl(cx_
, proto
, TenuredObject
);
10903 if (!templateObj
) {
10904 cx_
->recoverFromOutOfMemory();
10905 return AttachDecision::NoAction
;
10908 // Initialize the input operand.
10909 initializeInputOperand();
10911 // Guard callee is the 'create' native function.
10912 emitNativeCalleeGuard();
10914 // Guard on the proto argument.
10915 ValOperandId argId
= writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
10917 ObjOperandId protoId
= writer
.guardToObject(argId
);
10918 writer
.guardSpecificObject(protoId
, proto
);
10920 writer
.guardIsNull(argId
);
10923 writer
.objectCreateResult(templateObj
);
10924 writer
.returnFromIC();
10926 trackAttached("ObjectCreate");
10927 return AttachDecision::Attach
;
10930 AttachDecision
InlinableNativeIRGenerator::tryAttachObjectConstructor() {
10931 // Expecting no arguments or a single object argument.
10932 // TODO(Warp): Support all or more conversions to object.
10934 return AttachDecision::NoAction
;
10936 if (argc_
== 1 && !args_
[0].isObject()) {
10937 return AttachDecision::NoAction
;
10940 gc::AllocSite
* site
= nullptr;
10941 PlainObject
* templateObj
= nullptr;
10943 // Stub doesn't support metadata builder
10944 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
10945 return AttachDecision::NoAction
;
10948 site
= generator_
.maybeCreateAllocSite();
10950 return AttachDecision::NoAction
;
10953 // Create a temporary object to act as the template object.
10954 templateObj
= NewPlainObjectWithAllocKind(cx_
, NewObjectGCKind());
10955 if (!templateObj
) {
10956 cx_
->recoverFromOutOfMemory();
10957 return AttachDecision::NoAction
;
10961 // Initialize the input operand.
10962 initializeInputOperand();
10964 // Guard callee and newTarget (if constructing) are this Object constructor
10966 emitNativeCalleeGuard();
10969 uint32_t numFixedSlots
= templateObj
->numUsedFixedSlots();
10970 uint32_t numDynamicSlots
= templateObj
->numDynamicSlots();
10971 gc::AllocKind allocKind
= templateObj
->allocKindForTenure();
10972 Shape
* shape
= templateObj
->shape();
10974 writer
.guardNoAllocationMetadataBuilder(
10975 cx_
->realm()->addressOfMetadataBuilder());
10976 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
,
10979 // Use standard call flags when this is an inline Function.prototype.call(),
10980 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
10981 CallFlags flags
= flags_
;
10982 if (flags
.getArgFormat() == CallFlags::FunCall
) {
10983 flags
= CallFlags(CallFlags::Standard
);
10986 // Guard that the argument is an object.
10987 ValOperandId argId
=
10988 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
10989 ObjOperandId objId
= writer
.guardToObject(argId
);
10991 // Return the object.
10992 writer
.loadObjectResult(objId
);
10995 writer
.returnFromIC();
10997 trackAttached("ObjectConstructor");
10998 return AttachDecision::Attach
;
11001 AttachDecision
InlinableNativeIRGenerator::tryAttachArrayConstructor() {
11002 // Only optimize the |Array()| and |Array(n)| cases (with or without |new|)
11003 // for now. Note that self-hosted code calls this without |new| via std_Array.
11005 return AttachDecision::NoAction
;
11007 if (argc_
== 1 && !args_
[0].isInt32()) {
11008 return AttachDecision::NoAction
;
11011 int32_t length
= (argc_
== 1) ? args_
[0].toInt32() : 0;
11012 if (length
< 0 || uint32_t(length
) > ArrayObject::EagerAllocationMaxLength
) {
11013 return AttachDecision::NoAction
;
11016 // We allow inlining this function across realms so make sure the template
11017 // object is allocated in that realm. See CanInlineNativeCrossRealm.
11018 JSObject
* templateObj
;
11020 AutoRealm
ar(cx_
, callee_
);
11021 templateObj
= NewDenseFullyAllocatedArray(cx_
, length
, TenuredObject
);
11022 if (!templateObj
) {
11023 cx_
->clearPendingException();
11024 return AttachDecision::NoAction
;
11028 gc::AllocSite
* site
= generator_
.maybeCreateAllocSite();
11030 return AttachDecision::NoAction
;
11033 // Initialize the input operand.
11034 initializeInputOperand();
11036 // Guard callee and newTarget (if constructing) are this Array constructor
11038 emitNativeCalleeGuard();
11040 Int32OperandId lengthId
;
11042 // Use standard call flags when this is an inline Function.prototype.call(),
11043 // because GetIndexOfArgument() doesn't yet support |CallFlags::FunCall|.
11044 CallFlags flags
= flags_
;
11045 if (flags
.getArgFormat() == CallFlags::FunCall
) {
11046 flags
= CallFlags(CallFlags::Standard
);
11049 ValOperandId arg0Id
=
11050 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags
);
11051 lengthId
= writer
.guardToInt32(arg0Id
);
11053 MOZ_ASSERT(argc_
== 0);
11054 lengthId
= writer
.loadInt32Constant(0);
11057 writer
.newArrayFromLengthResult(templateObj
, lengthId
, site
);
11058 writer
.returnFromIC();
11060 trackAttached("ArrayConstructor");
11061 return AttachDecision::Attach
;
11064 AttachDecision
InlinableNativeIRGenerator::tryAttachTypedArrayConstructor() {
11065 MOZ_ASSERT(flags_
.isConstructing());
11067 if (argc_
== 0 || argc_
> 3) {
11068 return AttachDecision::NoAction
;
11071 if (!isFirstStub()) {
11072 // Attach only once to prevent slowdowns for polymorphic calls.
11073 return AttachDecision::NoAction
;
11076 // The first argument must be int32 or a non-proxy object.
11077 if (!args_
[0].isInt32() && !args_
[0].isObject()) {
11078 return AttachDecision::NoAction
;
11080 if (args_
[0].isObject() && args_
[0].toObject().is
<ProxyObject
>()) {
11081 return AttachDecision::NoAction
;
11084 #ifdef JS_CODEGEN_X86
11085 // Unfortunately NewTypedArrayFromArrayBufferResult needs more registers than
11086 // we can easily support on 32-bit x86 for now.
11087 if (args_
[0].isObject() &&
11088 args_
[0].toObject().is
<ArrayBufferObjectMaybeShared
>()) {
11089 return AttachDecision::NoAction
;
11093 RootedObject
templateObj(cx_
);
11094 if (!TypedArrayObject::GetTemplateObjectForNative(cx_
, callee_
->native(),
11095 args_
, &templateObj
)) {
11096 cx_
->recoverFromOutOfMemory();
11097 return AttachDecision::NoAction
;
11100 if (!templateObj
) {
11101 // This can happen for large length values.
11102 MOZ_ASSERT(args_
[0].isInt32());
11103 return AttachDecision::NoAction
;
11106 // Initialize the input operand.
11107 initializeInputOperand();
11109 // Guard callee and newTarget are this TypedArray constructor function.
11110 emitNativeCalleeGuard();
11112 ValOperandId arg0Id
=
11113 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
, flags_
);
11115 if (args_
[0].isInt32()) {
11117 Int32OperandId lengthId
= writer
.guardToInt32(arg0Id
);
11118 writer
.newTypedArrayFromLengthResult(templateObj
, lengthId
);
11120 JSObject
* obj
= &args_
[0].toObject();
11121 ObjOperandId objId
= writer
.guardToObject(arg0Id
);
11123 if (obj
->is
<ArrayBufferObjectMaybeShared
>()) {
11124 // From ArrayBuffer.
11125 if (obj
->is
<FixedLengthArrayBufferObject
>()) {
11126 writer
.guardClass(objId
, GuardClassKind::FixedLengthArrayBuffer
);
11127 } else if (obj
->is
<FixedLengthSharedArrayBufferObject
>()) {
11128 writer
.guardClass(objId
, GuardClassKind::FixedLengthSharedArrayBuffer
);
11129 } else if (obj
->is
<ResizableArrayBufferObject
>()) {
11130 writer
.guardClass(objId
, GuardClassKind::ResizableArrayBuffer
);
11132 MOZ_ASSERT(obj
->is
<GrowableSharedArrayBufferObject
>());
11133 writer
.guardClass(objId
, GuardClassKind::GrowableSharedArrayBuffer
);
11135 ValOperandId byteOffsetId
;
11138 writer
.loadArgumentFixedSlot(ArgumentKind::Arg1
, argc_
, flags_
);
11140 byteOffsetId
= writer
.loadUndefined();
11142 ValOperandId lengthId
;
11145 writer
.loadArgumentFixedSlot(ArgumentKind::Arg2
, argc_
, flags_
);
11147 lengthId
= writer
.loadUndefined();
11149 writer
.newTypedArrayFromArrayBufferResult(templateObj
, objId
,
11150 byteOffsetId
, lengthId
);
11152 // From Array-like.
11153 writer
.guardIsNotArrayBufferMaybeShared(objId
);
11154 writer
.guardIsNotProxy(objId
);
11155 writer
.newTypedArrayFromArrayResult(templateObj
, objId
);
11159 writer
.returnFromIC();
11161 trackAttached("TypedArrayConstructor");
11162 return AttachDecision::Attach
;
11165 AttachDecision
InlinableNativeIRGenerator::tryAttachSpecializedFunctionBind(
11166 Handle
<JSObject
*> target
, Handle
<BoundFunctionObject
*> templateObj
) {
11167 // Try to attach a faster stub that's more specialized than what we emit in
11168 // tryAttachFunctionBind. This lets us allocate and initialize a bound
11169 // function object in Ion without calling into C++.
11171 // We can do this if:
11173 // * The target's prototype is Function.prototype, because that's the proto we
11174 // use for the template object.
11175 // * All bound arguments can be stored inline.
11176 // * The `.name`, `.length`, and `IsConstructor` values match `target`.
11178 // We initialize the template object with the bound function's name, length,
11179 // and flags. At runtime we then only have to clone the template object and
11180 // initialize the slots for the target, the bound `this` and the bound
11183 if (!isFirstStub()) {
11184 return AttachDecision::NoAction
;
11186 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
11187 return AttachDecision::NoAction
;
11189 if (target
->staticPrototype() != &cx_
->global()->getFunctionPrototype()) {
11190 return AttachDecision::NoAction
;
11192 size_t numBoundArgs
= argc_
> 0 ? argc_
- 1 : 0;
11193 if (numBoundArgs
> BoundFunctionObject::MaxInlineBoundArgs
) {
11194 return AttachDecision::NoAction
;
11197 const bool targetIsConstructor
= target
->isConstructor();
11198 Rooted
<JSAtom
*> targetName(cx_
);
11199 uint32_t targetLength
= 0;
11201 if (target
->is
<JSFunction
>()) {
11202 Rooted
<JSFunction
*> fun(cx_
, &target
->as
<JSFunction
>());
11203 if (fun
->isNativeFun()) {
11204 return AttachDecision::NoAction
;
11206 if (fun
->hasResolvedLength() || fun
->hasResolvedName()) {
11207 return AttachDecision::NoAction
;
11210 if (!JSFunction::getUnresolvedLength(cx_
, fun
, &len
)) {
11211 cx_
->clearPendingException();
11212 return AttachDecision::NoAction
;
11214 targetName
= fun
->getUnresolvedName(cx_
);
11216 cx_
->clearPendingException();
11217 return AttachDecision::NoAction
;
11220 targetLength
= len
;
11222 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
11223 if (!targetIsConstructor
) {
11224 // Only support constructors for now. This lets us use
11225 // GuardBoundFunctionIsConstructor.
11226 return AttachDecision::NoAction
;
11228 Shape
* initialShape
=
11229 cx_
->global()->maybeBoundFunctionShapeWithDefaultProto();
11230 if (bound
->shape() != initialShape
) {
11231 return AttachDecision::NoAction
;
11233 Value lenVal
= bound
->getLengthForInitialShape();
11234 Value nameVal
= bound
->getNameForInitialShape();
11235 if (!lenVal
.isInt32() || lenVal
.toInt32() < 0 || !nameVal
.isString() ||
11236 !nameVal
.toString()->isAtom()) {
11237 return AttachDecision::NoAction
;
11239 targetName
= &nameVal
.toString()->asAtom();
11240 targetLength
= uint32_t(lenVal
.toInt32());
11243 if (!templateObj
->initTemplateSlotsForSpecializedBind(
11244 cx_
, numBoundArgs
, targetIsConstructor
, targetLength
, targetName
)) {
11245 cx_
->recoverFromOutOfMemory();
11246 return AttachDecision::NoAction
;
11249 initializeInputOperand();
11250 emitNativeCalleeGuard();
11252 ValOperandId thisValId
=
11253 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
11254 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
11256 // Ensure the JSClass and proto match, and that the `length` and `name`
11257 // properties haven't been redefined.
11258 writer
.guardShape(targetId
, target
->shape());
11260 // Emit guards for the `IsConstructor`, `.length`, and `.name` values.
11261 if (target
->is
<JSFunction
>()) {
11263 // * The BaseScript (because that's what JSFunction uses for the `length`).
11264 // Because MGuardFunctionScript doesn't support self-hosted functions yet,
11265 // we use GuardSpecificFunction instead in this case.
11266 // See assertion in MGuardFunctionScript::getAliasSet.
11267 // * The flags slot (for the CONSTRUCTOR, RESOLVED_NAME, RESOLVED_LENGTH,
11268 // HAS_INFERRED_NAME, and HAS_GUESSED_ATOM flags).
11269 // * The atom slot.
11270 JSFunction
* fun
= &target
->as
<JSFunction
>();
11271 if (fun
->isSelfHostedBuiltin()) {
11272 writer
.guardSpecificFunction(targetId
, fun
);
11274 writer
.guardFunctionScript(targetId
, fun
->baseScript());
11276 writer
.guardFixedSlotValue(
11277 targetId
, JSFunction::offsetOfFlagsAndArgCount(),
11278 fun
->getReservedSlot(JSFunction::FlagsAndArgCountSlot
));
11279 writer
.guardFixedSlotValue(targetId
, JSFunction::offsetOfAtom(),
11280 fun
->getReservedSlot(JSFunction::AtomSlot
));
11282 BoundFunctionObject
* bound
= &target
->as
<BoundFunctionObject
>();
11283 writer
.guardBoundFunctionIsConstructor(targetId
);
11284 writer
.guardFixedSlotValue(targetId
,
11285 BoundFunctionObject::offsetOfLengthSlot(),
11286 bound
->getLengthForInitialShape());
11287 writer
.guardFixedSlotValue(targetId
,
11288 BoundFunctionObject::offsetOfNameSlot(),
11289 bound
->getNameForInitialShape());
11292 writer
.specializedBindFunctionResult(targetId
, argc_
, templateObj
);
11293 writer
.returnFromIC();
11295 trackAttached("SpecializedFunctionBind");
11296 return AttachDecision::Attach
;
11299 AttachDecision
InlinableNativeIRGenerator::tryAttachFunctionBind() {
11300 // Ensure |this| (the target) is a function object or a bound function object.
11301 // We could support other callables too, but note that we rely on the target
11302 // having a static prototype in BoundFunctionObject::functionBindImpl.
11303 if (!thisval_
.isObject()) {
11304 return AttachDecision::NoAction
;
11306 Rooted
<JSObject
*> target(cx_
, &thisval_
.toObject());
11307 if (!target
->is
<JSFunction
>() && !target
->is
<BoundFunctionObject
>()) {
11308 return AttachDecision::NoAction
;
11311 // Only support standard, non-spread calls.
11312 if (flags_
.getArgFormat() != CallFlags::Standard
) {
11313 return AttachDecision::NoAction
;
11316 // Only optimize if the number of arguments is small. This ensures we don't
11317 // compile a lot of different stubs (because we bake in argc) and that we
11318 // don't get anywhere near ARGS_LENGTH_MAX.
11319 static constexpr size_t MaxArguments
= 6;
11320 if (argc_
> MaxArguments
) {
11321 return AttachDecision::NoAction
;
11324 Rooted
<BoundFunctionObject
*> templateObj(
11325 cx_
, BoundFunctionObject::createTemplateObject(cx_
));
11326 if (!templateObj
) {
11327 cx_
->recoverFromOutOfMemory();
11328 return AttachDecision::NoAction
;
11331 TRY_ATTACH(tryAttachSpecializedFunctionBind(target
, templateObj
));
11333 initializeInputOperand();
11335 emitNativeCalleeGuard();
11337 // Guard |this| is a function object or a bound function object.
11338 ValOperandId thisValId
=
11339 writer
.loadArgumentFixedSlot(ArgumentKind::This
, argc_
);
11340 ObjOperandId targetId
= writer
.guardToObject(thisValId
);
11341 if (target
->is
<JSFunction
>()) {
11342 writer
.guardClass(targetId
, GuardClassKind::JSFunction
);
11344 MOZ_ASSERT(target
->is
<BoundFunctionObject
>());
11345 writer
.guardClass(targetId
, GuardClassKind::BoundFunction
);
11348 writer
.bindFunctionResult(targetId
, argc_
, templateObj
);
11349 writer
.returnFromIC();
11351 trackAttached("FunctionBind");
11352 return AttachDecision::Attach
;
11355 AttachDecision
CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc
) {
11356 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
11358 if (calleeFunc
->native() != fun_apply
) {
11359 return AttachDecision::NoAction
;
11363 return AttachDecision::NoAction
;
11366 if (!thisval_
.isObject() || !thisval_
.toObject().is
<JSFunction
>()) {
11367 return AttachDecision::NoAction
;
11369 Rooted
<JSFunction
*> target(cx_
, &thisval_
.toObject().as
<JSFunction
>());
11371 bool isScripted
= target
->hasJitEntry();
11372 MOZ_ASSERT_IF(!isScripted
, target
->isNativeWithoutJitEntry());
11374 if (target
->isClassConstructor()) {
11375 return AttachDecision::NoAction
;
11378 CallFlags::ArgFormat format
= CallFlags::Standard
;
11380 // |fun.apply()| and |fun.apply(thisValue)| are equivalent to |fun.call()|
11381 // resp. |fun.call(thisValue)|.
11382 format
= CallFlags::FunCall
;
11383 } else if (args_
[1].isNullOrUndefined()) {
11384 // |fun.apply(thisValue, null)| and |fun.apply(thisValue, undefined)| are
11385 // also equivalent to |fun.call(thisValue)|, but we can't use FunCall
11386 // because we have to discard the second argument.
11387 format
= CallFlags::FunApplyNullUndefined
;
11388 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArgumentsObject
>()) {
11389 auto* argsObj
= &args_
[1].toObject().as
<ArgumentsObject
>();
11390 if (argsObj
->hasOverriddenElement() || argsObj
->anyArgIsForwarded() ||
11391 argsObj
->hasOverriddenLength() ||
11392 argsObj
->initialLength() > JIT_ARGS_LENGTH_MAX
) {
11393 return AttachDecision::NoAction
;
11395 format
= CallFlags::FunApplyArgsObj
;
11396 } else if (args_
[1].isObject() && args_
[1].toObject().is
<ArrayObject
>() &&
11397 args_
[1].toObject().as
<ArrayObject
>().length() <=
11398 JIT_ARGS_LENGTH_MAX
&&
11399 IsPackedArray(&args_
[1].toObject())) {
11400 format
= CallFlags::FunApplyArray
;
11402 return AttachDecision::NoAction
;
11405 Int32OperandId
argcId(writer
.setInputOperandId(0));
11407 CallFlags
targetFlags(format
);
11408 if (mode_
== ICState::Mode::Specialized
) {
11409 if (cx_
->realm() == target
->realm()) {
11410 targetFlags
.setIsSameRealm();
11414 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
11415 format
== CallFlags::FunApplyArray
) {
11416 HandleValue newTarget
= NullHandleValue
;
11417 HandleValue thisValue
= args_
[0];
11418 Rooted
<ArrayObject
*> aobj(cx_
, &args_
[1].toObject().as
<ArrayObject
>());
11419 HandleValueArray args
= HandleValueArray::fromMarkedLocation(
11420 aobj
->length(), aobj
->getDenseElements());
11422 // Check for specific native-function optimizations.
11423 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
11424 args
, targetFlags
);
11425 TRY_ATTACH(nativeGen
.tryAttachStub());
11428 // Don't inline when no arguments are passed, cf. |tryAttachFunCall()|.
11429 if (mode_
== ICState::Mode::Specialized
&& !isScripted
&&
11430 format
== CallFlags::FunCall
&& argc_
> 0) {
11431 MOZ_ASSERT(argc_
== 1);
11433 HandleValue newTarget
= NullHandleValue
;
11434 HandleValue thisValue
= args_
[0];
11435 HandleValueArray args
= HandleValueArray::empty();
11437 // Check for specific native-function optimizations.
11438 InlinableNativeIRGenerator
nativeGen(*this, target
, newTarget
, thisValue
,
11439 args
, targetFlags
);
11440 TRY_ATTACH(nativeGen
.tryAttachStub());
11443 ObjOperandId thisObjId
= emitFunApplyGuard(argcId
);
11445 uint32_t fixedArgc
;
11446 if (format
== CallFlags::FunApplyArray
||
11447 format
== CallFlags::FunApplyArgsObj
||
11448 format
== CallFlags::FunApplyNullUndefined
) {
11449 emitFunApplyArgsGuard(format
);
11451 // We always use MaxUnrolledArgCopy here because the fixed argc is
11452 // meaningless in a FunApply case.
11453 fixedArgc
= MaxUnrolledArgCopy
;
11455 MOZ_ASSERT(format
== CallFlags::FunCall
);
11457 // Whereas for the FunCall case we need to use the actual fixed argc value.
11458 fixedArgc
= ClampFixedArgc(argc_
);
11461 if (mode_
== ICState::Mode::Specialized
) {
11462 // Ensure that |this| is the expected target function.
11463 emitCalleeGuard(thisObjId
, target
);
11466 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11468 writer
.callNativeFunction(thisObjId
, argcId
, op_
, target
, targetFlags
,
11472 // Guard that |this| is a function.
11473 writer
.guardClass(thisObjId
, GuardClassKind::JSFunction
);
11475 // Guard that function is not a class constructor.
11476 writer
.guardNotClassConstructor(thisObjId
);
11479 // Guard that function is scripted.
11480 writer
.guardFunctionHasJitEntry(thisObjId
);
11481 writer
.callScriptedFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11483 // Guard that function is native.
11484 writer
.guardFunctionHasNoJitEntry(thisObjId
);
11485 writer
.callAnyNativeFunction(thisObjId
, argcId
, targetFlags
, fixedArgc
);
11489 writer
.returnFromIC();
11492 trackAttached("Call.ScriptedFunApply");
11494 trackAttached("Call.NativeFunApply");
11497 return AttachDecision::Attach
;
11500 AttachDecision
CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc
) {
11501 // Try to optimize calls into Wasm code by emitting the CallWasmFunction
11502 // CacheIR op. Baseline ICs currently treat this as a CallScriptedFunction op
11503 // (calling Wasm's JitEntry stub) but Warp transpiles it to a more direct call
11506 // Note: some code refers to these optimized Wasm calls as "inlined" calls.
11508 MOZ_ASSERT(calleeFunc
->isWasmWithJitEntry());
11510 if (!JitOptions
.enableWasmIonFastCalls
) {
11511 return AttachDecision::NoAction
;
11513 if (!isFirstStub_
) {
11514 return AttachDecision::NoAction
;
11516 JSOp op
= JSOp(*pc_
);
11517 if (op
!= JSOp::Call
&& op
!= JSOp::CallContent
&&
11518 op
!= JSOp::CallIgnoresRv
) {
11519 return AttachDecision::NoAction
;
11521 if (cx_
->realm() != calleeFunc
->realm()) {
11522 return AttachDecision::NoAction
;
11525 wasm::Instance
& inst
= wasm::ExportedFunctionToInstance(calleeFunc
);
11526 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(calleeFunc
);
11527 const wasm::CodeBlock
& codeBlock
= inst
.code().funcCodeBlock(funcIndex
);
11528 const wasm::FuncExport
& funcExport
= codeBlock
.lookupFuncExport(funcIndex
);
11529 const wasm::FuncType
& sig
=
11530 wasm::ExportedFunctionToTypeDef(calleeFunc
).funcType();
11532 MOZ_ASSERT(!IsInsideNursery(inst
.object()));
11533 MOZ_ASSERT(sig
.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
11535 // If there are too many arguments, don't optimize (we won't be able to store
11536 // the arguments in the LIR node).
11537 static_assert(wasm::MaxArgsForJitInlineCall
<= ArgumentKindArgIndexLimit
);
11538 if (sig
.args().length() > wasm::MaxArgsForJitInlineCall
||
11539 argc_
> ArgumentKindArgIndexLimit
) {
11540 return AttachDecision::NoAction
;
11543 // If there are too many results, don't optimize as Warp currently doesn't
11544 // have code to handle this.
11545 if (sig
.results().length() > wasm::MaxResultsForJitInlineCall
) {
11546 return AttachDecision::NoAction
;
11549 // Bug 1631656 - Don't try to optimize with I64 args on 32-bit platforms
11550 // because it is more difficult (because it requires multiple LIR arguments
11553 // Bug 1631650 - On 64-bit platforms, we also give up optimizing for I64 args
11554 // spilled to the stack because it causes problems with register allocation.
11556 constexpr bool optimizeWithI64
= true;
11558 constexpr bool optimizeWithI64
= false;
11560 ABIArgGenerator abi
;
11561 for (const auto& valType
: sig
.args()) {
11562 MIRType mirType
= valType
.toMIRType();
11563 ABIArg abiArg
= abi
.next(mirType
);
11564 if (mirType
!= MIRType::Int64
) {
11567 if (!optimizeWithI64
|| abiArg
.kind() == ABIArg::Stack
) {
11568 return AttachDecision::NoAction
;
11572 // Check that all arguments can be converted to the Wasm type in Warp code
11573 // without bailing out.
11574 for (size_t i
= 0; i
< sig
.args().length(); i
++) {
11575 Value argVal
= i
< argc_
? args_
[i
] : UndefinedValue();
11576 switch (sig
.args()[i
].kind()) {
11577 case wasm::ValType::I32
:
11578 case wasm::ValType::F32
:
11579 case wasm::ValType::F64
:
11580 if (!argVal
.isNumber() && !argVal
.isBoolean() &&
11581 !argVal
.isUndefined()) {
11582 return AttachDecision::NoAction
;
11585 case wasm::ValType::I64
:
11586 if (!argVal
.isBigInt() && !argVal
.isBoolean() && !argVal
.isString()) {
11587 return AttachDecision::NoAction
;
11590 case wasm::ValType::V128
:
11591 MOZ_CRASH("Function should not have a Wasm JitEntry");
11592 case wasm::ValType::Ref
:
11593 // canHaveJitEntry restricts args to externref, where all JS values are
11594 // valid and can be boxed.
11595 MOZ_ASSERT(sig
.args()[i
].refType().isExtern(),
11596 "Unexpected type for Wasm JitEntry");
11601 CallFlags
flags(/* isConstructing = */ false, /* isSpread = */ false,
11602 /* isSameRealm = */ true);
11605 Int32OperandId
argcId(writer
.setInputOperandId(0));
11607 // Load the callee and ensure it is an object
11608 ValOperandId calleeValId
=
11609 writer
.loadArgumentFixedSlot(ArgumentKind::Callee
, argc_
, flags
);
11610 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
11612 // Ensure the callee is this Wasm function.
11613 emitCalleeGuard(calleeObjId
, calleeFunc
);
11615 // Guard the argument types.
11616 uint32_t guardedArgs
= std::min
<uint32_t>(sig
.args().length(), argc_
);
11617 for (uint32_t i
= 0; i
< guardedArgs
; i
++) {
11618 ArgumentKind argKind
= ArgumentKindForArgIndex(i
);
11619 ValOperandId argId
= writer
.loadArgumentFixedSlot(argKind
, argc_
, flags
);
11620 writer
.guardWasmArg(argId
, sig
.args()[i
].kind());
11623 writer
.callWasmFunction(calleeObjId
, argcId
, flags
, ClampFixedArgc(argc_
),
11624 &funcExport
, inst
.object());
11625 writer
.returnFromIC();
11627 trackAttached("Call.WasmCall");
11629 return AttachDecision::Attach
;
11632 AttachDecision
CallIRGenerator::tryAttachInlinableNative(HandleFunction callee
,
11634 MOZ_ASSERT(mode_
== ICState::Mode::Specialized
);
11635 MOZ_ASSERT(callee
->isNativeWithoutJitEntry());
11636 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
||
11637 flags
.getArgFormat() == CallFlags::Spread
);
11639 // Special case functions are only optimized for normal calls.
11640 if (!BytecodeCallOpCanHaveInlinableNative(op_
)) {
11641 return AttachDecision::NoAction
;
11644 InlinableNativeIRGenerator
nativeGen(*this, callee
, newTarget_
, thisval_
,
11646 return nativeGen
.tryAttachStub();
11649 #ifdef FUZZING_JS_FUZZILLI
11650 AttachDecision
InlinableNativeIRGenerator::tryAttachFuzzilliHash() {
11652 return AttachDecision::NoAction
;
11655 // Initialize the input operand.
11656 initializeInputOperand();
11658 // Guard callee is the 'fuzzilli_hash' native function.
11659 emitNativeCalleeGuard();
11661 ValOperandId argValId
=
11662 writer
.loadArgumentFixedSlot(ArgumentKind::Arg0
, argc_
);
11664 writer
.fuzzilliHashResult(argValId
);
11665 writer
.returnFromIC();
11667 trackAttached("FuzzilliHash");
11668 return AttachDecision::Attach
;
11672 AttachDecision
InlinableNativeIRGenerator::tryAttachStub() {
11673 MOZ_ASSERT(BytecodeCallOpCanHaveInlinableNative(generator_
.op_
));
11675 if (!callee_
->hasJitInfo() ||
11676 callee_
->jitInfo()->type() != JSJitInfo::InlinableNative
) {
11677 return AttachDecision::NoAction
;
11680 InlinableNative native
= callee_
->jitInfo()->inlinableNative
;
11682 // Not all natives can be inlined cross-realm.
11683 if (cx_
->realm() != callee_
->realm() && !CanInlineNativeCrossRealm(native
)) {
11684 return AttachDecision::NoAction
;
11687 // Check for special-cased native constructors.
11688 if (flags_
.isConstructing()) {
11689 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
);
11691 // newTarget must match the callee. CacheIR for this is emitted in
11692 // emitNativeCalleeGuard.
11693 if (ObjectValue(*callee_
) != newTarget_
) {
11694 return AttachDecision::NoAction
;
11697 case InlinableNative::Array
:
11698 return tryAttachArrayConstructor();
11699 case InlinableNative::TypedArrayConstructor
:
11700 return tryAttachTypedArrayConstructor();
11701 case InlinableNative::String
:
11702 return tryAttachStringConstructor();
11703 case InlinableNative::Object
:
11704 return tryAttachObjectConstructor();
11708 return AttachDecision::NoAction
;
11711 // Check for special-cased native spread calls.
11712 if (flags_
.getArgFormat() == CallFlags::Spread
||
11713 flags_
.getArgFormat() == CallFlags::FunApplyArray
) {
11715 case InlinableNative::MathMin
:
11716 return tryAttachSpreadMathMinMax(/*isMax = */ false);
11717 case InlinableNative::MathMax
:
11718 return tryAttachSpreadMathMinMax(/*isMax = */ true);
11722 return AttachDecision::NoAction
;
11725 MOZ_ASSERT(flags_
.getArgFormat() == CallFlags::Standard
||
11726 flags_
.getArgFormat() == CallFlags::FunCall
);
11728 // Check for special-cased native functions.
11731 case InlinableNative::Array
:
11732 return tryAttachArrayConstructor();
11733 case InlinableNative::ArrayPush
:
11734 return tryAttachArrayPush();
11735 case InlinableNative::ArrayPop
:
11736 case InlinableNative::ArrayShift
:
11737 return tryAttachArrayPopShift(native
);
11738 case InlinableNative::ArrayJoin
:
11739 return tryAttachArrayJoin();
11740 case InlinableNative::ArraySlice
:
11741 return tryAttachArraySlice();
11742 case InlinableNative::ArrayIsArray
:
11743 return tryAttachArrayIsArray();
11745 // DataView natives.
11746 case InlinableNative::DataViewGetInt8
:
11747 return tryAttachDataViewGet(Scalar::Int8
);
11748 case InlinableNative::DataViewGetUint8
:
11749 return tryAttachDataViewGet(Scalar::Uint8
);
11750 case InlinableNative::DataViewGetInt16
:
11751 return tryAttachDataViewGet(Scalar::Int16
);
11752 case InlinableNative::DataViewGetUint16
:
11753 return tryAttachDataViewGet(Scalar::Uint16
);
11754 case InlinableNative::DataViewGetInt32
:
11755 return tryAttachDataViewGet(Scalar::Int32
);
11756 case InlinableNative::DataViewGetUint32
:
11757 return tryAttachDataViewGet(Scalar::Uint32
);
11758 case InlinableNative::DataViewGetFloat16
:
11759 return tryAttachDataViewGet(Scalar::Float16
);
11760 case InlinableNative::DataViewGetFloat32
:
11761 return tryAttachDataViewGet(Scalar::Float32
);
11762 case InlinableNative::DataViewGetFloat64
:
11763 return tryAttachDataViewGet(Scalar::Float64
);
11764 case InlinableNative::DataViewGetBigInt64
:
11765 return tryAttachDataViewGet(Scalar::BigInt64
);
11766 case InlinableNative::DataViewGetBigUint64
:
11767 return tryAttachDataViewGet(Scalar::BigUint64
);
11768 case InlinableNative::DataViewSetInt8
:
11769 return tryAttachDataViewSet(Scalar::Int8
);
11770 case InlinableNative::DataViewSetUint8
:
11771 return tryAttachDataViewSet(Scalar::Uint8
);
11772 case InlinableNative::DataViewSetInt16
:
11773 return tryAttachDataViewSet(Scalar::Int16
);
11774 case InlinableNative::DataViewSetUint16
:
11775 return tryAttachDataViewSet(Scalar::Uint16
);
11776 case InlinableNative::DataViewSetInt32
:
11777 return tryAttachDataViewSet(Scalar::Int32
);
11778 case InlinableNative::DataViewSetUint32
:
11779 return tryAttachDataViewSet(Scalar::Uint32
);
11780 case InlinableNative::DataViewSetFloat16
:
11781 return tryAttachDataViewSet(Scalar::Float16
);
11782 case InlinableNative::DataViewSetFloat32
:
11783 return tryAttachDataViewSet(Scalar::Float32
);
11784 case InlinableNative::DataViewSetFloat64
:
11785 return tryAttachDataViewSet(Scalar::Float64
);
11786 case InlinableNative::DataViewSetBigInt64
:
11787 return tryAttachDataViewSet(Scalar::BigInt64
);
11788 case InlinableNative::DataViewSetBigUint64
:
11789 return tryAttachDataViewSet(Scalar::BigUint64
);
11791 // Function natives.
11792 case InlinableNative::FunctionBind
:
11793 return tryAttachFunctionBind();
11796 case InlinableNative::IntlGuardToCollator
:
11797 case InlinableNative::IntlGuardToDateTimeFormat
:
11798 case InlinableNative::IntlGuardToDisplayNames
:
11799 case InlinableNative::IntlGuardToListFormat
:
11800 case InlinableNative::IntlGuardToNumberFormat
:
11801 case InlinableNative::IntlGuardToPluralRules
:
11802 case InlinableNative::IntlGuardToRelativeTimeFormat
:
11803 case InlinableNative::IntlGuardToSegmenter
:
11804 case InlinableNative::IntlGuardToSegments
:
11805 case InlinableNative::IntlGuardToSegmentIterator
:
11806 return tryAttachGuardToClass(native
);
11808 // Slot intrinsics.
11809 case InlinableNative::IntrinsicUnsafeGetReservedSlot
:
11810 case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot
:
11811 case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot
:
11812 case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot
:
11813 return tryAttachUnsafeGetReservedSlot(native
);
11814 case InlinableNative::IntrinsicUnsafeSetReservedSlot
:
11815 return tryAttachUnsafeSetReservedSlot();
11818 case InlinableNative::IntrinsicIsSuspendedGenerator
:
11819 return tryAttachIsSuspendedGenerator();
11820 case InlinableNative::IntrinsicToObject
:
11821 return tryAttachToObject();
11822 case InlinableNative::IntrinsicToInteger
:
11823 return tryAttachToInteger();
11824 case InlinableNative::IntrinsicToLength
:
11825 return tryAttachToLength();
11826 case InlinableNative::IntrinsicIsObject
:
11827 return tryAttachIsObject();
11828 case InlinableNative::IntrinsicIsPackedArray
:
11829 return tryAttachIsPackedArray();
11830 case InlinableNative::IntrinsicIsCallable
:
11831 return tryAttachIsCallable();
11832 case InlinableNative::IntrinsicIsConstructor
:
11833 return tryAttachIsConstructor();
11834 case InlinableNative::IntrinsicIsCrossRealmArrayConstructor
:
11835 return tryAttachIsCrossRealmArrayConstructor();
11836 case InlinableNative::IntrinsicGuardToArrayIterator
:
11837 case InlinableNative::IntrinsicGuardToMapIterator
:
11838 case InlinableNative::IntrinsicGuardToSetIterator
:
11839 case InlinableNative::IntrinsicGuardToStringIterator
:
11840 case InlinableNative::IntrinsicGuardToRegExpStringIterator
:
11841 case InlinableNative::IntrinsicGuardToWrapForValidIterator
:
11842 case InlinableNative::IntrinsicGuardToIteratorHelper
:
11843 case InlinableNative::IntrinsicGuardToAsyncIteratorHelper
:
11844 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
11845 case InlinableNative::IntrinsicGuardToAsyncDisposableStack
:
11847 return tryAttachGuardToClass(native
);
11848 case InlinableNative::IntrinsicSubstringKernel
:
11849 return tryAttachSubstringKernel();
11850 case InlinableNative::IntrinsicIsConstructing
:
11851 return tryAttachIsConstructing();
11852 case InlinableNative::IntrinsicNewArrayIterator
:
11853 return tryAttachNewArrayIterator();
11854 case InlinableNative::IntrinsicNewStringIterator
:
11855 return tryAttachNewStringIterator();
11856 case InlinableNative::IntrinsicNewRegExpStringIterator
:
11857 return tryAttachNewRegExpStringIterator();
11858 case InlinableNative::IntrinsicArrayIteratorPrototypeOptimizable
:
11859 return tryAttachArrayIteratorPrototypeOptimizable();
11860 case InlinableNative::IntrinsicObjectHasPrototype
:
11861 return tryAttachObjectHasPrototype();
11864 case InlinableNative::IsRegExpObject
:
11865 return tryAttachHasClass(&RegExpObject::class_
,
11866 /* isPossiblyWrapped = */ false);
11867 case InlinableNative::IsPossiblyWrappedRegExpObject
:
11868 return tryAttachHasClass(&RegExpObject::class_
,
11869 /* isPossiblyWrapped = */ true);
11870 case InlinableNative::RegExpMatcher
:
11871 case InlinableNative::RegExpSearcher
:
11872 return tryAttachRegExpMatcherSearcher(native
);
11873 case InlinableNative::RegExpSearcherLastLimit
:
11874 return tryAttachRegExpSearcherLastLimit();
11875 case InlinableNative::RegExpHasCaptureGroups
:
11876 return tryAttachRegExpHasCaptureGroups();
11877 case InlinableNative::RegExpPrototypeOptimizable
:
11878 return tryAttachRegExpPrototypeOptimizable();
11879 case InlinableNative::RegExpInstanceOptimizable
:
11880 return tryAttachRegExpInstanceOptimizable();
11881 case InlinableNative::GetFirstDollarIndex
:
11882 return tryAttachGetFirstDollarIndex();
11883 case InlinableNative::IntrinsicRegExpBuiltinExec
:
11884 case InlinableNative::IntrinsicRegExpBuiltinExecForTest
:
11885 return tryAttachIntrinsicRegExpBuiltinExec(native
);
11886 case InlinableNative::IntrinsicRegExpExec
:
11887 case InlinableNative::IntrinsicRegExpExecForTest
:
11888 return tryAttachIntrinsicRegExpExec(native
);
11891 case InlinableNative::String
:
11892 return tryAttachString();
11893 case InlinableNative::StringToString
:
11894 case InlinableNative::StringValueOf
:
11895 return tryAttachStringToStringValueOf();
11896 case InlinableNative::StringCharCodeAt
:
11897 return tryAttachStringCharCodeAt();
11898 case InlinableNative::StringCodePointAt
:
11899 return tryAttachStringCodePointAt();
11900 case InlinableNative::StringCharAt
:
11901 return tryAttachStringCharAt();
11902 case InlinableNative::StringAt
:
11903 return tryAttachStringAt();
11904 case InlinableNative::StringFromCharCode
:
11905 return tryAttachStringFromCharCode();
11906 case InlinableNative::StringFromCodePoint
:
11907 return tryAttachStringFromCodePoint();
11908 case InlinableNative::StringIncludes
:
11909 return tryAttachStringIncludes();
11910 case InlinableNative::StringIndexOf
:
11911 return tryAttachStringIndexOf();
11912 case InlinableNative::StringLastIndexOf
:
11913 return tryAttachStringLastIndexOf();
11914 case InlinableNative::StringStartsWith
:
11915 return tryAttachStringStartsWith();
11916 case InlinableNative::StringEndsWith
:
11917 return tryAttachStringEndsWith();
11918 case InlinableNative::StringToLowerCase
:
11919 return tryAttachStringToLowerCase();
11920 case InlinableNative::StringToUpperCase
:
11921 return tryAttachStringToUpperCase();
11922 case InlinableNative::StringTrim
:
11923 return tryAttachStringTrim();
11924 case InlinableNative::StringTrimStart
:
11925 return tryAttachStringTrimStart();
11926 case InlinableNative::StringTrimEnd
:
11927 return tryAttachStringTrimEnd();
11928 case InlinableNative::IntrinsicStringReplaceString
:
11929 return tryAttachStringReplaceString();
11930 case InlinableNative::IntrinsicStringSplitString
:
11931 return tryAttachStringSplitString();
11934 case InlinableNative::MathRandom
:
11935 return tryAttachMathRandom();
11936 case InlinableNative::MathAbs
:
11937 return tryAttachMathAbs();
11938 case InlinableNative::MathClz32
:
11939 return tryAttachMathClz32();
11940 case InlinableNative::MathSign
:
11941 return tryAttachMathSign();
11942 case InlinableNative::MathImul
:
11943 return tryAttachMathImul();
11944 case InlinableNative::MathFloor
:
11945 return tryAttachMathFloor();
11946 case InlinableNative::MathCeil
:
11947 return tryAttachMathCeil();
11948 case InlinableNative::MathTrunc
:
11949 return tryAttachMathTrunc();
11950 case InlinableNative::MathRound
:
11951 return tryAttachMathRound();
11952 case InlinableNative::MathSqrt
:
11953 return tryAttachMathSqrt();
11954 case InlinableNative::MathFRound
:
11955 return tryAttachMathFRound();
11956 case InlinableNative::MathF16Round
:
11957 return tryAttachMathF16Round();
11958 case InlinableNative::MathHypot
:
11959 return tryAttachMathHypot();
11960 case InlinableNative::MathATan2
:
11961 return tryAttachMathATan2();
11962 case InlinableNative::MathSin
:
11963 return tryAttachMathFunction(UnaryMathFunction::SinNative
);
11964 case InlinableNative::MathTan
:
11965 return tryAttachMathFunction(UnaryMathFunction::TanNative
);
11966 case InlinableNative::MathCos
:
11967 return tryAttachMathFunction(UnaryMathFunction::CosNative
);
11968 case InlinableNative::MathExp
:
11969 return tryAttachMathFunction(UnaryMathFunction::Exp
);
11970 case InlinableNative::MathLog
:
11971 return tryAttachMathFunction(UnaryMathFunction::Log
);
11972 case InlinableNative::MathASin
:
11973 return tryAttachMathFunction(UnaryMathFunction::ASin
);
11974 case InlinableNative::MathATan
:
11975 return tryAttachMathFunction(UnaryMathFunction::ATan
);
11976 case InlinableNative::MathACos
:
11977 return tryAttachMathFunction(UnaryMathFunction::ACos
);
11978 case InlinableNative::MathLog10
:
11979 return tryAttachMathFunction(UnaryMathFunction::Log10
);
11980 case InlinableNative::MathLog2
:
11981 return tryAttachMathFunction(UnaryMathFunction::Log2
);
11982 case InlinableNative::MathLog1P
:
11983 return tryAttachMathFunction(UnaryMathFunction::Log1P
);
11984 case InlinableNative::MathExpM1
:
11985 return tryAttachMathFunction(UnaryMathFunction::ExpM1
);
11986 case InlinableNative::MathCosH
:
11987 return tryAttachMathFunction(UnaryMathFunction::CosH
);
11988 case InlinableNative::MathSinH
:
11989 return tryAttachMathFunction(UnaryMathFunction::SinH
);
11990 case InlinableNative::MathTanH
:
11991 return tryAttachMathFunction(UnaryMathFunction::TanH
);
11992 case InlinableNative::MathACosH
:
11993 return tryAttachMathFunction(UnaryMathFunction::ACosH
);
11994 case InlinableNative::MathASinH
:
11995 return tryAttachMathFunction(UnaryMathFunction::ASinH
);
11996 case InlinableNative::MathATanH
:
11997 return tryAttachMathFunction(UnaryMathFunction::ATanH
);
11998 case InlinableNative::MathCbrt
:
11999 return tryAttachMathFunction(UnaryMathFunction::Cbrt
);
12000 case InlinableNative::MathPow
:
12001 return tryAttachMathPow();
12002 case InlinableNative::MathMin
:
12003 return tryAttachMathMinMax(/* isMax = */ false);
12004 case InlinableNative::MathMax
:
12005 return tryAttachMathMinMax(/* isMax = */ true);
12008 case InlinableNative::IntrinsicGuardToMapObject
:
12009 return tryAttachGuardToClass(GuardClassKind::Map
);
12010 case InlinableNative::IntrinsicGetNextMapEntryForIterator
:
12011 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ true);
12014 case InlinableNative::Number
:
12015 return tryAttachNumber();
12016 case InlinableNative::NumberParseInt
:
12017 return tryAttachNumberParseInt();
12018 case InlinableNative::NumberToString
:
12019 return tryAttachNumberToString();
12022 case InlinableNative::Object
:
12023 return tryAttachObjectConstructor();
12024 case InlinableNative::ObjectCreate
:
12025 return tryAttachObjectCreate();
12026 case InlinableNative::ObjectIs
:
12027 return tryAttachObjectIs();
12028 case InlinableNative::ObjectIsPrototypeOf
:
12029 return tryAttachObjectIsPrototypeOf();
12030 case InlinableNative::ObjectKeys
:
12031 return tryAttachObjectKeys();
12032 case InlinableNative::ObjectToString
:
12033 return tryAttachObjectToString();
12036 case InlinableNative::IntrinsicGuardToSetObject
:
12037 return tryAttachGuardToClass(GuardClassKind::Set
);
12038 case InlinableNative::IntrinsicGetNextSetEntryForIterator
:
12039 return tryAttachGetNextMapSetEntryForIterator(/* isMap = */ false);
12041 // ArrayBuffer intrinsics.
12042 case InlinableNative::IntrinsicGuardToArrayBuffer
:
12043 return tryAttachGuardToArrayBuffer();
12044 case InlinableNative::IntrinsicArrayBufferByteLength
:
12045 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false);
12046 case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength
:
12047 return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true);
12049 // SharedArrayBuffer intrinsics.
12050 case InlinableNative::IntrinsicGuardToSharedArrayBuffer
:
12051 return tryAttachGuardToSharedArrayBuffer();
12053 // TypedArray intrinsics.
12054 case InlinableNative::TypedArrayConstructor
:
12055 return AttachDecision::NoAction
; // Not callable.
12056 case InlinableNative::IntrinsicIsTypedArray
:
12057 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ false);
12058 case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray
:
12059 return tryAttachIsTypedArray(/* isPossiblyWrapped = */ true);
12060 case InlinableNative::IntrinsicIsTypedArrayConstructor
:
12061 return tryAttachIsTypedArrayConstructor();
12062 case InlinableNative::IntrinsicTypedArrayByteOffset
:
12063 return tryAttachTypedArrayByteOffset();
12064 case InlinableNative::IntrinsicTypedArrayElementSize
:
12065 return tryAttachTypedArrayElementSize();
12066 case InlinableNative::IntrinsicTypedArrayLength
:
12067 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false,
12068 /* allowOutOfBounds = */ false);
12069 case InlinableNative::IntrinsicTypedArrayLengthZeroOnOutOfBounds
:
12070 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ false,
12071 /* allowOutOfBounds = */ true);
12072 case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength
:
12073 return tryAttachTypedArrayLength(/* isPossiblyWrapped = */ true,
12074 /* allowOutOfBounds = */ false);
12076 // Reflect natives.
12077 case InlinableNative::ReflectGetPrototypeOf
:
12078 return tryAttachReflectGetPrototypeOf();
12080 // Atomics intrinsics:
12081 case InlinableNative::AtomicsCompareExchange
:
12082 return tryAttachAtomicsCompareExchange();
12083 case InlinableNative::AtomicsExchange
:
12084 return tryAttachAtomicsExchange();
12085 case InlinableNative::AtomicsAdd
:
12086 return tryAttachAtomicsAdd();
12087 case InlinableNative::AtomicsSub
:
12088 return tryAttachAtomicsSub();
12089 case InlinableNative::AtomicsAnd
:
12090 return tryAttachAtomicsAnd();
12091 case InlinableNative::AtomicsOr
:
12092 return tryAttachAtomicsOr();
12093 case InlinableNative::AtomicsXor
:
12094 return tryAttachAtomicsXor();
12095 case InlinableNative::AtomicsLoad
:
12096 return tryAttachAtomicsLoad();
12097 case InlinableNative::AtomicsStore
:
12098 return tryAttachAtomicsStore();
12099 case InlinableNative::AtomicsIsLockFree
:
12100 return tryAttachAtomicsIsLockFree();
12103 case InlinableNative::BigInt
:
12104 return tryAttachBigInt();
12105 case InlinableNative::BigIntAsIntN
:
12106 return tryAttachBigIntAsIntN();
12107 case InlinableNative::BigIntAsUintN
:
12108 return tryAttachBigIntAsUintN();
12110 // Boolean natives.
12111 case InlinableNative::Boolean
:
12112 return tryAttachBoolean();
12115 case InlinableNative::SetHas
:
12116 return tryAttachSetHas();
12117 case InlinableNative::SetSize
:
12118 return tryAttachSetSize();
12121 case InlinableNative::MapHas
:
12122 return tryAttachMapHas();
12123 case InlinableNative::MapGet
:
12124 return tryAttachMapGet();
12126 // Testing functions.
12127 case InlinableNative::TestBailout
:
12128 if (js::SupportDifferentialTesting()) {
12129 return AttachDecision::NoAction
;
12131 return tryAttachBailout();
12132 case InlinableNative::TestAssertFloat32
:
12133 return tryAttachAssertFloat32();
12134 case InlinableNative::TestAssertRecoveredOnBailout
:
12135 if (js::SupportDifferentialTesting()) {
12136 return AttachDecision::NoAction
;
12138 return tryAttachAssertRecoveredOnBailout();
12140 #ifdef FUZZING_JS_FUZZILLI
12141 // Fuzzilli function
12142 case InlinableNative::FuzzilliHash
:
12143 return tryAttachFuzzilliHash();
12146 case InlinableNative::Limit
:
12150 MOZ_CRASH("Shouldn't get here");
12153 // Remember the shape of the this object for any script being called as a
12154 // constructor, for later use during Ion compilation.
12155 ScriptedThisResult
CallIRGenerator::getThisShapeForScripted(
12156 HandleFunction calleeFunc
, Handle
<JSObject
*> newTarget
,
12157 MutableHandle
<Shape
*> result
) {
12158 // Some constructors allocate their own |this| object.
12159 if (calleeFunc
->constructorNeedsUninitializedThis()) {
12160 return ScriptedThisResult::UninitializedThis
;
12163 // Only attach a stub if the newTarget is a function with a
12164 // nonconfigurable prototype.
12165 if (!newTarget
->is
<JSFunction
>() ||
12166 !newTarget
->as
<JSFunction
>().hasNonConfigurablePrototypeDataProperty()) {
12167 return ScriptedThisResult::NoAction
;
12170 AutoRealm
ar(cx_
, calleeFunc
);
12171 Shape
* thisShape
= ThisShapeForFunction(cx_
, calleeFunc
, newTarget
);
12173 cx_
->clearPendingException();
12174 return ScriptedThisResult::NoAction
;
12177 MOZ_ASSERT(thisShape
->realm() == calleeFunc
->realm());
12178 result
.set(thisShape
);
12179 return ScriptedThisResult::PlainObjectShape
;
12182 static bool CanOptimizeScriptedCall(JSFunction
* callee
, bool isConstructing
) {
12183 if (!callee
->hasJitEntry()) {
12187 // If callee is not an interpreted constructor, we have to throw.
12188 if (isConstructing
&& !callee
->isConstructor()) {
12192 // Likewise, if the callee is a class constructor, we have to throw.
12193 if (!isConstructing
&& callee
->isClassConstructor()) {
12200 void CallIRGenerator::emitCallScriptedGuards(ObjOperandId calleeObjId
,
12201 JSFunction
* calleeFunc
,
12202 Int32OperandId argcId
,
12203 CallFlags flags
, Shape
* thisShape
,
12204 bool isBoundFunction
) {
12205 bool isConstructing
= flags
.isConstructing();
12207 if (mode_
== ICState::Mode::Specialized
) {
12208 MOZ_ASSERT_IF(isConstructing
, thisShape
|| flags
.needsUninitializedThis());
12210 // Ensure callee matches this stub's callee
12211 emitCalleeGuard(calleeObjId
, calleeFunc
);
12213 // Emit guards to ensure the newTarget's .prototype property is what we
12214 // expect. Note that getThisForScripted checked newTarget is a function
12215 // with a non-configurable .prototype data property.
12217 JSFunction
* newTarget
;
12218 ObjOperandId newTargetObjId
;
12219 if (isBoundFunction
) {
12220 newTarget
= calleeFunc
;
12221 newTargetObjId
= calleeObjId
;
12223 newTarget
= &newTarget_
.toObject().as
<JSFunction
>();
12224 ValOperandId newTargetValId
= writer
.loadArgumentDynamicSlot(
12225 ArgumentKind::NewTarget
, argcId
, flags
);
12226 newTargetObjId
= writer
.guardToObject(newTargetValId
);
12229 Maybe
<PropertyInfo
> prop
= newTarget
->lookupPure(cx_
->names().prototype
);
12230 MOZ_ASSERT(prop
.isSome());
12231 uint32_t slot
= prop
->slot();
12232 MOZ_ASSERT(slot
>= newTarget
->numFixedSlots(),
12233 "Stub code relies on this");
12235 writer
.guardShape(newTargetObjId
, newTarget
->shape());
12237 const Value
& value
= newTarget
->getSlot(slot
);
12238 if (value
.isObject()) {
12239 JSObject
* prototypeObject
= &value
.toObject();
12241 ObjOperandId protoId
= writer
.loadObject(prototypeObject
);
12242 writer
.guardDynamicSlotIsSpecificObject(
12243 newTargetObjId
, protoId
, slot
- newTarget
->numFixedSlots());
12245 writer
.guardDynamicSlotIsNotObject(newTargetObjId
,
12246 slot
- newTarget
->numFixedSlots());
12249 // Call metaScriptedThisShape before emitting the call, so that Warp can
12250 // use the shape to create the |this| object before transpiling the call.
12251 writer
.metaScriptedThisShape(thisShape
);
12254 // Guard that object is a scripted function
12255 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
12256 writer
.guardFunctionHasJitEntry(calleeObjId
);
12258 if (isConstructing
) {
12259 // If callee is not a constructor, we have to throw.
12260 writer
.guardFunctionIsConstructor(calleeObjId
);
12262 // If callee is a class constructor, we have to throw.
12263 writer
.guardNotClassConstructor(calleeObjId
);
12268 AttachDecision
CallIRGenerator::tryAttachCallScripted(
12269 HandleFunction calleeFunc
) {
12270 MOZ_ASSERT(calleeFunc
->hasJitEntry());
12272 if (calleeFunc
->isWasmWithJitEntry()) {
12273 TRY_ATTACH(tryAttachWasmCall(calleeFunc
));
12276 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
12278 bool isConstructing
= IsConstructPC(pc_
);
12279 bool isSpread
= IsSpreadPC(pc_
);
12280 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
12281 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
12283 if (!CanOptimizeScriptedCall(calleeFunc
, isConstructing
)) {
12284 return AttachDecision::NoAction
;
12287 if (isConstructing
&& !calleeFunc
->hasJitScript()) {
12288 // If we're constructing, require the callee to have a JitScript. This isn't
12289 // required for correctness but avoids allocating a template object below
12290 // for constructors that aren't hot. See bug 1419758.
12291 return AttachDecision::TemporarilyUnoptimizable
;
12294 // Verify that spread calls have a reasonable number of arguments.
12295 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
12296 return AttachDecision::NoAction
;
12299 Rooted
<Shape
*> thisShape(cx_
);
12300 if (isConstructing
&& isSpecialized
) {
12301 Rooted
<JSObject
*> newTarget(cx_
, &newTarget_
.toObject());
12302 switch (getThisShapeForScripted(calleeFunc
, newTarget
, &thisShape
)) {
12303 case ScriptedThisResult::PlainObjectShape
:
12305 case ScriptedThisResult::UninitializedThis
:
12306 flags
.setNeedsUninitializedThis();
12308 case ScriptedThisResult::NoAction
:
12309 return AttachDecision::NoAction
;
12314 Int32OperandId
argcId(writer
.setInputOperandId(0));
12316 // Load the callee and ensure it is an object
12317 ValOperandId calleeValId
=
12318 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12319 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12321 emitCallScriptedGuards(calleeObjId
, calleeFunc
, argcId
, flags
, thisShape
,
12322 /* isBoundFunction = */ false);
12324 writer
.callScriptedFunction(calleeObjId
, argcId
, flags
,
12325 ClampFixedArgc(argc_
));
12326 writer
.returnFromIC();
12328 if (isSpecialized
) {
12329 trackAttached("Call.CallScripted");
12331 trackAttached("Call.CallAnyScripted");
12334 return AttachDecision::Attach
;
12337 AttachDecision
CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc
) {
12338 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12340 bool isSpecialized
= mode_
== ICState::Mode::Specialized
;
12342 bool isSpread
= IsSpreadPC(pc_
);
12343 bool isSameRealm
= isSpecialized
&& cx_
->realm() == calleeFunc
->realm();
12344 bool isConstructing
= IsConstructPC(pc_
);
12345 CallFlags
flags(isConstructing
, isSpread
, isSameRealm
);
12347 if (isConstructing
&& !calleeFunc
->isConstructor()) {
12348 return AttachDecision::NoAction
;
12351 // Verify that spread calls have a reasonable number of arguments.
12352 if (isSpread
&& args_
.length() > JIT_ARGS_LENGTH_MAX
) {
12353 return AttachDecision::NoAction
;
12356 // Check for specific native-function optimizations.
12357 if (isSpecialized
) {
12358 TRY_ATTACH(tryAttachInlinableNative(calleeFunc
, flags
));
12362 Int32OperandId
argcId(writer
.setInputOperandId(0));
12364 // Load the callee and ensure it is an object
12365 ValOperandId calleeValId
=
12366 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12367 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12369 // DOM calls need an additional guard so only try optimizing the first stub.
12370 // Can only optimize normal (non-spread) calls.
12371 if (isFirstStub_
&& !isSpread
&& thisval_
.isObject() &&
12372 CanAttachDOMCall(cx_
, JSJitInfo::Method
, &thisval_
.toObject(), calleeFunc
,
12374 MOZ_ASSERT(!isConstructing
, "DOM functions are not constructors");
12376 // Guard that |this| is an object.
12377 ValOperandId thisValId
=
12378 writer
.loadArgumentDynamicSlot(ArgumentKind::This
, argcId
, flags
);
12379 ObjOperandId thisObjId
= writer
.guardToObject(thisValId
);
12381 // Guard on the |this| shape to make sure it's the right instance. This also
12382 // ensures DOM_OBJECT_SLOT is stored in a fixed slot. See CanAttachDOMCall.
12383 writer
.guardShape(thisObjId
, thisval_
.toObject().shape());
12385 // Ensure callee matches this stub's callee
12386 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
12387 writer
.callDOMFunction(calleeObjId
, argcId
, thisObjId
, calleeFunc
, flags
,
12388 ClampFixedArgc(argc_
));
12390 trackAttached("Call.CallDOM");
12391 } else if (isSpecialized
) {
12392 // Ensure callee matches this stub's callee
12393 writer
.guardSpecificFunction(calleeObjId
, calleeFunc
);
12394 writer
.callNativeFunction(calleeObjId
, argcId
, op_
, calleeFunc
, flags
,
12395 ClampFixedArgc(argc_
));
12397 trackAttached("Call.CallNative");
12399 // Guard that object is a native function
12400 writer
.guardClass(calleeObjId
, GuardClassKind::JSFunction
);
12401 writer
.guardFunctionHasNoJitEntry(calleeObjId
);
12403 if (isConstructing
) {
12404 // If callee is not a constructor, we have to throw.
12405 writer
.guardFunctionIsConstructor(calleeObjId
);
12407 // If callee is a class constructor, we have to throw.
12408 writer
.guardNotClassConstructor(calleeObjId
);
12410 writer
.callAnyNativeFunction(calleeObjId
, argcId
, flags
,
12411 ClampFixedArgc(argc_
));
12413 trackAttached("Call.CallAnyNative");
12416 writer
.returnFromIC();
12418 return AttachDecision::Attach
;
12421 AttachDecision
CallIRGenerator::tryAttachCallHook(HandleObject calleeObj
) {
12422 if (mode_
!= ICState::Mode::Specialized
) {
12423 // We do not have megamorphic call hook stubs.
12424 // TODO: Should we attach specialized call hook stubs in
12425 // megamorphic mode to avoid going generic?
12426 return AttachDecision::NoAction
;
12429 bool isSpread
= IsSpreadPC(pc_
);
12430 bool isConstructing
= IsConstructPC(pc_
);
12431 CallFlags
flags(isConstructing
, isSpread
);
12433 isConstructing
? calleeObj
->constructHook() : calleeObj
->callHook();
12435 return AttachDecision::NoAction
;
12438 // Bound functions have a JSClass construct hook but are not always
12440 if (isConstructing
&& !calleeObj
->isConstructor()) {
12441 return AttachDecision::NoAction
;
12444 // We don't support spread calls in the transpiler yet.
12446 return AttachDecision::NoAction
;
12450 Int32OperandId
argcId(writer
.setInputOperandId(0));
12452 // Load the callee and ensure it is an object
12453 ValOperandId calleeValId
=
12454 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12455 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12457 // Ensure the callee's class matches the one in this stub.
12458 writer
.guardAnyClass(calleeObjId
, calleeObj
->getClass());
12460 if (isConstructing
&& calleeObj
->is
<BoundFunctionObject
>()) {
12461 writer
.guardBoundFunctionIsConstructor(calleeObjId
);
12464 writer
.callClassHook(calleeObjId
, argcId
, hook
, flags
, ClampFixedArgc(argc_
));
12465 writer
.returnFromIC();
12467 trackAttached("Call.CallHook");
12469 return AttachDecision::Attach
;
12472 AttachDecision
CallIRGenerator::tryAttachBoundFunction(
12473 Handle
<BoundFunctionObject
*> calleeObj
) {
12474 // The target must be a JSFunction with a JitEntry.
12475 if (!calleeObj
->getTarget()->is
<JSFunction
>()) {
12476 return AttachDecision::NoAction
;
12479 bool isSpread
= IsSpreadPC(pc_
);
12480 bool isConstructing
= IsConstructPC(pc_
);
12482 // Spread calls are not supported yet.
12484 return AttachDecision::NoAction
;
12487 Rooted
<JSFunction
*> target(cx_
, &calleeObj
->getTarget()->as
<JSFunction
>());
12488 if (!CanOptimizeScriptedCall(target
, isConstructing
)) {
12489 return AttachDecision::NoAction
;
12492 // Limit the number of bound arguments to prevent us from compiling many
12493 // different stubs (we bake in numBoundArgs and it's usually very small).
12494 static constexpr size_t MaxBoundArgs
= 10;
12495 size_t numBoundArgs
= calleeObj
->numBoundArgs();
12496 if (numBoundArgs
> MaxBoundArgs
) {
12497 return AttachDecision::NoAction
;
12500 // Ensure we don't exceed JIT_ARGS_LENGTH_MAX.
12501 if (numBoundArgs
+ argc_
> JIT_ARGS_LENGTH_MAX
) {
12502 return AttachDecision::NoAction
;
12505 CallFlags
flags(isConstructing
, isSpread
);
12507 if (mode_
== ICState::Mode::Specialized
) {
12508 if (cx_
->realm() == target
->realm()) {
12509 flags
.setIsSameRealm();
12513 Rooted
<Shape
*> thisShape(cx_
);
12514 if (isConstructing
) {
12515 // Only optimize if newTarget == callee. This is the common case and ensures
12516 // we can always pass the bound function's target as newTarget.
12517 if (newTarget_
!= ObjectValue(*calleeObj
)) {
12518 return AttachDecision::NoAction
;
12521 if (mode_
== ICState::Mode::Specialized
) {
12522 Handle
<JSFunction
*> newTarget
= target
;
12523 switch (getThisShapeForScripted(target
, newTarget
, &thisShape
)) {
12524 case ScriptedThisResult::PlainObjectShape
:
12526 case ScriptedThisResult::UninitializedThis
:
12527 flags
.setNeedsUninitializedThis();
12529 case ScriptedThisResult::NoAction
:
12530 return AttachDecision::NoAction
;
12536 Int32OperandId
argcId(writer
.setInputOperandId(0));
12538 // Load the callee and ensure it's a bound function.
12539 ValOperandId calleeValId
=
12540 writer
.loadArgumentDynamicSlot(ArgumentKind::Callee
, argcId
, flags
);
12541 ObjOperandId calleeObjId
= writer
.guardToObject(calleeValId
);
12542 writer
.guardClass(calleeObjId
, GuardClassKind::BoundFunction
);
12544 // Ensure numBoundArgs matches.
12545 Int32OperandId numBoundArgsId
= writer
.loadBoundFunctionNumArgs(calleeObjId
);
12546 writer
.guardSpecificInt32(numBoundArgsId
, numBoundArgs
);
12548 if (isConstructing
) {
12549 // Guard newTarget == callee. We depend on this in CallBoundScriptedFunction
12550 // and in emitCallScriptedGuards by using boundTarget as newTarget.
12551 ValOperandId newTargetValId
=
12552 writer
.loadArgumentDynamicSlot(ArgumentKind::NewTarget
, argcId
, flags
);
12553 ObjOperandId newTargetObjId
= writer
.guardToObject(newTargetValId
);
12554 writer
.guardObjectIdentity(newTargetObjId
, calleeObjId
);
12557 ObjOperandId targetId
= writer
.loadBoundFunctionTarget(calleeObjId
);
12559 emitCallScriptedGuards(targetId
, target
, argcId
, flags
, thisShape
,
12560 /* isBoundFunction = */ true);
12562 writer
.callBoundScriptedFunction(calleeObjId
, targetId
, argcId
, flags
,
12564 writer
.returnFromIC();
12566 trackAttached("Call.BoundFunction");
12567 return AttachDecision::Attach
;
12570 AttachDecision
CallIRGenerator::tryAttachStub() {
12571 AutoAssertNoPendingException
aanpe(cx_
);
12573 // Some opcodes are not yet supported.
12576 case JSOp::CallContent
:
12577 case JSOp::CallIgnoresRv
:
12578 case JSOp::CallIter
:
12579 case JSOp::CallContentIter
:
12580 case JSOp::SpreadCall
:
12582 case JSOp::NewContent
:
12583 case JSOp::SpreadNew
:
12584 case JSOp::SuperCall
:
12585 case JSOp::SpreadSuperCall
:
12588 return AttachDecision::NoAction
;
12591 MOZ_ASSERT(mode_
!= ICState::Mode::Generic
);
12593 // Ensure callee is a function.
12594 if (!callee_
.isObject()) {
12595 return AttachDecision::NoAction
;
12598 RootedObject
calleeObj(cx_
, &callee_
.toObject());
12599 if (calleeObj
->is
<BoundFunctionObject
>()) {
12600 TRY_ATTACH(tryAttachBoundFunction(calleeObj
.as
<BoundFunctionObject
>()));
12602 if (!calleeObj
->is
<JSFunction
>()) {
12603 return tryAttachCallHook(calleeObj
);
12606 HandleFunction calleeFunc
= calleeObj
.as
<JSFunction
>();
12608 // Check for scripted optimizations.
12609 if (calleeFunc
->hasJitEntry()) {
12610 return tryAttachCallScripted(calleeFunc
);
12613 // Check for native-function optimizations.
12614 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
12616 // Try inlining Function.prototype.{call,apply}. We don't use the
12617 // InlinableNative mechanism for this because we want to optimize these more
12618 // aggressively than other natives.
12619 if (op_
== JSOp::Call
|| op_
== JSOp::CallContent
||
12620 op_
== JSOp::CallIgnoresRv
) {
12621 TRY_ATTACH(tryAttachFunCall(calleeFunc
));
12622 TRY_ATTACH(tryAttachFunApply(calleeFunc
));
12625 return tryAttachCallNative(calleeFunc
);
12628 void CallIRGenerator::trackAttached(const char* name
) {
12629 stubName_
= name
? name
: "NotAttached";
12630 #ifdef JS_CACHEIR_SPEW
12631 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
12632 sp
.valueProperty("callee", callee_
);
12633 sp
.valueProperty("thisval", thisval_
);
12634 sp
.valueProperty("argc", Int32Value(argc_
));
12636 // Try to log the first two arguments.
12637 if (args_
.length() >= 1) {
12638 sp
.valueProperty("arg0", args_
[0]);
12640 if (args_
.length() >= 2) {
12641 sp
.valueProperty("arg1", args_
[1]);
12647 // Class which holds a shape pointer for use when caches might reference data in
12649 static const JSClass shapeContainerClass
= {"ShapeContainer",
12650 JSCLASS_HAS_RESERVED_SLOTS(1)};
12652 static const size_t SHAPE_CONTAINER_SLOT
= 0;
12654 static JSObject
* NewWrapperWithObjectShape(JSContext
* cx
,
12655 Handle
<NativeObject
*> obj
) {
12656 MOZ_ASSERT(cx
->compartment() != obj
->compartment());
12658 RootedObject
wrapper(cx
);
12660 AutoRealm
ar(cx
, obj
);
12661 wrapper
= NewBuiltinClassInstance(cx
, &shapeContainerClass
);
12665 wrapper
->as
<NativeObject
>().setReservedSlot(
12666 SHAPE_CONTAINER_SLOT
, PrivateGCThingValue(obj
->shape()));
12668 if (!JS_WrapObject(cx
, &wrapper
)) {
12671 MOZ_ASSERT(IsWrapper(wrapper
));
12675 void jit::LoadShapeWrapperContents(MacroAssembler
& masm
, Register obj
,
12676 Register dst
, Label
* failure
) {
12677 masm
.loadPtr(Address(obj
, ProxyObject::offsetOfReservedSlots()), dst
);
12678 Address
privateAddr(dst
,
12679 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
12680 masm
.fallibleUnboxObject(privateAddr
, dst
, failure
);
12681 masm
.unboxNonDouble(
12682 Address(dst
, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT
)), dst
,
12683 JSVAL_TYPE_PRIVATE_GCTHING
);
12686 static bool CanConvertToInt32ForToNumber(const Value
& v
) {
12687 return v
.isInt32() || v
.isBoolean() || v
.isNull();
12690 static Int32OperandId
EmitGuardToInt32ForToNumber(CacheIRWriter
& writer
,
12694 return writer
.guardToInt32(id
);
12697 writer
.guardIsNull(id
);
12698 return writer
.loadInt32Constant(0);
12700 MOZ_ASSERT(v
.isBoolean());
12701 return writer
.guardBooleanToInt32(id
);
12704 static bool CanConvertToDoubleForToNumber(const Value
& v
) {
12705 return v
.isNumber() || v
.isBoolean() || v
.isNullOrUndefined();
12708 static NumberOperandId
EmitGuardToDoubleForToNumber(CacheIRWriter
& writer
,
12711 if (v
.isNumber()) {
12712 return writer
.guardIsNumber(id
);
12714 if (v
.isBoolean()) {
12715 BooleanOperandId boolId
= writer
.guardToBoolean(id
);
12716 return writer
.booleanToNumber(boolId
);
12719 writer
.guardIsNull(id
);
12720 return writer
.loadDoubleConstant(0.0);
12722 MOZ_ASSERT(v
.isUndefined());
12723 writer
.guardIsUndefined(id
);
12724 return writer
.loadDoubleConstant(JS::GenericNaN());
12727 CompareIRGenerator::CompareIRGenerator(JSContext
* cx
, HandleScript script
,
12728 jsbytecode
* pc
, ICState state
, JSOp op
,
12729 HandleValue lhsVal
, HandleValue rhsVal
)
12730 : IRGenerator(cx
, script
, pc
, CacheKind::Compare
, state
),
12735 AttachDecision
CompareIRGenerator::tryAttachString(ValOperandId lhsId
,
12736 ValOperandId rhsId
) {
12737 if (!lhsVal_
.isString() || !rhsVal_
.isString()) {
12738 return AttachDecision::NoAction
;
12741 StringOperandId lhsStrId
= writer
.guardToString(lhsId
);
12742 StringOperandId rhsStrId
= writer
.guardToString(rhsId
);
12743 writer
.compareStringResult(op_
, lhsStrId
, rhsStrId
);
12744 writer
.returnFromIC();
12746 trackAttached("Compare.String");
12747 return AttachDecision::Attach
;
12750 AttachDecision
CompareIRGenerator::tryAttachObject(ValOperandId lhsId
,
12751 ValOperandId rhsId
) {
12752 MOZ_ASSERT(IsEqualityOp(op_
));
12754 if (!lhsVal_
.isObject() || !rhsVal_
.isObject()) {
12755 return AttachDecision::NoAction
;
12758 ObjOperandId lhsObjId
= writer
.guardToObject(lhsId
);
12759 ObjOperandId rhsObjId
= writer
.guardToObject(rhsId
);
12760 writer
.compareObjectResult(op_
, lhsObjId
, rhsObjId
);
12761 writer
.returnFromIC();
12763 trackAttached("Compare.Object");
12764 return AttachDecision::Attach
;
12767 AttachDecision
CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId
,
12768 ValOperandId rhsId
) {
12769 MOZ_ASSERT(IsEqualityOp(op_
));
12771 if (!lhsVal_
.isSymbol() || !rhsVal_
.isSymbol()) {
12772 return AttachDecision::NoAction
;
12775 SymbolOperandId lhsSymId
= writer
.guardToSymbol(lhsId
);
12776 SymbolOperandId rhsSymId
= writer
.guardToSymbol(rhsId
);
12777 writer
.compareSymbolResult(op_
, lhsSymId
, rhsSymId
);
12778 writer
.returnFromIC();
12780 trackAttached("Compare.Symbol");
12781 return AttachDecision::Attach
;
12784 AttachDecision
CompareIRGenerator::tryAttachStrictDifferentTypes(
12785 ValOperandId lhsId
, ValOperandId rhsId
) {
12786 MOZ_ASSERT(IsEqualityOp(op_
));
12788 if (op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
) {
12789 return AttachDecision::NoAction
;
12792 // Probably can't hit some of these.
12793 if (SameType(lhsVal_
, rhsVal_
) ||
12794 (lhsVal_
.isNumber() && rhsVal_
.isNumber())) {
12795 return AttachDecision::NoAction
;
12799 ValueTagOperandId lhsTypeId
= writer
.loadValueTag(lhsId
);
12800 ValueTagOperandId rhsTypeId
= writer
.loadValueTag(rhsId
);
12801 writer
.guardTagNotEqual(lhsTypeId
, rhsTypeId
);
12803 // Now that we've passed the guard, we know differing types, so return the
12805 writer
.loadBooleanResult(op_
== JSOp::StrictNe
? true : false);
12806 writer
.returnFromIC();
12808 trackAttached("Compare.StrictDifferentTypes");
12809 return AttachDecision::Attach
;
12812 AttachDecision
CompareIRGenerator::tryAttachInt32(ValOperandId lhsId
,
12813 ValOperandId rhsId
) {
12814 if (!CanConvertToInt32ForToNumber(lhsVal_
) ||
12815 !CanConvertToInt32ForToNumber(rhsVal_
)) {
12816 return AttachDecision::NoAction
;
12819 // Strictly different types should have been handed by
12820 // tryAttachStrictDifferentTypes.
12821 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12822 lhsVal_
.type() == rhsVal_
.type());
12824 // Should have been handled by tryAttachAnyNullUndefined.
12825 MOZ_ASSERT_IF(lhsVal_
.isNull() || rhsVal_
.isNull(), !IsEqualityOp(op_
));
12827 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
12828 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
12830 writer
.compareInt32Result(op_
, lhsIntId
, rhsIntId
);
12831 writer
.returnFromIC();
12833 trackAttached("Compare.Int32");
12834 return AttachDecision::Attach
;
12837 AttachDecision
CompareIRGenerator::tryAttachNumber(ValOperandId lhsId
,
12838 ValOperandId rhsId
) {
12839 if (!CanConvertToDoubleForToNumber(lhsVal_
) ||
12840 !CanConvertToDoubleForToNumber(rhsVal_
)) {
12841 return AttachDecision::NoAction
;
12844 // Strictly different types should have been handed by
12845 // tryAttachStrictDifferentTypes.
12846 MOZ_ASSERT_IF(op_
== JSOp::StrictEq
|| op_
== JSOp::StrictNe
,
12847 lhsVal_
.type() == rhsVal_
.type() ||
12848 (lhsVal_
.isNumber() && rhsVal_
.isNumber()));
12850 // Should have been handled by tryAttachAnyNullUndefined.
12851 MOZ_ASSERT_IF(lhsVal_
.isNullOrUndefined() || rhsVal_
.isNullOrUndefined(),
12852 !IsEqualityOp(op_
));
12854 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
12855 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
12856 writer
.compareDoubleResult(op_
, lhs
, rhs
);
12857 writer
.returnFromIC();
12859 trackAttached("Compare.Number");
12860 return AttachDecision::Attach
;
12863 AttachDecision
CompareIRGenerator::tryAttachBigInt(ValOperandId lhsId
,
12864 ValOperandId rhsId
) {
12865 if (!lhsVal_
.isBigInt() || !rhsVal_
.isBigInt()) {
12866 return AttachDecision::NoAction
;
12869 BigIntOperandId lhs
= writer
.guardToBigInt(lhsId
);
12870 BigIntOperandId rhs
= writer
.guardToBigInt(rhsId
);
12872 writer
.compareBigIntResult(op_
, lhs
, rhs
);
12873 writer
.returnFromIC();
12875 trackAttached("Compare.BigInt");
12876 return AttachDecision::Attach
;
12879 AttachDecision
CompareIRGenerator::tryAttachAnyNullUndefined(
12880 ValOperandId lhsId
, ValOperandId rhsId
) {
12881 MOZ_ASSERT(IsEqualityOp(op_
));
12883 // Either RHS or LHS needs to be null/undefined.
12884 if (!lhsVal_
.isNullOrUndefined() && !rhsVal_
.isNullOrUndefined()) {
12885 return AttachDecision::NoAction
;
12888 // We assume that the side with null/undefined is usually constant, in
12889 // code like `if (x === undefined) { x = {}; }`.
12890 // That is why we don't attach when both sides are undefined/null,
12891 // because we would basically need to decide by chance which side is
12892 // the likely constant.
12893 // The actual generated code however handles null/undefined of course.
12894 if (lhsVal_
.isNullOrUndefined() && rhsVal_
.isNullOrUndefined()) {
12895 return AttachDecision::NoAction
;
12898 if (rhsVal_
.isNullOrUndefined()) {
12899 if (rhsVal_
.isNull()) {
12900 writer
.guardIsNull(rhsId
);
12901 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, lhsId
);
12902 trackAttached("Compare.AnyNull");
12904 writer
.guardIsUndefined(rhsId
);
12905 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, lhsId
);
12906 trackAttached("Compare.AnyUndefined");
12909 if (lhsVal_
.isNull()) {
12910 writer
.guardIsNull(lhsId
);
12911 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ false, rhsId
);
12912 trackAttached("Compare.NullAny");
12914 writer
.guardIsUndefined(lhsId
);
12915 writer
.compareNullUndefinedResult(op_
, /* isUndefined */ true, rhsId
);
12916 trackAttached("Compare.UndefinedAny");
12920 writer
.returnFromIC();
12921 return AttachDecision::Attach
;
12924 // Handle {null/undefined} x {null,undefined} equality comparisons
12925 AttachDecision
CompareIRGenerator::tryAttachNullUndefined(ValOperandId lhsId
,
12926 ValOperandId rhsId
) {
12927 if (!lhsVal_
.isNullOrUndefined() || !rhsVal_
.isNullOrUndefined()) {
12928 return AttachDecision::NoAction
;
12931 if (op_
== JSOp::Eq
|| op_
== JSOp::Ne
) {
12932 writer
.guardIsNullOrUndefined(lhsId
);
12933 writer
.guardIsNullOrUndefined(rhsId
);
12934 // Sloppy equality means we actually only care about the op:
12935 writer
.loadBooleanResult(op_
== JSOp::Eq
);
12936 trackAttached("Compare.SloppyNullUndefined");
12938 // Strict equality only hits this branch, and only in the
12939 // undef {!,=}== undef and null {!,=}== null cases.
12940 // The other cases should have hit tryAttachStrictDifferentTypes.
12941 MOZ_ASSERT(lhsVal_
.isNull() == rhsVal_
.isNull());
12942 lhsVal_
.isNull() ? writer
.guardIsNull(lhsId
)
12943 : writer
.guardIsUndefined(lhsId
);
12944 rhsVal_
.isNull() ? writer
.guardIsNull(rhsId
)
12945 : writer
.guardIsUndefined(rhsId
);
12946 writer
.loadBooleanResult(op_
== JSOp::StrictEq
);
12947 trackAttached("Compare.StrictNullUndefinedEquality");
12950 writer
.returnFromIC();
12951 return AttachDecision::Attach
;
12954 AttachDecision
CompareIRGenerator::tryAttachStringNumber(ValOperandId lhsId
,
12955 ValOperandId rhsId
) {
12956 // Ensure String x {Number, Boolean, Null, Undefined}
12957 if (!(lhsVal_
.isString() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
12958 !(rhsVal_
.isString() && CanConvertToDoubleForToNumber(lhsVal_
))) {
12959 return AttachDecision::NoAction
;
12962 // Case should have been handled by tryAttachStrictDifferentTypes
12963 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
12965 auto createGuards
= [&](const Value
& v
, ValOperandId vId
) {
12966 if (v
.isString()) {
12967 StringOperandId strId
= writer
.guardToString(vId
);
12968 return writer
.guardStringToNumber(strId
);
12970 return EmitGuardToDoubleForToNumber(writer
, vId
, v
);
12973 NumberOperandId lhsGuardedId
= createGuards(lhsVal_
, lhsId
);
12974 NumberOperandId rhsGuardedId
= createGuards(rhsVal_
, rhsId
);
12975 writer
.compareDoubleResult(op_
, lhsGuardedId
, rhsGuardedId
);
12976 writer
.returnFromIC();
12978 trackAttached("Compare.StringNumber");
12979 return AttachDecision::Attach
;
12982 AttachDecision
CompareIRGenerator::tryAttachPrimitiveSymbol(
12983 ValOperandId lhsId
, ValOperandId rhsId
) {
12984 MOZ_ASSERT(IsEqualityOp(op_
));
12986 // The set of primitive cases we want to handle here (excluding null,
12987 // undefined, and symbol)
12988 auto isPrimitive
= [](const Value
& x
) {
12989 return x
.isString() || x
.isBoolean() || x
.isNumber() || x
.isBigInt();
12992 // Ensure Symbol x {String, Bool, Number, BigInt}.
12993 if (!(lhsVal_
.isSymbol() && isPrimitive(rhsVal_
)) &&
12994 !(rhsVal_
.isSymbol() && isPrimitive(lhsVal_
))) {
12995 return AttachDecision::NoAction
;
12998 auto guardPrimitive
= [&](const Value
& v
, ValOperandId id
) {
12999 MOZ_ASSERT(isPrimitive(v
));
13000 if (v
.isNumber()) {
13001 writer
.guardIsNumber(id
);
13004 switch (v
.extractNonDoubleType()) {
13005 case JSVAL_TYPE_STRING
:
13006 writer
.guardToString(id
);
13008 case JSVAL_TYPE_BOOLEAN
:
13009 writer
.guardToBoolean(id
);
13011 case JSVAL_TYPE_BIGINT
:
13012 writer
.guardToBigInt(id
);
13015 MOZ_CRASH("unexpected type");
13020 if (lhsVal_
.isSymbol()) {
13021 writer
.guardToSymbol(lhsId
);
13022 guardPrimitive(rhsVal_
, rhsId
);
13024 guardPrimitive(lhsVal_
, lhsId
);
13025 writer
.guardToSymbol(rhsId
);
13028 // Comparing a primitive with symbol will always be true for Ne/StrictNe, and
13029 // always be false for other compare ops.
13030 writer
.loadBooleanResult(op_
== JSOp::Ne
|| op_
== JSOp::StrictNe
);
13031 writer
.returnFromIC();
13033 trackAttached("Compare.PrimitiveSymbol");
13034 return AttachDecision::Attach
;
13037 AttachDecision
CompareIRGenerator::tryAttachBigIntInt32(ValOperandId lhsId
,
13038 ValOperandId rhsId
) {
13039 // Ensure BigInt x {Int32, Boolean, Null}.
13040 if (!(lhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(rhsVal_
)) &&
13041 !(rhsVal_
.isBigInt() && CanConvertToInt32ForToNumber(lhsVal_
))) {
13042 return AttachDecision::NoAction
;
13045 // Case should have been handled by tryAttachStrictDifferentTypes
13046 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
13048 if (lhsVal_
.isBigInt()) {
13049 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
13050 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhsVal_
);
13052 writer
.compareBigIntInt32Result(op_
, bigIntId
, intId
);
13054 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhsVal_
);
13055 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
13057 writer
.compareBigIntInt32Result(ReverseCompareOp(op_
), bigIntId
, intId
);
13059 writer
.returnFromIC();
13061 trackAttached("Compare.BigIntInt32");
13062 return AttachDecision::Attach
;
13065 AttachDecision
CompareIRGenerator::tryAttachBigIntNumber(ValOperandId lhsId
,
13066 ValOperandId rhsId
) {
13067 // Ensure BigInt x {Number, Undefined}.
13068 if (!(lhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(rhsVal_
)) &&
13069 !(rhsVal_
.isBigInt() && CanConvertToDoubleForToNumber(lhsVal_
))) {
13070 return AttachDecision::NoAction
;
13073 // Case should have been handled by tryAttachStrictDifferentTypes
13074 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
13076 // Case should have been handled by tryAttachBigIntInt32.
13077 MOZ_ASSERT(!CanConvertToInt32ForToNumber(lhsVal_
));
13078 MOZ_ASSERT(!CanConvertToInt32ForToNumber(rhsVal_
));
13080 if (lhsVal_
.isBigInt()) {
13081 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
13082 NumberOperandId numId
=
13083 EmitGuardToDoubleForToNumber(writer
, rhsId
, rhsVal_
);
13085 writer
.compareBigIntNumberResult(op_
, bigIntId
, numId
);
13087 NumberOperandId numId
=
13088 EmitGuardToDoubleForToNumber(writer
, lhsId
, lhsVal_
);
13089 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
13091 writer
.compareBigIntNumberResult(ReverseCompareOp(op_
), bigIntId
, numId
);
13093 writer
.returnFromIC();
13095 trackAttached("Compare.BigIntNumber");
13096 return AttachDecision::Attach
;
13099 AttachDecision
CompareIRGenerator::tryAttachBigIntString(ValOperandId lhsId
,
13100 ValOperandId rhsId
) {
13101 // Ensure BigInt x String.
13102 if (!(lhsVal_
.isBigInt() && rhsVal_
.isString()) &&
13103 !(rhsVal_
.isBigInt() && lhsVal_
.isString())) {
13104 return AttachDecision::NoAction
;
13107 // Case should have been handled by tryAttachStrictDifferentTypes
13108 MOZ_ASSERT(op_
!= JSOp::StrictEq
&& op_
!= JSOp::StrictNe
);
13110 if (lhsVal_
.isBigInt()) {
13111 BigIntOperandId bigIntId
= writer
.guardToBigInt(lhsId
);
13112 StringOperandId strId
= writer
.guardToString(rhsId
);
13114 writer
.compareBigIntStringResult(op_
, bigIntId
, strId
);
13116 StringOperandId strId
= writer
.guardToString(lhsId
);
13117 BigIntOperandId bigIntId
= writer
.guardToBigInt(rhsId
);
13119 writer
.compareBigIntStringResult(ReverseCompareOp(op_
), bigIntId
, strId
);
13121 writer
.returnFromIC();
13123 trackAttached("Compare.BigIntString");
13124 return AttachDecision::Attach
;
13127 AttachDecision
CompareIRGenerator::tryAttachStub() {
13128 MOZ_ASSERT(cacheKind_
== CacheKind::Compare
);
13129 MOZ_ASSERT(IsEqualityOp(op_
) || IsRelationalOp(op_
));
13131 AutoAssertNoPendingException
aanpe(cx_
);
13133 constexpr uint8_t lhsIndex
= 0;
13134 constexpr uint8_t rhsIndex
= 1;
13136 ValOperandId
lhsId(writer
.setInputOperandId(lhsIndex
));
13137 ValOperandId
rhsId(writer
.setInputOperandId(rhsIndex
));
13139 // For sloppy equality ops, there are cases this IC does not handle:
13140 // - {Object} x {String, Symbol, Bool, Number, BigInt}.
13142 // For relational comparison ops, these cases aren't handled:
13143 // - Object x {String, Bool, Number, BigInt, Object, Null, Undefined}.
13144 // Note: |Symbol x any| always throws, so it doesn't need to be handled.
13146 // (The above lists omits the equivalent case {B} x {A} when {A} x {B} is
13147 // already present.)
13149 if (IsEqualityOp(op_
)) {
13150 TRY_ATTACH(tryAttachObject(lhsId
, rhsId
));
13151 TRY_ATTACH(tryAttachSymbol(lhsId
, rhsId
));
13153 // Handles any (non null or undefined) comparison with null/undefined.
13154 TRY_ATTACH(tryAttachAnyNullUndefined(lhsId
, rhsId
));
13156 // This covers -strict- equality/inequality using a type tag check, so
13157 // catches all different type pairs outside of Numbers, which cannot be
13158 // checked on tags alone.
13159 TRY_ATTACH(tryAttachStrictDifferentTypes(lhsId
, rhsId
));
13161 TRY_ATTACH(tryAttachNullUndefined(lhsId
, rhsId
));
13163 TRY_ATTACH(tryAttachPrimitiveSymbol(lhsId
, rhsId
));
13166 // We want these to be last, to allow us to bypass the
13167 // strictly-different-types cases in the below attachment code
13168 TRY_ATTACH(tryAttachInt32(lhsId
, rhsId
));
13169 TRY_ATTACH(tryAttachNumber(lhsId
, rhsId
));
13170 TRY_ATTACH(tryAttachBigInt(lhsId
, rhsId
));
13171 TRY_ATTACH(tryAttachString(lhsId
, rhsId
));
13173 TRY_ATTACH(tryAttachStringNumber(lhsId
, rhsId
));
13175 TRY_ATTACH(tryAttachBigIntInt32(lhsId
, rhsId
));
13176 TRY_ATTACH(tryAttachBigIntNumber(lhsId
, rhsId
));
13177 TRY_ATTACH(tryAttachBigIntString(lhsId
, rhsId
));
13179 // Strict equality is always supported.
13180 MOZ_ASSERT(!IsStrictEqualityOp(op_
));
13182 // Other operations are unsupported iff at least one operand is an object.
13183 MOZ_ASSERT(lhsVal_
.isObject() || rhsVal_
.isObject());
13185 trackAttached(IRGenerator::NotAttached
);
13186 return AttachDecision::NoAction
;
13189 void CompareIRGenerator::trackAttached(const char* name
) {
13190 stubName_
= name
? name
: "NotAttached";
13191 #ifdef JS_CACHEIR_SPEW
13192 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13193 sp
.valueProperty("lhs", lhsVal_
);
13194 sp
.valueProperty("rhs", rhsVal_
);
13195 sp
.opcodeProperty("op", op_
);
13200 ToBoolIRGenerator::ToBoolIRGenerator(JSContext
* cx
, HandleScript script
,
13201 jsbytecode
* pc
, ICState state
,
13203 : IRGenerator(cx
, script
, pc
, CacheKind::ToBool
, state
), val_(val
) {}
13205 void ToBoolIRGenerator::trackAttached(const char* name
) {
13206 stubName_
= name
? name
: "NotAttached";
13207 #ifdef JS_CACHEIR_SPEW
13208 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13209 sp
.valueProperty("val", val_
);
13214 AttachDecision
ToBoolIRGenerator::tryAttachStub() {
13215 AutoAssertNoPendingException
aanpe(cx_
);
13216 writer
.setTypeData(TypeData(JSValueType(val_
.type())));
13218 TRY_ATTACH(tryAttachBool());
13219 TRY_ATTACH(tryAttachInt32());
13220 TRY_ATTACH(tryAttachNumber());
13221 TRY_ATTACH(tryAttachString());
13222 TRY_ATTACH(tryAttachNullOrUndefined());
13223 TRY_ATTACH(tryAttachObject());
13224 TRY_ATTACH(tryAttachSymbol());
13225 TRY_ATTACH(tryAttachBigInt());
13227 trackAttached(IRGenerator::NotAttached
);
13228 return AttachDecision::NoAction
;
13231 AttachDecision
ToBoolIRGenerator::tryAttachBool() {
13232 if (!val_
.isBoolean()) {
13233 return AttachDecision::NoAction
;
13236 ValOperandId
valId(writer
.setInputOperandId(0));
13237 writer
.guardNonDoubleType(valId
, ValueType::Boolean
);
13238 writer
.loadOperandResult(valId
);
13239 writer
.returnFromIC();
13240 trackAttached("ToBool.Bool");
13241 return AttachDecision::Attach
;
13244 AttachDecision
ToBoolIRGenerator::tryAttachInt32() {
13245 if (!val_
.isInt32()) {
13246 return AttachDecision::NoAction
;
13249 ValOperandId
valId(writer
.setInputOperandId(0));
13250 writer
.guardNonDoubleType(valId
, ValueType::Int32
);
13251 writer
.loadInt32TruthyResult(valId
);
13252 writer
.returnFromIC();
13253 trackAttached("ToBool.Int32");
13254 return AttachDecision::Attach
;
13257 AttachDecision
ToBoolIRGenerator::tryAttachNumber() {
13258 if (!val_
.isNumber()) {
13259 return AttachDecision::NoAction
;
13262 ValOperandId
valId(writer
.setInputOperandId(0));
13263 NumberOperandId numId
= writer
.guardIsNumber(valId
);
13264 writer
.loadDoubleTruthyResult(numId
);
13265 writer
.returnFromIC();
13266 trackAttached("ToBool.Number");
13267 return AttachDecision::Attach
;
13270 AttachDecision
ToBoolIRGenerator::tryAttachSymbol() {
13271 if (!val_
.isSymbol()) {
13272 return AttachDecision::NoAction
;
13275 ValOperandId
valId(writer
.setInputOperandId(0));
13276 writer
.guardNonDoubleType(valId
, ValueType::Symbol
);
13277 writer
.loadBooleanResult(true);
13278 writer
.returnFromIC();
13279 trackAttached("ToBool.Symbol");
13280 return AttachDecision::Attach
;
13283 AttachDecision
ToBoolIRGenerator::tryAttachString() {
13284 if (!val_
.isString()) {
13285 return AttachDecision::NoAction
;
13288 ValOperandId
valId(writer
.setInputOperandId(0));
13289 StringOperandId strId
= writer
.guardToString(valId
);
13290 writer
.loadStringTruthyResult(strId
);
13291 writer
.returnFromIC();
13292 trackAttached("ToBool.String");
13293 return AttachDecision::Attach
;
13296 AttachDecision
ToBoolIRGenerator::tryAttachNullOrUndefined() {
13297 if (!val_
.isNullOrUndefined()) {
13298 return AttachDecision::NoAction
;
13301 ValOperandId
valId(writer
.setInputOperandId(0));
13302 writer
.guardIsNullOrUndefined(valId
);
13303 writer
.loadBooleanResult(false);
13304 writer
.returnFromIC();
13305 trackAttached("ToBool.NullOrUndefined");
13306 return AttachDecision::Attach
;
13309 AttachDecision
ToBoolIRGenerator::tryAttachObject() {
13310 if (!val_
.isObject()) {
13311 return AttachDecision::NoAction
;
13314 ValOperandId
valId(writer
.setInputOperandId(0));
13315 ObjOperandId objId
= writer
.guardToObject(valId
);
13316 writer
.loadObjectTruthyResult(objId
);
13317 writer
.returnFromIC();
13318 trackAttached("ToBool.Object");
13319 return AttachDecision::Attach
;
13322 AttachDecision
ToBoolIRGenerator::tryAttachBigInt() {
13323 if (!val_
.isBigInt()) {
13324 return AttachDecision::NoAction
;
13327 ValOperandId
valId(writer
.setInputOperandId(0));
13328 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13329 writer
.loadBigIntTruthyResult(bigIntId
);
13330 writer
.returnFromIC();
13331 trackAttached("ToBool.BigInt");
13332 return AttachDecision::Attach
;
13335 GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext
* cx
,
13336 HandleScript script
,
13337 jsbytecode
* pc
, ICState state
,
13339 : IRGenerator(cx
, script
, pc
, CacheKind::GetIntrinsic
, state
), val_(val
) {}
13341 void GetIntrinsicIRGenerator::trackAttached(const char* name
) {
13342 stubName_
= name
? name
: "NotAttached";
13343 #ifdef JS_CACHEIR_SPEW
13344 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13345 sp
.valueProperty("val", val_
);
13350 AttachDecision
GetIntrinsicIRGenerator::tryAttachStub() {
13351 AutoAssertNoPendingException
aanpe(cx_
);
13352 writer
.loadValueResult(val_
);
13353 writer
.returnFromIC();
13354 trackAttached("GetIntrinsic");
13355 return AttachDecision::Attach
;
13358 UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext
* cx
, HandleScript script
,
13359 jsbytecode
* pc
, ICState state
,
13360 JSOp op
, HandleValue val
,
13362 : IRGenerator(cx
, script
, pc
, CacheKind::UnaryArith
, state
),
13367 void UnaryArithIRGenerator::trackAttached(const char* name
) {
13368 stubName_
= name
? name
: "NotAttached";
13369 #ifdef JS_CACHEIR_SPEW
13370 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13371 sp
.valueProperty("val", val_
);
13372 sp
.valueProperty("res", res_
);
13377 AttachDecision
UnaryArithIRGenerator::tryAttachStub() {
13378 AutoAssertNoPendingException
aanpe(cx_
);
13379 TRY_ATTACH(tryAttachInt32());
13380 TRY_ATTACH(tryAttachNumber());
13381 TRY_ATTACH(tryAttachBitwise());
13382 TRY_ATTACH(tryAttachBigIntPtr());
13383 TRY_ATTACH(tryAttachBigInt());
13384 TRY_ATTACH(tryAttachStringInt32());
13385 TRY_ATTACH(tryAttachStringNumber());
13387 trackAttached(IRGenerator::NotAttached
);
13388 return AttachDecision::NoAction
;
13391 AttachDecision
UnaryArithIRGenerator::tryAttachInt32() {
13392 if (op_
== JSOp::BitNot
) {
13393 return AttachDecision::NoAction
;
13395 if (!CanConvertToInt32ForToNumber(val_
) || !res_
.isInt32()) {
13396 return AttachDecision::NoAction
;
13399 ValOperandId
valId(writer
.setInputOperandId(0));
13401 Int32OperandId intId
= EmitGuardToInt32ForToNumber(writer
, valId
, val_
);
13404 writer
.loadInt32Result(intId
);
13405 trackAttached("UnaryArith.Int32Pos");
13408 writer
.int32NegationResult(intId
);
13409 trackAttached("UnaryArith.Int32Neg");
13412 writer
.int32IncResult(intId
);
13413 trackAttached("UnaryArith.Int32Inc");
13416 writer
.int32DecResult(intId
);
13417 trackAttached("UnaryArith.Int32Dec");
13419 case JSOp::ToNumeric
:
13420 writer
.loadInt32Result(intId
);
13421 trackAttached("UnaryArith.Int32ToNumeric");
13424 MOZ_CRASH("unexpected OP");
13427 writer
.returnFromIC();
13428 return AttachDecision::Attach
;
13431 AttachDecision
UnaryArithIRGenerator::tryAttachNumber() {
13432 if (op_
== JSOp::BitNot
) {
13433 return AttachDecision::NoAction
;
13435 if (!CanConvertToDoubleForToNumber(val_
)) {
13436 return AttachDecision::NoAction
;
13438 MOZ_ASSERT(res_
.isNumber());
13440 ValOperandId
valId(writer
.setInputOperandId(0));
13441 NumberOperandId numId
= EmitGuardToDoubleForToNumber(writer
, valId
, val_
);
13445 writer
.loadDoubleResult(numId
);
13446 trackAttached("UnaryArith.DoublePos");
13449 writer
.doubleNegationResult(numId
);
13450 trackAttached("UnaryArith.DoubleNeg");
13453 writer
.doubleIncResult(numId
);
13454 trackAttached("UnaryArith.DoubleInc");
13457 writer
.doubleDecResult(numId
);
13458 trackAttached("UnaryArith.DoubleDec");
13460 case JSOp::ToNumeric
:
13461 writer
.loadDoubleResult(numId
);
13462 trackAttached("UnaryArith.DoubleToNumeric");
13465 MOZ_CRASH("Unexpected OP");
13468 writer
.returnFromIC();
13469 return AttachDecision::Attach
;
13472 static bool CanTruncateToInt32(const Value
& val
) {
13473 return val
.isNumber() || val
.isBoolean() || val
.isNullOrUndefined() ||
13477 // Convert type into int32 for the bitwise/shift operands.
13478 static Int32OperandId
EmitTruncateToInt32Guard(CacheIRWriter
& writer
,
13480 const Value
& val
) {
13481 MOZ_ASSERT(CanTruncateToInt32(val
));
13482 if (val
.isInt32()) {
13483 return writer
.guardToInt32(id
);
13485 if (val
.isBoolean()) {
13486 return writer
.guardBooleanToInt32(id
);
13488 if (val
.isNullOrUndefined()) {
13489 writer
.guardIsNullOrUndefined(id
);
13490 return writer
.loadInt32Constant(0);
13492 NumberOperandId numId
;
13493 if (val
.isString()) {
13494 StringOperandId strId
= writer
.guardToString(id
);
13495 numId
= writer
.guardStringToNumber(strId
);
13497 MOZ_ASSERT(val
.isDouble());
13498 numId
= writer
.guardIsNumber(id
);
13500 return writer
.truncateDoubleToUInt32(numId
);
13503 AttachDecision
UnaryArithIRGenerator::tryAttachBitwise() {
13504 // Only bitwise operators.
13505 if (op_
!= JSOp::BitNot
) {
13506 return AttachDecision::NoAction
;
13509 // Check guard conditions
13510 if (!CanTruncateToInt32(val_
)) {
13511 return AttachDecision::NoAction
;
13514 // Bitwise operators always produce Int32 values.
13515 MOZ_ASSERT(res_
.isInt32());
13517 ValOperandId
valId(writer
.setInputOperandId(0));
13518 Int32OperandId intId
= EmitTruncateToInt32Guard(writer
, valId
, val_
);
13519 writer
.int32NotResult(intId
);
13520 trackAttached("UnaryArith.BitwiseBitNot");
13522 writer
.returnFromIC();
13523 return AttachDecision::Attach
;
13526 AttachDecision
UnaryArithIRGenerator::tryAttachBigInt() {
13527 if (!val_
.isBigInt()) {
13528 return AttachDecision::NoAction
;
13530 MOZ_ASSERT(res_
.isBigInt());
13532 MOZ_ASSERT(op_
!= JSOp::Pos
,
13533 "Applying the unary + operator on BigInt values throws an error");
13535 ValOperandId
valId(writer
.setInputOperandId(0));
13536 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13539 writer
.bigIntNotResult(bigIntId
);
13540 trackAttached("UnaryArith.BigIntNot");
13543 writer
.bigIntNegationResult(bigIntId
);
13544 trackAttached("UnaryArith.BigIntNeg");
13547 writer
.bigIntIncResult(bigIntId
);
13548 trackAttached("UnaryArith.BigIntInc");
13551 writer
.bigIntDecResult(bigIntId
);
13552 trackAttached("UnaryArith.BigIntDec");
13554 case JSOp::ToNumeric
:
13555 writer
.loadBigIntResult(bigIntId
);
13556 trackAttached("UnaryArith.BigIntToNumeric");
13559 MOZ_CRASH("Unexpected OP");
13562 writer
.returnFromIC();
13563 return AttachDecision::Attach
;
13566 AttachDecision
UnaryArithIRGenerator::tryAttachBigIntPtr() {
13567 if (!val_
.isBigInt()) {
13568 return AttachDecision::NoAction
;
13570 MOZ_ASSERT(res_
.isBigInt());
13572 MOZ_ASSERT(op_
!= JSOp::Pos
,
13573 "Applying the unary + operator on BigInt values throws an error");
13581 case JSOp::ToNumeric
:
13582 return AttachDecision::NoAction
;
13584 MOZ_CRASH("Unexpected OP");
13588 if (!BigInt::isIntPtr(val_
.toBigInt(), &val
)) {
13589 return AttachDecision::NoAction
;
13592 using CheckedIntPtr
= mozilla::CheckedInt
<intptr_t>;
13595 case JSOp::BitNot
: {
13596 // Bitwise operations always return an intptr-sized result.
13600 auto result
= -CheckedIntPtr(val
);
13601 if (result
.isValid()) {
13604 return AttachDecision::NoAction
;
13607 auto result
= CheckedIntPtr(val
) + intptr_t(1);
13608 if (result
.isValid()) {
13611 return AttachDecision::NoAction
;
13614 auto result
= CheckedIntPtr(val
) - intptr_t(1);
13615 if (result
.isValid()) {
13618 return AttachDecision::NoAction
;
13621 MOZ_CRASH("Unexpected OP");
13624 ValOperandId
valId(writer
.setInputOperandId(0));
13625 BigIntOperandId bigIntId
= writer
.guardToBigInt(valId
);
13626 IntPtrOperandId intPtrId
= writer
.bigIntToIntPtr(bigIntId
);
13627 IntPtrOperandId resultId
;
13630 resultId
= writer
.bigIntPtrNot(intPtrId
);
13631 trackAttached("UnaryArith.BigIntPtrNot");
13634 resultId
= writer
.bigIntPtrNegation(intPtrId
);
13635 trackAttached("UnaryArith.BigIntPtrNeg");
13638 resultId
= writer
.bigIntPtrInc(intPtrId
);
13639 trackAttached("UnaryArith.BigIntPtrInc");
13642 resultId
= writer
.bigIntPtrDec(intPtrId
);
13643 trackAttached("UnaryArith.BigIntPtrDec");
13646 MOZ_CRASH("Unexpected OP");
13649 writer
.intPtrToBigIntResult(resultId
);
13650 writer
.returnFromIC();
13651 return AttachDecision::Attach
;
13654 AttachDecision
UnaryArithIRGenerator::tryAttachStringInt32() {
13655 if (!val_
.isString()) {
13656 return AttachDecision::NoAction
;
13658 MOZ_ASSERT(res_
.isNumber());
13660 // Case should have been handled by tryAttachBitwise.
13661 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13663 if (!res_
.isInt32()) {
13664 return AttachDecision::NoAction
;
13667 ValOperandId
valId(writer
.setInputOperandId(0));
13668 StringOperandId stringId
= writer
.guardToString(valId
);
13669 Int32OperandId intId
= writer
.guardStringToInt32(stringId
);
13673 writer
.loadInt32Result(intId
);
13674 trackAttached("UnaryArith.StringInt32Pos");
13677 writer
.int32NegationResult(intId
);
13678 trackAttached("UnaryArith.StringInt32Neg");
13681 writer
.int32IncResult(intId
);
13682 trackAttached("UnaryArith.StringInt32Inc");
13685 writer
.int32DecResult(intId
);
13686 trackAttached("UnaryArith.StringInt32Dec");
13688 case JSOp::ToNumeric
:
13689 writer
.loadInt32Result(intId
);
13690 trackAttached("UnaryArith.StringInt32ToNumeric");
13693 MOZ_CRASH("Unexpected OP");
13696 writer
.returnFromIC();
13697 return AttachDecision::Attach
;
13700 AttachDecision
UnaryArithIRGenerator::tryAttachStringNumber() {
13701 if (!val_
.isString()) {
13702 return AttachDecision::NoAction
;
13704 MOZ_ASSERT(res_
.isNumber());
13706 // Case should have been handled by tryAttachBitwise.
13707 MOZ_ASSERT(op_
!= JSOp::BitNot
);
13709 ValOperandId
valId(writer
.setInputOperandId(0));
13710 StringOperandId stringId
= writer
.guardToString(valId
);
13711 NumberOperandId numId
= writer
.guardStringToNumber(stringId
);
13713 Int32OperandId truncatedId
;
13716 writer
.loadDoubleResult(numId
);
13717 trackAttached("UnaryArith.StringNumberPos");
13720 writer
.doubleNegationResult(numId
);
13721 trackAttached("UnaryArith.StringNumberNeg");
13724 writer
.doubleIncResult(numId
);
13725 trackAttached("UnaryArith.StringNumberInc");
13728 writer
.doubleDecResult(numId
);
13729 trackAttached("UnaryArith.StringNumberDec");
13731 case JSOp::ToNumeric
:
13732 writer
.loadDoubleResult(numId
);
13733 trackAttached("UnaryArith.StringNumberToNumeric");
13736 MOZ_CRASH("Unexpected OP");
13739 writer
.returnFromIC();
13740 return AttachDecision::Attach
;
13743 ToPropertyKeyIRGenerator::ToPropertyKeyIRGenerator(JSContext
* cx
,
13744 HandleScript script
,
13748 : IRGenerator(cx
, script
, pc
, CacheKind::ToPropertyKey
, state
), val_(val
) {}
13750 void ToPropertyKeyIRGenerator::trackAttached(const char* name
) {
13751 stubName_
= name
? name
: "NotAttached";
13752 #ifdef JS_CACHEIR_SPEW
13753 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13754 sp
.valueProperty("val", val_
);
13759 AttachDecision
ToPropertyKeyIRGenerator::tryAttachStub() {
13760 AutoAssertNoPendingException
aanpe(cx_
);
13761 TRY_ATTACH(tryAttachInt32());
13762 TRY_ATTACH(tryAttachNumber());
13763 TRY_ATTACH(tryAttachString());
13764 TRY_ATTACH(tryAttachSymbol());
13766 trackAttached(IRGenerator::NotAttached
);
13767 return AttachDecision::NoAction
;
13770 AttachDecision
ToPropertyKeyIRGenerator::tryAttachInt32() {
13771 if (!val_
.isInt32()) {
13772 return AttachDecision::NoAction
;
13775 ValOperandId
valId(writer
.setInputOperandId(0));
13777 Int32OperandId intId
= writer
.guardToInt32(valId
);
13778 writer
.loadInt32Result(intId
);
13779 writer
.returnFromIC();
13781 trackAttached("ToPropertyKey.Int32");
13782 return AttachDecision::Attach
;
13785 AttachDecision
ToPropertyKeyIRGenerator::tryAttachNumber() {
13786 if (!val_
.isNumber()) {
13787 return AttachDecision::NoAction
;
13790 // We allow negative zero here because ToPropertyKey(-0.0) is 0.
13792 if (!mozilla::NumberEqualsInt32(val_
.toNumber(), &unused
)) {
13793 return AttachDecision::NoAction
;
13796 ValOperandId
valId(writer
.setInputOperandId(0));
13798 Int32OperandId intId
= writer
.guardToInt32Index(valId
);
13799 writer
.loadInt32Result(intId
);
13800 writer
.returnFromIC();
13802 trackAttached("ToPropertyKey.Number");
13803 return AttachDecision::Attach
;
13806 AttachDecision
ToPropertyKeyIRGenerator::tryAttachString() {
13807 if (!val_
.isString()) {
13808 return AttachDecision::NoAction
;
13811 ValOperandId
valId(writer
.setInputOperandId(0));
13813 StringOperandId strId
= writer
.guardToString(valId
);
13814 writer
.loadStringResult(strId
);
13815 writer
.returnFromIC();
13817 trackAttached("ToPropertyKey.String");
13818 return AttachDecision::Attach
;
13821 AttachDecision
ToPropertyKeyIRGenerator::tryAttachSymbol() {
13822 if (!val_
.isSymbol()) {
13823 return AttachDecision::NoAction
;
13826 ValOperandId
valId(writer
.setInputOperandId(0));
13828 SymbolOperandId strId
= writer
.guardToSymbol(valId
);
13829 writer
.loadSymbolResult(strId
);
13830 writer
.returnFromIC();
13832 trackAttached("ToPropertyKey.Symbol");
13833 return AttachDecision::Attach
;
13836 BinaryArithIRGenerator::BinaryArithIRGenerator(JSContext
* cx
,
13837 HandleScript script
,
13838 jsbytecode
* pc
, ICState state
,
13839 JSOp op
, HandleValue lhs
,
13840 HandleValue rhs
, HandleValue res
)
13841 : IRGenerator(cx
, script
, pc
, CacheKind::BinaryArith
, state
),
13847 void BinaryArithIRGenerator::trackAttached(const char* name
) {
13848 stubName_
= name
? name
: "NotAttached";
13849 #ifdef JS_CACHEIR_SPEW
13850 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
13851 sp
.opcodeProperty("op", op_
);
13852 sp
.valueProperty("rhs", rhs_
);
13853 sp
.valueProperty("lhs", lhs_
);
13858 AttachDecision
BinaryArithIRGenerator::tryAttachStub() {
13859 AutoAssertNoPendingException
aanpe(cx_
);
13860 // Arithmetic operations with Int32 operands
13861 TRY_ATTACH(tryAttachInt32());
13863 // Bitwise operations with Int32/Double/Boolean/Null/Undefined/String
13865 TRY_ATTACH(tryAttachBitwise());
13867 // Arithmetic operations with Double operands. This needs to come after
13868 // tryAttachInt32, as the guards overlap, and we'd prefer to attach the
13869 // more specialized Int32 IC if it is possible.
13870 TRY_ATTACH(tryAttachDouble());
13872 // String x {String,Number,Boolean,Null,Undefined}
13873 TRY_ATTACH(tryAttachStringConcat());
13876 TRY_ATTACH(tryAttachStringObjectConcat());
13878 // Arithmetic operations or bitwise operations with intptr-sized BigInt
13880 TRY_ATTACH(tryAttachBigIntPtr());
13882 // Arithmetic operations or bitwise operations with BigInt operands
13883 TRY_ATTACH(tryAttachBigInt());
13885 // Arithmetic operations (without addition) with String x Int32.
13886 TRY_ATTACH(tryAttachStringInt32Arith());
13888 // Arithmetic operations (without addition) with String x Number. This needs
13889 // to come after tryAttachStringInt32Arith, as the guards overlap, and we'd
13890 // prefer to attach the more specialized Int32 IC if it is possible.
13891 TRY_ATTACH(tryAttachStringNumberArith());
13893 trackAttached(IRGenerator::NotAttached
);
13894 return AttachDecision::NoAction
;
13897 AttachDecision
BinaryArithIRGenerator::tryAttachBitwise() {
13898 // Only bit-wise and shifts.
13899 if (op_
!= JSOp::BitOr
&& op_
!= JSOp::BitXor
&& op_
!= JSOp::BitAnd
&&
13900 op_
!= JSOp::Lsh
&& op_
!= JSOp::Rsh
&& op_
!= JSOp::Ursh
) {
13901 return AttachDecision::NoAction
;
13904 // Check guard conditions
13905 if (!CanTruncateToInt32(lhs_
) || !CanTruncateToInt32(rhs_
)) {
13906 return AttachDecision::NoAction
;
13909 // All ops, with the exception of Ursh, produce Int32 values.
13910 MOZ_ASSERT_IF(op_
!= JSOp::Ursh
, res_
.isInt32());
13912 ValOperandId
lhsId(writer
.setInputOperandId(0));
13913 ValOperandId
rhsId(writer
.setInputOperandId(1));
13915 Int32OperandId lhsIntId
= EmitTruncateToInt32Guard(writer
, lhsId
, lhs_
);
13916 Int32OperandId rhsIntId
= EmitTruncateToInt32Guard(writer
, rhsId
, rhs_
);
13920 writer
.int32BitOrResult(lhsIntId
, rhsIntId
);
13921 trackAttached("BinaryArith.BitwiseBitOr");
13924 writer
.int32BitXorResult(lhsIntId
, rhsIntId
);
13925 trackAttached("BinaryArith.BitwiseBitXor");
13928 writer
.int32BitAndResult(lhsIntId
, rhsIntId
);
13929 trackAttached("BinaryArith.BitwiseBitAnd");
13932 writer
.int32LeftShiftResult(lhsIntId
, rhsIntId
);
13933 trackAttached("BinaryArith.BitwiseLeftShift");
13936 writer
.int32RightShiftResult(lhsIntId
, rhsIntId
);
13937 trackAttached("BinaryArith.BitwiseRightShift");
13940 writer
.int32URightShiftResult(lhsIntId
, rhsIntId
, res_
.isDouble());
13941 trackAttached("BinaryArith.BitwiseUnsignedRightShift");
13944 MOZ_CRASH("Unhandled op in tryAttachBitwise");
13947 writer
.returnFromIC();
13948 return AttachDecision::Attach
;
13951 AttachDecision
BinaryArithIRGenerator::tryAttachDouble() {
13952 // Check valid opcodes
13953 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
13954 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
13955 return AttachDecision::NoAction
;
13958 // Check guard conditions.
13959 if (!CanConvertToDoubleForToNumber(lhs_
) ||
13960 !CanConvertToDoubleForToNumber(rhs_
)) {
13961 return AttachDecision::NoAction
;
13964 ValOperandId
lhsId(writer
.setInputOperandId(0));
13965 ValOperandId
rhsId(writer
.setInputOperandId(1));
13967 NumberOperandId lhs
= EmitGuardToDoubleForToNumber(writer
, lhsId
, lhs_
);
13968 NumberOperandId rhs
= EmitGuardToDoubleForToNumber(writer
, rhsId
, rhs_
);
13972 writer
.doubleAddResult(lhs
, rhs
);
13973 trackAttached("BinaryArith.DoubleAdd");
13976 writer
.doubleSubResult(lhs
, rhs
);
13977 trackAttached("BinaryArith.DoubleSub");
13980 writer
.doubleMulResult(lhs
, rhs
);
13981 trackAttached("BinaryArith.DoubleMul");
13984 writer
.doubleDivResult(lhs
, rhs
);
13985 trackAttached("BinaryArith.DoubleDiv");
13988 writer
.doubleModResult(lhs
, rhs
);
13989 trackAttached("BinaryArith.DoubleMod");
13992 writer
.doublePowResult(lhs
, rhs
);
13993 trackAttached("BinaryArith.DoublePow");
13996 MOZ_CRASH("Unhandled Op");
13998 writer
.returnFromIC();
13999 return AttachDecision::Attach
;
14002 AttachDecision
BinaryArithIRGenerator::tryAttachInt32() {
14003 // Check guard conditions.
14004 if (!CanConvertToInt32ForToNumber(lhs_
) ||
14005 !CanConvertToInt32ForToNumber(rhs_
)) {
14006 return AttachDecision::NoAction
;
14009 // These ICs will failure() if result can't be encoded in an Int32:
14010 // If sample result is not Int32, we should avoid IC.
14011 if (!res_
.isInt32()) {
14012 return AttachDecision::NoAction
;
14015 if (op_
!= JSOp::Add
&& op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&&
14016 op_
!= JSOp::Div
&& op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
14017 return AttachDecision::NoAction
;
14020 if (op_
== JSOp::Pow
&& !CanAttachInt32Pow(lhs_
, rhs_
)) {
14021 return AttachDecision::NoAction
;
14024 ValOperandId
lhsId(writer
.setInputOperandId(0));
14025 ValOperandId
rhsId(writer
.setInputOperandId(1));
14027 Int32OperandId lhsIntId
= EmitGuardToInt32ForToNumber(writer
, lhsId
, lhs_
);
14028 Int32OperandId rhsIntId
= EmitGuardToInt32ForToNumber(writer
, rhsId
, rhs_
);
14032 writer
.int32AddResult(lhsIntId
, rhsIntId
);
14033 trackAttached("BinaryArith.Int32Add");
14036 writer
.int32SubResult(lhsIntId
, rhsIntId
);
14037 trackAttached("BinaryArith.Int32Sub");
14040 writer
.int32MulResult(lhsIntId
, rhsIntId
);
14041 trackAttached("BinaryArith.Int32Mul");
14044 writer
.int32DivResult(lhsIntId
, rhsIntId
);
14045 trackAttached("BinaryArith.Int32Div");
14048 writer
.int32ModResult(lhsIntId
, rhsIntId
);
14049 trackAttached("BinaryArith.Int32Mod");
14052 writer
.int32PowResult(lhsIntId
, rhsIntId
);
14053 trackAttached("BinaryArith.Int32Pow");
14056 MOZ_CRASH("Unhandled op in tryAttachInt32");
14059 writer
.returnFromIC();
14060 return AttachDecision::Attach
;
14063 AttachDecision
BinaryArithIRGenerator::tryAttachStringConcat() {
14065 if (op_
!= JSOp::Add
) {
14066 return AttachDecision::NoAction
;
14069 // One side must be a string, the other side a primitive value we can easily
14070 // convert to a string.
14071 if (!(lhs_
.isString() && CanConvertToString(rhs_
)) &&
14072 !(CanConvertToString(lhs_
) && rhs_
.isString())) {
14073 return AttachDecision::NoAction
;
14076 ValOperandId
lhsId(writer
.setInputOperandId(0));
14077 ValOperandId
rhsId(writer
.setInputOperandId(1));
14079 StringOperandId lhsStrId
= emitToStringGuard(lhsId
, lhs_
);
14080 StringOperandId rhsStrId
= emitToStringGuard(rhsId
, rhs_
);
14082 writer
.callStringConcatResult(lhsStrId
, rhsStrId
);
14084 writer
.returnFromIC();
14085 trackAttached("BinaryArith.StringConcat");
14086 return AttachDecision::Attach
;
14089 AttachDecision
BinaryArithIRGenerator::tryAttachStringObjectConcat() {
14091 if (op_
!= JSOp::Add
) {
14092 return AttachDecision::NoAction
;
14096 if (!(lhs_
.isObject() && rhs_
.isString()) &&
14097 !(lhs_
.isString() && rhs_
.isObject()))
14098 return AttachDecision::NoAction
;
14100 ValOperandId
lhsId(writer
.setInputOperandId(0));
14101 ValOperandId
rhsId(writer
.setInputOperandId(1));
14103 // This guard is actually overly tight, as the runtime
14104 // helper can handle lhs or rhs being a string, so long
14105 // as the other is an object.
14106 if (lhs_
.isString()) {
14107 writer
.guardToString(lhsId
);
14108 writer
.guardToObject(rhsId
);
14110 writer
.guardToObject(lhsId
);
14111 writer
.guardToString(rhsId
);
14114 writer
.callStringObjectConcatResult(lhsId
, rhsId
);
14116 writer
.returnFromIC();
14117 trackAttached("BinaryArith.StringObjectConcat");
14118 return AttachDecision::Attach
;
14121 AttachDecision
BinaryArithIRGenerator::tryAttachBigInt() {
14123 if (!lhs_
.isBigInt() || !rhs_
.isBigInt()) {
14124 return AttachDecision::NoAction
;
14134 // Arithmetic operations.
14142 // Bitwise operations.
14146 return AttachDecision::NoAction
;
14149 ValOperandId
lhsId(writer
.setInputOperandId(0));
14150 ValOperandId
rhsId(writer
.setInputOperandId(1));
14152 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
14153 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
14157 writer
.bigIntAddResult(lhsBigIntId
, rhsBigIntId
);
14158 trackAttached("BinaryArith.BigIntAdd");
14161 writer
.bigIntSubResult(lhsBigIntId
, rhsBigIntId
);
14162 trackAttached("BinaryArith.BigIntSub");
14165 writer
.bigIntMulResult(lhsBigIntId
, rhsBigIntId
);
14166 trackAttached("BinaryArith.BigIntMul");
14169 writer
.bigIntDivResult(lhsBigIntId
, rhsBigIntId
);
14170 trackAttached("BinaryArith.BigIntDiv");
14173 writer
.bigIntModResult(lhsBigIntId
, rhsBigIntId
);
14174 trackAttached("BinaryArith.BigIntMod");
14177 writer
.bigIntPowResult(lhsBigIntId
, rhsBigIntId
);
14178 trackAttached("BinaryArith.BigIntPow");
14181 writer
.bigIntBitOrResult(lhsBigIntId
, rhsBigIntId
);
14182 trackAttached("BinaryArith.BigIntBitOr");
14185 writer
.bigIntBitXorResult(lhsBigIntId
, rhsBigIntId
);
14186 trackAttached("BinaryArith.BigIntBitXor");
14189 writer
.bigIntBitAndResult(lhsBigIntId
, rhsBigIntId
);
14190 trackAttached("BinaryArith.BigIntBitAnd");
14193 writer
.bigIntLeftShiftResult(lhsBigIntId
, rhsBigIntId
);
14194 trackAttached("BinaryArith.BigIntLeftShift");
14197 writer
.bigIntRightShiftResult(lhsBigIntId
, rhsBigIntId
);
14198 trackAttached("BinaryArith.BigIntRightShift");
14201 MOZ_CRASH("Unhandled op in tryAttachBigInt");
14204 writer
.returnFromIC();
14205 return AttachDecision::Attach
;
14208 AttachDecision
BinaryArithIRGenerator::tryAttachBigIntPtr() {
14210 if (!lhs_
.isBigInt() || !rhs_
.isBigInt()) {
14211 return AttachDecision::NoAction
;
14221 // Arithmetic operations.
14229 // Bitwise operations.
14233 return AttachDecision::NoAction
;
14238 if (!BigInt::isIntPtr(lhs_
.toBigInt(), &lhs
) ||
14239 !BigInt::isIntPtr(rhs_
.toBigInt(), &rhs
)) {
14240 return AttachDecision::NoAction
;
14243 using CheckedIntPtr
= mozilla::CheckedInt
<intptr_t>;
14247 auto result
= CheckedIntPtr(lhs
) + rhs
;
14248 if (result
.isValid()) {
14251 return AttachDecision::NoAction
;
14254 auto result
= CheckedIntPtr(lhs
) - rhs
;
14255 if (result
.isValid()) {
14258 return AttachDecision::NoAction
;
14261 auto result
= CheckedIntPtr(lhs
) * rhs
;
14262 if (result
.isValid()) {
14265 return AttachDecision::NoAction
;
14268 auto result
= CheckedIntPtr(lhs
) / rhs
;
14269 if (result
.isValid()) {
14272 return AttachDecision::NoAction
;
14275 // We can't use mozilla::CheckedInt here, because it disallows negative
14280 return AttachDecision::NoAction
;
14284 if (BigInt::powIntPtr(lhs
, rhs
, &result
)) {
14287 return AttachDecision::NoAction
;
14291 case JSOp::BitAnd
: {
14292 // Bitwise operations always return an intptr-sized result.
14296 if (lhs
== 0 || rhs
<= 0) {
14299 if (size_t(rhs
) < BigInt::DigitBits
) {
14300 intptr_t result
= lhs
<< rhs
;
14301 if ((result
>> rhs
) == lhs
) {
14305 return AttachDecision::NoAction
;
14308 if (lhs
== 0 || rhs
>= 0) {
14311 if (rhs
> -intptr_t(BigInt::DigitBits
)) {
14312 intptr_t result
= lhs
<< -rhs
;
14313 if ((result
>> -rhs
) == lhs
) {
14317 return AttachDecision::NoAction
;
14320 MOZ_CRASH("Unexpected OP");
14323 ValOperandId
lhsId(writer
.setInputOperandId(0));
14324 ValOperandId
rhsId(writer
.setInputOperandId(1));
14326 BigIntOperandId lhsBigIntId
= writer
.guardToBigInt(lhsId
);
14327 BigIntOperandId rhsBigIntId
= writer
.guardToBigInt(rhsId
);
14329 IntPtrOperandId lhsIntPtrId
= writer
.bigIntToIntPtr(lhsBigIntId
);
14330 IntPtrOperandId rhsIntPtrId
= writer
.bigIntToIntPtr(rhsBigIntId
);
14332 IntPtrOperandId resultId
;
14335 resultId
= writer
.bigIntPtrAdd(lhsIntPtrId
, rhsIntPtrId
);
14336 trackAttached("BinaryArith.BigIntPtr.Add");
14340 resultId
= writer
.bigIntPtrSub(lhsIntPtrId
, rhsIntPtrId
);
14341 trackAttached("BinaryArith.BigIntPtr.Sub");
14345 resultId
= writer
.bigIntPtrMul(lhsIntPtrId
, rhsIntPtrId
);
14346 trackAttached("BinaryArith.BigIntPtr.Mul");
14350 resultId
= writer
.bigIntPtrDiv(lhsIntPtrId
, rhsIntPtrId
);
14351 trackAttached("BinaryArith.BigIntPtr.Div");
14355 resultId
= writer
.bigIntPtrMod(lhsIntPtrId
, rhsIntPtrId
);
14356 trackAttached("BinaryArith.BigIntPtr.Mod");
14360 resultId
= writer
.bigIntPtrPow(lhsIntPtrId
, rhsIntPtrId
);
14361 trackAttached("BinaryArith.BigIntPtr.Pow");
14364 case JSOp::BitOr
: {
14365 resultId
= writer
.bigIntPtrBitOr(lhsIntPtrId
, rhsIntPtrId
);
14366 trackAttached("BinaryArith.BigIntPtr.BitOr");
14369 case JSOp::BitXor
: {
14370 resultId
= writer
.bigIntPtrBitXor(lhsIntPtrId
, rhsIntPtrId
);
14371 trackAttached("BinaryArith.BigIntPtr.BitXor");
14374 case JSOp::BitAnd
: {
14375 resultId
= writer
.bigIntPtrBitAnd(lhsIntPtrId
, rhsIntPtrId
);
14376 trackAttached("BinaryArith.BigIntPtr.BitAnd");
14380 resultId
= writer
.bigIntPtrLeftShift(lhsIntPtrId
, rhsIntPtrId
);
14381 trackAttached("BinaryArith.BigIntPtr.LeftShift");
14385 resultId
= writer
.bigIntPtrRightShift(lhsIntPtrId
, rhsIntPtrId
);
14386 trackAttached("BinaryArith.BigIntPtr.RightShift");
14390 MOZ_CRASH("Unexpected OP");
14393 writer
.intPtrToBigIntResult(resultId
);
14394 writer
.returnFromIC();
14395 return AttachDecision::Attach
;
14398 AttachDecision
BinaryArithIRGenerator::tryAttachStringInt32Arith() {
14399 // Check for either int32 x string or string x int32.
14400 if (!(lhs_
.isInt32() && rhs_
.isString()) &&
14401 !(lhs_
.isString() && rhs_
.isInt32())) {
14402 return AttachDecision::NoAction
;
14405 // The created ICs will fail if the result can't be encoded as as int32.
14406 // Thus skip this IC, if the sample result is not an int32.
14407 if (!res_
.isInt32()) {
14408 return AttachDecision::NoAction
;
14411 // Must _not_ support Add, because it would be string concatenation instead.
14412 // For Pow we can't easily determine the CanAttachInt32Pow conditions so we
14413 // reject that as well.
14414 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
14415 op_
!= JSOp::Mod
) {
14416 return AttachDecision::NoAction
;
14419 // The string operand must be convertable to an int32 value.
14420 JSString
* str
= lhs_
.isString() ? lhs_
.toString() : rhs_
.toString();
14423 if (!StringToNumber(cx_
, str
, &num
)) {
14424 cx_
->recoverFromOutOfMemory();
14425 return AttachDecision::NoAction
;
14429 if (!mozilla::NumberIsInt32(num
, &unused
)) {
14430 return AttachDecision::NoAction
;
14433 ValOperandId
lhsId(writer
.setInputOperandId(0));
14434 ValOperandId
rhsId(writer
.setInputOperandId(1));
14436 auto guardToInt32
= [&](ValOperandId id
, const Value
& v
) {
14438 return writer
.guardToInt32(id
);
14441 MOZ_ASSERT(v
.isString());
14442 StringOperandId strId
= writer
.guardToString(id
);
14443 return writer
.guardStringToInt32(strId
);
14446 Int32OperandId lhsIntId
= guardToInt32(lhsId
, lhs_
);
14447 Int32OperandId rhsIntId
= guardToInt32(rhsId
, rhs_
);
14451 writer
.int32SubResult(lhsIntId
, rhsIntId
);
14452 trackAttached("BinaryArith.StringInt32Sub");
14455 writer
.int32MulResult(lhsIntId
, rhsIntId
);
14456 trackAttached("BinaryArith.StringInt32Mul");
14459 writer
.int32DivResult(lhsIntId
, rhsIntId
);
14460 trackAttached("BinaryArith.StringInt32Div");
14463 writer
.int32ModResult(lhsIntId
, rhsIntId
);
14464 trackAttached("BinaryArith.StringInt32Mod");
14467 MOZ_CRASH("Unhandled op in tryAttachStringInt32Arith");
14470 writer
.returnFromIC();
14471 return AttachDecision::Attach
;
14474 AttachDecision
BinaryArithIRGenerator::tryAttachStringNumberArith() {
14475 // Check for either number x string or string x number.
14476 if (!(lhs_
.isNumber() && rhs_
.isString()) &&
14477 !(lhs_
.isString() && rhs_
.isNumber())) {
14478 return AttachDecision::NoAction
;
14481 // Must _not_ support Add, because it would be string concatenation instead.
14482 if (op_
!= JSOp::Sub
&& op_
!= JSOp::Mul
&& op_
!= JSOp::Div
&&
14483 op_
!= JSOp::Mod
&& op_
!= JSOp::Pow
) {
14484 return AttachDecision::NoAction
;
14487 ValOperandId
lhsId(writer
.setInputOperandId(0));
14488 ValOperandId
rhsId(writer
.setInputOperandId(1));
14490 auto guardToNumber
= [&](ValOperandId id
, const Value
& v
) {
14491 if (v
.isNumber()) {
14492 return writer
.guardIsNumber(id
);
14495 MOZ_ASSERT(v
.isString());
14496 StringOperandId strId
= writer
.guardToString(id
);
14497 return writer
.guardStringToNumber(strId
);
14500 NumberOperandId lhsIntId
= guardToNumber(lhsId
, lhs_
);
14501 NumberOperandId rhsIntId
= guardToNumber(rhsId
, rhs_
);
14505 writer
.doubleSubResult(lhsIntId
, rhsIntId
);
14506 trackAttached("BinaryArith.StringNumberSub");
14509 writer
.doubleMulResult(lhsIntId
, rhsIntId
);
14510 trackAttached("BinaryArith.StringNumberMul");
14513 writer
.doubleDivResult(lhsIntId
, rhsIntId
);
14514 trackAttached("BinaryArith.StringNumberDiv");
14517 writer
.doubleModResult(lhsIntId
, rhsIntId
);
14518 trackAttached("BinaryArith.StringNumberMod");
14521 writer
.doublePowResult(lhsIntId
, rhsIntId
);
14522 trackAttached("BinaryArith.StringNumberPow");
14525 MOZ_CRASH("Unhandled op in tryAttachStringNumberArith");
14528 writer
.returnFromIC();
14529 return AttachDecision::Attach
;
14532 NewArrayIRGenerator::NewArrayIRGenerator(JSContext
* cx
, HandleScript script
,
14533 jsbytecode
* pc
, ICState state
, JSOp op
,
14534 HandleObject templateObj
,
14535 BaselineFrame
* frame
)
14536 : IRGenerator(cx
, script
, pc
, CacheKind::NewArray
, state
, frame
),
14537 #ifdef JS_CACHEIR_SPEW
14540 templateObject_(templateObj
) {
14541 MOZ_ASSERT(templateObject_
);
14544 void NewArrayIRGenerator::trackAttached(const char* name
) {
14545 stubName_
= name
? name
: "NotAttached";
14546 #ifdef JS_CACHEIR_SPEW
14547 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14548 sp
.opcodeProperty("op", op_
);
14553 AttachDecision
NewArrayIRGenerator::tryAttachArrayObject() {
14554 ArrayObject
* arrayObj
= &templateObject_
->as
<ArrayObject
>();
14556 MOZ_ASSERT(arrayObj
->numUsedFixedSlots() == 0);
14557 MOZ_ASSERT(arrayObj
->numDynamicSlots() == 0);
14558 MOZ_ASSERT(!arrayObj
->isSharedMemory());
14560 // The macro assembler only supports creating arrays with fixed elements.
14561 if (arrayObj
->hasDynamicElements()) {
14562 return AttachDecision::NoAction
;
14565 // Stub doesn't support metadata builder
14566 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
14567 return AttachDecision::NoAction
;
14570 writer
.guardNoAllocationMetadataBuilder(
14571 cx_
->realm()->addressOfMetadataBuilder());
14573 gc::AllocSite
* site
= maybeCreateAllocSite();
14575 return AttachDecision::NoAction
;
14578 Shape
* shape
= arrayObj
->shape();
14579 uint32_t length
= arrayObj
->length();
14581 writer
.newArrayObjectResult(length
, shape
, site
);
14583 writer
.returnFromIC();
14585 trackAttached("NewArray.Object");
14586 return AttachDecision::Attach
;
14589 AttachDecision
NewArrayIRGenerator::tryAttachStub() {
14590 AutoAssertNoPendingException
aanpe(cx_
);
14592 TRY_ATTACH(tryAttachArrayObject());
14594 trackAttached(IRGenerator::NotAttached
);
14595 return AttachDecision::NoAction
;
14598 NewObjectIRGenerator::NewObjectIRGenerator(JSContext
* cx
, HandleScript script
,
14599 jsbytecode
* pc
, ICState state
,
14600 JSOp op
, HandleObject templateObj
,
14601 BaselineFrame
* frame
)
14602 : IRGenerator(cx
, script
, pc
, CacheKind::NewObject
, state
, frame
),
14603 #ifdef JS_CACHEIR_SPEW
14606 templateObject_(templateObj
) {
14607 MOZ_ASSERT(templateObject_
);
14610 void NewObjectIRGenerator::trackAttached(const char* name
) {
14611 stubName_
= name
? name
: "NotAttached";
14612 #ifdef JS_CACHEIR_SPEW
14613 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14614 sp
.opcodeProperty("op", op_
);
14619 AttachDecision
NewObjectIRGenerator::tryAttachPlainObject() {
14620 // Don't optimize allocations with too many dynamic slots. We use an unrolled
14621 // loop when initializing slots and this avoids generating too much code.
14622 static const uint32_t MaxDynamicSlotsToOptimize
= 64;
14624 NativeObject
* nativeObj
= &templateObject_
->as
<NativeObject
>();
14625 MOZ_ASSERT(nativeObj
->is
<PlainObject
>());
14627 // Stub doesn't support metadata builder
14628 if (cx_
->realm()->hasAllocationMetadataBuilder()) {
14629 return AttachDecision::NoAction
;
14632 if (nativeObj
->numDynamicSlots() > MaxDynamicSlotsToOptimize
) {
14633 return AttachDecision::NoAction
;
14636 MOZ_ASSERT(!nativeObj
->hasDynamicElements());
14637 MOZ_ASSERT(!nativeObj
->isSharedMemory());
14639 gc::AllocSite
* site
= maybeCreateAllocSite();
14641 return AttachDecision::NoAction
;
14644 uint32_t numFixedSlots
= nativeObj
->numUsedFixedSlots();
14645 uint32_t numDynamicSlots
= nativeObj
->numDynamicSlots();
14646 gc::AllocKind allocKind
= nativeObj
->allocKindForTenure();
14647 Shape
* shape
= nativeObj
->shape();
14649 writer
.guardNoAllocationMetadataBuilder(
14650 cx_
->realm()->addressOfMetadataBuilder());
14651 writer
.newPlainObjectResult(numFixedSlots
, numDynamicSlots
, allocKind
, shape
,
14654 writer
.returnFromIC();
14656 trackAttached("NewObject.PlainObject");
14657 return AttachDecision::Attach
;
14660 AttachDecision
NewObjectIRGenerator::tryAttachStub() {
14661 AutoAssertNoPendingException
aanpe(cx_
);
14663 TRY_ATTACH(tryAttachPlainObject());
14665 trackAttached(IRGenerator::NotAttached
);
14666 return AttachDecision::NoAction
;
14669 CloseIterIRGenerator::CloseIterIRGenerator(JSContext
* cx
, HandleScript script
,
14670 jsbytecode
* pc
, ICState state
,
14672 CompletionKind kind
)
14673 : IRGenerator(cx
, script
, pc
, CacheKind::CloseIter
, state
),
14677 void CloseIterIRGenerator::trackAttached(const char* name
) {
14678 #ifdef JS_CACHEIR_SPEW
14679 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14680 sp
.valueProperty("iter", ObjectValue(*iter_
));
14685 AttachDecision
CloseIterIRGenerator::tryAttachNoReturnMethod() {
14686 Maybe
<PropertyInfo
> prop
;
14687 NativeObject
* holder
= nullptr;
14689 // If we can guard that the iterator does not have a |return| method,
14690 // then this CloseIter is a no-op.
14691 NativeGetPropKind kind
= CanAttachNativeGetProp(
14692 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
14693 if (kind
!= NativeGetPropKind::Missing
) {
14694 return AttachDecision::NoAction
;
14696 MOZ_ASSERT(!holder
);
14698 ObjOperandId
objId(writer
.setInputOperandId(0));
14700 EmitMissingPropGuard(writer
, &iter_
->as
<NativeObject
>(), objId
);
14702 // There is no return method, so we don't have to do anything.
14703 writer
.returnFromIC();
14705 trackAttached("CloseIter.NoReturn");
14706 return AttachDecision::Attach
;
14709 AttachDecision
CloseIterIRGenerator::tryAttachScriptedReturn() {
14710 Maybe
<PropertyInfo
> prop
;
14711 NativeObject
* holder
= nullptr;
14713 NativeGetPropKind kind
= CanAttachNativeGetProp(
14714 cx_
, iter_
, NameToId(cx_
->names().return_
), &holder
, &prop
, pc_
);
14715 if (kind
!= NativeGetPropKind::Slot
) {
14716 return AttachDecision::NoAction
;
14718 MOZ_ASSERT(holder
);
14719 MOZ_ASSERT(prop
->isDataProperty());
14721 size_t slot
= prop
->slot();
14722 Value calleeVal
= holder
->getSlot(slot
);
14723 if (!calleeVal
.isObject() || !calleeVal
.toObject().is
<JSFunction
>()) {
14724 return AttachDecision::NoAction
;
14727 JSFunction
* callee
= &calleeVal
.toObject().as
<JSFunction
>();
14728 if (!callee
->hasJitEntry()) {
14729 return AttachDecision::NoAction
;
14731 if (callee
->isClassConstructor()) {
14732 return AttachDecision::NoAction
;
14735 // We don't support cross-realm |return|.
14736 if (cx_
->realm() != callee
->realm()) {
14737 return AttachDecision::NoAction
;
14740 ObjOperandId
objId(writer
.setInputOperandId(0));
14742 ObjOperandId holderId
=
14743 EmitReadSlotGuard(writer
, &iter_
->as
<NativeObject
>(), holder
, objId
);
14745 ValOperandId calleeValId
= EmitLoadSlot(writer
, holder
, holderId
, slot
);
14746 ObjOperandId calleeId
= writer
.guardToObject(calleeValId
);
14747 emitCalleeGuard(calleeId
, callee
);
14749 writer
.closeIterScriptedResult(objId
, calleeId
, kind_
, callee
->nargs());
14751 writer
.returnFromIC();
14752 trackAttached("CloseIter.ScriptedReturn");
14754 return AttachDecision::Attach
;
14757 AttachDecision
CloseIterIRGenerator::tryAttachStub() {
14758 AutoAssertNoPendingException
aanpe(cx_
);
14760 TRY_ATTACH(tryAttachNoReturnMethod());
14761 TRY_ATTACH(tryAttachScriptedReturn());
14763 trackAttached(IRGenerator::NotAttached
);
14764 return AttachDecision::NoAction
;
14767 OptimizeGetIteratorIRGenerator::OptimizeGetIteratorIRGenerator(
14768 JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, ICState state
,
14770 : IRGenerator(cx
, script
, pc
, CacheKind::OptimizeGetIterator
, state
),
14773 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachStub() {
14774 MOZ_ASSERT(cacheKind_
== CacheKind::OptimizeGetIterator
);
14776 AutoAssertNoPendingException
aanpe(cx_
);
14778 TRY_ATTACH(tryAttachArray());
14779 TRY_ATTACH(tryAttachNotOptimizable());
14781 MOZ_CRASH("Failed to attach unoptimizable case.");
14784 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachArray() {
14785 if (!isFirstStub_
) {
14786 return AttachDecision::NoAction
;
14789 // The value must be a packed array.
14790 if (!val_
.isObject()) {
14791 return AttachDecision::NoAction
;
14793 Rooted
<JSObject
*> obj(cx_
, &val_
.toObject());
14794 if (!IsPackedArray(obj
)) {
14795 return AttachDecision::NoAction
;
14798 // Prototype must be Array.prototype and Array.prototype[@@iterator] must not
14800 Rooted
<NativeObject
*> arrProto(cx_
);
14801 uint32_t arrProtoIterSlot
;
14802 Rooted
<JSFunction
*> iterFun(cx_
);
14803 if (!IsArrayInstanceOptimizable(cx_
, obj
.as
<ArrayObject
>(), &arrProto
)) {
14804 return AttachDecision::NoAction
;
14807 if (!IsArrayPrototypeOptimizable(cx_
, obj
.as
<ArrayObject
>(), arrProto
,
14808 &arrProtoIterSlot
, &iterFun
)) {
14809 // Fuse should be popped.
14811 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14812 return AttachDecision::NoAction
;
14815 // %ArrayIteratorPrototype%.next must not be modified and
14816 // %ArrayIteratorPrototype%.return must not be present.
14817 Rooted
<NativeObject
*> arrayIteratorProto(cx_
);
14819 Rooted
<JSFunction
*> nextFun(cx_
);
14820 if (!IsArrayIteratorPrototypeOptimizable(
14821 cx_
, AllowIteratorReturn::No
, &arrayIteratorProto
, &slot
, &nextFun
)) {
14822 // Fuse should be popped.
14824 !obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact());
14825 return AttachDecision::NoAction
;
14828 ValOperandId
valId(writer
.setInputOperandId(0));
14829 ObjOperandId objId
= writer
.guardToObject(valId
);
14831 // Guard the object is a packed array with Array.prototype as proto.
14832 MOZ_ASSERT(obj
->is
<ArrayObject
>());
14833 writer
.guardShape(objId
, obj
->shape());
14834 writer
.guardArrayIsPacked(objId
);
14835 bool intact
= obj
->nonCCWRealm()->realmFuses
.optimizeGetIteratorFuse
.intact();
14837 // If the fuse isn't intact but we've still passed all these dynamic checks
14838 // then we can attach a version of the IC that dynamically checks to ensure
14839 // the required invariants still hold.
14841 // As an example of how this could be the case, consider an assignment
14843 // Array.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]
14845 // This assignment pops the fuse, however we can still use the dynamic check
14846 // version of this IC, as the actual -value- is still correct.
14847 bool useDynamicCheck
= !intact
|| !JS::Prefs::destructuring_fuse();
14848 if (useDynamicCheck
) {
14849 // Guard on Array.prototype[@@iterator].
14850 ObjOperandId arrProtoId
= writer
.loadObject(arrProto
);
14851 ObjOperandId iterId
= writer
.loadObject(iterFun
);
14852 writer
.guardShape(arrProtoId
, arrProto
->shape());
14853 writer
.guardDynamicSlotIsSpecificObject(arrProtoId
, iterId
,
14856 // Guard on %ArrayIteratorPrototype%.next.
14857 ObjOperandId iterProtoId
= writer
.loadObject(arrayIteratorProto
);
14858 ObjOperandId nextId
= writer
.loadObject(nextFun
);
14859 writer
.guardShape(iterProtoId
, arrayIteratorProto
->shape());
14860 writer
.guardDynamicSlotIsSpecificObject(iterProtoId
, nextId
, slot
);
14862 // Guard on the prototype chain to ensure no "return" method is present.
14863 ShapeGuardProtoChain(writer
, arrayIteratorProto
, iterProtoId
);
14865 // Guard on Array.prototype[@@iterator] and %ArrayIteratorPrototype%.next.
14866 // This fuse also ensures the prototype chain for Array Iterator is
14867 // maintained and that no return method is added.
14868 writer
.guardFuse(RealmFuses::FuseIndex::OptimizeGetIteratorFuse
);
14871 writer
.loadBooleanResult(true);
14872 writer
.returnFromIC();
14874 if (useDynamicCheck
) {
14875 trackAttached("OptimizeGetIterator.Array.Dynamic");
14877 trackAttached("OptimizeGetIterator.Array.Fuse");
14879 return AttachDecision::Attach
;
14882 AttachDecision
OptimizeGetIteratorIRGenerator::tryAttachNotOptimizable() {
14883 ValOperandId
valId(writer
.setInputOperandId(0));
14885 writer
.loadBooleanResult(false);
14886 writer
.returnFromIC();
14888 trackAttached("OptimizeGetIterator.NotOptimizable");
14889 return AttachDecision::Attach
;
14892 void OptimizeGetIteratorIRGenerator::trackAttached(const char* name
) {
14893 stubName_
= name
? name
: "NotAttached";
14895 #ifdef JS_CACHEIR_SPEW
14896 if (const CacheIRSpewer::Guard
& sp
= CacheIRSpewer::Guard(*this, name
)) {
14897 sp
.valueProperty("val", val_
);
14902 #ifdef JS_SIMULATOR
14903 bool js::jit::CallAnyNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
14904 CallArgs args
= CallArgsFromVp(argc
, vp
);
14905 JSObject
* calleeObj
= &args
.callee();
14907 MOZ_ASSERT(calleeObj
->is
<JSFunction
>());
14908 auto* calleeFunc
= &calleeObj
->as
<JSFunction
>();
14909 MOZ_ASSERT(calleeFunc
->isNativeWithoutJitEntry());
14911 JSNative native
= calleeFunc
->native();
14912 return native(cx
, args
.length(), args
.base());
14915 const void* js::jit::RedirectedCallAnyNative() {
14916 // The simulator requires native calls to be redirected to a
14917 // special swi instruction. If we are calling an arbitrary native
14918 // function, we can't wrap the real target ahead of time, so we
14919 // call a wrapper function (CallAnyNative) that calls the target
14920 // itself, and redirect that wrapper.
14921 JSNative target
= CallAnyNative
;
14922 void* rawPtr
= JS_FUNC_TO_DATA_PTR(void*, target
);
14923 void* redirected
= Simulator::RedirectNativeFunction(rawPtr
, Args_General3
);