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/WarpCacheIRTranspiler.h"
9 #include "mozilla/Casting.h"
10 #include "mozilla/Maybe.h"
14 #include "builtin/DataViewObject.h"
15 #include "builtin/MapObject.h"
16 #include "jit/AtomicOp.h"
17 #include "jit/CacheIR.h"
18 #include "jit/CacheIRCompiler.h"
19 #include "jit/CacheIROpsGenerated.h"
20 #include "jit/CacheIRReader.h"
23 #include "jit/MIRGenerator.h"
24 #include "jit/MIRGraph.h"
25 #include "jit/WarpBuilder.h"
26 #include "jit/WarpBuilderShared.h"
27 #include "jit/WarpSnapshot.h"
28 #include "js/ScalarType.h" // js::Scalar::Type
29 #include "vm/ArgumentsObject.h"
30 #include "vm/BytecodeLocation.h"
31 #include "wasm/WasmCode.h"
33 #include "gc/ObjectKind-inl.h"
34 #include "vm/NativeObject-inl.h"
35 #include "wasm/WasmInstance-inl.h"
38 using namespace js::jit
;
40 // The CacheIR transpiler generates MIR from Baseline CacheIR.
41 class MOZ_RAII WarpCacheIRTranspiler
: public WarpBuilderShared
{
42 WarpBuilder
* builder_
;
43 BytecodeLocation loc_
;
44 const CacheIRStubInfo
* stubInfo_
;
45 const uint8_t* stubData_
;
47 // Vector mapping OperandId to corresponding MDefinition.
48 using MDefinitionStackVector
= Vector
<MDefinition
*, 8, SystemAllocPolicy
>;
49 MDefinitionStackVector operands_
;
53 // Array mapping call arguments to OperandId.
54 using ArgumentKindArray
=
55 mozilla::EnumeratedArray
<ArgumentKind
, ArgumentKind::NumKinds
, OperandId
>;
56 ArgumentKindArray argumentOperandIds_
;
58 void setArgumentId(ArgumentKind kind
, OperandId id
) {
59 MOZ_ASSERT(kind
!= ArgumentKind::Callee
);
60 MOZ_ASSERT(!argumentOperandIds_
[kind
].valid());
61 argumentOperandIds_
[kind
] = id
;
64 void updateArgumentsFromOperands();
67 // Used to assert that there is only one effectful instruction
68 // per stub. And that this instruction has a resume point.
69 MInstruction
* effectful_
= nullptr;
70 bool pushedResult_
= false;
73 inline void addUnchecked(MInstruction
* ins
) {
76 // If we have not set a more specific bailout kind, mark this instruction
77 // as transpiled CacheIR. If one of these instructions bails out, we
78 // expect to hit the baseline fallback stub and invalidate the Warp script
80 if (ins
->bailoutKind() == BailoutKind::Unknown
) {
81 ins
->setBailoutKind(BailoutKind::TranspiledCacheIR
);
85 inline void add(MInstruction
* ins
) {
86 MOZ_ASSERT(!ins
->isEffectful());
90 inline void addEffectful(MInstruction
* ins
) {
91 MOZ_ASSERT(ins
->isEffectful());
92 MOZ_ASSERT(!effectful_
, "Can only have one effectful instruction");
99 // Bypasses all checks in addEffectful. Only used for testing functions.
100 inline void addEffectfulUnsafe(MInstruction
* ins
) {
101 MOZ_ASSERT(ins
->isEffectful());
105 [[nodiscard
]] bool resumeAfterUnchecked(MInstruction
* ins
) {
106 return WarpBuilderShared::resumeAfter(ins
, loc_
);
108 [[nodiscard
]] bool resumeAfter(MInstruction
* ins
) {
109 MOZ_ASSERT(effectful_
== ins
);
110 return resumeAfterUnchecked(ins
);
113 // CacheIR instructions writing to the IC's result register (the *Result
114 // instructions) must call this to push the result onto the virtual stack.
115 void pushResult(MDefinition
* result
) {
116 MOZ_ASSERT(!pushedResult_
, "Can't have more than one result");
117 current
->push(result
);
119 pushedResult_
= true;
123 MDefinition
* getOperand(OperandId id
) const { return operands_
[id
.id()]; }
125 void setOperand(OperandId id
, MDefinition
* def
) { operands_
[id
.id()] = def
; }
127 [[nodiscard
]] bool defineOperand(OperandId id
, MDefinition
* def
) {
128 MOZ_ASSERT(id
.id() == operands_
.length());
129 return operands_
.append(def
);
132 uintptr_t readStubWord(uint32_t offset
) {
133 return stubInfo_
->getStubRawWord(stubData_
, offset
);
136 Shape
* shapeStubField(uint32_t offset
) {
137 return reinterpret_cast<Shape
*>(readStubWord(offset
));
139 GetterSetter
* getterSetterStubField(uint32_t offset
) {
140 return reinterpret_cast<GetterSetter
*>(readStubWord(offset
));
142 const JSClass
* classStubField(uint32_t offset
) {
143 return reinterpret_cast<const JSClass
*>(readStubWord(offset
));
145 JSString
* stringStubField(uint32_t offset
) {
146 return reinterpret_cast<JSString
*>(readStubWord(offset
));
148 JS::Symbol
* symbolStubField(uint32_t offset
) {
149 return reinterpret_cast<JS::Symbol
*>(readStubWord(offset
));
151 BaseScript
* baseScriptStubField(uint32_t offset
) {
152 return reinterpret_cast<BaseScript
*>(readStubWord(offset
));
154 const JSJitInfo
* jitInfoStubField(uint32_t offset
) {
155 return reinterpret_cast<const JSJitInfo
*>(readStubWord(offset
));
157 JSNative
jsnativeStubField(uint32_t offset
) {
158 return reinterpret_cast<JSNative
>(readStubWord(offset
));
160 JS::ExpandoAndGeneration
* expandoAndGenerationField(uint32_t offset
) {
161 return reinterpret_cast<JS::ExpandoAndGeneration
*>(readStubWord(offset
));
163 const wasm::FuncExport
* wasmFuncExportField(uint32_t offset
) {
164 return reinterpret_cast<const wasm::FuncExport
*>(readStubWord(offset
));
166 NativeIteratorListHead
* nativeIteratorListHeadStubField(uint32_t offset
) {
167 return reinterpret_cast<NativeIteratorListHead
*>(readStubWord(offset
));
169 size_t* fuseStubField(uint32_t offset
) {
170 return reinterpret_cast<size_t*>(readStubWord(offset
));
172 gc::Heap
allocSiteInitialHeapField(uint32_t offset
) {
173 uintptr_t word
= readStubWord(offset
);
174 MOZ_ASSERT(word
== uintptr_t(gc::Heap::Default
) ||
175 word
== uintptr_t(gc::Heap::Tenured
));
176 return gc::Heap(word
);
178 const void* rawPointerField(uint32_t offset
) {
179 return reinterpret_cast<const void*>(readStubWord(offset
));
181 jsid
idStubField(uint32_t offset
) {
182 return jsid::fromRawBits(readStubWord(offset
));
184 int32_t int32StubField(uint32_t offset
) {
185 return static_cast<int32_t>(readStubWord(offset
));
187 uint32_t uint32StubField(uint32_t offset
) {
188 return static_cast<uint32_t>(readStubWord(offset
));
190 uint64_t uint64StubField(uint32_t offset
) {
191 return static_cast<uint64_t>(stubInfo_
->getStubRawInt64(stubData_
, offset
));
193 Value
valueStubField(uint32_t offset
) {
195 static_cast<uint64_t>(stubInfo_
->getStubRawInt64(stubData_
, offset
));
196 Value val
= Value::fromRawBits(raw
);
197 MOZ_ASSERT_IF(val
.isGCThing(), val
.toGCThing()->isTenured());
200 double doubleStubField(uint32_t offset
) {
202 static_cast<uint64_t>(stubInfo_
->getStubRawInt64(stubData_
, offset
));
203 return mozilla::BitwiseCast
<double>(raw
);
206 // This must only be called when the caller knows the object is tenured and
207 // not a nursery index.
208 JSObject
* tenuredObjectStubField(uint32_t offset
) {
209 WarpObjectField field
= WarpObjectField::fromData(readStubWord(offset
));
210 return field
.toObject();
213 // Returns either MConstant or MNurseryIndex. See WarpObjectField.
214 MInstruction
* objectStubField(uint32_t offset
);
216 const JSClass
* classForGuardClassKind(GuardClassKind kind
);
218 [[nodiscard
]] bool emitGuardTo(ValOperandId inputId
, MIRType type
);
220 [[nodiscard
]] bool emitToString(OperandId inputId
, StringOperandId resultId
);
222 template <typename T
>
223 [[nodiscard
]] bool emitDoubleBinaryArithResult(NumberOperandId lhsId
,
224 NumberOperandId rhsId
);
226 template <typename T
>
227 [[nodiscard
]] bool emitInt32BinaryArithResult(Int32OperandId lhsId
,
228 Int32OperandId rhsId
);
230 template <typename T
>
231 [[nodiscard
]] bool emitBigIntBinaryArithResult(BigIntOperandId lhsId
,
232 BigIntOperandId rhsId
);
234 template <typename T
>
235 [[nodiscard
]] bool emitBigIntBinaryArithEffectfulResult(
236 BigIntOperandId lhsId
, BigIntOperandId rhsId
);
238 template <typename T
>
239 [[nodiscard
]] bool emitBigIntUnaryArithResult(BigIntOperandId inputId
);
241 [[nodiscard
]] bool emitCompareResult(JSOp op
, OperandId lhsId
,
243 MCompare::CompareType compareType
);
245 [[nodiscard
]] bool emitTruthyResult(OperandId inputId
);
247 [[nodiscard
]] bool emitNewIteratorResult(MNewIterator::Type type
,
248 uint32_t templateObjectOffset
);
250 MInstruction
* addBoundsCheck(MDefinition
* index
, MDefinition
* length
);
252 [[nodiscard
]] MInstruction
* convertToBoolean(MDefinition
* input
);
254 bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind
,
255 ObjOperandId objId
, uint32_t offsetOffset
,
256 ValOperandId rhsId
, uint32_t newShapeOffset
);
258 void addDataViewData(MDefinition
* obj
, Scalar::Type type
,
259 MDefinition
** offset
, MInstruction
** elements
);
261 [[nodiscard
]] bool emitAtomicsBinaryOp(ObjOperandId objId
,
262 IntPtrOperandId indexId
,
264 Scalar::Type elementType
,
265 bool forEffect
, AtomicOp op
);
267 [[nodiscard
]] bool emitLoadArgumentSlot(ValOperandId resultId
,
270 // Calls are either Native (native function without a JitEntry),
271 // a DOM Native (native function with a JitInfo OpType::Method),
272 // or Scripted (scripted function or native function with a JitEntry).
273 enum class CallKind
{ Native
, DOM
, Scripted
};
275 [[nodiscard
]] bool updateCallInfo(MDefinition
* callee
, CallFlags flags
);
277 [[nodiscard
]] bool emitCallFunction(ObjOperandId calleeId
,
278 Int32OperandId argcId
,
279 mozilla::Maybe
<ObjOperandId
> thisObjId
,
280 CallFlags flags
, CallKind kind
);
281 [[nodiscard
]] bool emitFunApplyArgsObj(WrappedFunction
* wrappedTarget
,
284 MDefinition
* convertWasmArg(MDefinition
* arg
, wasm::ValType::Kind kind
);
286 WrappedFunction
* maybeWrappedFunction(MDefinition
* callee
, CallKind kind
,
287 uint16_t nargs
, FunctionFlags flags
);
288 WrappedFunction
* maybeCallTarget(MDefinition
* callee
, CallKind kind
);
290 bool maybeCreateThis(MDefinition
* callee
, CallFlags flags
, CallKind kind
);
292 [[nodiscard
]] bool emitCallGetterResult(CallKind kind
,
293 ValOperandId receiverId
,
294 uint32_t getterOffset
, bool sameRealm
,
295 uint32_t nargsAndFlagsOffset
);
296 [[nodiscard
]] bool emitCallSetter(CallKind kind
, ObjOperandId receiverId
,
297 uint32_t setterOffset
, ValOperandId rhsId
,
299 uint32_t nargsAndFlagsOffset
);
301 #ifndef JS_CODEGEN_X86
302 [[nodiscard
]] bool emitCallScriptedProxyGetShared(
303 MDefinition
* target
, MDefinition
* receiver
, MDefinition
* handler
,
304 MDefinition
* id
, MDefinition
* trapDef
, WrappedFunction
* trap
);
307 CACHE_IR_TRANSPILER_GENERATED
310 WarpCacheIRTranspiler(WarpBuilder
* builder
, BytecodeLocation loc
,
311 CallInfo
* callInfo
, const WarpCacheIR
* cacheIRSnapshot
)
312 : WarpBuilderShared(builder
->snapshot(), builder
->mirGen(),
313 builder
->currentBlock()),
316 stubInfo_(cacheIRSnapshot
->stubInfo()),
317 stubData_(cacheIRSnapshot
->stubData()),
318 callInfo_(callInfo
) {}
320 [[nodiscard
]] bool transpile(std::initializer_list
<MDefinition
*> inputs
);
323 bool WarpCacheIRTranspiler::transpile(
324 std::initializer_list
<MDefinition
*> inputs
) {
325 if (!operands_
.append(inputs
.begin(), inputs
.end())) {
329 CacheIRReader
reader(stubInfo_
);
331 CacheOp op
= reader
.readOp();
333 #define DEFINE_OP(op, ...) \
335 if (!emit##op(reader)) { \
339 CACHE_IR_TRANSPILER_OPS(DEFINE_OP
)
343 fprintf(stderr
, "Unsupported op: %s\n", CacheIROpNames
[size_t(op
)]);
344 MOZ_CRASH("Unsupported op");
346 } while (reader
.more());
348 // Effectful instructions should have a resume point. MIonToWasmCall is an
349 // exception: we can attach the resume point to the MInt64ToBigInt instruction
351 MOZ_ASSERT_IF(effectful_
,
352 effectful_
->resumePoint() || effectful_
->isIonToWasmCall());
356 MInstruction
* WarpCacheIRTranspiler::objectStubField(uint32_t offset
) {
357 WarpObjectField field
= WarpObjectField::fromData(readStubWord(offset
));
359 if (field
.isNurseryIndex()) {
360 auto* ins
= MNurseryObject::New(alloc(), field
.toNurseryIndex());
365 auto* ins
= MConstant::NewObject(alloc(), field
.toObject());
370 bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId
,
371 GuardClassKind kind
) {
372 MDefinition
* def
= getOperand(objId
);
375 if (kind
== GuardClassKind::JSFunction
) {
376 ins
= MGuardToFunction::New(alloc(), def
);
378 const JSClass
* classp
= classForGuardClassKind(kind
);
379 ins
= MGuardToClass::New(alloc(), def
, classp
);
384 setOperand(objId
, ins
);
388 const JSClass
* WarpCacheIRTranspiler::classForGuardClassKind(
389 GuardClassKind kind
) {
391 case GuardClassKind::Array
:
392 return &ArrayObject::class_
;
393 case GuardClassKind::PlainObject
:
394 return &PlainObject::class_
;
395 case GuardClassKind::ArrayBuffer
:
396 return &ArrayBufferObject::class_
;
397 case GuardClassKind::SharedArrayBuffer
:
398 return &SharedArrayBufferObject::class_
;
399 case GuardClassKind::DataView
:
400 return &DataViewObject::class_
;
401 case GuardClassKind::MappedArguments
:
402 return &MappedArgumentsObject::class_
;
403 case GuardClassKind::UnmappedArguments
:
404 return &UnmappedArgumentsObject::class_
;
405 case GuardClassKind::WindowProxy
:
406 return mirGen().runtime
->maybeWindowProxyClass();
407 case GuardClassKind::Set
:
408 return &SetObject::class_
;
409 case GuardClassKind::Map
:
410 return &MapObject::class_
;
411 case GuardClassKind::BoundFunction
:
412 return &BoundFunctionObject::class_
;
413 case GuardClassKind::JSFunction
:
416 MOZ_CRASH("unexpected kind");
419 bool WarpCacheIRTranspiler::emitGuardAnyClass(ObjOperandId objId
,
420 uint32_t claspOffset
) {
421 MDefinition
* def
= getOperand(objId
);
422 const JSClass
* classp
= classStubField(claspOffset
);
424 auto* ins
= MGuardToClass::New(alloc(), def
, classp
);
427 setOperand(objId
, ins
);
431 bool WarpCacheIRTranspiler::emitGuardShape(ObjOperandId objId
,
432 uint32_t shapeOffset
) {
433 MDefinition
* def
= getOperand(objId
);
435 // No read barrier is required because snapshot data is not weak and is traced
436 // as part of IonCompileTask.
437 Shape
* shape
= shapeStubField(shapeOffset
);
439 auto* ins
= MGuardShape::New(alloc(), def
, shape
);
442 setOperand(objId
, ins
);
446 bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex
) {
447 auto* ins
= MGuardFuse::New(alloc(), fuseIndex
);
453 bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId
,
454 uint32_t shapesOffset
) {
455 MDefinition
* def
= getOperand(objId
);
456 MInstruction
* shapeList
= objectStubField(shapesOffset
);
458 auto* ins
= MGuardMultipleShapes::New(alloc(), def
, shapeList
);
459 if (builder_
->info().inlineScriptTree()->hasSharedICScript()) {
460 ins
->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding
);
464 setOperand(objId
, ins
);
468 bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId
) {
469 MDefinition
* def
= getOperand(objId
);
471 auto* ins
= MGuardNullProto::New(alloc(), def
);
474 setOperand(objId
, ins
);
478 bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId
) {
479 MDefinition
* obj
= getOperand(objId
);
481 auto* ins
= MGuardIsNativeObject::New(alloc(), obj
);
484 setOperand(objId
, ins
);
488 bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId
) {
489 MDefinition
* obj
= getOperand(objId
);
491 auto* ins
= MGuardIsProxy::New(alloc(), obj
);
494 setOperand(objId
, ins
);
498 bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId
) {
499 MDefinition
* obj
= getOperand(objId
);
501 auto* ins
= MGuardIsNotProxy::New(alloc(), obj
);
504 setOperand(objId
, ins
);
508 bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId
) {
509 MDefinition
* obj
= getOperand(objId
);
511 auto* ins
= MGuardIsNotDOMProxy::New(alloc(), obj
);
514 setOperand(objId
, ins
);
518 bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
519 ObjOperandId objId
, uint32_t idOffset
, uint32_t getterSetterOffset
) {
520 MDefinition
* obj
= getOperand(objId
);
521 jsid id
= idStubField(idOffset
);
522 GetterSetter
* gs
= getterSetterStubField(getterSetterOffset
);
524 auto* ins
= MGuardHasGetterSetter::New(alloc(), obj
, id
, gs
);
527 setOperand(objId
, ins
);
531 bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId
,
533 MDefinition
* obj
= getOperand(objId
);
534 jsid id
= idStubField(idOffset
);
536 auto* ins
= MProxyGet::New(alloc(), obj
, id
);
540 return resumeAfter(ins
);
543 bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId
,
545 MDefinition
* obj
= getOperand(objId
);
546 MDefinition
* id
= getOperand(idId
);
548 auto* ins
= MProxyGetByValue::New(alloc(), obj
, id
);
552 return resumeAfter(ins
);
555 bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId
,
558 MDefinition
* obj
= getOperand(objId
);
559 MDefinition
* id
= getOperand(idId
);
561 auto* ins
= MProxyHasProp::New(alloc(), obj
, id
, hasOwn
);
565 return resumeAfter(ins
);
568 bool WarpCacheIRTranspiler::emitProxySet(ObjOperandId objId
, uint32_t idOffset
,
569 ValOperandId rhsId
, bool strict
) {
570 MDefinition
* obj
= getOperand(objId
);
571 jsid id
= idStubField(idOffset
);
572 MDefinition
* rhs
= getOperand(rhsId
);
574 auto* ins
= MProxySet::New(alloc(), obj
, rhs
, id
, strict
);
577 return resumeAfter(ins
);
580 bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId
,
584 MDefinition
* obj
= getOperand(objId
);
585 MDefinition
* id
= getOperand(idId
);
586 MDefinition
* rhs
= getOperand(rhsId
);
588 auto* ins
= MProxySetByValue::New(alloc(), obj
, id
, rhs
, strict
);
591 return resumeAfter(ins
);
594 bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId
,
596 ValOperandId rhsId
) {
597 MDefinition
* obj
= getOperand(objId
);
598 MDefinition
* rhs
= getOperand(rhsId
);
600 auto* ins
= MCallSetArrayLength::New(alloc(), obj
, rhs
, strict
);
603 return resumeAfter(ins
);
606 bool WarpCacheIRTranspiler::emitCallDOMGetterResult(ObjOperandId objId
,
607 uint32_t jitInfoOffset
) {
608 MDefinition
* obj
= getOperand(objId
);
609 const JSJitInfo
* jitInfo
= jitInfoStubField(jitInfoOffset
);
612 if (jitInfo
->isAlwaysInSlot
) {
613 ins
= MGetDOMMember::New(alloc(), jitInfo
, obj
, nullptr, nullptr);
615 // TODO(post-Warp): realms, guard operands (movable?).
616 ins
= MGetDOMProperty::New(alloc(), jitInfo
, DOMObjectKind::Native
,
617 (JS::Realm
*)mirGen().realm
->realmPtr(), obj
,
625 if (ins
->isEffectful()) {
628 return resumeAfter(ins
);
636 bool WarpCacheIRTranspiler::emitCallDOMSetter(ObjOperandId objId
,
637 uint32_t jitInfoOffset
,
638 ValOperandId rhsId
) {
639 MDefinition
* obj
= getOperand(objId
);
640 const JSJitInfo
* jitInfo
= jitInfoStubField(jitInfoOffset
);
641 MDefinition
* value
= getOperand(rhsId
);
643 MOZ_ASSERT(jitInfo
->type() == JSJitInfo::Setter
);
645 MSetDOMProperty::New(alloc(), jitInfo
->setter
, DOMObjectKind::Native
,
646 (JS::Realm
*)mirGen().realm
->realmPtr(), obj
, value
);
648 return resumeAfter(set
);
651 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValue(ObjOperandId objId
,
652 ValOperandId resultId
) {
653 MDefinition
* proxy
= getOperand(objId
);
655 auto* ins
= MLoadDOMExpandoValue::New(alloc(), proxy
);
658 return defineOperand(resultId
, ins
);
661 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueGuardGeneration(
662 ObjOperandId objId
, uint32_t expandoAndGenerationOffset
,
663 uint32_t generationOffset
, ValOperandId resultId
) {
664 MDefinition
* proxy
= getOperand(objId
);
665 JS::ExpandoAndGeneration
* expandoAndGeneration
=
666 expandoAndGenerationField(expandoAndGenerationOffset
);
667 uint64_t generation
= uint64StubField(generationOffset
);
669 auto* ins
= MLoadDOMExpandoValueGuardGeneration::New(
670 alloc(), proxy
, expandoAndGeneration
, generation
);
673 return defineOperand(resultId
, ins
);
676 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueIgnoreGeneration(
677 ObjOperandId objId
, ValOperandId resultId
) {
678 MDefinition
* proxy
= getOperand(objId
);
680 auto* ins
= MLoadDOMExpandoValueIgnoreGeneration::New(alloc(), proxy
);
683 return defineOperand(resultId
, ins
);
686 bool WarpCacheIRTranspiler::emitGuardDOMExpandoMissingOrGuardShape(
687 ValOperandId expandoId
, uint32_t shapeOffset
) {
688 MDefinition
* expando
= getOperand(expandoId
);
689 Shape
* shape
= shapeStubField(shapeOffset
);
691 auto* ins
= MGuardDOMExpandoMissingOrGuardShape::New(alloc(), expando
, shape
);
694 setOperand(expandoId
, ins
);
698 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotResult(ObjOperandId objId
,
699 uint32_t nameOffset
) {
700 MDefinition
* obj
= getOperand(objId
);
701 PropertyName
* name
= stringStubField(nameOffset
)->asAtom().asPropertyName();
703 auto* ins
= MMegamorphicLoadSlot::New(alloc(), obj
, NameToId(name
));
710 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValueResult(
711 ObjOperandId objId
, ValOperandId idId
) {
712 MDefinition
* obj
= getOperand(objId
);
713 MDefinition
* id
= getOperand(idId
);
715 auto* ins
= MMegamorphicLoadSlotByValue::New(alloc(), obj
, id
);
722 bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId
,
726 MDefinition
* obj
= getOperand(objId
);
727 jsid id
= idStubField(idOffset
);
728 MDefinition
* rhs
= getOperand(rhsId
);
730 auto* ins
= MMegamorphicStoreSlot::New(alloc(), obj
, rhs
, id
, strict
);
733 return resumeAfter(ins
);
736 bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId
,
739 MDefinition
* obj
= getOperand(objId
);
740 MDefinition
* id
= getOperand(idId
);
742 auto* ins
= MMegamorphicHasProp::New(alloc(), obj
, id
, hasOwn
);
749 bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId
,
753 MDefinition
* obj
= getOperand(objId
);
754 MDefinition
* id
= getOperand(idId
);
755 MDefinition
* rhs
= getOperand(rhsId
);
757 auto* ins
= MMegamorphicSetElement::New(alloc(), obj
, id
, rhs
, strict
);
760 return resumeAfter(ins
);
763 bool WarpCacheIRTranspiler::emitObjectToIteratorResult(
764 ObjOperandId objId
, uint32_t enumeratorsAddrOffset
) {
765 MDefinition
* obj
= getOperand(objId
);
766 NativeIteratorListHead
* enumeratorsAddr
=
767 nativeIteratorListHeadStubField(enumeratorsAddrOffset
);
769 auto* ins
= MObjectToIterator::New(alloc(), obj
, enumeratorsAddr
);
772 if (!resumeAfter(ins
)) {
779 bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId
) {
780 MDefinition
* val
= getOperand(valId
);
782 auto* ins
= MValueToIterator::New(alloc(), val
);
786 return resumeAfter(ins
);
789 bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
790 ObjOperandId objId
) {
791 MDefinition
* obj
= getOperand(objId
);
793 auto* ins
= MGuardIsNotArrayBufferMaybeShared::New(alloc(), obj
);
796 setOperand(objId
, ins
);
800 bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId
) {
801 MDefinition
* obj
= getOperand(objId
);
803 auto* ins
= MGuardIsTypedArray::New(alloc(), obj
);
806 setOperand(objId
, ins
);
810 bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId
,
811 uint32_t handlerOffset
) {
812 MDefinition
* obj
= getOperand(objId
);
813 const void* handler
= rawPointerField(handlerOffset
);
815 auto* ins
= MGuardHasProxyHandler::New(alloc(), obj
, handler
);
818 setOperand(objId
, ins
);
822 bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId
,
823 uint32_t protoOffset
) {
824 MDefinition
* def
= getOperand(objId
);
825 MDefinition
* proto
= objectStubField(protoOffset
);
827 auto* ins
= MGuardProto::New(alloc(), def
, proto
);
830 setOperand(objId
, ins
);
834 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsSpecificObject(
835 ObjOperandId objId
, ObjOperandId expectedId
, uint32_t slotOffset
) {
836 size_t slotIndex
= int32StubField(slotOffset
);
837 MDefinition
* obj
= getOperand(objId
);
838 MDefinition
* expected
= getOperand(expectedId
);
840 auto* slots
= MSlots::New(alloc(), obj
);
843 auto* load
= MLoadDynamicSlot::New(alloc(), slots
, slotIndex
);
846 auto* unbox
= MUnbox::New(alloc(), load
, MIRType::Object
, MUnbox::Fallible
);
849 auto* guard
= MGuardObjectIdentity::New(alloc(), unbox
, expected
,
850 /* bailOnEquality = */ false);
855 bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId
,
857 uint32_t slotOffset
) {
858 size_t slotIndex
= int32StubField(slotOffset
);
859 MDefinition
* obj
= getOperand(objId
);
861 auto* slots
= MSlots::New(alloc(), obj
);
864 auto* load
= MLoadDynamicSlot::New(alloc(), slots
, slotIndex
);
867 return defineOperand(resultId
, load
);
870 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject(
871 ObjOperandId objId
, uint32_t slotOffset
) {
872 size_t slotIndex
= int32StubField(slotOffset
);
873 MDefinition
* obj
= getOperand(objId
);
875 auto* slots
= MSlots::New(alloc(), obj
);
878 auto* load
= MLoadDynamicSlot::New(alloc(), slots
, slotIndex
);
881 auto* guard
= MGuardIsNotObject::New(alloc(), load
);
886 bool WarpCacheIRTranspiler::emitGuardFixedSlotValue(ObjOperandId objId
,
887 uint32_t offsetOffset
,
888 uint32_t valOffset
) {
889 MDefinition
* obj
= getOperand(objId
);
891 size_t offset
= int32StubField(offsetOffset
);
892 Value val
= valueStubField(valOffset
);
894 uint32_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
896 auto* load
= MLoadFixedSlot::New(alloc(), obj
, slotIndex
);
899 auto* guard
= MGuardValue::New(alloc(), load
, val
);
904 bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId
,
905 uint32_t offsetOffset
,
906 uint32_t valOffset
) {
907 MDefinition
* obj
= getOperand(objId
);
909 size_t offset
= int32StubField(offsetOffset
);
910 Value val
= valueStubField(valOffset
);
912 size_t slotIndex
= NativeObject::getDynamicSlotIndexFromOffset(offset
);
914 auto* slots
= MSlots::New(alloc(), obj
);
917 auto* load
= MLoadDynamicSlot::New(alloc(), slots
, slotIndex
);
920 auto* guard
= MGuardValue::New(alloc(), load
, val
);
925 bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ValOperandId resultId
,
926 ObjOperandId objId
) {
927 MDefinition
* obj
= getOperand(objId
);
929 auto* load
= MLoadScriptedProxyHandler::New(alloc(), obj
);
932 return defineOperand(resultId
, load
);
935 bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId
,
937 MDefinition
* id
= getOperand(idId
);
939 auto* ins
= MIdToStringOrSymbol::New(alloc(), id
);
942 return defineOperand(resultId
, ins
);
945 bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId
,
946 uint32_t expectedOffset
) {
947 MDefinition
* str
= getOperand(strId
);
948 JSString
* expected
= stringStubField(expectedOffset
);
950 auto* ins
= MGuardSpecificAtom::New(alloc(), str
, &expected
->asAtom());
953 setOperand(strId
, ins
);
957 bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId
,
958 uint32_t expectedOffset
) {
959 MDefinition
* symbol
= getOperand(symId
);
960 JS::Symbol
* expected
= symbolStubField(expectedOffset
);
962 auto* ins
= MGuardSpecificSymbol::New(alloc(), symbol
, expected
);
965 setOperand(symId
, ins
);
969 bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId
,
971 MDefinition
* num
= getOperand(numId
);
973 auto* ins
= MGuardSpecificInt32::New(alloc(), num
, expected
);
976 setOperand(numId
, ins
);
980 bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId
,
981 uint32_t expectedOffset
) {
982 MDefinition
* obj
= getOperand(objId
);
983 MDefinition
* expected
= objectStubField(expectedOffset
);
985 auto* ins
= MGuardObjectIdentity::New(alloc(), obj
, expected
,
986 /* bailOnEquality = */ false);
989 setOperand(objId
, ins
);
993 bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
994 ObjOperandId objId
, uint32_t expectedOffset
, uint32_t nargsAndFlagsOffset
) {
995 MDefinition
* obj
= getOperand(objId
);
996 MDefinition
* expected
= objectStubField(expectedOffset
);
997 uint32_t nargsAndFlags
= uint32StubField(nargsAndFlagsOffset
);
999 uint16_t nargs
= nargsAndFlags
>> 16;
1000 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
1002 auto* ins
= MGuardSpecificFunction::New(alloc(), obj
, expected
, nargs
, flags
);
1005 setOperand(objId
, ins
);
1009 bool WarpCacheIRTranspiler::emitGuardFunctionScript(
1010 ObjOperandId funId
, uint32_t expectedOffset
, uint32_t nargsAndFlagsOffset
) {
1011 MDefinition
* fun
= getOperand(funId
);
1012 BaseScript
* expected
= baseScriptStubField(expectedOffset
);
1013 uint32_t nargsAndFlags
= uint32StubField(nargsAndFlagsOffset
);
1015 uint16_t nargs
= nargsAndFlags
>> 16;
1016 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
1018 auto* ins
= MGuardFunctionScript::New(alloc(), fun
, expected
, nargs
, flags
);
1021 setOperand(funId
, ins
);
1025 bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId
,
1026 Int32OperandId resultId
) {
1027 MDefinition
* str
= getOperand(strId
);
1029 auto* ins
= MGuardStringToIndex::New(alloc(), str
);
1032 return defineOperand(resultId
, ins
);
1035 bool WarpCacheIRTranspiler::emitGuardStringToInt32(StringOperandId strId
,
1036 Int32OperandId resultId
) {
1037 MDefinition
* str
= getOperand(strId
);
1039 auto* ins
= MGuardStringToInt32::New(alloc(), str
);
1042 return defineOperand(resultId
, ins
);
1045 bool WarpCacheIRTranspiler::emitGuardStringToNumber(StringOperandId strId
,
1046 NumberOperandId resultId
) {
1047 MDefinition
* str
= getOperand(strId
);
1049 auto* ins
= MGuardStringToDouble::New(alloc(), str
);
1052 return defineOperand(resultId
, ins
);
1055 bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId
) {
1056 MDefinition
* obj
= getOperand(objId
);
1058 auto* ins
= MGuardNoDenseElements::New(alloc(), obj
);
1061 setOperand(objId
, ins
);
1065 bool WarpCacheIRTranspiler::emitGuardFunctionHasJitEntry(ObjOperandId funId
,
1066 bool constructing
) {
1067 MDefinition
* fun
= getOperand(funId
);
1068 uint16_t expectedFlags
= FunctionFlags::HasJitEntryFlags(constructing
);
1069 uint16_t unexpectedFlags
= 0;
1072 MGuardFunctionFlags::New(alloc(), fun
, expectedFlags
, unexpectedFlags
);
1075 setOperand(funId
, ins
);
1079 bool WarpCacheIRTranspiler::emitGuardFunctionHasNoJitEntry(ObjOperandId funId
) {
1080 MDefinition
* fun
= getOperand(funId
);
1081 uint16_t expectedFlags
= 0;
1082 uint16_t unexpectedFlags
=
1083 FunctionFlags::HasJitEntryFlags(/*isConstructing=*/false);
1086 MGuardFunctionFlags::New(alloc(), fun
, expectedFlags
, unexpectedFlags
);
1089 setOperand(funId
, ins
);
1093 bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
1094 ObjOperandId funId
) {
1095 MDefinition
* fun
= getOperand(funId
);
1097 auto* ins
= MGuardFunctionIsNonBuiltinCtor::New(alloc(), fun
);
1100 setOperand(funId
, ins
);
1104 bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId
) {
1105 MDefinition
* fun
= getOperand(funId
);
1106 uint16_t expectedFlags
= FunctionFlags::CONSTRUCTOR
;
1107 uint16_t unexpectedFlags
= 0;
1110 MGuardFunctionFlags::New(alloc(), fun
, expectedFlags
, unexpectedFlags
);
1113 setOperand(funId
, ins
);
1117 bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId
) {
1118 MDefinition
* fun
= getOperand(funId
);
1121 MGuardFunctionKind::New(alloc(), fun
, FunctionFlags::ClassConstructor
,
1122 /*bailOnEquality=*/true);
1125 setOperand(funId
, ins
);
1129 bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId
) {
1130 MDefinition
* array
= getOperand(arrayId
);
1132 auto* ins
= MGuardArrayIsPacked::New(alloc(), array
);
1135 setOperand(arrayId
, ins
);
1139 bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId
,
1141 MDefinition
* obj
= getOperand(objId
);
1143 auto* ins
= MGuardArgumentsObjectFlags::New(alloc(), obj
, flags
);
1146 setOperand(objId
, ins
);
1150 bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId
,
1153 case ValueType::String
:
1154 case ValueType::Symbol
:
1155 case ValueType::BigInt
:
1156 case ValueType::Int32
:
1157 case ValueType::Boolean
:
1158 return emitGuardTo(inputId
, MIRTypeFromValueType(JSValueType(type
)));
1159 case ValueType::Undefined
:
1160 return emitGuardIsUndefined(inputId
);
1161 case ValueType::Null
:
1162 return emitGuardIsNull(inputId
);
1163 case ValueType::Double
:
1164 case ValueType::Magic
:
1165 case ValueType::PrivateGCThing
:
1166 case ValueType::Object
:
1167 #ifdef ENABLE_RECORD_TUPLE
1168 case ValueType::ExtendedPrimitive
:
1173 MOZ_CRASH("unexpected type");
1176 bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId
, MIRType type
) {
1177 MDefinition
* def
= getOperand(inputId
);
1178 if (def
->type() == type
) {
1182 auto* ins
= MUnbox::New(alloc(), def
, type
, MUnbox::Fallible
);
1185 setOperand(inputId
, ins
);
1189 bool WarpCacheIRTranspiler::emitGuardToObject(ValOperandId inputId
) {
1190 return emitGuardTo(inputId
, MIRType::Object
);
1193 bool WarpCacheIRTranspiler::emitGuardToString(ValOperandId inputId
) {
1194 return emitGuardTo(inputId
, MIRType::String
);
1197 bool WarpCacheIRTranspiler::emitGuardToSymbol(ValOperandId inputId
) {
1198 return emitGuardTo(inputId
, MIRType::Symbol
);
1201 bool WarpCacheIRTranspiler::emitGuardToBigInt(ValOperandId inputId
) {
1202 return emitGuardTo(inputId
, MIRType::BigInt
);
1205 bool WarpCacheIRTranspiler::emitGuardToBoolean(ValOperandId inputId
) {
1206 return emitGuardTo(inputId
, MIRType::Boolean
);
1209 bool WarpCacheIRTranspiler::emitGuardToInt32(ValOperandId inputId
) {
1210 return emitGuardTo(inputId
, MIRType::Int32
);
1213 bool WarpCacheIRTranspiler::emitGuardBooleanToInt32(ValOperandId inputId
,
1214 Int32OperandId resultId
) {
1215 if (!emitGuardTo(inputId
, MIRType::Boolean
)) {
1219 MDefinition
* input
= getOperand(inputId
);
1220 MOZ_ASSERT(input
->type() == MIRType::Boolean
);
1222 auto* ins
= MBooleanToInt32::New(alloc(), input
);
1225 return defineOperand(resultId
, ins
);
1228 bool WarpCacheIRTranspiler::emitGuardIsNumber(ValOperandId inputId
) {
1229 // Prefer MToDouble because it gets further optimizations downstream.
1230 MDefinition
* def
= getOperand(inputId
);
1231 if (def
->type() == MIRType::Int32
) {
1232 auto* ins
= MToDouble::New(alloc(), def
);
1235 setOperand(inputId
, ins
);
1239 // MIRType::Double also implies int32 in Ion.
1240 return emitGuardTo(inputId
, MIRType::Double
);
1243 bool WarpCacheIRTranspiler::emitGuardIsNullOrUndefined(ValOperandId inputId
) {
1244 MDefinition
* input
= getOperand(inputId
);
1245 if (input
->type() == MIRType::Null
|| input
->type() == MIRType::Undefined
) {
1249 auto* ins
= MGuardNullOrUndefined::New(alloc(), input
);
1252 setOperand(inputId
, ins
);
1256 bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId
) {
1257 MDefinition
* input
= getOperand(inputId
);
1258 if (input
->type() == MIRType::Null
) {
1262 auto* ins
= MGuardValue::New(alloc(), input
, NullValue());
1264 setOperand(inputId
, ins
);
1268 bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId
) {
1269 MDefinition
* input
= getOperand(inputId
);
1270 if (input
->type() == MIRType::Undefined
) {
1274 auto* ins
= MGuardValue::New(alloc(), input
, UndefinedValue());
1276 setOperand(inputId
, ins
);
1280 bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId
) {
1281 MDefinition
* obj
= getOperand(objId
);
1283 auto* ins
= MGuardIsExtensible::New(alloc(), obj
);
1285 setOperand(objId
, ins
);
1289 bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
1290 Int32OperandId indexId
) {
1291 MDefinition
* index
= getOperand(indexId
);
1293 auto* ins
= MGuardInt32IsNonNegative::New(alloc(), index
);
1295 setOperand(indexId
, ins
);
1299 bool WarpCacheIRTranspiler::emitGuardIndexIsNotDenseElement(
1300 ObjOperandId objId
, Int32OperandId indexId
) {
1301 MDefinition
* obj
= getOperand(objId
);
1302 MDefinition
* index
= getOperand(indexId
);
1304 auto* ins
= MGuardIndexIsNotDenseElement::New(alloc(), obj
, index
);
1306 setOperand(indexId
, ins
);
1310 bool WarpCacheIRTranspiler::emitGuardIndexIsValidUpdateOrAdd(
1311 ObjOperandId objId
, Int32OperandId indexId
) {
1312 MDefinition
* obj
= getOperand(objId
);
1313 MDefinition
* index
= getOperand(indexId
);
1315 auto* ins
= MGuardIndexIsValidUpdateOrAdd::New(alloc(), obj
, index
);
1317 setOperand(indexId
, ins
);
1321 bool WarpCacheIRTranspiler::emitCallAddOrUpdateSparseElementHelper(
1322 ObjOperandId objId
, Int32OperandId idId
, ValOperandId rhsId
, bool strict
) {
1323 MDefinition
* obj
= getOperand(objId
);
1324 MDefinition
* id
= getOperand(idId
);
1325 MDefinition
* rhs
= getOperand(rhsId
);
1327 auto* ins
= MCallAddOrUpdateSparseElement::New(alloc(), obj
, id
, rhs
, strict
);
1330 return resumeAfter(ins
);
1333 bool WarpCacheIRTranspiler::emitGuardTagNotEqual(ValueTagOperandId lhsId
,
1334 ValueTagOperandId rhsId
) {
1335 MDefinition
* lhs
= getOperand(lhsId
);
1336 MDefinition
* rhs
= getOperand(rhsId
);
1338 auto* ins
= MGuardTagNotEqual::New(alloc(), lhs
, rhs
);
1344 bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId
,
1345 Int32OperandId resultId
) {
1346 MDefinition
* input
= getOperand(inputId
);
1348 MToNumberInt32::New(alloc(), input
, IntConversionInputKind::NumbersOnly
);
1350 // ToPropertyKey(-0) is "0", so we can silently convert -0 to 0 here.
1351 ins
->setNeedsNegativeZeroCheck(false);
1354 return defineOperand(resultId
, ins
);
1357 bool WarpCacheIRTranspiler::emitTruncateDoubleToUInt32(
1358 NumberOperandId inputId
, Int32OperandId resultId
) {
1359 MDefinition
* input
= getOperand(inputId
);
1360 auto* ins
= MTruncateToInt32::New(alloc(), input
);
1363 return defineOperand(resultId
, ins
);
1366 bool WarpCacheIRTranspiler::emitGuardToInt32ModUint32(ValOperandId valId
,
1367 Int32OperandId resultId
) {
1368 MDefinition
* input
= getOperand(valId
);
1369 auto* ins
= MTruncateToInt32::New(alloc(), input
);
1372 return defineOperand(resultId
, ins
);
1375 bool WarpCacheIRTranspiler::emitGuardToUint8Clamped(ValOperandId valId
,
1376 Int32OperandId resultId
) {
1377 MDefinition
* input
= getOperand(valId
);
1378 auto* ins
= MClampToUint8::New(alloc(), input
);
1381 return defineOperand(resultId
, ins
);
1384 bool WarpCacheIRTranspiler::emitToString(OperandId inputId
,
1385 StringOperandId resultId
) {
1386 MDefinition
* input
= getOperand(inputId
);
1388 MToString::New(alloc(), input
, MToString::SideEffectHandling::Bailout
);
1391 return defineOperand(resultId
, ins
);
1394 bool WarpCacheIRTranspiler::emitInt32ToIntPtr(Int32OperandId inputId
,
1395 IntPtrOperandId resultId
) {
1396 MDefinition
* input
= getOperand(inputId
);
1397 auto* ins
= MInt32ToIntPtr::New(alloc(), input
);
1399 return defineOperand(resultId
, ins
);
1402 bool WarpCacheIRTranspiler::emitGuardNumberToIntPtrIndex(
1403 NumberOperandId inputId
, bool supportOOB
, IntPtrOperandId resultId
) {
1404 MDefinition
* input
= getOperand(inputId
);
1405 auto* ins
= MGuardNumberToIntPtrIndex::New(alloc(), input
, supportOOB
);
1407 return defineOperand(resultId
, ins
);
1410 bool WarpCacheIRTranspiler::emitCallInt32ToString(Int32OperandId inputId
,
1411 StringOperandId resultId
) {
1412 return emitToString(inputId
, resultId
);
1415 bool WarpCacheIRTranspiler::emitCallNumberToString(NumberOperandId inputId
,
1416 StringOperandId resultId
) {
1417 return emitToString(inputId
, resultId
);
1420 bool WarpCacheIRTranspiler::emitInt32ToStringWithBaseResult(
1421 Int32OperandId inputId
, Int32OperandId baseId
) {
1422 MDefinition
* input
= getOperand(inputId
);
1423 MDefinition
* base
= getOperand(baseId
);
1425 auto* guardedBase
= MGuardInt32Range::New(alloc(), base
, 2, 36);
1428 // Use lower-case characters by default.
1429 constexpr bool lower
= true;
1431 auto* ins
= MInt32ToStringWithBase::New(alloc(), input
, guardedBase
, lower
);
1438 bool WarpCacheIRTranspiler::emitBooleanToString(BooleanOperandId inputId
,
1439 StringOperandId resultId
) {
1440 return emitToString(inputId
, resultId
);
1443 bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId
,
1444 NumberOperandId resultId
) {
1445 MDefinition
* input
= getOperand(inputId
);
1447 auto* ins
= MToDouble::New(alloc(), input
);
1450 return defineOperand(resultId
, ins
);
1453 bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId
) {
1454 MDefinition
* val
= getOperand(valId
);
1455 MOZ_ASSERT(val
->type() == MIRType::Int32
);
1460 bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId
) {
1461 MDefinition
* val
= getOperand(valId
);
1462 MOZ_ASSERT(val
->type() == MIRType::Double
);
1467 bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId
) {
1468 MDefinition
* val
= getOperand(valId
);
1469 MOZ_ASSERT(val
->type() == MIRType::BigInt
);
1474 bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId
) {
1475 MDefinition
* obj
= getOperand(objId
);
1476 MOZ_ASSERT(obj
->type() == MIRType::Object
);
1481 bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId
) {
1482 MDefinition
* str
= getOperand(strId
);
1483 MOZ_ASSERT(str
->type() == MIRType::String
);
1488 bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId
) {
1489 MDefinition
* sym
= getOperand(symId
);
1490 MOZ_ASSERT(sym
->type() == MIRType::Symbol
);
1495 bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
1496 pushResult(constant(UndefinedValue()));
1500 bool WarpCacheIRTranspiler::emitLoadBooleanResult(bool val
) {
1501 pushResult(constant(BooleanValue(val
)));
1505 bool WarpCacheIRTranspiler::emitLoadInt32Constant(uint32_t valOffset
,
1506 Int32OperandId resultId
) {
1507 int32_t val
= int32StubField(valOffset
);
1508 auto* valConst
= constant(Int32Value(val
));
1509 return defineOperand(resultId
, valConst
);
1512 bool WarpCacheIRTranspiler::emitLoadDoubleConstant(uint32_t valOffset
,
1513 NumberOperandId resultId
) {
1514 double val
= doubleStubField(valOffset
);
1515 auto* valConst
= constant(DoubleValue(val
));
1516 return defineOperand(resultId
, valConst
);
1519 bool WarpCacheIRTranspiler::emitLoadBooleanConstant(bool val
,
1520 BooleanOperandId resultId
) {
1521 auto* valConst
= constant(BooleanValue(val
));
1522 return defineOperand(resultId
, valConst
);
1525 bool WarpCacheIRTranspiler::emitLoadUndefined(ValOperandId resultId
) {
1526 auto* valConst
= constant(UndefinedValue());
1527 return defineOperand(resultId
, valConst
);
1530 bool WarpCacheIRTranspiler::emitLoadConstantString(uint32_t strOffset
,
1531 StringOperandId resultId
) {
1532 JSString
* val
= stringStubField(strOffset
);
1533 auto* valConst
= constant(StringValue(val
));
1534 return defineOperand(resultId
, valConst
);
1537 bool WarpCacheIRTranspiler::emitLoadConstantStringResult(uint32_t strOffset
) {
1538 JSString
* val
= stringStubField(strOffset
);
1539 auto* valConst
= constant(StringValue(val
));
1540 pushResult(valConst
);
1544 bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId
) {
1545 MDefinition
* obj
= getOperand(objId
);
1546 auto* typeOf
= MTypeOf::New(alloc(), obj
);
1549 auto* ins
= MTypeOfName::New(alloc(), typeOf
);
1555 bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
1556 ObjOperandId objId
, ObjOperandId resultId
) {
1557 MDefinition
* env
= getOperand(objId
);
1558 auto* ins
= MEnclosingEnvironment::New(alloc(), env
);
1561 return defineOperand(resultId
, ins
);
1564 bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId
,
1565 uint32_t objOffset
) {
1566 MInstruction
* ins
= objectStubField(objOffset
);
1568 return defineOperand(resultId
, ins
);
1571 bool WarpCacheIRTranspiler::emitLoadProtoObject(ObjOperandId resultId
,
1573 ObjOperandId receiverObjId
) {
1574 MInstruction
* ins
= objectStubField(objOffset
);
1575 if (ins
->isConstant()) {
1576 MDefinition
* receiverObj
= getOperand(receiverObjId
);
1578 ins
= MConstantProto::New(alloc(), ins
, receiverObj
->skipObjectGuards());
1581 return defineOperand(resultId
, ins
);
1584 bool WarpCacheIRTranspiler::emitLoadProto(ObjOperandId objId
,
1585 ObjOperandId resultId
) {
1586 MDefinition
* obj
= getOperand(objId
);
1588 auto* ins
= MObjectStaticProto::New(alloc(), obj
);
1591 return defineOperand(resultId
, ins
);
1594 bool WarpCacheIRTranspiler::emitLoadInstanceOfObjectResult(
1595 ValOperandId lhsId
, ObjOperandId protoId
) {
1596 MDefinition
* lhs
= getOperand(lhsId
);
1597 MDefinition
* proto
= getOperand(protoId
);
1599 auto* instanceOf
= MInstanceOf::New(alloc(), lhs
, proto
);
1600 addEffectful(instanceOf
);
1602 pushResult(instanceOf
);
1603 return resumeAfter(instanceOf
);
1606 bool WarpCacheIRTranspiler::emitLoadValueTag(ValOperandId valId
,
1607 ValueTagOperandId resultId
) {
1608 MDefinition
* val
= getOperand(valId
);
1610 auto* ins
= MLoadValueTag::New(alloc(), val
);
1613 return defineOperand(resultId
, ins
);
1616 bool WarpCacheIRTranspiler::emitLoadDynamicSlotResult(ObjOperandId objId
,
1617 uint32_t offsetOffset
) {
1618 int32_t offset
= int32StubField(offsetOffset
);
1620 MDefinition
* obj
= getOperand(objId
);
1621 size_t slotIndex
= NativeObject::getDynamicSlotIndexFromOffset(offset
);
1623 auto* slots
= MSlots::New(alloc(), obj
);
1626 auto* load
= MLoadDynamicSlot::New(alloc(), slots
, slotIndex
);
1633 bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId
,
1635 uint32_t offsetOffset
) {
1636 MDefinition
* obj
= getOperand(objId
);
1638 size_t offset
= int32StubField(offsetOffset
);
1639 uint32_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
1641 auto* load
= MLoadFixedSlot::New(alloc(), obj
, slotIndex
);
1644 return defineOperand(resultId
, load
);
1647 bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId
,
1648 uint32_t offsetOffset
) {
1649 int32_t offset
= int32StubField(offsetOffset
);
1651 MDefinition
* obj
= getOperand(objId
);
1652 uint32_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
1654 auto* load
= MLoadFixedSlot::New(alloc(), obj
, slotIndex
);
1661 bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId
,
1662 uint32_t offsetOffset
,
1664 int32_t offset
= int32StubField(offsetOffset
);
1666 MDefinition
* obj
= getOperand(objId
);
1667 uint32_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
1669 auto* load
= MLoadFixedSlot::New(alloc(), obj
, slotIndex
);
1670 load
->setResultType(MIRTypeFromValueType(JSValueType(type
)));
1677 bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
1678 ValOperandId valId
) {
1679 MDefinition
* val
= getOperand(valId
);
1681 auto* lexicalCheck
= MLexicalCheck::New(alloc(), val
);
1684 if (snapshot().bailoutInfo().failedLexicalCheck()) {
1685 lexicalCheck
->setNotMovable();
1688 setOperand(valId
, lexicalCheck
);
1692 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId
) {
1693 MDefinition
* obj
= getOperand(objId
);
1695 auto* elements
= MElements::New(alloc(), obj
);
1698 auto* length
= MArrayLength::New(alloc(), elements
);
1705 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId
,
1706 Int32OperandId resultId
) {
1707 MDefinition
* obj
= getOperand(objId
);
1709 auto* elements
= MElements::New(alloc(), obj
);
1712 auto* length
= MArrayLength::New(alloc(), elements
);
1715 return defineOperand(resultId
, length
);
1718 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgResult(
1719 ObjOperandId objId
, Int32OperandId indexId
) {
1720 MDefinition
* obj
= getOperand(objId
);
1721 MDefinition
* index
= getOperand(indexId
);
1723 auto* load
= MLoadArgumentsObjectArg::New(alloc(), obj
, index
);
1730 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgHoleResult(
1731 ObjOperandId objId
, Int32OperandId indexId
) {
1732 MDefinition
* obj
= getOperand(objId
);
1733 MDefinition
* index
= getOperand(indexId
);
1735 auto* load
= MLoadArgumentsObjectArgHole::New(alloc(), obj
, index
);
1742 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgExistsResult(
1743 ObjOperandId objId
, Int32OperandId indexId
) {
1744 MDefinition
* obj
= getOperand(objId
);
1745 MDefinition
* index
= getOperand(indexId
);
1747 auto* ins
= MInArgumentsObjectArg::New(alloc(), obj
, index
);
1754 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
1755 ObjOperandId objId
) {
1756 MDefinition
* obj
= getOperand(objId
);
1758 auto* length
= MArgumentsObjectLength::New(alloc(), obj
);
1765 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
1766 ObjOperandId objId
, Int32OperandId resultId
) {
1767 MDefinition
* obj
= getOperand(objId
);
1769 auto* length
= MArgumentsObjectLength::New(alloc(), obj
);
1772 return defineOperand(resultId
, length
);
1775 bool WarpCacheIRTranspiler::emitLoadBoundFunctionNumArgs(
1776 ObjOperandId objId
, Int32OperandId resultId
) {
1777 MDefinition
* obj
= getOperand(objId
);
1779 auto* numArgs
= MBoundFunctionNumArgs::New(alloc(), obj
);
1782 return defineOperand(resultId
, numArgs
);
1785 bool WarpCacheIRTranspiler::emitLoadBoundFunctionTarget(ObjOperandId objId
,
1786 ObjOperandId resultId
) {
1787 MDefinition
* obj
= getOperand(objId
);
1789 auto* target
= MLoadFixedSlotAndUnbox::New(
1790 alloc(), obj
, BoundFunctionObject::targetSlot(), MUnbox::Mode::Infallible
,
1794 return defineOperand(resultId
, target
);
1797 bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
1798 ObjOperandId objId
) {
1799 MDefinition
* obj
= getOperand(objId
);
1801 auto* guard
= MGuardBoundFunctionIsConstructor::New(alloc(), obj
);
1804 setOperand(objId
, guard
);
1808 bool WarpCacheIRTranspiler::emitGuardObjectIdentity(ObjOperandId obj1Id
,
1809 ObjOperandId obj2Id
) {
1810 MDefinition
* obj1
= getOperand(obj1Id
);
1811 MDefinition
* obj2
= getOperand(obj2Id
);
1813 auto* guard
= MGuardObjectIdentity::New(alloc(), obj1
, obj2
,
1814 /* bailOnEquality = */ false);
1819 bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
1820 ObjOperandId objId
, uint32_t shapeOffset
) {
1821 MDefinition
* obj
= getOperand(objId
);
1822 Shape
* shape
= shapeStubField(shapeOffset
);
1825 auto* array
= MArrayFromArgumentsObject::New(alloc(), obj
, shape
);
1826 addEffectful(array
);
1829 return resumeAfter(array
);
1832 bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId
) {
1833 MDefinition
* obj
= getOperand(objId
);
1835 auto* length
= MFunctionLength::New(alloc(), obj
);
1842 bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId
) {
1843 MDefinition
* obj
= getOperand(objId
);
1845 auto* name
= MFunctionName::New(alloc(), obj
);
1852 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
1853 ObjOperandId objId
) {
1854 MDefinition
* obj
= getOperand(objId
);
1856 auto* length
= MArrayBufferByteLength::New(alloc(), obj
);
1859 auto* lengthInt32
= MNonNegativeIntPtrToInt32::New(alloc(), length
);
1862 pushResult(lengthInt32
);
1866 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
1867 ObjOperandId objId
) {
1868 MDefinition
* obj
= getOperand(objId
);
1870 auto* length
= MArrayBufferByteLength::New(alloc(), obj
);
1873 auto* lengthDouble
= MIntPtrToDouble::New(alloc(), length
);
1876 pushResult(lengthDouble
);
1880 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthInt32Result(
1881 ObjOperandId objId
) {
1882 MDefinition
* obj
= getOperand(objId
);
1884 // Use a separate instruction for converting the length to Int32, so that we
1885 // can fold the MArrayBufferViewLength instruction with length instructions
1886 // added for bounds checks.
1888 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
1891 auto* lengthInt32
= MNonNegativeIntPtrToInt32::New(alloc(), length
);
1894 pushResult(lengthInt32
);
1898 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
1899 ObjOperandId objId
) {
1900 MDefinition
* obj
= getOperand(objId
);
1902 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
1905 auto* lengthDouble
= MIntPtrToDouble::New(alloc(), length
);
1908 pushResult(lengthDouble
);
1912 bool WarpCacheIRTranspiler::emitLoadStringLengthResult(StringOperandId strId
) {
1913 MDefinition
* str
= getOperand(strId
);
1915 auto* length
= MStringLength::New(alloc(), str
);
1922 MInstruction
* WarpCacheIRTranspiler::addBoundsCheck(MDefinition
* index
,
1923 MDefinition
* length
) {
1924 MInstruction
* check
= MBoundsCheck::New(alloc(), index
, length
);
1927 if (snapshot().bailoutInfo().failedBoundsCheck()) {
1928 check
->setNotMovable();
1931 if (JitOptions
.spectreIndexMasking
) {
1932 // Use a separate MIR instruction for the index masking. Doing this as
1933 // part of MBoundsCheck would be unsound because bounds checks can be
1934 // optimized or eliminated completely. Consider this:
1936 // for (var i = 0; i < x; i++)
1939 // If we can prove |x < arr.length|, we are able to eliminate the bounds
1940 // check, but we should not get rid of the index masking because the
1941 // |i < x| branch could still be mispredicted.
1943 // Using a separate instruction lets us eliminate the bounds check
1944 // without affecting the index masking.
1945 check
= MSpectreMaskIndex::New(alloc(), check
, length
);
1952 bool WarpCacheIRTranspiler::emitLoadDenseElementResult(ObjOperandId objId
,
1953 Int32OperandId indexId
) {
1954 MDefinition
* obj
= getOperand(objId
);
1955 MDefinition
* index
= getOperand(indexId
);
1957 auto* elements
= MElements::New(alloc(), obj
);
1960 auto* length
= MInitializedLength::New(alloc(), elements
);
1963 index
= addBoundsCheck(index
, length
);
1965 auto* load
= MLoadElement::New(alloc(), elements
, index
);
1972 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleResult(
1973 ObjOperandId objId
, Int32OperandId indexId
) {
1974 MDefinition
* obj
= getOperand(objId
);
1975 MDefinition
* index
= getOperand(indexId
);
1977 auto* elements
= MElements::New(alloc(), obj
);
1980 auto* length
= MInitializedLength::New(alloc(), elements
);
1983 auto* load
= MLoadElementHole::New(alloc(), elements
, index
, length
);
1990 bool WarpCacheIRTranspiler::emitCallGetSparseElementResult(
1991 ObjOperandId objId
, Int32OperandId indexId
) {
1992 MDefinition
* obj
= getOperand(objId
);
1993 MDefinition
* index
= getOperand(indexId
);
1995 auto* call
= MCallGetSparseElement::New(alloc(), obj
, index
);
1999 return resumeAfter(call
);
2002 bool WarpCacheIRTranspiler::emitCallNativeGetElementResult(
2003 ObjOperandId objId
, Int32OperandId indexId
) {
2004 MDefinition
* obj
= getOperand(objId
);
2005 MDefinition
* index
= getOperand(indexId
);
2007 auto* call
= MCallNativeGetElement::New(alloc(), obj
, index
);
2011 return resumeAfter(call
);
2014 bool WarpCacheIRTranspiler::emitCallNativeGetElementSuperResult(
2015 ObjOperandId objId
, Int32OperandId indexId
, ValOperandId receiverId
) {
2016 MDefinition
* obj
= getOperand(objId
);
2017 MDefinition
* index
= getOperand(indexId
);
2018 MDefinition
* receiver
= getOperand(receiverId
);
2020 auto* call
= MCallNativeGetElementSuper::New(alloc(), obj
, index
, receiver
);
2024 return resumeAfter(call
);
2027 bool WarpCacheIRTranspiler::emitLoadDenseElementExistsResult(
2028 ObjOperandId objId
, Int32OperandId indexId
) {
2029 MDefinition
* obj
= getOperand(objId
);
2030 MDefinition
* index
= getOperand(indexId
);
2032 // Get the elements vector.
2033 auto* elements
= MElements::New(alloc(), obj
);
2036 auto* length
= MInitializedLength::New(alloc(), elements
);
2039 // Check if id < initLength.
2040 index
= addBoundsCheck(index
, length
);
2042 // And check elem[id] is not a hole.
2043 auto* guard
= MGuardElementNotHole::New(alloc(), elements
, index
);
2046 pushResult(constant(BooleanValue(true)));
2050 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleExistsResult(
2051 ObjOperandId objId
, Int32OperandId indexId
) {
2052 MDefinition
* obj
= getOperand(objId
);
2053 MDefinition
* index
= getOperand(indexId
);
2055 // Get the elements vector.
2056 auto* elements
= MElements::New(alloc(), obj
);
2059 auto* length
= MInitializedLength::New(alloc(), elements
);
2062 // Check if id < initLength and elem[id] not a hole.
2063 auto* ins
= MInArray::New(alloc(), elements
, index
, length
);
2070 bool WarpCacheIRTranspiler::emitCallObjectHasSparseElementResult(
2071 ObjOperandId objId
, Int32OperandId indexId
) {
2072 MDefinition
* obj
= getOperand(objId
);
2073 MDefinition
* index
= getOperand(indexId
);
2075 auto* ins
= MCallObjectHasSparseElement::New(alloc(), obj
, index
);
2082 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementExistsResult(
2083 ObjOperandId objId
, IntPtrOperandId indexId
) {
2084 MDefinition
* obj
= getOperand(objId
);
2085 MDefinition
* index
= getOperand(indexId
);
2087 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
2090 // Unsigned comparison to catch negative indices.
2091 auto* ins
= MCompare::New(alloc(), index
, length
, JSOp::Lt
,
2092 MCompare::Compare_UIntPtr
);
2099 static MIRType
MIRTypeForArrayBufferViewRead(Scalar::Type arrayType
,
2100 bool forceDoubleForUint32
) {
2101 switch (arrayType
) {
2104 case Scalar::Uint8Clamped
:
2106 case Scalar::Uint16
:
2108 return MIRType::Int32
;
2109 case Scalar::Uint32
:
2110 return forceDoubleForUint32
? MIRType::Double
: MIRType::Int32
;
2111 case Scalar::Float32
:
2112 return MIRType::Float32
;
2113 case Scalar::Float64
:
2114 return MIRType::Double
;
2115 case Scalar::BigInt64
:
2116 case Scalar::BigUint64
:
2117 return MIRType::BigInt
;
2121 MOZ_CRASH("Unknown typed array type");
2124 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementResult(
2125 ObjOperandId objId
, IntPtrOperandId indexId
, Scalar::Type elementType
,
2126 bool handleOOB
, bool forceDoubleForUint32
) {
2127 MDefinition
* obj
= getOperand(objId
);
2128 MDefinition
* index
= getOperand(indexId
);
2131 auto* load
= MLoadTypedArrayElementHole::New(
2132 alloc(), obj
, index
, elementType
, forceDoubleForUint32
);
2139 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
2142 index
= addBoundsCheck(index
, length
);
2144 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
2147 auto* load
= MLoadUnboxedScalar::New(alloc(), elements
, index
, elementType
);
2148 load
->setResultType(
2149 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
));
2156 bool WarpCacheIRTranspiler::emitLinearizeForCharAccess(
2157 StringOperandId strId
, Int32OperandId indexId
, StringOperandId resultId
) {
2158 MDefinition
* str
= getOperand(strId
);
2159 MDefinition
* index
= getOperand(indexId
);
2161 auto* ins
= MLinearizeForCharAccess::New(alloc(), str
, index
);
2164 return defineOperand(resultId
, ins
);
2167 bool WarpCacheIRTranspiler::emitLinearizeForCodePointAccess(
2168 StringOperandId strId
, Int32OperandId indexId
, StringOperandId resultId
) {
2169 MDefinition
* str
= getOperand(strId
);
2170 MDefinition
* index
= getOperand(indexId
);
2172 auto* ins
= MLinearizeForCodePointAccess::New(alloc(), str
, index
);
2175 return defineOperand(resultId
, ins
);
2178 bool WarpCacheIRTranspiler::emitLoadStringCharResult(StringOperandId strId
,
2179 Int32OperandId indexId
,
2181 MDefinition
* str
= getOperand(strId
);
2182 MDefinition
* index
= getOperand(indexId
);
2185 auto* charCode
= MCharCodeAtOrNegative::New(alloc(), str
, index
);
2188 auto* fromCharCode
= MFromCharCodeEmptyIfNegative::New(alloc(), charCode
);
2191 pushResult(fromCharCode
);
2195 auto* length
= MStringLength::New(alloc(), str
);
2198 index
= addBoundsCheck(index
, length
);
2200 auto* charCode
= MCharCodeAt::New(alloc(), str
, index
);
2203 auto* fromCharCode
= MFromCharCode::New(alloc(), charCode
);
2206 pushResult(fromCharCode
);
2210 bool WarpCacheIRTranspiler::emitLoadStringCharCodeResult(StringOperandId strId
,
2211 Int32OperandId indexId
,
2213 MDefinition
* str
= getOperand(strId
);
2214 MDefinition
* index
= getOperand(indexId
);
2217 auto* charCode
= MCharCodeAtOrNegative::New(alloc(), str
, index
);
2220 auto* ins
= MNegativeToNaN::New(alloc(), charCode
);
2227 auto* length
= MStringLength::New(alloc(), str
);
2230 index
= addBoundsCheck(index
, length
);
2232 auto* charCode
= MCharCodeAt::New(alloc(), str
, index
);
2235 pushResult(charCode
);
2239 bool WarpCacheIRTranspiler::emitLoadStringCodePointResult(
2240 StringOperandId strId
, Int32OperandId indexId
, bool handleOOB
) {
2241 MDefinition
* str
= getOperand(strId
);
2242 MDefinition
* index
= getOperand(indexId
);
2245 auto* codePoint
= MCodePointAtOrNegative::New(alloc(), str
, index
);
2248 auto* ins
= MNegativeToUndefined::New(alloc(), codePoint
);
2255 auto* length
= MStringLength::New(alloc(), str
);
2258 index
= addBoundsCheck(index
, length
);
2260 auto* codePoint
= MCodePointAt::New(alloc(), str
, index
);
2263 pushResult(codePoint
);
2267 bool WarpCacheIRTranspiler::emitNewStringObjectResult(
2268 uint32_t templateObjectOffset
, StringOperandId strId
) {
2269 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
2270 MDefinition
* string
= getOperand(strId
);
2272 auto* obj
= MNewStringObject::New(alloc(), string
, templateObj
);
2276 return resumeAfter(obj
);
2279 bool WarpCacheIRTranspiler::emitStringFromCharCodeResult(
2280 Int32OperandId codeId
) {
2281 MDefinition
* code
= getOperand(codeId
);
2283 auto* fromCharCode
= MFromCharCode::New(alloc(), code
);
2286 pushResult(fromCharCode
);
2290 bool WarpCacheIRTranspiler::emitStringFromCodePointResult(
2291 Int32OperandId codeId
) {
2292 MDefinition
* code
= getOperand(codeId
);
2294 auto* fromCodePoint
= MFromCodePoint::New(alloc(), code
);
2297 pushResult(fromCodePoint
);
2301 bool WarpCacheIRTranspiler::emitStringIncludesResult(
2302 StringOperandId strId
, StringOperandId searchStrId
) {
2303 MDefinition
* str
= getOperand(strId
);
2304 MDefinition
* searchStr
= getOperand(searchStrId
);
2306 auto* includes
= MStringIncludes::New(alloc(), str
, searchStr
);
2309 pushResult(includes
);
2313 bool WarpCacheIRTranspiler::emitStringIndexOfResult(
2314 StringOperandId strId
, StringOperandId searchStrId
) {
2315 MDefinition
* str
= getOperand(strId
);
2316 MDefinition
* searchStr
= getOperand(searchStrId
);
2318 auto* indexOf
= MStringIndexOf::New(alloc(), str
, searchStr
);
2321 pushResult(indexOf
);
2325 bool WarpCacheIRTranspiler::emitStringLastIndexOfResult(
2326 StringOperandId strId
, StringOperandId searchStrId
) {
2327 MDefinition
* str
= getOperand(strId
);
2328 MDefinition
* searchStr
= getOperand(searchStrId
);
2330 auto* lastIndexOf
= MStringLastIndexOf::New(alloc(), str
, searchStr
);
2333 pushResult(lastIndexOf
);
2337 bool WarpCacheIRTranspiler::emitStringStartsWithResult(
2338 StringOperandId strId
, StringOperandId searchStrId
) {
2339 MDefinition
* str
= getOperand(strId
);
2340 MDefinition
* searchStr
= getOperand(searchStrId
);
2342 auto* startsWith
= MStringStartsWith::New(alloc(), str
, searchStr
);
2345 pushResult(startsWith
);
2349 bool WarpCacheIRTranspiler::emitStringEndsWithResult(
2350 StringOperandId strId
, StringOperandId searchStrId
) {
2351 MDefinition
* str
= getOperand(strId
);
2352 MDefinition
* searchStr
= getOperand(searchStrId
);
2354 auto* endsWith
= MStringEndsWith::New(alloc(), str
, searchStr
);
2357 pushResult(endsWith
);
2361 bool WarpCacheIRTranspiler::emitStringToLowerCaseResult(StringOperandId strId
) {
2362 MDefinition
* str
= getOperand(strId
);
2365 MStringConvertCase::New(alloc(), str
, MStringConvertCase::LowerCase
);
2368 pushResult(convert
);
2372 bool WarpCacheIRTranspiler::emitStringToUpperCaseResult(StringOperandId strId
) {
2373 MDefinition
* str
= getOperand(strId
);
2376 MStringConvertCase::New(alloc(), str
, MStringConvertCase::UpperCase
);
2379 pushResult(convert
);
2383 bool WarpCacheIRTranspiler::emitStringTrimResult(StringOperandId strId
) {
2384 MDefinition
* str
= getOperand(strId
);
2386 auto* linear
= MLinearizeString::New(alloc(), str
);
2389 auto* start
= MStringTrimStartIndex::New(alloc(), linear
);
2392 auto* end
= MStringTrimEndIndex::New(alloc(), linear
, start
);
2395 // Safe to truncate because both operands are positive and end >= start.
2396 auto* length
= MSub::New(alloc(), end
, start
, MIRType::Int32
);
2397 length
->setTruncateKind(TruncateKind::Truncate
);
2400 auto* substr
= MSubstr::New(alloc(), linear
, start
, length
);
2407 bool WarpCacheIRTranspiler::emitStringTrimStartResult(StringOperandId strId
) {
2408 MDefinition
* str
= getOperand(strId
);
2410 auto* linear
= MLinearizeString::New(alloc(), str
);
2413 auto* start
= MStringTrimStartIndex::New(alloc(), linear
);
2416 auto* end
= MStringLength::New(alloc(), linear
);
2419 // Safe to truncate because both operands are positive and end >= start.
2420 auto* length
= MSub::New(alloc(), end
, start
, MIRType::Int32
);
2421 length
->setTruncateKind(TruncateKind::Truncate
);
2424 auto* substr
= MSubstr::New(alloc(), linear
, start
, length
);
2431 bool WarpCacheIRTranspiler::emitStringTrimEndResult(StringOperandId strId
) {
2432 MDefinition
* str
= getOperand(strId
);
2434 auto* linear
= MLinearizeString::New(alloc(), str
);
2437 auto* start
= constant(Int32Value(0));
2439 auto* length
= MStringTrimEndIndex::New(alloc(), linear
, start
);
2442 auto* substr
= MSubstr::New(alloc(), linear
, start
, length
);
2449 bool WarpCacheIRTranspiler::emitStoreDynamicSlot(ObjOperandId objId
,
2450 uint32_t offsetOffset
,
2451 ValOperandId rhsId
) {
2452 int32_t offset
= int32StubField(offsetOffset
);
2454 MDefinition
* obj
= getOperand(objId
);
2455 size_t slotIndex
= NativeObject::getDynamicSlotIndexFromOffset(offset
);
2456 MDefinition
* rhs
= getOperand(rhsId
);
2458 auto* barrier
= MPostWriteBarrier::New(alloc(), obj
, rhs
);
2461 auto* slots
= MSlots::New(alloc(), obj
);
2464 auto* store
= MStoreDynamicSlot::NewBarriered(alloc(), slots
, slotIndex
, rhs
);
2465 addEffectful(store
);
2466 return resumeAfter(store
);
2469 bool WarpCacheIRTranspiler::emitStoreFixedSlot(ObjOperandId objId
,
2470 uint32_t offsetOffset
,
2471 ValOperandId rhsId
) {
2472 int32_t offset
= int32StubField(offsetOffset
);
2474 MDefinition
* obj
= getOperand(objId
);
2475 size_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
2476 MDefinition
* rhs
= getOperand(rhsId
);
2478 auto* barrier
= MPostWriteBarrier::New(alloc(), obj
, rhs
);
2481 auto* store
= MStoreFixedSlot::NewBarriered(alloc(), obj
, slotIndex
, rhs
);
2482 addEffectful(store
);
2483 return resumeAfter(store
);
2486 bool WarpCacheIRTranspiler::emitStoreFixedSlotUndefinedResult(
2487 ObjOperandId objId
, uint32_t offsetOffset
, ValOperandId rhsId
) {
2488 int32_t offset
= int32StubField(offsetOffset
);
2490 MDefinition
* obj
= getOperand(objId
);
2491 size_t slotIndex
= NativeObject::getFixedSlotIndexFromOffset(offset
);
2492 MDefinition
* rhs
= getOperand(rhsId
);
2494 auto* barrier
= MPostWriteBarrier::New(alloc(), obj
, rhs
);
2497 auto* store
= MStoreFixedSlot::NewBarriered(alloc(), obj
, slotIndex
, rhs
);
2498 addEffectful(store
);
2500 auto* undef
= constant(UndefinedValue());
2503 return resumeAfter(store
);
2506 bool WarpCacheIRTranspiler::emitAddAndStoreSlotShared(
2507 MAddAndStoreSlot::Kind kind
, ObjOperandId objId
, uint32_t offsetOffset
,
2508 ValOperandId rhsId
, uint32_t newShapeOffset
) {
2509 int32_t offset
= int32StubField(offsetOffset
);
2510 Shape
* shape
= shapeStubField(newShapeOffset
);
2512 MDefinition
* obj
= getOperand(objId
);
2513 MDefinition
* rhs
= getOperand(rhsId
);
2515 auto* barrier
= MPostWriteBarrier::New(alloc(), obj
, rhs
);
2519 MAddAndStoreSlot::New(alloc(), obj
, rhs
, kind
, offset
, shape
);
2520 addEffectful(addAndStore
);
2522 return resumeAfter(addAndStore
);
2525 bool WarpCacheIRTranspiler::emitAddAndStoreFixedSlot(ObjOperandId objId
,
2526 uint32_t offsetOffset
,
2528 uint32_t newShapeOffset
) {
2529 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::FixedSlot
, objId
,
2530 offsetOffset
, rhsId
, newShapeOffset
);
2533 bool WarpCacheIRTranspiler::emitAddAndStoreDynamicSlot(
2534 ObjOperandId objId
, uint32_t offsetOffset
, ValOperandId rhsId
,
2535 uint32_t newShapeOffset
) {
2536 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::DynamicSlot
, objId
,
2537 offsetOffset
, rhsId
, newShapeOffset
);
2540 bool WarpCacheIRTranspiler::emitAllocateAndStoreDynamicSlot(
2541 ObjOperandId objId
, uint32_t offsetOffset
, ValOperandId rhsId
,
2542 uint32_t newShapeOffset
, uint32_t numNewSlotsOffset
) {
2543 int32_t offset
= int32StubField(offsetOffset
);
2544 Shape
* shape
= shapeStubField(newShapeOffset
);
2545 uint32_t numNewSlots
= uint32StubField(numNewSlotsOffset
);
2547 MDefinition
* obj
= getOperand(objId
);
2548 MDefinition
* rhs
= getOperand(rhsId
);
2550 auto* barrier
= MPostWriteBarrier::New(alloc(), obj
, rhs
);
2553 auto* allocateAndStore
=
2554 MAllocateAndStoreSlot::New(alloc(), obj
, rhs
, offset
, shape
, numNewSlots
);
2555 addEffectful(allocateAndStore
);
2557 return resumeAfter(allocateAndStore
);
2560 bool WarpCacheIRTranspiler::emitAddSlotAndCallAddPropHook(
2561 ObjOperandId objId
, ValOperandId rhsId
, uint32_t newShapeOffset
) {
2562 Shape
* shape
= shapeStubField(newShapeOffset
);
2563 MDefinition
* obj
= getOperand(objId
);
2564 MDefinition
* rhs
= getOperand(rhsId
);
2566 auto* addProp
= MAddSlotAndCallAddPropHook::New(alloc(), obj
, rhs
, shape
);
2567 addEffectful(addProp
);
2569 return resumeAfter(addProp
);
2572 bool WarpCacheIRTranspiler::emitStoreDenseElement(ObjOperandId objId
,
2573 Int32OperandId indexId
,
2574 ValOperandId rhsId
) {
2575 MDefinition
* obj
= getOperand(objId
);
2576 MDefinition
* index
= getOperand(indexId
);
2577 MDefinition
* rhs
= getOperand(rhsId
);
2579 auto* elements
= MElements::New(alloc(), obj
);
2582 auto* length
= MInitializedLength::New(alloc(), elements
);
2585 index
= addBoundsCheck(index
, length
);
2587 auto* barrier
= MPostWriteElementBarrier::New(alloc(), obj
, rhs
, index
);
2590 bool needsHoleCheck
= true;
2591 auto* store
= MStoreElement::NewBarriered(alloc(), elements
, index
, rhs
,
2593 addEffectful(store
);
2594 return resumeAfter(store
);
2597 bool WarpCacheIRTranspiler::emitStoreDenseElementHole(ObjOperandId objId
,
2598 Int32OperandId indexId
,
2601 MDefinition
* obj
= getOperand(objId
);
2602 MDefinition
* index
= getOperand(indexId
);
2603 MDefinition
* rhs
= getOperand(rhsId
);
2605 auto* elements
= MElements::New(alloc(), obj
);
2608 MInstruction
* store
;
2610 // TODO(post-Warp): Consider changing MStoreElementHole to match IC code.
2611 store
= MStoreElementHole::New(alloc(), obj
, elements
, index
, rhs
);
2613 auto* length
= MInitializedLength::New(alloc(), elements
);
2616 index
= addBoundsCheck(index
, length
);
2618 auto* barrier
= MPostWriteElementBarrier::New(alloc(), obj
, rhs
, index
);
2621 bool needsHoleCheck
= false;
2622 store
= MStoreElement::NewBarriered(alloc(), elements
, index
, rhs
,
2625 addEffectful(store
);
2627 return resumeAfter(store
);
2630 bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(ObjOperandId objId
,
2631 Scalar::Type elementType
,
2632 IntPtrOperandId indexId
,
2635 MDefinition
* obj
= getOperand(objId
);
2636 MDefinition
* index
= getOperand(indexId
);
2637 MDefinition
* rhs
= getOperand(ValOperandId(rhsId
));
2639 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
2643 // MStoreTypedArrayElementHole does the bounds checking.
2644 index
= addBoundsCheck(index
, length
);
2647 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
2650 MInstruction
* store
;
2652 store
= MStoreTypedArrayElementHole::New(alloc(), elements
, length
, index
,
2656 MStoreUnboxedScalar::New(alloc(), elements
, index
, rhs
, elementType
);
2658 addEffectful(store
);
2659 return resumeAfter(store
);
2662 void WarpCacheIRTranspiler::addDataViewData(MDefinition
* obj
, Scalar::Type type
,
2663 MDefinition
** offset
,
2664 MInstruction
** elements
) {
2665 MInstruction
* length
= MArrayBufferViewLength::New(alloc(), obj
);
2668 // Adjust the length to account for accesses near the end of the dataview.
2669 if (size_t byteSize
= Scalar::byteSize(type
); byteSize
> 1) {
2670 // To ensure |0 <= offset && offset + byteSize <= length|, first adjust the
2671 // length by subtracting |byteSize - 1| (bailing out if that becomes
2673 length
= MAdjustDataViewLength::New(alloc(), length
, byteSize
);
2677 *offset
= addBoundsCheck(*offset
, length
);
2679 *elements
= MArrayBufferViewElements::New(alloc(), obj
);
2683 bool WarpCacheIRTranspiler::emitLoadDataViewValueResult(
2684 ObjOperandId objId
, IntPtrOperandId offsetId
,
2685 BooleanOperandId littleEndianId
, Scalar::Type elementType
,
2686 bool forceDoubleForUint32
) {
2687 MDefinition
* obj
= getOperand(objId
);
2688 MDefinition
* offset
= getOperand(offsetId
);
2689 MDefinition
* littleEndian
= getOperand(littleEndianId
);
2691 // Add bounds check and get the DataViewObject's elements.
2692 MInstruction
* elements
;
2693 addDataViewData(obj
, elementType
, &offset
, &elements
);
2695 // Load the element.
2697 if (Scalar::byteSize(elementType
) == 1) {
2698 load
= MLoadUnboxedScalar::New(alloc(), elements
, offset
, elementType
);
2700 load
= MLoadDataViewElement::New(alloc(), elements
, offset
, littleEndian
,
2706 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
);
2707 load
->setResultType(knownType
);
2713 bool WarpCacheIRTranspiler::emitStoreDataViewValueResult(
2714 ObjOperandId objId
, IntPtrOperandId offsetId
, uint32_t valueId
,
2715 BooleanOperandId littleEndianId
, Scalar::Type elementType
) {
2716 MDefinition
* obj
= getOperand(objId
);
2717 MDefinition
* offset
= getOperand(offsetId
);
2718 MDefinition
* value
= getOperand(ValOperandId(valueId
));
2719 MDefinition
* littleEndian
= getOperand(littleEndianId
);
2721 // Add bounds check and get the DataViewObject's elements.
2722 MInstruction
* elements
;
2723 addDataViewData(obj
, elementType
, &offset
, &elements
);
2725 // Store the element.
2726 MInstruction
* store
;
2727 if (Scalar::byteSize(elementType
) == 1) {
2729 MStoreUnboxedScalar::New(alloc(), elements
, offset
, value
, elementType
);
2731 store
= MStoreDataViewElement::New(alloc(), elements
, offset
, value
,
2732 littleEndian
, elementType
);
2734 addEffectful(store
);
2736 pushResult(constant(UndefinedValue()));
2738 return resumeAfter(store
);
2741 bool WarpCacheIRTranspiler::emitInt32IncResult(Int32OperandId inputId
) {
2742 MDefinition
* input
= getOperand(inputId
);
2744 auto* constOne
= MConstant::New(alloc(), Int32Value(1));
2747 auto* ins
= MAdd::New(alloc(), input
, constOne
, MIRType::Int32
);
2754 bool WarpCacheIRTranspiler::emitDoubleIncResult(NumberOperandId inputId
) {
2755 MDefinition
* input
= getOperand(inputId
);
2757 auto* constOne
= MConstant::New(alloc(), DoubleValue(1.0));
2760 auto* ins
= MAdd::New(alloc(), input
, constOne
, MIRType::Double
);
2767 bool WarpCacheIRTranspiler::emitInt32DecResult(Int32OperandId inputId
) {
2768 MDefinition
* input
= getOperand(inputId
);
2770 auto* constOne
= MConstant::New(alloc(), Int32Value(1));
2773 auto* ins
= MSub::New(alloc(), input
, constOne
, MIRType::Int32
);
2780 bool WarpCacheIRTranspiler::emitDoubleDecResult(NumberOperandId inputId
) {
2781 MDefinition
* input
= getOperand(inputId
);
2783 auto* constOne
= MConstant::New(alloc(), DoubleValue(1.0));
2786 auto* ins
= MSub::New(alloc(), input
, constOne
, MIRType::Double
);
2793 bool WarpCacheIRTranspiler::emitInt32NegationResult(Int32OperandId inputId
) {
2794 MDefinition
* input
= getOperand(inputId
);
2796 auto* constNegOne
= MConstant::New(alloc(), Int32Value(-1));
2799 auto* ins
= MMul::New(alloc(), input
, constNegOne
, MIRType::Int32
);
2806 bool WarpCacheIRTranspiler::emitDoubleNegationResult(NumberOperandId inputId
) {
2807 MDefinition
* input
= getOperand(inputId
);
2809 auto* constNegOne
= MConstant::New(alloc(), DoubleValue(-1.0));
2812 auto* ins
= MMul::New(alloc(), input
, constNegOne
, MIRType::Double
);
2819 bool WarpCacheIRTranspiler::emitInt32NotResult(Int32OperandId inputId
) {
2820 MDefinition
* input
= getOperand(inputId
);
2822 auto* ins
= MBitNot::New(alloc(), input
);
2829 template <typename T
>
2830 bool WarpCacheIRTranspiler::emitDoubleBinaryArithResult(NumberOperandId lhsId
,
2831 NumberOperandId rhsId
) {
2832 MDefinition
* lhs
= getOperand(lhsId
);
2833 MDefinition
* rhs
= getOperand(rhsId
);
2835 auto* ins
= T::New(alloc(), lhs
, rhs
, MIRType::Double
);
2842 bool WarpCacheIRTranspiler::emitDoubleAddResult(NumberOperandId lhsId
,
2843 NumberOperandId rhsId
) {
2844 return emitDoubleBinaryArithResult
<MAdd
>(lhsId
, rhsId
);
2847 bool WarpCacheIRTranspiler::emitDoubleSubResult(NumberOperandId lhsId
,
2848 NumberOperandId rhsId
) {
2849 return emitDoubleBinaryArithResult
<MSub
>(lhsId
, rhsId
);
2852 bool WarpCacheIRTranspiler::emitDoubleMulResult(NumberOperandId lhsId
,
2853 NumberOperandId rhsId
) {
2854 return emitDoubleBinaryArithResult
<MMul
>(lhsId
, rhsId
);
2857 bool WarpCacheIRTranspiler::emitDoubleDivResult(NumberOperandId lhsId
,
2858 NumberOperandId rhsId
) {
2859 return emitDoubleBinaryArithResult
<MDiv
>(lhsId
, rhsId
);
2862 bool WarpCacheIRTranspiler::emitDoubleModResult(NumberOperandId lhsId
,
2863 NumberOperandId rhsId
) {
2864 return emitDoubleBinaryArithResult
<MMod
>(lhsId
, rhsId
);
2867 bool WarpCacheIRTranspiler::emitDoublePowResult(NumberOperandId lhsId
,
2868 NumberOperandId rhsId
) {
2869 return emitDoubleBinaryArithResult
<MPow
>(lhsId
, rhsId
);
2872 template <typename T
>
2873 bool WarpCacheIRTranspiler::emitInt32BinaryArithResult(Int32OperandId lhsId
,
2874 Int32OperandId rhsId
) {
2875 MDefinition
* lhs
= getOperand(lhsId
);
2876 MDefinition
* rhs
= getOperand(rhsId
);
2878 auto* ins
= T::New(alloc(), lhs
, rhs
, MIRType::Int32
);
2885 bool WarpCacheIRTranspiler::emitInt32AddResult(Int32OperandId lhsId
,
2886 Int32OperandId rhsId
) {
2887 return emitInt32BinaryArithResult
<MAdd
>(lhsId
, rhsId
);
2890 bool WarpCacheIRTranspiler::emitInt32SubResult(Int32OperandId lhsId
,
2891 Int32OperandId rhsId
) {
2892 return emitInt32BinaryArithResult
<MSub
>(lhsId
, rhsId
);
2895 bool WarpCacheIRTranspiler::emitInt32MulResult(Int32OperandId lhsId
,
2896 Int32OperandId rhsId
) {
2897 return emitInt32BinaryArithResult
<MMul
>(lhsId
, rhsId
);
2900 bool WarpCacheIRTranspiler::emitInt32DivResult(Int32OperandId lhsId
,
2901 Int32OperandId rhsId
) {
2902 return emitInt32BinaryArithResult
<MDiv
>(lhsId
, rhsId
);
2905 bool WarpCacheIRTranspiler::emitInt32ModResult(Int32OperandId lhsId
,
2906 Int32OperandId rhsId
) {
2907 return emitInt32BinaryArithResult
<MMod
>(lhsId
, rhsId
);
2910 bool WarpCacheIRTranspiler::emitInt32PowResult(Int32OperandId lhsId
,
2911 Int32OperandId rhsId
) {
2912 return emitInt32BinaryArithResult
<MPow
>(lhsId
, rhsId
);
2915 bool WarpCacheIRTranspiler::emitInt32BitOrResult(Int32OperandId lhsId
,
2916 Int32OperandId rhsId
) {
2917 return emitInt32BinaryArithResult
<MBitOr
>(lhsId
, rhsId
);
2920 bool WarpCacheIRTranspiler::emitInt32BitXorResult(Int32OperandId lhsId
,
2921 Int32OperandId rhsId
) {
2922 return emitInt32BinaryArithResult
<MBitXor
>(lhsId
, rhsId
);
2925 bool WarpCacheIRTranspiler::emitInt32BitAndResult(Int32OperandId lhsId
,
2926 Int32OperandId rhsId
) {
2927 return emitInt32BinaryArithResult
<MBitAnd
>(lhsId
, rhsId
);
2930 bool WarpCacheIRTranspiler::emitInt32LeftShiftResult(Int32OperandId lhsId
,
2931 Int32OperandId rhsId
) {
2932 return emitInt32BinaryArithResult
<MLsh
>(lhsId
, rhsId
);
2935 bool WarpCacheIRTranspiler::emitInt32RightShiftResult(Int32OperandId lhsId
,
2936 Int32OperandId rhsId
) {
2937 return emitInt32BinaryArithResult
<MRsh
>(lhsId
, rhsId
);
2940 bool WarpCacheIRTranspiler::emitInt32URightShiftResult(Int32OperandId lhsId
,
2941 Int32OperandId rhsId
,
2943 MDefinition
* lhs
= getOperand(lhsId
);
2944 MDefinition
* rhs
= getOperand(rhsId
);
2946 MIRType specialization
= forceDouble
? MIRType::Double
: MIRType::Int32
;
2947 auto* ins
= MUrsh::New(alloc(), lhs
, rhs
, specialization
);
2954 template <typename T
>
2955 bool WarpCacheIRTranspiler::emitBigIntBinaryArithResult(BigIntOperandId lhsId
,
2956 BigIntOperandId rhsId
) {
2957 MDefinition
* lhs
= getOperand(lhsId
);
2958 MDefinition
* rhs
= getOperand(rhsId
);
2960 auto* ins
= T::New(alloc(), lhs
, rhs
);
2967 bool WarpCacheIRTranspiler::emitBigIntAddResult(BigIntOperandId lhsId
,
2968 BigIntOperandId rhsId
) {
2969 return emitBigIntBinaryArithResult
<MBigIntAdd
>(lhsId
, rhsId
);
2972 bool WarpCacheIRTranspiler::emitBigIntSubResult(BigIntOperandId lhsId
,
2973 BigIntOperandId rhsId
) {
2974 return emitBigIntBinaryArithResult
<MBigIntSub
>(lhsId
, rhsId
);
2977 bool WarpCacheIRTranspiler::emitBigIntMulResult(BigIntOperandId lhsId
,
2978 BigIntOperandId rhsId
) {
2979 return emitBigIntBinaryArithResult
<MBigIntMul
>(lhsId
, rhsId
);
2982 template <typename T
>
2983 bool WarpCacheIRTranspiler::emitBigIntBinaryArithEffectfulResult(
2984 BigIntOperandId lhsId
, BigIntOperandId rhsId
) {
2985 MDefinition
* lhs
= getOperand(lhsId
);
2986 MDefinition
* rhs
= getOperand(rhsId
);
2988 auto* ins
= T::New(alloc(), lhs
, rhs
);
2990 if (ins
->isEffectful()) {
2994 return resumeAfter(ins
);
3003 bool WarpCacheIRTranspiler::emitBigIntDivResult(BigIntOperandId lhsId
,
3004 BigIntOperandId rhsId
) {
3005 return emitBigIntBinaryArithEffectfulResult
<MBigIntDiv
>(lhsId
, rhsId
);
3008 bool WarpCacheIRTranspiler::emitBigIntModResult(BigIntOperandId lhsId
,
3009 BigIntOperandId rhsId
) {
3010 return emitBigIntBinaryArithEffectfulResult
<MBigIntMod
>(lhsId
, rhsId
);
3013 bool WarpCacheIRTranspiler::emitBigIntPowResult(BigIntOperandId lhsId
,
3014 BigIntOperandId rhsId
) {
3015 return emitBigIntBinaryArithEffectfulResult
<MBigIntPow
>(lhsId
, rhsId
);
3018 bool WarpCacheIRTranspiler::emitBigIntBitAndResult(BigIntOperandId lhsId
,
3019 BigIntOperandId rhsId
) {
3020 return emitBigIntBinaryArithResult
<MBigIntBitAnd
>(lhsId
, rhsId
);
3023 bool WarpCacheIRTranspiler::emitBigIntBitOrResult(BigIntOperandId lhsId
,
3024 BigIntOperandId rhsId
) {
3025 return emitBigIntBinaryArithResult
<MBigIntBitOr
>(lhsId
, rhsId
);
3028 bool WarpCacheIRTranspiler::emitBigIntBitXorResult(BigIntOperandId lhsId
,
3029 BigIntOperandId rhsId
) {
3030 return emitBigIntBinaryArithResult
<MBigIntBitXor
>(lhsId
, rhsId
);
3033 bool WarpCacheIRTranspiler::emitBigIntLeftShiftResult(BigIntOperandId lhsId
,
3034 BigIntOperandId rhsId
) {
3035 return emitBigIntBinaryArithResult
<MBigIntLsh
>(lhsId
, rhsId
);
3038 bool WarpCacheIRTranspiler::emitBigIntRightShiftResult(BigIntOperandId lhsId
,
3039 BigIntOperandId rhsId
) {
3040 return emitBigIntBinaryArithResult
<MBigIntRsh
>(lhsId
, rhsId
);
3043 template <typename T
>
3044 bool WarpCacheIRTranspiler::emitBigIntUnaryArithResult(
3045 BigIntOperandId inputId
) {
3046 MDefinition
* input
= getOperand(inputId
);
3048 auto* ins
= T::New(alloc(), input
);
3055 bool WarpCacheIRTranspiler::emitBigIntIncResult(BigIntOperandId inputId
) {
3056 return emitBigIntUnaryArithResult
<MBigIntIncrement
>(inputId
);
3059 bool WarpCacheIRTranspiler::emitBigIntDecResult(BigIntOperandId inputId
) {
3060 return emitBigIntUnaryArithResult
<MBigIntDecrement
>(inputId
);
3063 bool WarpCacheIRTranspiler::emitBigIntNegationResult(BigIntOperandId inputId
) {
3064 return emitBigIntUnaryArithResult
<MBigIntNegate
>(inputId
);
3067 bool WarpCacheIRTranspiler::emitBigIntNotResult(BigIntOperandId inputId
) {
3068 return emitBigIntUnaryArithResult
<MBigIntBitNot
>(inputId
);
3071 bool WarpCacheIRTranspiler::emitCallStringConcatResult(StringOperandId lhsId
,
3072 StringOperandId rhsId
) {
3073 MDefinition
* lhs
= getOperand(lhsId
);
3074 MDefinition
* rhs
= getOperand(rhsId
);
3076 auto* ins
= MConcat::New(alloc(), lhs
, rhs
);
3083 bool WarpCacheIRTranspiler::emitCompareResult(
3084 JSOp op
, OperandId lhsId
, OperandId rhsId
,
3085 MCompare::CompareType compareType
) {
3086 MDefinition
* lhs
= getOperand(lhsId
);
3087 MDefinition
* rhs
= getOperand(rhsId
);
3089 auto* ins
= MCompare::New(alloc(), lhs
, rhs
, op
, compareType
);
3096 bool WarpCacheIRTranspiler::emitCompareInt32Result(JSOp op
,
3097 Int32OperandId lhsId
,
3098 Int32OperandId rhsId
) {
3099 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_Int32
);
3102 bool WarpCacheIRTranspiler::emitCompareDoubleResult(JSOp op
,
3103 NumberOperandId lhsId
,
3104 NumberOperandId rhsId
) {
3105 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_Double
);
3108 bool WarpCacheIRTranspiler::emitCompareObjectResult(JSOp op
, ObjOperandId lhsId
,
3109 ObjOperandId rhsId
) {
3110 MOZ_ASSERT(IsEqualityOp(op
));
3111 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_Object
);
3114 bool WarpCacheIRTranspiler::emitCompareStringResult(JSOp op
,
3115 StringOperandId lhsId
,
3116 StringOperandId rhsId
) {
3117 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_String
);
3120 bool WarpCacheIRTranspiler::emitCompareSymbolResult(JSOp op
,
3121 SymbolOperandId lhsId
,
3122 SymbolOperandId rhsId
) {
3123 MOZ_ASSERT(IsEqualityOp(op
));
3124 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_Symbol
);
3127 bool WarpCacheIRTranspiler::emitCompareBigIntResult(JSOp op
,
3128 BigIntOperandId lhsId
,
3129 BigIntOperandId rhsId
) {
3130 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_BigInt
);
3133 bool WarpCacheIRTranspiler::emitCompareBigIntInt32Result(JSOp op
,
3134 BigIntOperandId lhsId
,
3135 Int32OperandId rhsId
) {
3136 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_BigInt_Int32
);
3139 bool WarpCacheIRTranspiler::emitCompareBigIntNumberResult(
3140 JSOp op
, BigIntOperandId lhsId
, NumberOperandId rhsId
) {
3141 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_BigInt_Double
);
3144 bool WarpCacheIRTranspiler::emitCompareBigIntStringResult(
3145 JSOp op
, BigIntOperandId lhsId
, StringOperandId rhsId
) {
3146 return emitCompareResult(op
, lhsId
, rhsId
, MCompare::Compare_BigInt_String
);
3149 bool WarpCacheIRTranspiler::emitCompareNullUndefinedResult(
3150 JSOp op
, bool isUndefined
, ValOperandId inputId
) {
3151 MDefinition
* input
= getOperand(inputId
);
3153 MOZ_ASSERT(IsEqualityOp(op
));
3155 // A previously emitted guard ensures that one side of the comparison
3156 // is null or undefined.
3158 isUndefined
? constant(UndefinedValue()) : constant(NullValue());
3160 isUndefined
? MCompare::Compare_Undefined
: MCompare::Compare_Null
;
3161 auto* ins
= MCompare::New(alloc(), input
, cst
, op
, compareType
);
3168 bool WarpCacheIRTranspiler::emitCompareDoubleSameValueResult(
3169 NumberOperandId lhsId
, NumberOperandId rhsId
) {
3170 MDefinition
* lhs
= getOperand(lhsId
);
3171 MDefinition
* rhs
= getOperand(rhsId
);
3173 auto* sameValue
= MSameValueDouble::New(alloc(), lhs
, rhs
);
3176 pushResult(sameValue
);
3180 bool WarpCacheIRTranspiler::emitSameValueResult(ValOperandId lhsId
,
3181 ValOperandId rhsId
) {
3182 MDefinition
* lhs
= getOperand(lhsId
);
3183 MDefinition
* rhs
= getOperand(rhsId
);
3185 auto* sameValue
= MSameValue::New(alloc(), lhs
, rhs
);
3188 pushResult(sameValue
);
3192 bool WarpCacheIRTranspiler::emitIndirectTruncateInt32Result(
3193 Int32OperandId valId
) {
3194 MDefinition
* val
= getOperand(valId
);
3195 MOZ_ASSERT(val
->type() == MIRType::Int32
);
3198 MLimitedTruncate::New(alloc(), val
, TruncateKind::IndirectTruncate
);
3201 pushResult(truncate
);
3205 bool WarpCacheIRTranspiler::emitMathHypot2NumberResult(
3206 NumberOperandId firstId
, NumberOperandId secondId
) {
3207 MDefinitionVector
vector(alloc());
3208 if (!vector
.reserve(2)) {
3212 vector
.infallibleAppend(getOperand(firstId
));
3213 vector
.infallibleAppend(getOperand(secondId
));
3215 auto* ins
= MHypot::New(alloc(), vector
);
3225 bool WarpCacheIRTranspiler::emitMathHypot3NumberResult(
3226 NumberOperandId firstId
, NumberOperandId secondId
,
3227 NumberOperandId thirdId
) {
3228 MDefinitionVector
vector(alloc());
3229 if (!vector
.reserve(3)) {
3233 vector
.infallibleAppend(getOperand(firstId
));
3234 vector
.infallibleAppend(getOperand(secondId
));
3235 vector
.infallibleAppend(getOperand(thirdId
));
3237 auto* ins
= MHypot::New(alloc(), vector
);
3247 bool WarpCacheIRTranspiler::emitMathHypot4NumberResult(
3248 NumberOperandId firstId
, NumberOperandId secondId
, NumberOperandId thirdId
,
3249 NumberOperandId fourthId
) {
3250 MDefinitionVector
vector(alloc());
3251 if (!vector
.reserve(4)) {
3255 vector
.infallibleAppend(getOperand(firstId
));
3256 vector
.infallibleAppend(getOperand(secondId
));
3257 vector
.infallibleAppend(getOperand(thirdId
));
3258 vector
.infallibleAppend(getOperand(fourthId
));
3260 auto* ins
= MHypot::New(alloc(), vector
);
3270 bool WarpCacheIRTranspiler::emitMathRandomResult(uint32_t rngOffset
) {
3272 // CodeGenerator uses CompileRealm::addressOfRandomNumberGenerator. Assert it
3273 // matches the RNG pointer stored in the stub field.
3274 const void* rng
= rawPointerField(rngOffset
);
3275 MOZ_ASSERT(rng
== mirGen().realm
->addressOfRandomNumberGenerator());
3278 auto* ins
= MRandom::New(alloc());
3282 return resumeAfter(ins
);
3285 bool WarpCacheIRTranspiler::emitInt32MinMax(bool isMax
, Int32OperandId firstId
,
3286 Int32OperandId secondId
,
3287 Int32OperandId resultId
) {
3288 MDefinition
* first
= getOperand(firstId
);
3289 MDefinition
* second
= getOperand(secondId
);
3291 auto* ins
= MMinMax::New(alloc(), first
, second
, MIRType::Int32
, isMax
);
3294 return defineOperand(resultId
, ins
);
3297 bool WarpCacheIRTranspiler::emitNumberMinMax(bool isMax
,
3298 NumberOperandId firstId
,
3299 NumberOperandId secondId
,
3300 NumberOperandId resultId
) {
3301 MDefinition
* first
= getOperand(firstId
);
3302 MDefinition
* second
= getOperand(secondId
);
3304 auto* ins
= MMinMax::New(alloc(), first
, second
, MIRType::Double
, isMax
);
3307 return defineOperand(resultId
, ins
);
3310 bool WarpCacheIRTranspiler::emitInt32MinMaxArrayResult(ObjOperandId arrayId
,
3312 MDefinition
* array
= getOperand(arrayId
);
3314 auto* ins
= MMinMaxArray::New(alloc(), array
, MIRType::Int32
, isMax
);
3321 bool WarpCacheIRTranspiler::emitNumberMinMaxArrayResult(ObjOperandId arrayId
,
3323 MDefinition
* array
= getOperand(arrayId
);
3325 auto* ins
= MMinMaxArray::New(alloc(), array
, MIRType::Double
, isMax
);
3332 bool WarpCacheIRTranspiler::emitMathAbsInt32Result(Int32OperandId inputId
) {
3333 MDefinition
* input
= getOperand(inputId
);
3335 auto* ins
= MAbs::New(alloc(), input
, MIRType::Int32
);
3342 bool WarpCacheIRTranspiler::emitMathAbsNumberResult(NumberOperandId inputId
) {
3343 MDefinition
* input
= getOperand(inputId
);
3345 auto* ins
= MAbs::New(alloc(), input
, MIRType::Double
);
3352 bool WarpCacheIRTranspiler::emitMathClz32Result(Int32OperandId inputId
) {
3353 MDefinition
* input
= getOperand(inputId
);
3355 auto* ins
= MClz::New(alloc(), input
, MIRType::Int32
);
3362 bool WarpCacheIRTranspiler::emitMathSignInt32Result(Int32OperandId inputId
) {
3363 MDefinition
* input
= getOperand(inputId
);
3365 auto* ins
= MSign::New(alloc(), input
, MIRType::Int32
);
3372 bool WarpCacheIRTranspiler::emitMathSignNumberResult(NumberOperandId inputId
) {
3373 MDefinition
* input
= getOperand(inputId
);
3375 auto* ins
= MSign::New(alloc(), input
, MIRType::Double
);
3382 bool WarpCacheIRTranspiler::emitMathSignNumberToInt32Result(
3383 NumberOperandId inputId
) {
3384 MDefinition
* input
= getOperand(inputId
);
3386 auto* ins
= MSign::New(alloc(), input
, MIRType::Int32
);
3393 bool WarpCacheIRTranspiler::emitMathImulResult(Int32OperandId lhsId
,
3394 Int32OperandId rhsId
) {
3395 MDefinition
* lhs
= getOperand(lhsId
);
3396 MDefinition
* rhs
= getOperand(rhsId
);
3398 auto* ins
= MMul::New(alloc(), lhs
, rhs
, MIRType::Int32
, MMul::Integer
);
3405 bool WarpCacheIRTranspiler::emitMathFloorToInt32Result(
3406 NumberOperandId inputId
) {
3407 MDefinition
* input
= getOperand(inputId
);
3409 auto* ins
= MFloor::New(alloc(), input
);
3416 bool WarpCacheIRTranspiler::emitMathCeilToInt32Result(NumberOperandId inputId
) {
3417 MDefinition
* input
= getOperand(inputId
);
3419 auto* ins
= MCeil::New(alloc(), input
);
3426 bool WarpCacheIRTranspiler::emitMathTruncToInt32Result(
3427 NumberOperandId inputId
) {
3428 MDefinition
* input
= getOperand(inputId
);
3430 auto* ins
= MTrunc::New(alloc(), input
);
3437 bool WarpCacheIRTranspiler::emitMathRoundToInt32Result(
3438 NumberOperandId inputId
) {
3439 MDefinition
* input
= getOperand(inputId
);
3441 auto* ins
= MRound::New(alloc(), input
);
3448 bool WarpCacheIRTranspiler::emitMathSqrtNumberResult(NumberOperandId inputId
) {
3449 MDefinition
* input
= getOperand(inputId
);
3451 auto* ins
= MSqrt::New(alloc(), input
, MIRType::Double
);
3458 bool WarpCacheIRTranspiler::emitMathFRoundNumberResult(
3459 NumberOperandId inputId
) {
3460 MDefinition
* input
= getOperand(inputId
);
3462 auto* ins
= MToFloat32::New(alloc(), input
);
3469 bool WarpCacheIRTranspiler::emitMathAtan2NumberResult(NumberOperandId yId
,
3470 NumberOperandId xId
) {
3471 MDefinition
* y
= getOperand(yId
);
3472 MDefinition
* x
= getOperand(xId
);
3474 auto* ins
= MAtan2::New(alloc(), y
, x
);
3481 bool WarpCacheIRTranspiler::emitMathFunctionNumberResult(
3482 NumberOperandId inputId
, UnaryMathFunction fun
) {
3483 MDefinition
* input
= getOperand(inputId
);
3485 auto* ins
= MMathFunction::New(alloc(), input
, fun
);
3492 bool WarpCacheIRTranspiler::emitMathFloorNumberResult(NumberOperandId inputId
) {
3493 MDefinition
* input
= getOperand(inputId
);
3496 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down
)) {
3497 ins
= MNearbyInt::New(alloc(), input
, MIRType::Double
, RoundingMode::Down
);
3499 ins
= MMathFunction::New(alloc(), input
, UnaryMathFunction::Floor
);
3507 bool WarpCacheIRTranspiler::emitMathCeilNumberResult(NumberOperandId inputId
) {
3508 MDefinition
* input
= getOperand(inputId
);
3511 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up
)) {
3512 ins
= MNearbyInt::New(alloc(), input
, MIRType::Double
, RoundingMode::Up
);
3514 ins
= MMathFunction::New(alloc(), input
, UnaryMathFunction::Ceil
);
3522 bool WarpCacheIRTranspiler::emitMathTruncNumberResult(NumberOperandId inputId
) {
3523 MDefinition
* input
= getOperand(inputId
);
3526 if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero
)) {
3527 ins
= MNearbyInt::New(alloc(), input
, MIRType::Double
,
3528 RoundingMode::TowardsZero
);
3530 ins
= MMathFunction::New(alloc(), input
, UnaryMathFunction::Trunc
);
3538 bool WarpCacheIRTranspiler::emitNumberParseIntResult(StringOperandId strId
,
3539 Int32OperandId radixId
) {
3540 MDefinition
* str
= getOperand(strId
);
3541 MDefinition
* radix
= getOperand(radixId
);
3543 auto* ins
= MNumberParseInt::New(alloc(), str
, radix
);
3550 bool WarpCacheIRTranspiler::emitDoubleParseIntResult(NumberOperandId numId
) {
3551 MDefinition
* num
= getOperand(numId
);
3553 auto* ins
= MDoubleParseInt::New(alloc(), num
);
3560 bool WarpCacheIRTranspiler::emitObjectToStringResult(ObjOperandId objId
) {
3561 MDefinition
* obj
= getOperand(objId
);
3563 auto* ins
= MObjectClassToString::New(alloc(), obj
);
3570 bool WarpCacheIRTranspiler::emitReflectGetPrototypeOfResult(
3571 ObjOperandId objId
) {
3572 MDefinition
* obj
= getOperand(objId
);
3574 auto* ins
= MGetPrototypeOf::New(alloc(), obj
);
3578 return resumeAfter(ins
);
3581 bool WarpCacheIRTranspiler::emitArrayPush(ObjOperandId objId
,
3582 ValOperandId rhsId
) {
3583 MDefinition
* obj
= getOperand(objId
);
3584 MDefinition
* value
= getOperand(rhsId
);
3586 auto* ins
= MArrayPush::New(alloc(), obj
, value
);
3590 return resumeAfter(ins
);
3593 bool WarpCacheIRTranspiler::emitArrayJoinResult(ObjOperandId objId
,
3594 StringOperandId sepId
) {
3595 MDefinition
* obj
= getOperand(objId
);
3596 MDefinition
* sep
= getOperand(sepId
);
3598 auto* join
= MArrayJoin::New(alloc(), obj
, sep
);
3602 return resumeAfter(join
);
3605 bool WarpCacheIRTranspiler::emitObjectKeysResult(ObjOperandId objId
) {
3606 MDefinition
* obj
= getOperand(objId
);
3608 auto* join
= MObjectKeys::New(alloc(), obj
);
3612 return resumeAfter(join
);
3615 bool WarpCacheIRTranspiler::emitPackedArrayPopResult(ObjOperandId arrayId
) {
3616 MDefinition
* array
= getOperand(arrayId
);
3618 auto* ins
= MArrayPopShift::New(alloc(), array
, MArrayPopShift::Pop
);
3622 return resumeAfter(ins
);
3625 bool WarpCacheIRTranspiler::emitPackedArrayShiftResult(ObjOperandId arrayId
) {
3626 MDefinition
* array
= getOperand(arrayId
);
3628 auto* ins
= MArrayPopShift::New(alloc(), array
, MArrayPopShift::Shift
);
3632 return resumeAfter(ins
);
3635 bool WarpCacheIRTranspiler::emitPackedArraySliceResult(
3636 uint32_t templateObjectOffset
, ObjOperandId arrayId
, Int32OperandId beginId
,
3637 Int32OperandId endId
) {
3638 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
3640 MDefinition
* array
= getOperand(arrayId
);
3641 MDefinition
* begin
= getOperand(beginId
);
3642 MDefinition
* end
= getOperand(endId
);
3644 // TODO: support pre-tenuring.
3645 gc::Heap heap
= gc::Heap::Default
;
3647 auto* ins
= MArraySlice::New(alloc(), array
, begin
, end
, templateObj
, heap
);
3651 return resumeAfter(ins
);
3654 bool WarpCacheIRTranspiler::emitArgumentsSliceResult(
3655 uint32_t templateObjectOffset
, ObjOperandId argsId
, Int32OperandId beginId
,
3656 Int32OperandId endId
) {
3657 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
3659 MDefinition
* args
= getOperand(argsId
);
3660 MDefinition
* begin
= getOperand(beginId
);
3661 MDefinition
* end
= getOperand(endId
);
3663 // TODO: support pre-tenuring.
3664 gc::Heap heap
= gc::Heap::Default
;
3667 MArgumentsSlice::New(alloc(), args
, begin
, end
, templateObj
, heap
);
3671 return resumeAfter(ins
);
3674 bool WarpCacheIRTranspiler::emitHasClassResult(ObjOperandId objId
,
3675 uint32_t claspOffset
) {
3676 MDefinition
* obj
= getOperand(objId
);
3677 const JSClass
* clasp
= classStubField(claspOffset
);
3679 auto* hasClass
= MHasClass::New(alloc(), obj
, clasp
);
3682 pushResult(hasClass
);
3686 bool WarpCacheIRTranspiler::emitCallRegExpMatcherResult(
3687 ObjOperandId regexpId
, StringOperandId inputId
, Int32OperandId lastIndexId
,
3688 uint32_t stubOffset
) {
3689 MDefinition
* regexp
= getOperand(regexpId
);
3690 MDefinition
* input
= getOperand(inputId
);
3691 MDefinition
* lastIndex
= getOperand(lastIndexId
);
3693 auto* matcher
= MRegExpMatcher::New(alloc(), regexp
, input
, lastIndex
);
3694 addEffectful(matcher
);
3695 pushResult(matcher
);
3697 return resumeAfter(matcher
);
3700 bool WarpCacheIRTranspiler::emitCallRegExpSearcherResult(
3701 ObjOperandId regexpId
, StringOperandId inputId
, Int32OperandId lastIndexId
,
3702 uint32_t stubOffset
) {
3703 MDefinition
* regexp
= getOperand(regexpId
);
3704 MDefinition
* input
= getOperand(inputId
);
3705 MDefinition
* lastIndex
= getOperand(lastIndexId
);
3707 auto* searcher
= MRegExpSearcher::New(alloc(), regexp
, input
, lastIndex
);
3708 addEffectful(searcher
);
3709 pushResult(searcher
);
3711 return resumeAfter(searcher
);
3714 bool WarpCacheIRTranspiler::emitRegExpSearcherLastLimitResult() {
3715 auto* limit
= MRegExpSearcherLastLimit::New(alloc());
3716 addEffectful(limit
);
3719 return resumeAfter(limit
);
3722 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecMatchResult(
3723 ObjOperandId regexpId
, StringOperandId inputId
, uint32_t stubOffset
) {
3724 MDefinition
* regexp
= getOperand(regexpId
);
3725 MDefinition
* input
= getOperand(inputId
);
3727 auto* ins
= MRegExpExecMatch::New(alloc(), regexp
, input
);
3731 return resumeAfter(ins
);
3734 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecTestResult(
3735 ObjOperandId regexpId
, StringOperandId inputId
, uint32_t stubOffset
) {
3736 MDefinition
* regexp
= getOperand(regexpId
);
3737 MDefinition
* input
= getOperand(inputId
);
3739 auto* ins
= MRegExpExecTest::New(alloc(), regexp
, input
);
3743 return resumeAfter(ins
);
3746 bool WarpCacheIRTranspiler::emitRegExpHasCaptureGroupsResult(
3747 ObjOperandId regexpId
, StringOperandId inputId
) {
3748 MDefinition
* regexp
= getOperand(regexpId
);
3749 MDefinition
* input
= getOperand(inputId
);
3751 auto* result
= MRegExpHasCaptureGroups::New(alloc(), regexp
, input
);
3752 addEffectful(result
);
3755 return resumeAfter(result
);
3758 MInstruction
* WarpCacheIRTranspiler::convertToBoolean(MDefinition
* input
) {
3759 // Convert to bool with the '!!' idiom.
3761 // The FoldTests and GVN passes both specifically handle this pattern. If you
3762 // change this code, make sure to update FoldTests and GVN, too.
3764 auto* resultInverted
= MNot::New(alloc(), input
);
3765 add(resultInverted
);
3766 auto* result
= MNot::New(alloc(), resultInverted
);
3772 bool WarpCacheIRTranspiler::emitRegExpFlagResult(ObjOperandId regexpId
,
3773 int32_t flagsMask
) {
3774 MDefinition
* regexp
= getOperand(regexpId
);
3776 auto* flags
= MLoadFixedSlot::New(alloc(), regexp
, RegExpObject::flagsSlot());
3777 flags
->setResultType(MIRType::Int32
);
3780 auto* mask
= MConstant::New(alloc(), Int32Value(flagsMask
));
3783 auto* maskedFlag
= MBitAnd::New(alloc(), flags
, mask
, MIRType::Int32
);
3786 auto* result
= convertToBoolean(maskedFlag
);
3792 bool WarpCacheIRTranspiler::emitCallSubstringKernelResult(
3793 StringOperandId strId
, Int32OperandId beginId
, Int32OperandId lengthId
) {
3794 MDefinition
* str
= getOperand(strId
);
3795 MDefinition
* begin
= getOperand(beginId
);
3796 MDefinition
* length
= getOperand(lengthId
);
3798 auto* substr
= MSubstr::New(alloc(), str
, begin
, length
);
3805 bool WarpCacheIRTranspiler::emitStringReplaceStringResult(
3806 StringOperandId strId
, StringOperandId patternId
,
3807 StringOperandId replacementId
) {
3808 MDefinition
* str
= getOperand(strId
);
3809 MDefinition
* pattern
= getOperand(patternId
);
3810 MDefinition
* replacement
= getOperand(replacementId
);
3812 auto* replace
= MStringReplace::New(alloc(), str
, pattern
, replacement
);
3815 pushResult(replace
);
3819 bool WarpCacheIRTranspiler::emitStringSplitStringResult(
3820 StringOperandId strId
, StringOperandId separatorId
) {
3821 MDefinition
* str
= getOperand(strId
);
3822 MDefinition
* separator
= getOperand(separatorId
);
3824 auto* split
= MStringSplit::New(alloc(), str
, separator
);
3831 bool WarpCacheIRTranspiler::emitRegExpPrototypeOptimizableResult(
3832 ObjOperandId protoId
) {
3833 MDefinition
* proto
= getOperand(protoId
);
3835 auto* optimizable
= MRegExpPrototypeOptimizable::New(alloc(), proto
);
3838 pushResult(optimizable
);
3842 bool WarpCacheIRTranspiler::emitRegExpInstanceOptimizableResult(
3843 ObjOperandId regexpId
, ObjOperandId protoId
) {
3844 MDefinition
* regexp
= getOperand(regexpId
);
3845 MDefinition
* proto
= getOperand(protoId
);
3847 auto* optimizable
= MRegExpInstanceOptimizable::New(alloc(), regexp
, proto
);
3850 pushResult(optimizable
);
3854 bool WarpCacheIRTranspiler::emitGetFirstDollarIndexResult(
3855 StringOperandId strId
) {
3856 MDefinition
* str
= getOperand(strId
);
3858 auto* firstDollarIndex
= MGetFirstDollarIndex::New(alloc(), str
);
3859 add(firstDollarIndex
);
3861 pushResult(firstDollarIndex
);
3865 bool WarpCacheIRTranspiler::emitIsArrayResult(ValOperandId inputId
) {
3866 MDefinition
* value
= getOperand(inputId
);
3868 auto* isArray
= MIsArray::New(alloc(), value
);
3869 addEffectful(isArray
);
3870 pushResult(isArray
);
3872 return resumeAfter(isArray
);
3875 bool WarpCacheIRTranspiler::emitIsObjectResult(ValOperandId inputId
) {
3876 MDefinition
* value
= getOperand(inputId
);
3878 if (value
->type() == MIRType::Object
) {
3879 pushResult(constant(BooleanValue(true)));
3881 auto* isObject
= MIsObject::New(alloc(), value
);
3883 pushResult(isObject
);
3889 bool WarpCacheIRTranspiler::emitIsPackedArrayResult(ObjOperandId objId
) {
3890 MDefinition
* obj
= getOperand(objId
);
3892 auto* isPackedArray
= MIsPackedArray::New(alloc(), obj
);
3895 pushResult(isPackedArray
);
3899 bool WarpCacheIRTranspiler::emitIsCallableResult(ValOperandId inputId
) {
3900 MDefinition
* value
= getOperand(inputId
);
3902 auto* isCallable
= MIsCallable::New(alloc(), value
);
3905 pushResult(isCallable
);
3909 bool WarpCacheIRTranspiler::emitIsConstructorResult(ObjOperandId objId
) {
3910 MDefinition
* obj
= getOperand(objId
);
3912 auto* isConstructor
= MIsConstructor::New(alloc(), obj
);
3915 pushResult(isConstructor
);
3919 bool WarpCacheIRTranspiler::emitIsCrossRealmArrayConstructorResult(
3920 ObjOperandId objId
) {
3921 MDefinition
* obj
= getOperand(objId
);
3923 auto* ins
= MIsCrossRealmArrayConstructor::New(alloc(), obj
);
3930 bool WarpCacheIRTranspiler::emitIsTypedArrayResult(ObjOperandId objId
,
3931 bool isPossiblyWrapped
) {
3932 MDefinition
* obj
= getOperand(objId
);
3934 auto* ins
= MIsTypedArray::New(alloc(), obj
, isPossiblyWrapped
);
3935 if (isPossiblyWrapped
) {
3943 if (isPossiblyWrapped
) {
3944 if (!resumeAfter(ins
)) {
3952 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetInt32Result(
3953 ObjOperandId objId
) {
3954 MDefinition
* obj
= getOperand(objId
);
3956 auto* byteOffset
= MArrayBufferViewByteOffset::New(alloc(), obj
);
3959 auto* byteOffsetInt32
= MNonNegativeIntPtrToInt32::New(alloc(), byteOffset
);
3960 add(byteOffsetInt32
);
3962 pushResult(byteOffsetInt32
);
3966 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetDoubleResult(
3967 ObjOperandId objId
) {
3968 MDefinition
* obj
= getOperand(objId
);
3970 auto* byteOffset
= MArrayBufferViewByteOffset::New(alloc(), obj
);
3973 auto* byteOffsetDouble
= MIntPtrToDouble::New(alloc(), byteOffset
);
3974 add(byteOffsetDouble
);
3976 pushResult(byteOffsetDouble
);
3980 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthInt32Result(
3981 ObjOperandId objId
) {
3982 MDefinition
* obj
= getOperand(objId
);
3984 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
3987 auto* lengthInt32
= MNonNegativeIntPtrToInt32::New(alloc(), length
);
3990 auto* size
= MTypedArrayElementSize::New(alloc(), obj
);
3993 auto* mul
= MMul::New(alloc(), lengthInt32
, size
, MIRType::Int32
);
3994 mul
->setCanBeNegativeZero(false);
4001 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthDoubleResult(
4002 ObjOperandId objId
) {
4003 MDefinition
* obj
= getOperand(objId
);
4005 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4008 auto* lengthDouble
= MIntPtrToDouble::New(alloc(), length
);
4011 auto* size
= MTypedArrayElementSize::New(alloc(), obj
);
4014 auto* sizeDouble
= MToDouble::New(alloc(), size
);
4017 auto* mul
= MMul::New(alloc(), lengthDouble
, sizeDouble
, MIRType::Double
);
4018 mul
->setCanBeNegativeZero(false);
4025 bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult(
4026 ObjOperandId objId
) {
4027 MDefinition
* obj
= getOperand(objId
);
4029 auto* ins
= MTypedArrayElementSize::New(alloc(), obj
);
4036 bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer(
4037 ObjOperandId objId
) {
4038 MDefinition
* obj
= getOperand(objId
);
4040 auto* ins
= MGuardHasAttachedArrayBuffer::New(alloc(), obj
);
4043 setOperand(objId
, ins
);
4047 bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult(
4048 ObjOperandId objId
) {
4049 MDefinition
* obj
= getOperand(objId
);
4051 auto* ins
= MIsTypedArrayConstructor::New(alloc(), obj
);
4058 bool WarpCacheIRTranspiler::emitGetNextMapSetEntryForIteratorResult(
4059 ObjOperandId iterId
, ObjOperandId resultArrId
, bool isMap
) {
4060 MDefinition
* iter
= getOperand(iterId
);
4061 MDefinition
* resultArr
= getOperand(resultArrId
);
4063 MGetNextEntryForIterator::Mode mode
=
4064 isMap
? MGetNextEntryForIterator::Map
: MGetNextEntryForIterator::Set
;
4065 auto* ins
= MGetNextEntryForIterator::New(alloc(), iter
, resultArr
, mode
);
4069 return resumeAfter(ins
);
4072 bool WarpCacheIRTranspiler::emitFrameIsConstructingResult() {
4073 if (const CallInfo
* callInfo
= builder_
->inlineCallInfo()) {
4074 auto* ins
= constant(BooleanValue(callInfo
->constructing()));
4079 auto* ins
= MIsConstructing::New(alloc());
4085 bool WarpCacheIRTranspiler::emitNewIteratorResult(
4086 MNewIterator::Type type
, uint32_t templateObjectOffset
) {
4087 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4089 auto* templateConst
= constant(ObjectValue(*templateObj
));
4090 auto* iter
= MNewIterator::New(alloc(), templateConst
, type
);
4097 bool WarpCacheIRTranspiler::emitNewArrayIteratorResult(
4098 uint32_t templateObjectOffset
) {
4099 return emitNewIteratorResult(MNewIterator::ArrayIterator
,
4100 templateObjectOffset
);
4103 bool WarpCacheIRTranspiler::emitNewStringIteratorResult(
4104 uint32_t templateObjectOffset
) {
4105 return emitNewIteratorResult(MNewIterator::StringIterator
,
4106 templateObjectOffset
);
4109 bool WarpCacheIRTranspiler::emitNewRegExpStringIteratorResult(
4110 uint32_t templateObjectOffset
) {
4111 return emitNewIteratorResult(MNewIterator::RegExpStringIterator
,
4112 templateObjectOffset
);
4115 bool WarpCacheIRTranspiler::emitObjectCreateResult(
4116 uint32_t templateObjectOffset
) {
4117 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4119 auto* templateConst
= constant(ObjectValue(*templateObj
));
4121 // TODO: support pre-tenuring.
4122 gc::Heap heap
= gc::Heap::Default
;
4124 MNewObject::New(alloc(), templateConst
, heap
, MNewObject::ObjectCreate
);
4128 return resumeAfter(obj
);
4131 bool WarpCacheIRTranspiler::emitNewArrayFromLengthResult(
4132 uint32_t templateObjectOffset
, Int32OperandId lengthId
) {
4133 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4134 MDefinition
* length
= getOperand(lengthId
);
4136 // TODO: support pre-tenuring.
4137 gc::Heap heap
= gc::Heap::Default
;
4139 if (length
->isConstant()) {
4140 int32_t lenInt32
= length
->toConstant()->toInt32();
4141 if (lenInt32
>= 0 &&
4142 uint32_t(lenInt32
) == templateObj
->as
<ArrayObject
>().length()) {
4143 uint32_t len
= uint32_t(lenInt32
);
4144 auto* templateConst
= constant(ObjectValue(*templateObj
));
4146 size_t inlineLength
=
4147 gc::GetGCKindSlots(templateObj
->asTenured().getAllocKind()) -
4148 ObjectElements::VALUES_PER_HEADER
;
4151 if (len
> inlineLength
) {
4152 obj
= MNewArray::NewVM(alloc(), len
, templateConst
, heap
);
4154 obj
= MNewArray::New(alloc(), len
, templateConst
, heap
);
4162 auto* obj
= MNewArrayDynamicLength::New(alloc(), length
, templateObj
, heap
);
4165 return resumeAfter(obj
);
4168 bool WarpCacheIRTranspiler::emitNewTypedArrayFromLengthResult(
4169 uint32_t templateObjectOffset
, Int32OperandId lengthId
) {
4170 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4171 MDefinition
* length
= getOperand(lengthId
);
4173 // TODO: support pre-tenuring.
4174 gc::Heap heap
= gc::Heap::Default
;
4176 if (length
->isConstant()) {
4177 int32_t len
= length
->toConstant()->toInt32();
4179 uint32_t(len
) == templateObj
->as
<TypedArrayObject
>().length()) {
4180 auto* templateConst
= constant(ObjectValue(*templateObj
));
4181 auto* obj
= MNewTypedArray::New(alloc(), templateConst
, heap
);
4189 MNewTypedArrayDynamicLength::New(alloc(), length
, templateObj
, heap
);
4192 return resumeAfter(obj
);
4195 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayBufferResult(
4196 uint32_t templateObjectOffset
, ObjOperandId bufferId
,
4197 ValOperandId byteOffsetId
, ValOperandId lengthId
) {
4198 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4199 MDefinition
* buffer
= getOperand(bufferId
);
4200 MDefinition
* byteOffset
= getOperand(byteOffsetId
);
4201 MDefinition
* length
= getOperand(lengthId
);
4203 // TODO: support pre-tenuring.
4204 gc::Heap heap
= gc::Heap::Default
;
4206 auto* obj
= MNewTypedArrayFromArrayBuffer::New(alloc(), buffer
, byteOffset
,
4207 length
, templateObj
, heap
);
4211 return resumeAfter(obj
);
4214 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayResult(
4215 uint32_t templateObjectOffset
, ObjOperandId arrayId
) {
4216 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
4217 MDefinition
* array
= getOperand(arrayId
);
4219 // TODO: support pre-tenuring.
4220 gc::Heap heap
= gc::Heap::Default
;
4222 auto* obj
= MNewTypedArrayFromArray::New(alloc(), array
, templateObj
, heap
);
4226 return resumeAfter(obj
);
4229 bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult(
4230 ObjOperandId objId
, IntPtrOperandId indexId
, uint32_t expectedId
,
4231 uint32_t replacementId
, Scalar::Type elementType
) {
4232 MDefinition
* obj
= getOperand(objId
);
4233 MDefinition
* index
= getOperand(indexId
);
4234 MDefinition
* expected
= getOperand(ValOperandId(expectedId
));
4235 MDefinition
* replacement
= getOperand(ValOperandId(replacementId
));
4237 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4240 index
= addBoundsCheck(index
, length
);
4242 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
4245 bool forceDoubleForUint32
= true;
4247 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
);
4249 auto* cas
= MCompareExchangeTypedArrayElement::New(
4250 alloc(), elements
, index
, elementType
, expected
, replacement
);
4251 cas
->setResultType(knownType
);
4255 return resumeAfter(cas
);
4258 bool WarpCacheIRTranspiler::emitAtomicsExchangeResult(
4259 ObjOperandId objId
, IntPtrOperandId indexId
, uint32_t valueId
,
4260 Scalar::Type elementType
) {
4261 MDefinition
* obj
= getOperand(objId
);
4262 MDefinition
* index
= getOperand(indexId
);
4263 MDefinition
* value
= getOperand(ValOperandId(valueId
));
4265 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4268 index
= addBoundsCheck(index
, length
);
4270 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
4273 bool forceDoubleForUint32
= true;
4275 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
);
4277 auto* exchange
= MAtomicExchangeTypedArrayElement::New(
4278 alloc(), elements
, index
, value
, elementType
);
4279 exchange
->setResultType(knownType
);
4280 addEffectful(exchange
);
4282 pushResult(exchange
);
4283 return resumeAfter(exchange
);
4286 bool WarpCacheIRTranspiler::emitAtomicsBinaryOp(ObjOperandId objId
,
4287 IntPtrOperandId indexId
,
4289 Scalar::Type elementType
,
4290 bool forEffect
, AtomicOp op
) {
4291 MDefinition
* obj
= getOperand(objId
);
4292 MDefinition
* index
= getOperand(indexId
);
4293 MDefinition
* value
= getOperand(ValOperandId(valueId
));
4295 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4298 index
= addBoundsCheck(index
, length
);
4300 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
4303 bool forceDoubleForUint32
= true;
4305 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
);
4307 auto* binop
= MAtomicTypedArrayElementBinop::New(
4308 alloc(), op
, elements
, index
, elementType
, value
, forEffect
);
4310 binop
->setResultType(knownType
);
4312 addEffectful(binop
);
4317 pushResult(constant(UndefinedValue()));
4319 return resumeAfter(binop
);
4322 bool WarpCacheIRTranspiler::emitAtomicsAddResult(ObjOperandId objId
,
4323 IntPtrOperandId indexId
,
4325 Scalar::Type elementType
,
4327 return emitAtomicsBinaryOp(objId
, indexId
, valueId
, elementType
, forEffect
,
4331 bool WarpCacheIRTranspiler::emitAtomicsSubResult(ObjOperandId objId
,
4332 IntPtrOperandId indexId
,
4334 Scalar::Type elementType
,
4336 return emitAtomicsBinaryOp(objId
, indexId
, valueId
, elementType
, forEffect
,
4340 bool WarpCacheIRTranspiler::emitAtomicsAndResult(ObjOperandId objId
,
4341 IntPtrOperandId indexId
,
4343 Scalar::Type elementType
,
4345 return emitAtomicsBinaryOp(objId
, indexId
, valueId
, elementType
, forEffect
,
4349 bool WarpCacheIRTranspiler::emitAtomicsOrResult(ObjOperandId objId
,
4350 IntPtrOperandId indexId
,
4352 Scalar::Type elementType
,
4354 return emitAtomicsBinaryOp(objId
, indexId
, valueId
, elementType
, forEffect
,
4358 bool WarpCacheIRTranspiler::emitAtomicsXorResult(ObjOperandId objId
,
4359 IntPtrOperandId indexId
,
4361 Scalar::Type elementType
,
4363 return emitAtomicsBinaryOp(objId
, indexId
, valueId
, elementType
, forEffect
,
4367 bool WarpCacheIRTranspiler::emitAtomicsLoadResult(ObjOperandId objId
,
4368 IntPtrOperandId indexId
,
4369 Scalar::Type elementType
) {
4370 MDefinition
* obj
= getOperand(objId
);
4371 MDefinition
* index
= getOperand(indexId
);
4373 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4376 index
= addBoundsCheck(index
, length
);
4378 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
4381 bool forceDoubleForUint32
= true;
4383 MIRTypeForArrayBufferViewRead(elementType
, forceDoubleForUint32
);
4385 auto* load
= MLoadUnboxedScalar::New(alloc(), elements
, index
, elementType
,
4386 DoesRequireMemoryBarrier
);
4387 load
->setResultType(knownType
);
4391 return resumeAfter(load
);
4394 bool WarpCacheIRTranspiler::emitAtomicsStoreResult(ObjOperandId objId
,
4395 IntPtrOperandId indexId
,
4397 Scalar::Type elementType
) {
4398 MDefinition
* obj
= getOperand(objId
);
4399 MDefinition
* index
= getOperand(indexId
);
4400 MDefinition
* value
= getOperand(ValOperandId(valueId
));
4402 auto* length
= MArrayBufferViewLength::New(alloc(), obj
);
4405 index
= addBoundsCheck(index
, length
);
4407 auto* elements
= MArrayBufferViewElements::New(alloc(), obj
);
4410 auto* store
= MStoreUnboxedScalar::New(alloc(), elements
, index
, value
,
4411 elementType
, DoesRequireMemoryBarrier
);
4412 addEffectful(store
);
4415 return resumeAfter(store
);
4418 bool WarpCacheIRTranspiler::emitAtomicsIsLockFreeResult(
4419 Int32OperandId valueId
) {
4420 MDefinition
* value
= getOperand(valueId
);
4422 auto* ilf
= MAtomicIsLockFree::New(alloc(), value
);
4429 bool WarpCacheIRTranspiler::emitBigIntAsIntNResult(Int32OperandId bitsId
,
4430 BigIntOperandId bigIntId
) {
4431 MDefinition
* bits
= getOperand(bitsId
);
4432 MDefinition
* bigInt
= getOperand(bigIntId
);
4434 auto* ins
= MBigIntAsIntN::New(alloc(), bits
, bigInt
);
4441 bool WarpCacheIRTranspiler::emitBigIntAsUintNResult(Int32OperandId bitsId
,
4442 BigIntOperandId bigIntId
) {
4443 MDefinition
* bits
= getOperand(bitsId
);
4444 MDefinition
* bigInt
= getOperand(bigIntId
);
4446 auto* ins
= MBigIntAsUintN::New(alloc(), bits
, bigInt
);
4453 bool WarpCacheIRTranspiler::emitGuardToNonGCThing(ValOperandId inputId
) {
4454 MDefinition
* def
= getOperand(inputId
);
4455 if (IsNonGCThing(def
->type())) {
4459 auto* ins
= MGuardNonGCThing::New(alloc(), def
);
4462 setOperand(inputId
, ins
);
4466 bool WarpCacheIRTranspiler::emitSetHasNonGCThingResult(ObjOperandId setId
,
4467 ValOperandId valId
) {
4468 MDefinition
* set
= getOperand(setId
);
4469 MDefinition
* val
= getOperand(valId
);
4471 auto* hashValue
= MToHashableNonGCThing::New(alloc(), val
);
4474 auto* hash
= MHashNonGCThing::New(alloc(), hashValue
);
4477 auto* ins
= MSetObjectHasNonBigInt::New(alloc(), set
, hashValue
, hash
);
4484 bool WarpCacheIRTranspiler::emitSetHasStringResult(ObjOperandId setId
,
4485 StringOperandId strId
) {
4486 MDefinition
* set
= getOperand(setId
);
4487 MDefinition
* str
= getOperand(strId
);
4489 auto* hashValue
= MToHashableString::New(alloc(), str
);
4492 auto* hash
= MHashString::New(alloc(), hashValue
);
4495 auto* ins
= MSetObjectHasNonBigInt::New(alloc(), set
, hashValue
, hash
);
4502 bool WarpCacheIRTranspiler::emitSetHasSymbolResult(ObjOperandId setId
,
4503 SymbolOperandId symId
) {
4504 MDefinition
* set
= getOperand(setId
);
4505 MDefinition
* sym
= getOperand(symId
);
4507 auto* hash
= MHashSymbol::New(alloc(), sym
);
4510 auto* ins
= MSetObjectHasNonBigInt::New(alloc(), set
, sym
, hash
);
4517 bool WarpCacheIRTranspiler::emitSetHasBigIntResult(ObjOperandId setId
,
4518 BigIntOperandId bigIntId
) {
4519 MDefinition
* set
= getOperand(setId
);
4520 MDefinition
* bigInt
= getOperand(bigIntId
);
4522 auto* hash
= MHashBigInt::New(alloc(), bigInt
);
4525 auto* ins
= MSetObjectHasBigInt::New(alloc(), set
, bigInt
, hash
);
4532 bool WarpCacheIRTranspiler::emitSetHasObjectResult(ObjOperandId setId
,
4533 ObjOperandId objId
) {
4534 MDefinition
* set
= getOperand(setId
);
4535 MDefinition
* obj
= getOperand(objId
);
4537 auto* hash
= MHashObject::New(alloc(), set
, obj
);
4540 auto* ins
= MSetObjectHasNonBigInt::New(alloc(), set
, obj
, hash
);
4547 bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId
,
4548 ValOperandId valId
) {
4549 MDefinition
* set
= getOperand(setId
);
4550 MDefinition
* val
= getOperand(valId
);
4553 auto* hashValue
= MToHashableValue::New(alloc(), val
);
4556 auto* hash
= MHashValue::New(alloc(), set
, hashValue
);
4559 auto* ins
= MSetObjectHasValue::New(alloc(), set
, hashValue
, hash
);
4562 auto* ins
= MSetObjectHasValueVMCall::New(alloc(), set
, val
);
4570 bool WarpCacheIRTranspiler::emitSetSizeResult(ObjOperandId setId
) {
4571 MDefinition
* set
= getOperand(setId
);
4573 auto* ins
= MSetObjectSize::New(alloc(), set
);
4580 bool WarpCacheIRTranspiler::emitMapHasNonGCThingResult(ObjOperandId mapId
,
4581 ValOperandId valId
) {
4582 MDefinition
* map
= getOperand(mapId
);
4583 MDefinition
* val
= getOperand(valId
);
4585 auto* hashValue
= MToHashableNonGCThing::New(alloc(), val
);
4588 auto* hash
= MHashNonGCThing::New(alloc(), hashValue
);
4591 auto* ins
= MMapObjectHasNonBigInt::New(alloc(), map
, hashValue
, hash
);
4598 bool WarpCacheIRTranspiler::emitMapHasStringResult(ObjOperandId mapId
,
4599 StringOperandId strId
) {
4600 MDefinition
* map
= getOperand(mapId
);
4601 MDefinition
* str
= getOperand(strId
);
4603 auto* hashValue
= MToHashableString::New(alloc(), str
);
4606 auto* hash
= MHashString::New(alloc(), hashValue
);
4609 auto* ins
= MMapObjectHasNonBigInt::New(alloc(), map
, hashValue
, hash
);
4616 bool WarpCacheIRTranspiler::emitMapHasSymbolResult(ObjOperandId mapId
,
4617 SymbolOperandId symId
) {
4618 MDefinition
* map
= getOperand(mapId
);
4619 MDefinition
* sym
= getOperand(symId
);
4621 auto* hash
= MHashSymbol::New(alloc(), sym
);
4624 auto* ins
= MMapObjectHasNonBigInt::New(alloc(), map
, sym
, hash
);
4631 bool WarpCacheIRTranspiler::emitMapHasBigIntResult(ObjOperandId mapId
,
4632 BigIntOperandId bigIntId
) {
4633 MDefinition
* map
= getOperand(mapId
);
4634 MDefinition
* bigInt
= getOperand(bigIntId
);
4636 auto* hash
= MHashBigInt::New(alloc(), bigInt
);
4639 auto* ins
= MMapObjectHasBigInt::New(alloc(), map
, bigInt
, hash
);
4646 bool WarpCacheIRTranspiler::emitMapHasObjectResult(ObjOperandId mapId
,
4647 ObjOperandId objId
) {
4648 MDefinition
* map
= getOperand(mapId
);
4649 MDefinition
* obj
= getOperand(objId
);
4651 auto* hash
= MHashObject::New(alloc(), map
, obj
);
4654 auto* ins
= MMapObjectHasNonBigInt::New(alloc(), map
, obj
, hash
);
4661 bool WarpCacheIRTranspiler::emitMapHasResult(ObjOperandId mapId
,
4662 ValOperandId valId
) {
4663 MDefinition
* map
= getOperand(mapId
);
4664 MDefinition
* val
= getOperand(valId
);
4667 auto* hashValue
= MToHashableValue::New(alloc(), val
);
4670 auto* hash
= MHashValue::New(alloc(), map
, hashValue
);
4673 auto* ins
= MMapObjectHasValue::New(alloc(), map
, hashValue
, hash
);
4676 auto* ins
= MMapObjectHasValueVMCall::New(alloc(), map
, val
);
4684 bool WarpCacheIRTranspiler::emitMapGetNonGCThingResult(ObjOperandId mapId
,
4685 ValOperandId valId
) {
4686 MDefinition
* map
= getOperand(mapId
);
4687 MDefinition
* val
= getOperand(valId
);
4689 auto* hashValue
= MToHashableNonGCThing::New(alloc(), val
);
4692 auto* hash
= MHashNonGCThing::New(alloc(), hashValue
);
4695 auto* ins
= MMapObjectGetNonBigInt::New(alloc(), map
, hashValue
, hash
);
4702 bool WarpCacheIRTranspiler::emitMapGetStringResult(ObjOperandId mapId
,
4703 StringOperandId strId
) {
4704 MDefinition
* map
= getOperand(mapId
);
4705 MDefinition
* str
= getOperand(strId
);
4707 auto* hashValue
= MToHashableString::New(alloc(), str
);
4710 auto* hash
= MHashString::New(alloc(), hashValue
);
4713 auto* ins
= MMapObjectGetNonBigInt::New(alloc(), map
, hashValue
, hash
);
4720 bool WarpCacheIRTranspiler::emitMapGetSymbolResult(ObjOperandId mapId
,
4721 SymbolOperandId symId
) {
4722 MDefinition
* map
= getOperand(mapId
);
4723 MDefinition
* sym
= getOperand(symId
);
4725 auto* hash
= MHashSymbol::New(alloc(), sym
);
4728 auto* ins
= MMapObjectGetNonBigInt::New(alloc(), map
, sym
, hash
);
4735 bool WarpCacheIRTranspiler::emitMapGetBigIntResult(ObjOperandId mapId
,
4736 BigIntOperandId bigIntId
) {
4737 MDefinition
* map
= getOperand(mapId
);
4738 MDefinition
* bigInt
= getOperand(bigIntId
);
4740 auto* hash
= MHashBigInt::New(alloc(), bigInt
);
4743 auto* ins
= MMapObjectGetBigInt::New(alloc(), map
, bigInt
, hash
);
4750 bool WarpCacheIRTranspiler::emitMapGetObjectResult(ObjOperandId mapId
,
4751 ObjOperandId objId
) {
4752 MDefinition
* map
= getOperand(mapId
);
4753 MDefinition
* obj
= getOperand(objId
);
4755 auto* hash
= MHashObject::New(alloc(), map
, obj
);
4758 auto* ins
= MMapObjectGetNonBigInt::New(alloc(), map
, obj
, hash
);
4765 bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId
,
4766 ValOperandId valId
) {
4767 MDefinition
* map
= getOperand(mapId
);
4768 MDefinition
* val
= getOperand(valId
);
4771 auto* hashValue
= MToHashableValue::New(alloc(), val
);
4774 auto* hash
= MHashValue::New(alloc(), map
, hashValue
);
4777 auto* ins
= MMapObjectGetValue::New(alloc(), map
, hashValue
, hash
);
4780 auto* ins
= MMapObjectGetValueVMCall::New(alloc(), map
, val
);
4788 bool WarpCacheIRTranspiler::emitMapSizeResult(ObjOperandId mapId
) {
4789 MDefinition
* map
= getOperand(mapId
);
4791 auto* ins
= MMapObjectSize::New(alloc(), map
);
4798 bool WarpCacheIRTranspiler::emitTruthyResult(OperandId inputId
) {
4799 MDefinition
* input
= getOperand(inputId
);
4801 auto* result
= convertToBoolean(input
);
4807 bool WarpCacheIRTranspiler::emitLoadInt32TruthyResult(ValOperandId inputId
) {
4808 return emitTruthyResult(inputId
);
4811 bool WarpCacheIRTranspiler::emitLoadDoubleTruthyResult(
4812 NumberOperandId inputId
) {
4813 return emitTruthyResult(inputId
);
4816 bool WarpCacheIRTranspiler::emitLoadStringTruthyResult(
4817 StringOperandId inputId
) {
4818 return emitTruthyResult(inputId
);
4821 bool WarpCacheIRTranspiler::emitLoadObjectTruthyResult(ObjOperandId inputId
) {
4822 return emitTruthyResult(inputId
);
4825 bool WarpCacheIRTranspiler::emitLoadBigIntTruthyResult(
4826 BigIntOperandId inputId
) {
4827 return emitTruthyResult(inputId
);
4830 bool WarpCacheIRTranspiler::emitLoadValueTruthyResult(ValOperandId inputId
) {
4831 return emitTruthyResult(inputId
);
4834 bool WarpCacheIRTranspiler::emitLoadOperandResult(ValOperandId inputId
) {
4835 MDefinition
* input
= getOperand(inputId
);
4840 bool WarpCacheIRTranspiler::emitLoadWrapperTarget(ObjOperandId objId
,
4841 ObjOperandId resultId
) {
4842 MDefinition
* obj
= getOperand(objId
);
4844 auto* ins
= MLoadWrapperTarget::New(alloc(), obj
);
4847 return defineOperand(resultId
, ins
);
4850 // When we transpile a call, we may generate guards for some
4851 // arguments. To make sure the call instruction depends on those
4852 // guards, when the transpiler creates an operand for an argument, we
4853 // register the OperandId of that argument in argumentIds_. (See
4854 // emitLoadArgumentSlot.) Before generating the call, we update the
4855 // CallInfo to use the appropriate value from operands_.
4856 // Note: The callee is an explicit argument to the call op, and is
4857 // tracked separately.
4858 void WarpCacheIRTranspiler::updateArgumentsFromOperands() {
4859 for (uint32_t i
= 0; i
< uint32_t(ArgumentKind::NumKinds
); i
++) {
4860 ArgumentKind kind
= ArgumentKind(i
);
4861 OperandId id
= argumentOperandIds_
[kind
];
4864 case ArgumentKind::This
:
4865 callInfo_
->setThis(getOperand(id
));
4867 case ArgumentKind::NewTarget
:
4868 callInfo_
->setNewTarget(getOperand(id
));
4870 case ArgumentKind::Arg0
:
4871 callInfo_
->setArg(0, getOperand(id
));
4873 case ArgumentKind::Arg1
:
4874 callInfo_
->setArg(1, getOperand(id
));
4876 case ArgumentKind::Arg2
:
4877 callInfo_
->setArg(2, getOperand(id
));
4879 case ArgumentKind::Arg3
:
4880 callInfo_
->setArg(3, getOperand(id
));
4882 case ArgumentKind::Arg4
:
4883 callInfo_
->setArg(4, getOperand(id
));
4885 case ArgumentKind::Arg5
:
4886 callInfo_
->setArg(5, getOperand(id
));
4888 case ArgumentKind::Arg6
:
4889 callInfo_
->setArg(6, getOperand(id
));
4891 case ArgumentKind::Arg7
:
4892 callInfo_
->setArg(7, getOperand(id
));
4894 case ArgumentKind::Callee
:
4895 case ArgumentKind::NumKinds
:
4896 MOZ_CRASH("Unexpected argument kind");
4902 bool WarpCacheIRTranspiler::emitLoadArgumentSlot(ValOperandId resultId
,
4903 uint32_t slotIndex
) {
4904 // Reverse of GetIndexOfArgument.
4907 // NewTarget | Args.. (reversed) | ThisValue | Callee
4908 // 0 | ArgC .. Arg1 Arg0 (+1) | argc (+1) | argc + 1 (+ 1)
4909 // ^ (if constructing)
4911 // NewTarget (optional)
4912 if (callInfo_
->constructing()) {
4913 if (slotIndex
== 0) {
4914 setArgumentId(ArgumentKind::NewTarget
, resultId
);
4915 return defineOperand(resultId
, callInfo_
->getNewTarget());
4918 slotIndex
-= 1; // Adjust slot index to match non-constructing calls.
4922 if (slotIndex
< callInfo_
->argc()) {
4923 uint32_t arg
= callInfo_
->argc() - 1 - slotIndex
;
4924 ArgumentKind kind
= ArgumentKindForArgIndex(arg
);
4925 MOZ_ASSERT(kind
< ArgumentKind::NumKinds
);
4926 setArgumentId(kind
, resultId
);
4927 return defineOperand(resultId
, callInfo_
->getArg(arg
));
4931 if (slotIndex
== callInfo_
->argc()) {
4932 setArgumentId(ArgumentKind::This
, resultId
);
4933 return defineOperand(resultId
, callInfo_
->thisArg());
4937 MOZ_ASSERT(slotIndex
== callInfo_
->argc() + 1);
4938 return defineOperand(resultId
, callInfo_
->callee());
4941 bool WarpCacheIRTranspiler::emitLoadArgumentFixedSlot(ValOperandId resultId
,
4942 uint8_t slotIndex
) {
4943 return emitLoadArgumentSlot(resultId
, slotIndex
);
4946 bool WarpCacheIRTranspiler::emitLoadArgumentDynamicSlot(ValOperandId resultId
,
4947 Int32OperandId argcId
,
4948 uint8_t slotIndex
) {
4950 MDefinition
* argc
= getOperand(argcId
);
4951 MOZ_ASSERT(argc
->toConstant()->toInt32() ==
4952 static_cast<int32_t>(callInfo_
->argc()));
4955 return emitLoadArgumentSlot(resultId
, callInfo_
->argc() + slotIndex
);
4958 WrappedFunction
* WarpCacheIRTranspiler::maybeWrappedFunction(
4959 MDefinition
* callee
, CallKind kind
, uint16_t nargs
, FunctionFlags flags
) {
4960 MOZ_ASSERT(callee
->isConstant() || callee
->isNurseryObject());
4962 // If this is a native without a JitEntry, WrappedFunction needs to know the
4963 // target JSFunction.
4964 // TODO: support nursery-allocated natives with WrappedFunction, maybe by
4965 // storing the JSNative in the Baseline stub like flags/nargs.
4966 bool isNative
= flags
.isNativeWithoutJitEntry();
4967 if (isNative
&& !callee
->isConstant()) {
4971 JSFunction
* nativeTarget
= nullptr;
4973 nativeTarget
= &callee
->toConstant()->toObject().as
<JSFunction
>();
4976 WrappedFunction
* wrappedTarget
=
4977 new (alloc()) WrappedFunction(nativeTarget
, nargs
, flags
);
4978 MOZ_ASSERT_IF(kind
== CallKind::Native
|| kind
== CallKind::DOM
,
4979 wrappedTarget
->isNativeWithoutJitEntry());
4980 MOZ_ASSERT_IF(kind
== CallKind::Scripted
, wrappedTarget
->hasJitEntry());
4981 return wrappedTarget
;
4984 WrappedFunction
* WarpCacheIRTranspiler::maybeCallTarget(MDefinition
* callee
,
4986 // CacheIR emits the following for specialized calls:
4987 // GuardSpecificFunction <callee> <func> ..
4988 // Call(Native|Scripted)Function <callee> ..
4990 // GuardClass <callee> ..
4991 // GuardFunctionScript <callee> <script> ..
4992 // CallScriptedFunction <callee> ..
4994 // We can use the <func> JSFunction or <script> BaseScript to specialize this
4996 if (callee
->isGuardSpecificFunction()) {
4997 auto* guard
= callee
->toGuardSpecificFunction();
4998 return maybeWrappedFunction(guard
->expected(), kind
, guard
->nargs(),
5001 if (callee
->isGuardFunctionScript()) {
5002 MOZ_ASSERT(kind
== CallKind::Scripted
);
5003 auto* guard
= callee
->toGuardFunctionScript();
5004 WrappedFunction
* wrappedTarget
= new (alloc()) WrappedFunction(
5005 /* nativeFun = */ nullptr, guard
->nargs(), guard
->flags());
5006 MOZ_ASSERT(wrappedTarget
->hasJitEntry());
5007 return wrappedTarget
;
5012 // If it is possible to use MCall for this call, update callInfo_ to use
5013 // the correct arguments. Otherwise, update the ArgFormat of callInfo_.
5014 bool WarpCacheIRTranspiler::updateCallInfo(MDefinition
* callee
,
5016 // The transpilation will add various guards to the callee.
5017 // We replace the callee referenced by the CallInfo, so that
5018 // the resulting call instruction depends on these guards.
5019 callInfo_
->setCallee(callee
);
5021 // The transpilation may also add guards to other arguments.
5022 // We replace those arguments in the CallInfo here.
5023 updateArgumentsFromOperands();
5025 switch (flags
.getArgFormat()) {
5026 case CallFlags::Standard
:
5027 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5029 case CallFlags::Spread
:
5030 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Array
);
5032 case CallFlags::FunCall
:
5033 // Note: We already changed the callee to the target
5034 // function instead of the |call| function.
5035 MOZ_ASSERT(!callInfo_
->constructing());
5036 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5038 if (callInfo_
->argc() == 0) {
5039 // Special case for fun.call() with no arguments.
5040 auto* undef
= constant(UndefinedValue());
5041 callInfo_
->setThis(undef
);
5043 // The first argument for |call| is the new this value.
5044 callInfo_
->setThis(callInfo_
->getArg(0));
5046 // Shift down all other arguments by removing the first.
5047 callInfo_
->removeArg(0);
5050 case CallFlags::FunApplyArgsObj
:
5051 MOZ_ASSERT(!callInfo_
->constructing());
5052 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5054 callInfo_
->setArgFormat(CallInfo::ArgFormat::FunApplyArgsObj
);
5056 case CallFlags::FunApplyArray
: {
5057 MDefinition
* argFunc
= callInfo_
->thisArg();
5058 MDefinition
* argThis
= callInfo_
->getArg(0);
5059 callInfo_
->setCallee(argFunc
);
5060 callInfo_
->setThis(argThis
);
5061 callInfo_
->setArgFormat(CallInfo::ArgFormat::Array
);
5064 case CallFlags::FunApplyNullUndefined
:
5065 // Note: We already changed the callee to the target
5066 // function instead of the |apply| function.
5067 MOZ_ASSERT(callInfo_
->argc() == 2);
5068 MOZ_ASSERT(!callInfo_
->constructing());
5069 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5070 callInfo_
->setThis(callInfo_
->getArg(0));
5071 callInfo_
->getArg(1)->setImplicitlyUsedUnchecked();
5072 callInfo_
->removeArg(1);
5073 callInfo_
->removeArg(0);
5076 MOZ_CRASH("Unsupported arg format");
5081 // Returns true if we are generating a call to CreateThisFromIon and
5082 // must check its return value.
5083 bool WarpCacheIRTranspiler::maybeCreateThis(MDefinition
* callee
,
5084 CallFlags flags
, CallKind kind
) {
5085 MOZ_ASSERT(kind
!= CallKind::DOM
, "DOM functions are not constructors");
5086 MDefinition
* thisArg
= callInfo_
->thisArg();
5088 if (kind
== CallKind::Native
) {
5089 // Native functions keep the is-constructing MagicValue as |this|.
5090 // If one of the arguments uses spread syntax this can be a loop phi with
5092 MOZ_ASSERT(thisArg
->type() == MIRType::MagicIsConstructing
||
5096 MOZ_ASSERT(kind
== CallKind::Scripted
);
5098 if (thisArg
->isNewPlainObject()) {
5099 // We have already updated |this| based on MetaScriptedThisShape. We do
5100 // not need to generate a check.
5103 if (flags
.needsUninitializedThis()) {
5104 MConstant
* uninit
= constant(MagicValue(JS_UNINITIALIZED_LEXICAL
));
5105 thisArg
->setImplicitlyUsedUnchecked();
5106 callInfo_
->setThis(uninit
);
5109 // See the Native case above.
5110 MOZ_ASSERT(thisArg
->type() == MIRType::MagicIsConstructing
||
5113 auto* newTarget
= unboxObjectInfallible(callInfo_
->getNewTarget());
5114 auto* createThis
= MCreateThis::New(alloc(), callee
, newTarget
);
5117 thisArg
->setImplicitlyUsedUnchecked();
5118 callInfo_
->setThis(createThis
);
5123 bool WarpCacheIRTranspiler::emitCallFunction(
5124 ObjOperandId calleeId
, Int32OperandId argcId
,
5125 mozilla::Maybe
<ObjOperandId
> thisObjId
, CallFlags flags
, CallKind kind
) {
5126 MDefinition
* callee
= getOperand(calleeId
);
5127 if (kind
== CallKind::Scripted
&& callInfo_
&& callInfo_
->isInlined()) {
5128 // We are transpiling to generate the correct guards. We also
5129 // update the CallInfo to use the correct arguments. Code for the
5130 // inlined function itself will be generated in
5131 // WarpBuilder::buildInlinedCall.
5132 if (!updateCallInfo(callee
, flags
)) {
5135 if (callInfo_
->constructing()) {
5136 MOZ_ASSERT(flags
.isConstructing());
5138 // We call maybeCreateThis to update |this|, but inlined constructors
5139 // never need a VM call. CallIRGenerator::getThisForScripted ensures that
5140 // we don't attach a specialized stub unless we have a template object or
5141 // know that the constructor needs uninitialized this.
5142 MOZ_ALWAYS_FALSE(maybeCreateThis(callee
, flags
, CallKind::Scripted
));
5143 mozilla::DebugOnly
<MDefinition
*> thisArg
= callInfo_
->thisArg();
5144 MOZ_ASSERT(thisArg
->isNewPlainObject() ||
5145 thisArg
->type() == MIRType::MagicUninitializedLexical
);
5148 if (flags
.getArgFormat() == CallFlags::FunCall
) {
5149 callInfo_
->setInliningResumeMode(ResumeMode::InlinedFunCall
);
5151 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
);
5152 callInfo_
->setInliningResumeMode(ResumeMode::InlinedStandardCall
);
5155 switch (callInfo_
->argFormat()) {
5156 case CallInfo::ArgFormat::Standard
:
5159 MOZ_CRASH("Unsupported arg format");
5165 MDefinition
* argc
= getOperand(argcId
);
5166 MOZ_ASSERT(argc
->toConstant()->toInt32() ==
5167 static_cast<int32_t>(callInfo_
->argc()));
5170 if (!updateCallInfo(callee
, flags
)) {
5174 if (kind
== CallKind::DOM
) {
5175 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::Standard
);
5176 // For DOM calls |this| has a class guard.
5177 MDefinition
* thisObj
= getOperand(*thisObjId
);
5178 callInfo_
->setThis(thisObj
);
5181 WrappedFunction
* wrappedTarget
= maybeCallTarget(callee
, kind
);
5183 bool needsThisCheck
= false;
5184 if (callInfo_
->constructing()) {
5185 MOZ_ASSERT(flags
.isConstructing());
5186 needsThisCheck
= maybeCreateThis(callee
, flags
, kind
);
5187 if (needsThisCheck
) {
5188 wrappedTarget
= nullptr;
5192 switch (callInfo_
->argFormat()) {
5193 case CallInfo::ArgFormat::Standard
: {
5194 MCall
* call
= makeCall(*callInfo_
, needsThisCheck
, wrappedTarget
,
5195 kind
== CallKind::DOM
);
5200 if (flags
.isSameRealm()) {
5201 call
->setNotCrossRealm();
5204 if (call
->isEffectful()) {
5207 return resumeAfter(call
);
5210 MOZ_ASSERT(kind
== CallKind::DOM
);
5215 case CallInfo::ArgFormat::Array
: {
5216 MInstruction
* call
= makeSpreadCall(*callInfo_
, needsThisCheck
,
5217 flags
.isSameRealm(), wrappedTarget
);
5224 return resumeAfter(call
);
5226 case CallInfo::ArgFormat::FunApplyArgsObj
: {
5227 return emitFunApplyArgsObj(wrappedTarget
, flags
);
5230 MOZ_CRASH("unreachable");
5233 bool WarpCacheIRTranspiler::emitFunApplyArgsObj(WrappedFunction
* wrappedTarget
,
5235 MOZ_ASSERT(!callInfo_
->constructing());
5237 MDefinition
* callee
= callInfo_
->thisArg();
5238 MDefinition
* thisArg
= callInfo_
->getArg(0);
5239 MDefinition
* argsObj
= callInfo_
->getArg(1);
5241 MApplyArgsObj
* apply
=
5242 MApplyArgsObj::New(alloc(), wrappedTarget
, callee
, argsObj
, thisArg
);
5244 if (flags
.isSameRealm()) {
5245 apply
->setNotCrossRealm();
5247 if (callInfo_
->ignoresReturnValue()) {
5248 apply
->setIgnoresReturnValue();
5251 addEffectful(apply
);
5254 return resumeAfter(apply
);
5257 #ifndef JS_SIMULATOR
5258 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId
,
5259 Int32OperandId argcId
,
5262 bool ignoresReturnValue
) {
5263 // Instead of ignoresReturnValue we use CallInfo::ignoresReturnValue.
5264 return emitCallFunction(calleeId
, argcId
, mozilla::Nothing(), flags
,
5268 bool WarpCacheIRTranspiler::emitCallDOMFunction(ObjOperandId calleeId
,
5269 Int32OperandId argcId
,
5270 ObjOperandId thisObjId
,
5272 uint32_t argcFixed
) {
5273 return emitCallFunction(calleeId
, argcId
, mozilla::Some(thisObjId
), flags
,
5277 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId
,
5278 Int32OperandId argcId
,
5281 uint32_t targetOffset
) {
5282 return emitCallFunction(calleeId
, argcId
, mozilla::Nothing(), flags
,
5286 bool WarpCacheIRTranspiler::emitCallDOMFunction(
5287 ObjOperandId calleeId
, Int32OperandId argcId
, ObjOperandId thisObjId
,
5288 CallFlags flags
, uint32_t argcFixed
, uint32_t targetOffset
) {
5289 return emitCallFunction(calleeId
, argcId
, mozilla::Some(thisObjId
), flags
,
5294 bool WarpCacheIRTranspiler::emitCallScriptedFunction(ObjOperandId calleeId
,
5295 Int32OperandId argcId
,
5297 uint32_t argcFixed
) {
5298 return emitCallFunction(calleeId
, argcId
, mozilla::Nothing(), flags
,
5299 CallKind::Scripted
);
5302 bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId
,
5303 Int32OperandId argcId
,
5304 uint32_t icScriptOffset
,
5306 uint32_t argcFixed
) {
5307 return emitCallFunction(calleeId
, argcId
, mozilla::Nothing(), flags
,
5308 CallKind::Scripted
);
5312 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetShared(
5313 MDefinition
* target
, MDefinition
* receiver
, MDefinition
* handler
,
5314 MDefinition
* id
, MDefinition
* trapDef
, WrappedFunction
* trap
) {
5315 CallInfo
callInfo(alloc(), /* constructing = */ false,
5316 /* ignoresRval = */ false);
5317 callInfo
.initForProxyGet(trapDef
, handler
, target
, id
, receiver
);
5319 MCall
* call
= makeCall(callInfo
, /* needsThisCheck = */ false, trap
);
5326 if (!current
->ensureHasSlots(3)) {
5329 current
->push(call
);
5331 current
->push(target
);
5333 MResumePoint
* resumePoint
=
5334 MResumePoint::New(alloc(), current
, loc_
.toRawBytecode(),
5335 ResumeMode::ResumeAfterCheckProxyGetResult
);
5339 call
->setResumePoint(resumePoint
);
5344 MCheckScriptedProxyGetResult
* check
=
5345 MCheckScriptedProxyGetResult::New(alloc(), target
, id
, call
);
5346 addEffectfulUnsafe(check
);
5348 return resumeAfterUnchecked(check
);
5351 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetResult(
5352 ValOperandId targetId
, ObjOperandId receiverId
, ObjOperandId handlerId
,
5353 uint32_t trapOffset
, uint32_t idOffset
, uint32_t nargsAndFlags
) {
5354 MDefinition
* target
= getOperand(targetId
);
5355 MDefinition
* receiver
= getOperand(receiverId
);
5356 MDefinition
* handler
= getOperand(handlerId
);
5357 MDefinition
* trap
= objectStubField(trapOffset
);
5358 jsid id
= idStubField(idOffset
);
5359 MDefinition
* idDef
= constant(StringValue(id
.toAtom()));
5360 uint16_t nargs
= nargsAndFlags
>> 16;
5361 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
5362 WrappedFunction
* wrappedTarget
=
5363 maybeWrappedFunction(trap
, CallKind::Scripted
, nargs
, flags
);
5364 return emitCallScriptedProxyGetShared(target
, receiver
, handler
, idDef
, trap
,
5368 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetByValueResult(
5369 ValOperandId targetId
, ObjOperandId receiverId
, ObjOperandId handlerId
,
5370 ValOperandId idId
, uint32_t trapOffset
, uint32_t nargsAndFlags
) {
5371 MDefinition
* target
= getOperand(targetId
);
5372 MDefinition
* receiver
= getOperand(receiverId
);
5373 MDefinition
* handler
= getOperand(handlerId
);
5374 MDefinition
* trap
= objectStubField(trapOffset
);
5375 MDefinition
* idDef
= getOperand(idId
);
5376 uint16_t nargs
= nargsAndFlags
>> 16;
5377 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
5378 WrappedFunction
* wrappedTarget
=
5379 maybeWrappedFunction(trap
, CallKind::Scripted
, nargs
, flags
);
5380 return emitCallScriptedProxyGetShared(target
, receiver
, handler
, idDef
, trap
,
5385 bool WarpCacheIRTranspiler::emitCallClassHook(ObjOperandId calleeId
,
5386 Int32OperandId argcId
,
5389 uint32_t targetOffset
) {
5390 MDefinition
* callee
= getOperand(calleeId
);
5391 JSNative target
= jsnativeStubField(targetOffset
);
5394 MDefinition
* argc
= getOperand(argcId
);
5395 MOZ_ASSERT(argc
->toConstant()->toInt32() ==
5396 static_cast<int32_t>(callInfo_
->argc()));
5399 if (!updateCallInfo(callee
, flags
)) {
5403 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5404 MOZ_ASSERT(flags
.getArgFormat() == CallFlags::ArgFormat::Standard
);
5406 // Callees can be from any realm. If this changes, we should update
5407 // MCallClassHook::maybeCrossRealm.
5408 MOZ_ASSERT(!flags
.isSameRealm());
5410 auto* call
= MCallClassHook::New(alloc(), target
, callInfo_
->argc(),
5411 callInfo_
->constructing());
5416 if (callInfo_
->ignoresReturnValue()) {
5417 call
->setIgnoresReturnValue();
5420 call
->initCallee(callInfo_
->callee());
5421 call
->addArg(0, callInfo_
->thisArg());
5423 for (uint32_t i
= 0; i
< callInfo_
->argc(); i
++) {
5424 call
->addArg(i
+ 1, callInfo_
->getArg(i
));
5427 if (callInfo_
->constructing()) {
5428 call
->addArg(1 + callInfo_
->argc(), callInfo_
->getNewTarget());
5434 return resumeAfter(call
);
5437 bool WarpCacheIRTranspiler::emitCallBoundScriptedFunction(
5438 ObjOperandId calleeId
, ObjOperandId targetId
, Int32OperandId argcId
,
5439 CallFlags flags
, uint32_t numBoundArgs
) {
5440 MDefinition
* callee
= getOperand(calleeId
);
5441 MDefinition
* target
= getOperand(targetId
);
5443 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5444 MOZ_ASSERT(callInfo_
->constructing() == flags
.isConstructing());
5446 callInfo_
->setCallee(target
);
5447 updateArgumentsFromOperands();
5449 WrappedFunction
* wrappedTarget
= maybeCallTarget(target
, CallKind::Scripted
);
5451 bool needsThisCheck
= false;
5452 if (callInfo_
->constructing()) {
5453 callInfo_
->setNewTarget(target
);
5454 needsThisCheck
= maybeCreateThis(target
, flags
, CallKind::Scripted
);
5455 if (needsThisCheck
) {
5456 wrappedTarget
= nullptr;
5459 auto* thisv
= MLoadFixedSlot::New(alloc(), callee
,
5460 BoundFunctionObject::boundThisSlot());
5462 callInfo_
->thisArg()->setImplicitlyUsedUnchecked();
5463 callInfo_
->setThis(thisv
);
5466 bool usingInlineBoundArgs
=
5467 numBoundArgs
<= BoundFunctionObject::MaxInlineBoundArgs
;
5469 MElements
* elements
= nullptr;
5470 if (!usingInlineBoundArgs
) {
5471 auto* boundArgs
= MLoadFixedSlot::New(
5472 alloc(), callee
, BoundFunctionObject::firstInlineBoundArgSlot());
5474 auto* boundArgsObj
= unboxObjectInfallible(boundArgs
, IsMovable::Yes
);
5475 elements
= MElements::New(alloc(), boundArgsObj
);
5479 auto loadBoundArg
= [&](size_t index
) {
5481 if (usingInlineBoundArgs
) {
5482 size_t slot
= BoundFunctionObject::firstInlineBoundArgSlot() + index
;
5483 arg
= MLoadFixedSlot::New(alloc(), callee
, slot
);
5485 arg
= MLoadElement::New(alloc(), elements
, constant(Int32Value(index
)));
5490 if (!callInfo_
->prependArgs(numBoundArgs
, loadBoundArg
)) {
5494 MCall
* call
= makeCall(*callInfo_
, needsThisCheck
, wrappedTarget
);
5499 if (flags
.isSameRealm()) {
5500 call
->setNotCrossRealm();
5505 return resumeAfter(call
);
5508 bool WarpCacheIRTranspiler::emitBindFunctionResult(
5509 ObjOperandId targetId
, uint32_t argc
, uint32_t templateObjectOffset
) {
5510 MDefinition
* target
= getOperand(targetId
);
5511 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
5513 MOZ_ASSERT(callInfo_
->argc() == argc
);
5515 auto* bound
= MBindFunction::New(alloc(), target
, argc
, templateObj
);
5519 addEffectful(bound
);
5521 for (uint32_t i
= 0; i
< argc
; i
++) {
5522 bound
->initArg(i
, callInfo_
->getArg(i
));
5526 return resumeAfter(bound
);
5529 bool WarpCacheIRTranspiler::emitSpecializedBindFunctionResult(
5530 ObjOperandId targetId
, uint32_t argc
, uint32_t templateObjectOffset
) {
5531 MDefinition
* target
= getOperand(targetId
);
5532 JSObject
* templateObj
= tenuredObjectStubField(templateObjectOffset
);
5534 MOZ_ASSERT(callInfo_
->argc() == argc
);
5536 auto* bound
= MNewBoundFunction::New(alloc(), templateObj
);
5539 size_t numBoundArgs
= argc
> 0 ? argc
- 1 : 0;
5540 MOZ_ASSERT(numBoundArgs
<= BoundFunctionObject::MaxInlineBoundArgs
);
5542 auto initSlot
= [&](size_t slot
, MDefinition
* value
) {
5544 // Assert we can elide the post write barrier. See also the comment in
5545 // WarpBuilder::buildNamedLambdaEnv.
5546 add(MAssertCanElidePostWriteBarrier::New(alloc(), bound
, value
));
5548 addUnchecked(MStoreFixedSlot::NewUnbarriered(alloc(), bound
, slot
, value
));
5551 initSlot(BoundFunctionObject::targetSlot(), target
);
5553 initSlot(BoundFunctionObject::boundThisSlot(), callInfo_
->getArg(0));
5555 for (size_t i
= 0; i
< numBoundArgs
; i
++) {
5556 size_t slot
= BoundFunctionObject::firstInlineBoundArgSlot() + i
;
5557 initSlot(slot
, callInfo_
->getArg(1 + i
));
5564 bool WarpCacheIRTranspiler::emitCallWasmFunction(
5565 ObjOperandId calleeId
, Int32OperandId argcId
, CallFlags flags
,
5566 uint32_t argcFixed
, uint32_t funcExportOffset
, uint32_t instanceOffset
) {
5567 MDefinition
* callee
= getOperand(calleeId
);
5569 MDefinition
* argc
= getOperand(argcId
);
5570 MOZ_ASSERT(argc
->toConstant()->toInt32() ==
5571 static_cast<int32_t>(callInfo_
->argc()));
5573 JSObject
* instanceObject
= tenuredObjectStubField(instanceOffset
);
5574 auto* wasmInstanceObj
= &instanceObject
->as
<WasmInstanceObject
>();
5575 const wasm::FuncExport
* funcExport
= wasmFuncExportField(funcExportOffset
);
5576 const wasm::FuncType
& sig
=
5577 wasmInstanceObj
->instance().metadata().getFuncExportType(*funcExport
);
5579 if (!updateCallInfo(callee
, flags
)) {
5583 static_assert(wasm::MaxArgsForJitInlineCall
<= MaxNumLInstructionOperands
,
5584 "arguments must fit in LIR operands");
5585 MOZ_ASSERT(sig
.args().length() <= wasm::MaxArgsForJitInlineCall
);
5587 MOZ_ASSERT(callInfo_
->argFormat() == CallInfo::ArgFormat::Standard
);
5589 auto* call
= MIonToWasmCall::New(alloc(), wasmInstanceObj
, *funcExport
);
5594 mozilla::Maybe
<MDefinition
*> undefined
;
5595 for (size_t i
= 0; i
< sig
.args().length(); i
++) {
5596 if (!alloc().ensureBallast()) {
5601 if (i
< callInfo_
->argc()) {
5602 arg
= callInfo_
->getArg(i
);
5605 undefined
.emplace(constant(UndefinedValue()));
5607 arg
= convertWasmArg(*undefined
, sig
.args()[i
].kind());
5609 call
->initArg(i
, arg
);
5614 // Add any post-function call conversions that are necessary.
5615 MInstruction
* postConversion
= call
;
5616 const wasm::ValTypeVector
& results
= sig
.results();
5617 MOZ_ASSERT(results
.length() <= 1, "Multi-value returns not supported.");
5618 if (results
.length() == 0) {
5619 // No results to convert.
5621 switch (results
[0].kind()) {
5622 case wasm::ValType::I64
:
5623 // JS expects a BigInt from I64 types.
5624 postConversion
= MInt64ToBigInt::New(alloc(), call
);
5626 // Make non-movable so we can attach a resume point.
5627 postConversion
->setNotMovable();
5629 add(postConversion
);
5632 // No spectre.index_masking of i32 results required, as the generated
5633 // stub takes care of that.
5638 // The resume point has to be attached to the post-conversion instruction
5639 // (if present) instead of to the call. This way, if the call triggers an
5640 // invalidation bailout, we will have the BigInt value on the Baseline stack.
5641 // Potential alternative solution: attach the resume point to the call and
5642 // have bailouts turn the Int64 value into a BigInt, maybe with a recover
5644 pushResult(postConversion
);
5645 return resumeAfterUnchecked(postConversion
);
5648 MDefinition
* WarpCacheIRTranspiler::convertWasmArg(MDefinition
* arg
,
5649 wasm::ValType::Kind kind
) {
5650 // An invariant in this code is that any type conversion operation that has
5651 // externally visible effects, such as invoking valueOf on an object argument,
5652 // must bailout so that we don't have to worry about replaying effects during
5653 // argument conversion.
5654 MInstruction
* conversion
= nullptr;
5656 case wasm::ValType::I32
:
5657 conversion
= MTruncateToInt32::New(alloc(), arg
);
5659 case wasm::ValType::I64
:
5660 conversion
= MToInt64::New(alloc(), arg
);
5662 case wasm::ValType::F32
:
5663 conversion
= MToFloat32::New(alloc(), arg
);
5665 case wasm::ValType::F64
:
5666 conversion
= MToDouble::New(alloc(), arg
);
5668 case wasm::ValType::V128
:
5669 MOZ_CRASH("Unexpected type for Wasm JitEntry");
5670 case wasm::ValType::Ref
:
5671 // Transform the JS representation into an AnyRef representation.
5672 // The resulting type is MIRType::WasmAnyRef. These cases are all
5674 switch (arg
->type()) {
5675 case MIRType::Object
:
5676 conversion
= MWasmAnyRefFromJSObject::New(alloc(), arg
);
5678 case MIRType::String
:
5679 conversion
= MWasmAnyRefFromJSString::New(alloc(), arg
);
5682 arg
->setImplicitlyUsedUnchecked();
5683 conversion
= MWasmNullConstant::New(alloc());
5686 conversion
= MWasmAnyRefFromJSValue::New(alloc(), arg
);
5696 bool WarpCacheIRTranspiler::emitGuardWasmArg(ValOperandId argId
,
5697 wasm::ValType::Kind kind
) {
5698 MDefinition
* arg
= getOperand(argId
);
5699 MDefinition
* conversion
= convertWasmArg(arg
, kind
);
5701 setOperand(argId
, conversion
);
5705 bool WarpCacheIRTranspiler::emitCallGetterResult(CallKind kind
,
5706 ValOperandId receiverId
,
5707 uint32_t getterOffset
,
5709 uint32_t nargsAndFlagsOffset
) {
5710 MDefinition
* receiver
= getOperand(receiverId
);
5711 MDefinition
* getter
= objectStubField(getterOffset
);
5712 if (kind
== CallKind::Scripted
&& callInfo_
&& callInfo_
->isInlined()) {
5713 // We are transpiling to generate the correct guards. We also update the
5714 // CallInfo to use the correct arguments. Code for the inlined getter
5715 // itself will be generated in WarpBuilder::buildInlinedCall.
5716 callInfo_
->initForGetterCall(getter
, receiver
);
5717 callInfo_
->setInliningResumeMode(ResumeMode::InlinedAccessor
);
5719 // Make sure there's enough room to push the arguments on the stack.
5720 if (!current
->ensureHasSlots(2)) {
5727 uint32_t nargsAndFlags
= uint32StubField(nargsAndFlagsOffset
);
5728 uint16_t nargs
= nargsAndFlags
>> 16;
5729 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
5730 WrappedFunction
* wrappedTarget
=
5731 maybeWrappedFunction(getter
, kind
, nargs
, flags
);
5733 bool ignoresRval
= loc_
.resultIsPopped();
5734 CallInfo
callInfo(alloc(), /* constructing = */ false, ignoresRval
);
5735 callInfo
.initForGetterCall(getter
, receiver
);
5737 MCall
* call
= makeCall(callInfo
, /* needsThisCheck = */ false, wrappedTarget
);
5743 call
->setNotCrossRealm();
5749 return resumeAfter(call
);
5752 bool WarpCacheIRTranspiler::emitCallScriptedGetterResult(
5753 ValOperandId receiverId
, uint32_t getterOffset
, bool sameRealm
,
5754 uint32_t nargsAndFlagsOffset
) {
5755 return emitCallGetterResult(CallKind::Scripted
, receiverId
, getterOffset
,
5756 sameRealm
, nargsAndFlagsOffset
);
5759 bool WarpCacheIRTranspiler::emitCallInlinedGetterResult(
5760 ValOperandId receiverId
, uint32_t getterOffset
, uint32_t icScriptOffset
,
5761 bool sameRealm
, uint32_t nargsAndFlagsOffset
) {
5762 return emitCallGetterResult(CallKind::Scripted
, receiverId
, getterOffset
,
5763 sameRealm
, nargsAndFlagsOffset
);
5766 bool WarpCacheIRTranspiler::emitCallNativeGetterResult(
5767 ValOperandId receiverId
, uint32_t getterOffset
, bool sameRealm
,
5768 uint32_t nargsAndFlagsOffset
) {
5769 return emitCallGetterResult(CallKind::Native
, receiverId
, getterOffset
,
5770 sameRealm
, nargsAndFlagsOffset
);
5773 bool WarpCacheIRTranspiler::emitCallSetter(CallKind kind
,
5774 ObjOperandId receiverId
,
5775 uint32_t setterOffset
,
5776 ValOperandId rhsId
, bool sameRealm
,
5777 uint32_t nargsAndFlagsOffset
) {
5778 MDefinition
* receiver
= getOperand(receiverId
);
5779 MDefinition
* setter
= objectStubField(setterOffset
);
5780 MDefinition
* rhs
= getOperand(rhsId
);
5781 if (kind
== CallKind::Scripted
&& callInfo_
&& callInfo_
->isInlined()) {
5782 // We are transpiling to generate the correct guards. We also update the
5783 // CallInfo to use the correct arguments. Code for the inlined setter
5784 // itself will be generated in WarpBuilder::buildInlinedCall.
5785 callInfo_
->initForSetterCall(setter
, receiver
, rhs
);
5786 callInfo_
->setInliningResumeMode(ResumeMode::InlinedAccessor
);
5788 // Make sure there's enough room to push the arguments on the stack.
5789 if (!current
->ensureHasSlots(3)) {
5796 uint32_t nargsAndFlags
= uint32StubField(nargsAndFlagsOffset
);
5797 uint16_t nargs
= nargsAndFlags
>> 16;
5798 FunctionFlags flags
= FunctionFlags(uint16_t(nargsAndFlags
));
5799 WrappedFunction
* wrappedTarget
=
5800 maybeWrappedFunction(setter
, kind
, nargs
, flags
);
5802 CallInfo
callInfo(alloc(), /* constructing = */ false,
5803 /* ignoresReturnValue = */ true);
5804 callInfo
.initForSetterCall(setter
, receiver
, rhs
);
5806 MCall
* call
= makeCall(callInfo
, /* needsThisCheck = */ false, wrappedTarget
);
5812 call
->setNotCrossRealm();
5816 return resumeAfter(call
);
5819 bool WarpCacheIRTranspiler::emitCallScriptedSetter(
5820 ObjOperandId receiverId
, uint32_t setterOffset
, ValOperandId rhsId
,
5821 bool sameRealm
, uint32_t nargsAndFlagsOffset
) {
5822 return emitCallSetter(CallKind::Scripted
, receiverId
, setterOffset
, rhsId
,
5823 sameRealm
, nargsAndFlagsOffset
);
5826 bool WarpCacheIRTranspiler::emitCallInlinedSetter(
5827 ObjOperandId receiverId
, uint32_t setterOffset
, ValOperandId rhsId
,
5828 uint32_t icScriptOffset
, bool sameRealm
, uint32_t nargsAndFlagsOffset
) {
5829 return emitCallSetter(CallKind::Scripted
, receiverId
, setterOffset
, rhsId
,
5830 sameRealm
, nargsAndFlagsOffset
);
5833 bool WarpCacheIRTranspiler::emitCallNativeSetter(ObjOperandId receiverId
,
5834 uint32_t setterOffset
,
5837 uint32_t nargsAndFlagsOffset
) {
5838 return emitCallSetter(CallKind::Native
, receiverId
, setterOffset
, rhsId
,
5839 sameRealm
, nargsAndFlagsOffset
);
5842 bool WarpCacheIRTranspiler::emitMetaScriptedThisShape(
5843 uint32_t thisShapeOffset
) {
5844 SharedShape
* shape
= &shapeStubField(thisShapeOffset
)->asShared();
5845 MOZ_ASSERT(shape
->getObjectClass() == &PlainObject::class_
);
5847 MConstant
* shapeConst
= MConstant::NewShape(alloc(), shape
);
5850 // TODO: support pre-tenuring.
5851 gc::Heap heap
= gc::Heap::Default
;
5853 uint32_t numFixedSlots
= shape
->numFixedSlots();
5854 uint32_t numDynamicSlots
= NativeObject::calculateDynamicSlots(shape
);
5855 gc::AllocKind kind
= gc::GetGCObjectKind(numFixedSlots
);
5856 MOZ_ASSERT(gc::CanChangeToBackgroundAllocKind(kind
, &PlainObject::class_
));
5857 kind
= gc::ForegroundToBackgroundAllocKind(kind
);
5859 auto* createThis
= MNewPlainObject::New(alloc(), shapeConst
, numFixedSlots
,
5860 numDynamicSlots
, kind
, heap
);
5863 callInfo_
->thisArg()->setImplicitlyUsedUnchecked();
5864 callInfo_
->setThis(createThis
);
5868 bool WarpCacheIRTranspiler::emitReturnFromIC() { return true; }
5870 bool WarpCacheIRTranspiler::emitBailout() {
5871 auto* bail
= MBail::New(alloc());
5877 bool WarpCacheIRTranspiler::emitAssertPropertyLookup(ObjOperandId objId
,
5879 uint32_t slotOffset
) {
5880 // We currently only emit checks in baseline.
5884 bool WarpCacheIRTranspiler::emitAssertRecoveredOnBailoutResult(
5885 ValOperandId valId
, bool mustBeRecovered
) {
5886 MDefinition
* val
= getOperand(valId
);
5888 // Don't assert for recovered instructions when recovering is disabled.
5889 if (JitOptions
.disableRecoverIns
) {
5890 pushResult(constant(UndefinedValue()));
5894 if (JitOptions
.checkRangeAnalysis
) {
5895 // If we are checking the range of all instructions, then the guards
5896 // inserted by Range Analysis prevent the use of recover instruction. Thus,
5897 // we just disable these checks.
5898 pushResult(constant(UndefinedValue()));
5902 auto* assert = MAssertRecoveredOnBailout::New(alloc(), val
, mustBeRecovered
);
5903 addEffectfulUnsafe(assert);
5904 current
->push(assert);
5906 // Create an instruction sequence which implies that the argument of the
5907 // assertRecoveredOnBailout function would be encoded at least in one
5909 auto* nop
= MNop::New(alloc());
5912 auto* resumePoint
= MResumePoint::New(
5913 alloc(), nop
->block(), loc_
.toRawBytecode(), ResumeMode::ResumeAfter
);
5917 nop
->setResumePoint(resumePoint
);
5919 auto* encode
= MEncodeSnapshot::New(alloc());
5920 addEffectfulUnsafe(encode
);
5924 pushResult(constant(UndefinedValue()));
5928 bool WarpCacheIRTranspiler::emitGuardNoAllocationMetadataBuilder(
5929 uint32_t builderAddrOffset
) {
5930 // This is a no-op because we discard all JIT code when set an allocation
5931 // metadata callback.
5935 bool WarpCacheIRTranspiler::emitNewPlainObjectResult(uint32_t numFixedSlots
,
5936 uint32_t numDynamicSlots
,
5937 gc::AllocKind allocKind
,
5938 uint32_t shapeOffset
,
5939 uint32_t siteOffset
) {
5940 Shape
* shape
= shapeStubField(shapeOffset
);
5941 gc::Heap heap
= allocSiteInitialHeapField(siteOffset
);
5943 auto* shapeConstant
= MConstant::NewShape(alloc(), shape
);
5946 auto* obj
= MNewPlainObject::New(alloc(), shapeConstant
, numFixedSlots
,
5947 numDynamicSlots
, allocKind
, heap
);
5954 bool WarpCacheIRTranspiler::emitNewArrayObjectResult(uint32_t length
,
5955 uint32_t shapeOffset
,
5956 uint32_t siteOffset
) {
5957 Shape
* shape
= shapeStubField(shapeOffset
);
5958 gc::Heap heap
= allocSiteInitialHeapField(siteOffset
);
5960 auto* shapeConstant
= MConstant::NewShape(alloc(), shape
);
5963 auto* obj
= MNewArrayObject::New(alloc(), shapeConstant
, length
, heap
);
5970 bool WarpCacheIRTranspiler::emitCloseIterScriptedResult(ObjOperandId iterId
,
5971 ObjOperandId calleeId
,
5972 CompletionKind kind
,
5973 uint32_t calleeNargs
) {
5974 MDefinition
* iter
= getOperand(iterId
);
5975 MDefinition
* callee
= getOperand(calleeId
);
5977 WrappedFunction
* wrappedTarget
= maybeCallTarget(callee
, CallKind::Scripted
);
5978 MOZ_ASSERT(wrappedTarget
);
5979 MOZ_ASSERT(wrappedTarget
->nargs() == calleeNargs
);
5980 MOZ_ASSERT(wrappedTarget
->hasJitEntry());
5982 bool constructing
= false;
5983 bool ignoresRval
= false;
5984 bool needsThisCheck
= false;
5985 bool isDOMCall
= false;
5986 CallInfo
callInfo(alloc(), constructing
, ignoresRval
);
5987 callInfo
.initForCloseIter(iter
, callee
);
5988 MCall
* call
= makeCall(callInfo
, needsThisCheck
, wrappedTarget
, isDOMCall
);
5993 if (kind
== CompletionKind::Throw
) {
5994 return resumeAfter(call
);
5997 // If we bail out here, after the call but before the CheckIsObj, we
5998 // can't simply resume in the baseline interpreter. If we resume
5999 // after the CloseIter, we won't check the return value. If we
6000 // resume at the CloseIter, we will call the |return| method twice.
6001 // Instead, we use a special resume mode that captures the
6002 // intermediate value, and then checks that it's an object while
6004 current
->push(call
);
6005 MResumePoint
* resumePoint
=
6006 MResumePoint::New(alloc(), current
, loc_
.toRawBytecode(),
6007 ResumeMode::ResumeAfterCheckIsObject
);
6011 call
->setResumePoint(resumePoint
);
6014 MCheckIsObj
* check
= MCheckIsObj::New(
6015 alloc(), call
, uint8_t(CheckIsObjectKind::IteratorReturn
));
6016 addEffectfulUnsafe(check
);
6018 return resumeAfterUnchecked(check
);
6021 bool WarpCacheIRTranspiler::emitGuardGlobalGeneration(
6022 uint32_t expectedOffset
, uint32_t generationAddrOffset
) {
6023 uint32_t expected
= uint32StubField(expectedOffset
);
6024 const void* generationAddr
= rawPointerField(generationAddrOffset
);
6026 auto guard
= MGuardGlobalGeneration::New(alloc(), expected
, generationAddr
);
6032 #ifdef FUZZING_JS_FUZZILLI
6033 bool WarpCacheIRTranspiler::emitFuzzilliHashResult(ValOperandId valId
) {
6034 MDefinition
* input
= getOperand(valId
);
6036 auto* hash
= MFuzzilliHash::New(alloc(), input
);
6039 auto* store
= MFuzzilliHashStore::New(alloc(), hash
);
6040 addEffectful(store
);
6041 pushResult(constant(UndefinedValue()));
6043 return resumeAfter(store
);
6047 static void MaybeSetImplicitlyUsed(uint32_t numInstructionIdsBefore
,
6048 MDefinition
* input
) {
6049 // When building MIR from bytecode, for each MDefinition that's an operand to
6050 // a bytecode instruction, we must either add an SSA use or set the
6051 // ImplicitlyUsed flag on that definition. The ImplicitlyUsed flag prevents
6052 // the backend from optimizing-out values that will be used by Baseline after
6055 // WarpBuilder uses WarpPoppedValueUseChecker to assert this invariant in
6058 // This function is responsible for setting the ImplicitlyUsed flag for an
6059 // input when using the transpiler. It looks at the input's most recent use
6060 // and if that's an instruction that was added while transpiling this JSOp
6061 // (based on the MIR instruction id) we don't set the ImplicitlyUsed flag.
6063 if (input
->isImplicitlyUsed()) {
6068 // If the most recent use of 'input' is an instruction we just added, there is
6070 MDefinition
* inputUse
= input
->maybeMostRecentlyAddedDefUse();
6071 if (inputUse
&& inputUse
->id() >= numInstructionIdsBefore
) {
6075 // The transpiler didn't add a use for 'input'.
6076 input
->setImplicitlyUsed();
6079 bool jit::TranspileCacheIRToMIR(WarpBuilder
* builder
, BytecodeLocation loc
,
6080 const WarpCacheIR
* cacheIRSnapshot
,
6081 std::initializer_list
<MDefinition
*> inputs
,
6082 CallInfo
* maybeCallInfo
) {
6083 uint32_t numInstructionIdsBefore
=
6084 builder
->mirGen().graph().getNumInstructionIds();
6086 WarpCacheIRTranspiler
transpiler(builder
, loc
, maybeCallInfo
,
6088 if (!transpiler
.transpile(inputs
)) {
6092 for (MDefinition
* input
: inputs
) {
6093 MaybeSetImplicitlyUsed(numInstructionIdsBefore
, input
);
6096 if (maybeCallInfo
) {
6097 auto maybeSetFlag
= [numInstructionIdsBefore
](MDefinition
* def
) {
6098 MaybeSetImplicitlyUsed(numInstructionIdsBefore
, def
);
6100 maybeCallInfo
->forEachCallOperand(maybeSetFlag
);