Bug 1874683 - Part 7: Transpile LoadStringCodePoint. r=jandem,nbp
[gecko.git] / js / src / jit / WarpCacheIRTranspiler.cpp
blob31856a62a666e92f96e452efb25f1e88fa98ae84
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"
12 #include "jsmath.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"
21 #include "jit/LIR.h"
22 #include "jit/MIR.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"
37 using namespace js;
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_;
51 CallInfo* callInfo_;
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();
66 #ifdef DEBUG
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;
71 #endif
73 inline void addUnchecked(MInstruction* ins) {
74 current->add(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
79 // in tryAttach.
80 if (ins->bailoutKind() == BailoutKind::Unknown) {
81 ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
85 inline void add(MInstruction* ins) {
86 MOZ_ASSERT(!ins->isEffectful());
87 addUnchecked(ins);
90 inline void addEffectful(MInstruction* ins) {
91 MOZ_ASSERT(ins->isEffectful());
92 MOZ_ASSERT(!effectful_, "Can only have one effectful instruction");
93 addUnchecked(ins);
94 #ifdef DEBUG
95 effectful_ = ins;
96 #endif
99 // Bypasses all checks in addEffectful. Only used for testing functions.
100 inline void addEffectfulUnsafe(MInstruction* ins) {
101 MOZ_ASSERT(ins->isEffectful());
102 addUnchecked(ins);
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);
118 #ifdef DEBUG
119 pushedResult_ = true;
120 #endif
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) {
194 uint64_t raw =
195 static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
196 Value val = Value::fromRawBits(raw);
197 MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
198 return val;
200 double doubleStubField(uint32_t offset) {
201 uint64_t raw =
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,
242 OperandId rhsId,
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,
263 uint32_t valueId,
264 Scalar::Type elementType,
265 bool forEffect, AtomicOp op);
267 [[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId,
268 uint32_t slotIndex);
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,
282 CallFlags flags);
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,
298 bool sameRealm,
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);
305 #endif
307 CACHE_IR_TRANSPILER_GENERATED
309 public:
310 WarpCacheIRTranspiler(WarpBuilder* builder, BytecodeLocation loc,
311 CallInfo* callInfo, const WarpCacheIR* cacheIRSnapshot)
312 : WarpBuilderShared(builder->snapshot(), builder->mirGen(),
313 builder->currentBlock()),
314 builder_(builder),
315 loc_(loc),
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())) {
326 return false;
329 CacheIRReader reader(stubInfo_);
330 do {
331 CacheOp op = reader.readOp();
332 switch (op) {
333 #define DEFINE_OP(op, ...) \
334 case CacheOp::op: \
335 if (!emit##op(reader)) { \
336 return false; \
338 break;
339 CACHE_IR_TRANSPILER_OPS(DEFINE_OP)
340 #undef DEFINE_OP
342 default:
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
350 // instead.
351 MOZ_ASSERT_IF(effectful_,
352 effectful_->resumePoint() || effectful_->isIonToWasmCall());
353 return true;
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());
361 add(ins);
362 return ins;
365 auto* ins = MConstant::NewObject(alloc(), field.toObject());
366 add(ins);
367 return ins;
370 bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
371 GuardClassKind kind) {
372 MDefinition* def = getOperand(objId);
374 MInstruction* ins;
375 if (kind == GuardClassKind::JSFunction) {
376 ins = MGuardToFunction::New(alloc(), def);
377 } else {
378 const JSClass* classp = classForGuardClassKind(kind);
379 ins = MGuardToClass::New(alloc(), def, classp);
382 add(ins);
384 setOperand(objId, ins);
385 return true;
388 const JSClass* WarpCacheIRTranspiler::classForGuardClassKind(
389 GuardClassKind kind) {
390 switch (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:
414 break;
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);
425 add(ins);
427 setOperand(objId, ins);
428 return true;
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);
440 add(ins);
442 setOperand(objId, ins);
443 return true;
446 bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
447 auto* ins = MGuardFuse::New(alloc(), fuseIndex);
448 add(ins);
450 return true;
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);
462 add(ins);
464 setOperand(objId, ins);
465 return true;
468 bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
469 MDefinition* def = getOperand(objId);
471 auto* ins = MGuardNullProto::New(alloc(), def);
472 add(ins);
474 setOperand(objId, ins);
475 return true;
478 bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId) {
479 MDefinition* obj = getOperand(objId);
481 auto* ins = MGuardIsNativeObject::New(alloc(), obj);
482 add(ins);
484 setOperand(objId, ins);
485 return true;
488 bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId) {
489 MDefinition* obj = getOperand(objId);
491 auto* ins = MGuardIsProxy::New(alloc(), obj);
492 add(ins);
494 setOperand(objId, ins);
495 return true;
498 bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId) {
499 MDefinition* obj = getOperand(objId);
501 auto* ins = MGuardIsNotProxy::New(alloc(), obj);
502 add(ins);
504 setOperand(objId, ins);
505 return true;
508 bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
509 MDefinition* obj = getOperand(objId);
511 auto* ins = MGuardIsNotDOMProxy::New(alloc(), obj);
512 add(ins);
514 setOperand(objId, ins);
515 return true;
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);
525 add(ins);
527 setOperand(objId, ins);
528 return true;
531 bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId,
532 uint32_t idOffset) {
533 MDefinition* obj = getOperand(objId);
534 jsid id = idStubField(idOffset);
536 auto* ins = MProxyGet::New(alloc(), obj, id);
537 addEffectful(ins);
539 pushResult(ins);
540 return resumeAfter(ins);
543 bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId,
544 ValOperandId idId) {
545 MDefinition* obj = getOperand(objId);
546 MDefinition* id = getOperand(idId);
548 auto* ins = MProxyGetByValue::New(alloc(), obj, id);
549 addEffectful(ins);
551 pushResult(ins);
552 return resumeAfter(ins);
555 bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId,
556 ValOperandId idId,
557 bool hasOwn) {
558 MDefinition* obj = getOperand(objId);
559 MDefinition* id = getOperand(idId);
561 auto* ins = MProxyHasProp::New(alloc(), obj, id, hasOwn);
562 addEffectful(ins);
564 pushResult(ins);
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);
575 addEffectful(ins);
577 return resumeAfter(ins);
580 bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId,
581 ValOperandId idId,
582 ValOperandId rhsId,
583 bool strict) {
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);
589 addEffectful(ins);
591 return resumeAfter(ins);
594 bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId,
595 bool strict,
596 ValOperandId rhsId) {
597 MDefinition* obj = getOperand(objId);
598 MDefinition* rhs = getOperand(rhsId);
600 auto* ins = MCallSetArrayLength::New(alloc(), obj, rhs, strict);
601 addEffectful(ins);
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);
611 MInstruction* ins;
612 if (jitInfo->isAlwaysInSlot) {
613 ins = MGetDOMMember::New(alloc(), jitInfo, obj, nullptr, nullptr);
614 } else {
615 // TODO(post-Warp): realms, guard operands (movable?).
616 ins = MGetDOMProperty::New(alloc(), jitInfo, DOMObjectKind::Native,
617 (JS::Realm*)mirGen().realm->realmPtr(), obj,
618 nullptr, nullptr);
621 if (!ins) {
622 return false;
625 if (ins->isEffectful()) {
626 addEffectful(ins);
627 pushResult(ins);
628 return resumeAfter(ins);
631 add(ins);
632 pushResult(ins);
633 return true;
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);
644 auto* set =
645 MSetDOMProperty::New(alloc(), jitInfo->setter, DOMObjectKind::Native,
646 (JS::Realm*)mirGen().realm->realmPtr(), obj, value);
647 addEffectful(set);
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);
656 add(ins);
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);
671 add(ins);
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);
681 add(ins);
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);
692 add(ins);
694 setOperand(expandoId, ins);
695 return true;
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));
704 add(ins);
706 pushResult(ins);
707 return true;
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);
716 add(ins);
718 pushResult(ins);
719 return true;
722 bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId,
723 uint32_t idOffset,
724 ValOperandId rhsId,
725 bool strict) {
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);
731 addEffectful(ins);
733 return resumeAfter(ins);
736 bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId,
737 ValOperandId idId,
738 bool hasOwn) {
739 MDefinition* obj = getOperand(objId);
740 MDefinition* id = getOperand(idId);
742 auto* ins = MMegamorphicHasProp::New(alloc(), obj, id, hasOwn);
743 add(ins);
745 pushResult(ins);
746 return true;
749 bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId,
750 ValOperandId idId,
751 ValOperandId rhsId,
752 bool strict) {
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);
758 addEffectful(ins);
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);
770 addEffectful(ins);
771 pushResult(ins);
772 if (!resumeAfter(ins)) {
773 return false;
776 return true;
779 bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId) {
780 MDefinition* val = getOperand(valId);
782 auto* ins = MValueToIterator::New(alloc(), val);
783 addEffectful(ins);
785 pushResult(ins);
786 return resumeAfter(ins);
789 bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
790 ObjOperandId objId) {
791 MDefinition* obj = getOperand(objId);
793 auto* ins = MGuardIsNotArrayBufferMaybeShared::New(alloc(), obj);
794 add(ins);
796 setOperand(objId, ins);
797 return true;
800 bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
801 MDefinition* obj = getOperand(objId);
803 auto* ins = MGuardIsTypedArray::New(alloc(), obj);
804 add(ins);
806 setOperand(objId, ins);
807 return true;
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);
816 add(ins);
818 setOperand(objId, ins);
819 return true;
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);
828 add(ins);
830 setOperand(objId, ins);
831 return true;
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);
841 add(slots);
843 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
844 add(load);
846 auto* unbox = MUnbox::New(alloc(), load, MIRType::Object, MUnbox::Fallible);
847 add(unbox);
849 auto* guard = MGuardObjectIdentity::New(alloc(), unbox, expected,
850 /* bailOnEquality = */ false);
851 add(guard);
852 return true;
855 bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId,
856 ObjOperandId objId,
857 uint32_t slotOffset) {
858 size_t slotIndex = int32StubField(slotOffset);
859 MDefinition* obj = getOperand(objId);
861 auto* slots = MSlots::New(alloc(), obj);
862 add(slots);
864 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
865 add(load);
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);
876 add(slots);
878 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
879 add(load);
881 auto* guard = MGuardIsNotObject::New(alloc(), load);
882 add(guard);
883 return true;
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);
897 add(load);
899 auto* guard = MGuardValue::New(alloc(), load, val);
900 add(guard);
901 return true;
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);
915 add(slots);
917 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
918 add(load);
920 auto* guard = MGuardValue::New(alloc(), load, val);
921 add(guard);
922 return true;
925 bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ValOperandId resultId,
926 ObjOperandId objId) {
927 MDefinition* obj = getOperand(objId);
929 auto* load = MLoadScriptedProxyHandler::New(alloc(), obj);
930 add(load);
932 return defineOperand(resultId, load);
935 bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId,
936 ValOperandId idId) {
937 MDefinition* id = getOperand(idId);
939 auto* ins = MIdToStringOrSymbol::New(alloc(), id);
940 add(ins);
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());
951 add(ins);
953 setOperand(strId, ins);
954 return true;
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);
963 add(ins);
965 setOperand(symId, ins);
966 return true;
969 bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId,
970 int32_t expected) {
971 MDefinition* num = getOperand(numId);
973 auto* ins = MGuardSpecificInt32::New(alloc(), num, expected);
974 add(ins);
976 setOperand(numId, ins);
977 return true;
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);
987 add(ins);
989 setOperand(objId, ins);
990 return true;
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);
1003 add(ins);
1005 setOperand(objId, ins);
1006 return true;
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);
1019 add(ins);
1021 setOperand(funId, ins);
1022 return true;
1025 bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId,
1026 Int32OperandId resultId) {
1027 MDefinition* str = getOperand(strId);
1029 auto* ins = MGuardStringToIndex::New(alloc(), str);
1030 add(ins);
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);
1040 add(ins);
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);
1050 add(ins);
1052 return defineOperand(resultId, ins);
1055 bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId) {
1056 MDefinition* obj = getOperand(objId);
1058 auto* ins = MGuardNoDenseElements::New(alloc(), obj);
1059 add(ins);
1061 setOperand(objId, ins);
1062 return true;
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;
1071 auto* ins =
1072 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1073 add(ins);
1075 setOperand(funId, ins);
1076 return true;
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);
1085 auto* ins =
1086 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1087 add(ins);
1089 setOperand(funId, ins);
1090 return true;
1093 bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
1094 ObjOperandId funId) {
1095 MDefinition* fun = getOperand(funId);
1097 auto* ins = MGuardFunctionIsNonBuiltinCtor::New(alloc(), fun);
1098 add(ins);
1100 setOperand(funId, ins);
1101 return true;
1104 bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId) {
1105 MDefinition* fun = getOperand(funId);
1106 uint16_t expectedFlags = FunctionFlags::CONSTRUCTOR;
1107 uint16_t unexpectedFlags = 0;
1109 auto* ins =
1110 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1111 add(ins);
1113 setOperand(funId, ins);
1114 return true;
1117 bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId) {
1118 MDefinition* fun = getOperand(funId);
1120 auto* ins =
1121 MGuardFunctionKind::New(alloc(), fun, FunctionFlags::ClassConstructor,
1122 /*bailOnEquality=*/true);
1123 add(ins);
1125 setOperand(funId, ins);
1126 return true;
1129 bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId) {
1130 MDefinition* array = getOperand(arrayId);
1132 auto* ins = MGuardArrayIsPacked::New(alloc(), array);
1133 add(ins);
1135 setOperand(arrayId, ins);
1136 return true;
1139 bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId,
1140 uint8_t flags) {
1141 MDefinition* obj = getOperand(objId);
1143 auto* ins = MGuardArgumentsObjectFlags::New(alloc(), obj, flags);
1144 add(ins);
1146 setOperand(objId, ins);
1147 return true;
1150 bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId,
1151 ValueType type) {
1152 switch (type) {
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:
1169 #endif
1170 break;
1173 MOZ_CRASH("unexpected type");
1176 bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId, MIRType type) {
1177 MDefinition* def = getOperand(inputId);
1178 if (def->type() == type) {
1179 return true;
1182 auto* ins = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
1183 add(ins);
1185 setOperand(inputId, ins);
1186 return true;
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)) {
1216 return false;
1219 MDefinition* input = getOperand(inputId);
1220 MOZ_ASSERT(input->type() == MIRType::Boolean);
1222 auto* ins = MBooleanToInt32::New(alloc(), input);
1223 add(ins);
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);
1233 add(ins);
1235 setOperand(inputId, ins);
1236 return true;
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) {
1246 return true;
1249 auto* ins = MGuardNullOrUndefined::New(alloc(), input);
1250 add(ins);
1252 setOperand(inputId, ins);
1253 return true;
1256 bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId) {
1257 MDefinition* input = getOperand(inputId);
1258 if (input->type() == MIRType::Null) {
1259 return true;
1262 auto* ins = MGuardValue::New(alloc(), input, NullValue());
1263 add(ins);
1264 setOperand(inputId, ins);
1265 return true;
1268 bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId) {
1269 MDefinition* input = getOperand(inputId);
1270 if (input->type() == MIRType::Undefined) {
1271 return true;
1274 auto* ins = MGuardValue::New(alloc(), input, UndefinedValue());
1275 add(ins);
1276 setOperand(inputId, ins);
1277 return true;
1280 bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId) {
1281 MDefinition* obj = getOperand(objId);
1283 auto* ins = MGuardIsExtensible::New(alloc(), obj);
1284 add(ins);
1285 setOperand(objId, ins);
1286 return true;
1289 bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
1290 Int32OperandId indexId) {
1291 MDefinition* index = getOperand(indexId);
1293 auto* ins = MGuardInt32IsNonNegative::New(alloc(), index);
1294 add(ins);
1295 setOperand(indexId, ins);
1296 return true;
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);
1305 add(ins);
1306 setOperand(indexId, ins);
1307 return true;
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);
1316 add(ins);
1317 setOperand(indexId, ins);
1318 return true;
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);
1328 addEffectful(ins);
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);
1339 add(ins);
1341 return true;
1344 bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId,
1345 Int32OperandId resultId) {
1346 MDefinition* input = getOperand(inputId);
1347 auto* ins =
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);
1352 add(ins);
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);
1361 add(ins);
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);
1370 add(ins);
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);
1379 add(ins);
1381 return defineOperand(resultId, ins);
1384 bool WarpCacheIRTranspiler::emitToString(OperandId inputId,
1385 StringOperandId resultId) {
1386 MDefinition* input = getOperand(inputId);
1387 auto* ins =
1388 MToString::New(alloc(), input, MToString::SideEffectHandling::Bailout);
1389 add(ins);
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);
1398 add(ins);
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);
1406 add(ins);
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);
1426 add(guardedBase);
1428 // Use lower-case characters by default.
1429 constexpr bool lower = true;
1431 auto* ins = MInt32ToStringWithBase::New(alloc(), input, guardedBase, lower);
1432 add(ins);
1434 pushResult(ins);
1435 return true;
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);
1448 add(ins);
1450 return defineOperand(resultId, ins);
1453 bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
1454 MDefinition* val = getOperand(valId);
1455 MOZ_ASSERT(val->type() == MIRType::Int32);
1456 pushResult(val);
1457 return true;
1460 bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId) {
1461 MDefinition* val = getOperand(valId);
1462 MOZ_ASSERT(val->type() == MIRType::Double);
1463 pushResult(val);
1464 return true;
1467 bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId) {
1468 MDefinition* val = getOperand(valId);
1469 MOZ_ASSERT(val->type() == MIRType::BigInt);
1470 pushResult(val);
1471 return true;
1474 bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId) {
1475 MDefinition* obj = getOperand(objId);
1476 MOZ_ASSERT(obj->type() == MIRType::Object);
1477 pushResult(obj);
1478 return true;
1481 bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId) {
1482 MDefinition* str = getOperand(strId);
1483 MOZ_ASSERT(str->type() == MIRType::String);
1484 pushResult(str);
1485 return true;
1488 bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId) {
1489 MDefinition* sym = getOperand(symId);
1490 MOZ_ASSERT(sym->type() == MIRType::Symbol);
1491 pushResult(sym);
1492 return true;
1495 bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
1496 pushResult(constant(UndefinedValue()));
1497 return true;
1500 bool WarpCacheIRTranspiler::emitLoadBooleanResult(bool val) {
1501 pushResult(constant(BooleanValue(val)));
1502 return true;
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);
1541 return true;
1544 bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId) {
1545 MDefinition* obj = getOperand(objId);
1546 auto* typeOf = MTypeOf::New(alloc(), obj);
1547 add(typeOf);
1549 auto* ins = MTypeOfName::New(alloc(), typeOf);
1550 add(ins);
1551 pushResult(ins);
1552 return true;
1555 bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
1556 ObjOperandId objId, ObjOperandId resultId) {
1557 MDefinition* env = getOperand(objId);
1558 auto* ins = MEnclosingEnvironment::New(alloc(), env);
1559 add(ins);
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,
1572 uint32_t objOffset,
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());
1579 add(ins);
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);
1589 add(ins);
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);
1611 add(ins);
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);
1624 add(slots);
1626 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
1627 add(load);
1629 pushResult(load);
1630 return true;
1633 bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId,
1634 ObjOperandId objId,
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);
1642 add(load);
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);
1655 add(load);
1657 pushResult(load);
1658 return true;
1661 bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId,
1662 uint32_t offsetOffset,
1663 ValueType type) {
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)));
1671 add(load);
1673 pushResult(load);
1674 return true;
1677 bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
1678 ValOperandId valId) {
1679 MDefinition* val = getOperand(valId);
1681 auto* lexicalCheck = MLexicalCheck::New(alloc(), val);
1682 add(lexicalCheck);
1684 if (snapshot().bailoutInfo().failedLexicalCheck()) {
1685 lexicalCheck->setNotMovable();
1688 setOperand(valId, lexicalCheck);
1689 return true;
1692 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId) {
1693 MDefinition* obj = getOperand(objId);
1695 auto* elements = MElements::New(alloc(), obj);
1696 add(elements);
1698 auto* length = MArrayLength::New(alloc(), elements);
1699 add(length);
1701 pushResult(length);
1702 return true;
1705 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId,
1706 Int32OperandId resultId) {
1707 MDefinition* obj = getOperand(objId);
1709 auto* elements = MElements::New(alloc(), obj);
1710 add(elements);
1712 auto* length = MArrayLength::New(alloc(), elements);
1713 add(length);
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);
1724 add(load);
1726 pushResult(load);
1727 return true;
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);
1736 add(load);
1738 pushResult(load);
1739 return true;
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);
1748 add(ins);
1750 pushResult(ins);
1751 return true;
1754 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
1755 ObjOperandId objId) {
1756 MDefinition* obj = getOperand(objId);
1758 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1759 add(length);
1761 pushResult(length);
1762 return true;
1765 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
1766 ObjOperandId objId, Int32OperandId resultId) {
1767 MDefinition* obj = getOperand(objId);
1769 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1770 add(length);
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);
1780 add(numArgs);
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,
1791 MIRType::Object);
1792 add(target);
1794 return defineOperand(resultId, target);
1797 bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
1798 ObjOperandId objId) {
1799 MDefinition* obj = getOperand(objId);
1801 auto* guard = MGuardBoundFunctionIsConstructor::New(alloc(), obj);
1802 add(guard);
1804 setOperand(objId, guard);
1805 return true;
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);
1815 add(guard);
1816 return true;
1819 bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
1820 ObjOperandId objId, uint32_t shapeOffset) {
1821 MDefinition* obj = getOperand(objId);
1822 Shape* shape = shapeStubField(shapeOffset);
1823 MOZ_ASSERT(shape);
1825 auto* array = MArrayFromArgumentsObject::New(alloc(), obj, shape);
1826 addEffectful(array);
1828 pushResult(array);
1829 return resumeAfter(array);
1832 bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId) {
1833 MDefinition* obj = getOperand(objId);
1835 auto* length = MFunctionLength::New(alloc(), obj);
1836 add(length);
1838 pushResult(length);
1839 return true;
1842 bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId) {
1843 MDefinition* obj = getOperand(objId);
1845 auto* name = MFunctionName::New(alloc(), obj);
1846 add(name);
1848 pushResult(name);
1849 return true;
1852 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
1853 ObjOperandId objId) {
1854 MDefinition* obj = getOperand(objId);
1856 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1857 add(length);
1859 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1860 add(lengthInt32);
1862 pushResult(lengthInt32);
1863 return true;
1866 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
1867 ObjOperandId objId) {
1868 MDefinition* obj = getOperand(objId);
1870 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1871 add(length);
1873 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1874 add(lengthDouble);
1876 pushResult(lengthDouble);
1877 return true;
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);
1889 add(length);
1891 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1892 add(lengthInt32);
1894 pushResult(lengthInt32);
1895 return true;
1898 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
1899 ObjOperandId objId) {
1900 MDefinition* obj = getOperand(objId);
1902 auto* length = MArrayBufferViewLength::New(alloc(), obj);
1903 add(length);
1905 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1906 add(lengthDouble);
1908 pushResult(lengthDouble);
1909 return true;
1912 bool WarpCacheIRTranspiler::emitLoadStringLengthResult(StringOperandId strId) {
1913 MDefinition* str = getOperand(strId);
1915 auto* length = MStringLength::New(alloc(), str);
1916 add(length);
1918 pushResult(length);
1919 return true;
1922 MInstruction* WarpCacheIRTranspiler::addBoundsCheck(MDefinition* index,
1923 MDefinition* length) {
1924 MInstruction* check = MBoundsCheck::New(alloc(), index, length);
1925 add(check);
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++)
1937 // res = arr[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);
1946 add(check);
1949 return check;
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);
1958 add(elements);
1960 auto* length = MInitializedLength::New(alloc(), elements);
1961 add(length);
1963 index = addBoundsCheck(index, length);
1965 auto* load = MLoadElement::New(alloc(), elements, index);
1966 add(load);
1968 pushResult(load);
1969 return true;
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);
1978 add(elements);
1980 auto* length = MInitializedLength::New(alloc(), elements);
1981 add(length);
1983 auto* load = MLoadElementHole::New(alloc(), elements, index, length);
1984 add(load);
1986 pushResult(load);
1987 return true;
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);
1996 addEffectful(call);
1998 pushResult(call);
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);
2008 addEffectful(call);
2010 pushResult(call);
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);
2021 addEffectful(call);
2023 pushResult(call);
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);
2034 add(elements);
2036 auto* length = MInitializedLength::New(alloc(), elements);
2037 add(length);
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);
2044 add(guard);
2046 pushResult(constant(BooleanValue(true)));
2047 return 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);
2057 add(elements);
2059 auto* length = MInitializedLength::New(alloc(), elements);
2060 add(length);
2062 // Check if id < initLength and elem[id] not a hole.
2063 auto* ins = MInArray::New(alloc(), elements, index, length);
2064 add(ins);
2066 pushResult(ins);
2067 return true;
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);
2076 add(ins);
2078 pushResult(ins);
2079 return true;
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);
2088 add(length);
2090 // Unsigned comparison to catch negative indices.
2091 auto* ins = MCompare::New(alloc(), index, length, JSOp::Lt,
2092 MCompare::Compare_UIntPtr);
2093 add(ins);
2095 pushResult(ins);
2096 return true;
2099 static MIRType MIRTypeForArrayBufferViewRead(Scalar::Type arrayType,
2100 bool forceDoubleForUint32) {
2101 switch (arrayType) {
2102 case Scalar::Int8:
2103 case Scalar::Uint8:
2104 case Scalar::Uint8Clamped:
2105 case Scalar::Int16:
2106 case Scalar::Uint16:
2107 case Scalar::Int32:
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;
2118 default:
2119 break;
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);
2130 if (handleOOB) {
2131 auto* load = MLoadTypedArrayElementHole::New(
2132 alloc(), obj, index, elementType, forceDoubleForUint32);
2133 add(load);
2135 pushResult(load);
2136 return true;
2139 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2140 add(length);
2142 index = addBoundsCheck(index, length);
2144 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2145 add(elements);
2147 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType);
2148 load->setResultType(
2149 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32));
2150 add(load);
2152 pushResult(load);
2153 return true;
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);
2162 add(ins);
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);
2173 add(ins);
2175 return defineOperand(resultId, ins);
2178 bool WarpCacheIRTranspiler::emitLoadStringCharResult(StringOperandId strId,
2179 Int32OperandId indexId,
2180 bool handleOOB) {
2181 MDefinition* str = getOperand(strId);
2182 MDefinition* index = getOperand(indexId);
2184 if (handleOOB) {
2185 auto* charCode = MCharCodeAtOrNegative::New(alloc(), str, index);
2186 add(charCode);
2188 auto* fromCharCode = MFromCharCodeEmptyIfNegative::New(alloc(), charCode);
2189 add(fromCharCode);
2191 pushResult(fromCharCode);
2192 return true;
2195 auto* length = MStringLength::New(alloc(), str);
2196 add(length);
2198 index = addBoundsCheck(index, length);
2200 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2201 add(charCode);
2203 auto* fromCharCode = MFromCharCode::New(alloc(), charCode);
2204 add(fromCharCode);
2206 pushResult(fromCharCode);
2207 return true;
2210 bool WarpCacheIRTranspiler::emitLoadStringCharCodeResult(StringOperandId strId,
2211 Int32OperandId indexId,
2212 bool handleOOB) {
2213 MDefinition* str = getOperand(strId);
2214 MDefinition* index = getOperand(indexId);
2216 if (handleOOB) {
2217 auto* charCode = MCharCodeAtOrNegative::New(alloc(), str, index);
2218 add(charCode);
2220 auto* ins = MNegativeToNaN::New(alloc(), charCode);
2221 add(ins);
2223 pushResult(ins);
2224 return true;
2227 auto* length = MStringLength::New(alloc(), str);
2228 add(length);
2230 index = addBoundsCheck(index, length);
2232 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2233 add(charCode);
2235 pushResult(charCode);
2236 return true;
2239 bool WarpCacheIRTranspiler::emitLoadStringCodePointResult(
2240 StringOperandId strId, Int32OperandId indexId, bool handleOOB) {
2241 MDefinition* str = getOperand(strId);
2242 MDefinition* index = getOperand(indexId);
2244 if (handleOOB) {
2245 auto* codePoint = MCodePointAtOrNegative::New(alloc(), str, index);
2246 add(codePoint);
2248 auto* ins = MNegativeToUndefined::New(alloc(), codePoint);
2249 add(ins);
2251 pushResult(ins);
2252 return true;
2255 auto* length = MStringLength::New(alloc(), str);
2256 add(length);
2258 index = addBoundsCheck(index, length);
2260 auto* codePoint = MCodePointAt::New(alloc(), str, index);
2261 add(codePoint);
2263 pushResult(codePoint);
2264 return true;
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);
2273 addEffectful(obj);
2275 pushResult(obj);
2276 return resumeAfter(obj);
2279 bool WarpCacheIRTranspiler::emitStringFromCharCodeResult(
2280 Int32OperandId codeId) {
2281 MDefinition* code = getOperand(codeId);
2283 auto* fromCharCode = MFromCharCode::New(alloc(), code);
2284 add(fromCharCode);
2286 pushResult(fromCharCode);
2287 return true;
2290 bool WarpCacheIRTranspiler::emitStringFromCodePointResult(
2291 Int32OperandId codeId) {
2292 MDefinition* code = getOperand(codeId);
2294 auto* fromCodePoint = MFromCodePoint::New(alloc(), code);
2295 add(fromCodePoint);
2297 pushResult(fromCodePoint);
2298 return true;
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);
2307 add(includes);
2309 pushResult(includes);
2310 return true;
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);
2319 add(indexOf);
2321 pushResult(indexOf);
2322 return true;
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);
2331 add(lastIndexOf);
2333 pushResult(lastIndexOf);
2334 return true;
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);
2343 add(startsWith);
2345 pushResult(startsWith);
2346 return true;
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);
2355 add(endsWith);
2357 pushResult(endsWith);
2358 return true;
2361 bool WarpCacheIRTranspiler::emitStringToLowerCaseResult(StringOperandId strId) {
2362 MDefinition* str = getOperand(strId);
2364 auto* convert =
2365 MStringConvertCase::New(alloc(), str, MStringConvertCase::LowerCase);
2366 add(convert);
2368 pushResult(convert);
2369 return true;
2372 bool WarpCacheIRTranspiler::emitStringToUpperCaseResult(StringOperandId strId) {
2373 MDefinition* str = getOperand(strId);
2375 auto* convert =
2376 MStringConvertCase::New(alloc(), str, MStringConvertCase::UpperCase);
2377 add(convert);
2379 pushResult(convert);
2380 return true;
2383 bool WarpCacheIRTranspiler::emitStringTrimResult(StringOperandId strId) {
2384 MDefinition* str = getOperand(strId);
2386 auto* linear = MLinearizeString::New(alloc(), str);
2387 add(linear);
2389 auto* start = MStringTrimStartIndex::New(alloc(), linear);
2390 add(start);
2392 auto* end = MStringTrimEndIndex::New(alloc(), linear, start);
2393 add(end);
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);
2398 add(length);
2400 auto* substr = MSubstr::New(alloc(), linear, start, length);
2401 add(substr);
2403 pushResult(substr);
2404 return true;
2407 bool WarpCacheIRTranspiler::emitStringTrimStartResult(StringOperandId strId) {
2408 MDefinition* str = getOperand(strId);
2410 auto* linear = MLinearizeString::New(alloc(), str);
2411 add(linear);
2413 auto* start = MStringTrimStartIndex::New(alloc(), linear);
2414 add(start);
2416 auto* end = MStringLength::New(alloc(), linear);
2417 add(end);
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);
2422 add(length);
2424 auto* substr = MSubstr::New(alloc(), linear, start, length);
2425 add(substr);
2427 pushResult(substr);
2428 return true;
2431 bool WarpCacheIRTranspiler::emitStringTrimEndResult(StringOperandId strId) {
2432 MDefinition* str = getOperand(strId);
2434 auto* linear = MLinearizeString::New(alloc(), str);
2435 add(linear);
2437 auto* start = constant(Int32Value(0));
2439 auto* length = MStringTrimEndIndex::New(alloc(), linear, start);
2440 add(length);
2442 auto* substr = MSubstr::New(alloc(), linear, start, length);
2443 add(substr);
2445 pushResult(substr);
2446 return true;
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);
2459 add(barrier);
2461 auto* slots = MSlots::New(alloc(), obj);
2462 add(slots);
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);
2479 add(barrier);
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);
2495 add(barrier);
2497 auto* store = MStoreFixedSlot::NewBarriered(alloc(), obj, slotIndex, rhs);
2498 addEffectful(store);
2500 auto* undef = constant(UndefinedValue());
2501 pushResult(undef);
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);
2516 add(barrier);
2518 auto* addAndStore =
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,
2527 ValOperandId rhsId,
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);
2551 add(barrier);
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);
2580 add(elements);
2582 auto* length = MInitializedLength::New(alloc(), elements);
2583 add(length);
2585 index = addBoundsCheck(index, length);
2587 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2588 add(barrier);
2590 bool needsHoleCheck = true;
2591 auto* store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2592 needsHoleCheck);
2593 addEffectful(store);
2594 return resumeAfter(store);
2597 bool WarpCacheIRTranspiler::emitStoreDenseElementHole(ObjOperandId objId,
2598 Int32OperandId indexId,
2599 ValOperandId rhsId,
2600 bool handleAdd) {
2601 MDefinition* obj = getOperand(objId);
2602 MDefinition* index = getOperand(indexId);
2603 MDefinition* rhs = getOperand(rhsId);
2605 auto* elements = MElements::New(alloc(), obj);
2606 add(elements);
2608 MInstruction* store;
2609 if (handleAdd) {
2610 // TODO(post-Warp): Consider changing MStoreElementHole to match IC code.
2611 store = MStoreElementHole::New(alloc(), obj, elements, index, rhs);
2612 } else {
2613 auto* length = MInitializedLength::New(alloc(), elements);
2614 add(length);
2616 index = addBoundsCheck(index, length);
2618 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2619 add(barrier);
2621 bool needsHoleCheck = false;
2622 store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2623 needsHoleCheck);
2625 addEffectful(store);
2627 return resumeAfter(store);
2630 bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(ObjOperandId objId,
2631 Scalar::Type elementType,
2632 IntPtrOperandId indexId,
2633 uint32_t rhsId,
2634 bool handleOOB) {
2635 MDefinition* obj = getOperand(objId);
2636 MDefinition* index = getOperand(indexId);
2637 MDefinition* rhs = getOperand(ValOperandId(rhsId));
2639 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2640 add(length);
2642 if (!handleOOB) {
2643 // MStoreTypedArrayElementHole does the bounds checking.
2644 index = addBoundsCheck(index, length);
2647 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2648 add(elements);
2650 MInstruction* store;
2651 if (handleOOB) {
2652 store = MStoreTypedArrayElementHole::New(alloc(), elements, length, index,
2653 rhs, elementType);
2654 } else {
2655 store =
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);
2666 add(length);
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
2672 // negative).
2673 length = MAdjustDataViewLength::New(alloc(), length, byteSize);
2674 add(length);
2677 *offset = addBoundsCheck(*offset, length);
2679 *elements = MArrayBufferViewElements::New(alloc(), obj);
2680 add(*elements);
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.
2696 MInstruction* load;
2697 if (Scalar::byteSize(elementType) == 1) {
2698 load = MLoadUnboxedScalar::New(alloc(), elements, offset, elementType);
2699 } else {
2700 load = MLoadDataViewElement::New(alloc(), elements, offset, littleEndian,
2701 elementType);
2703 add(load);
2705 MIRType knownType =
2706 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
2707 load->setResultType(knownType);
2709 pushResult(load);
2710 return true;
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) {
2728 store =
2729 MStoreUnboxedScalar::New(alloc(), elements, offset, value, elementType);
2730 } else {
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));
2745 add(constOne);
2747 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Int32);
2748 add(ins);
2750 pushResult(ins);
2751 return true;
2754 bool WarpCacheIRTranspiler::emitDoubleIncResult(NumberOperandId inputId) {
2755 MDefinition* input = getOperand(inputId);
2757 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2758 add(constOne);
2760 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Double);
2761 add(ins);
2763 pushResult(ins);
2764 return true;
2767 bool WarpCacheIRTranspiler::emitInt32DecResult(Int32OperandId inputId) {
2768 MDefinition* input = getOperand(inputId);
2770 auto* constOne = MConstant::New(alloc(), Int32Value(1));
2771 add(constOne);
2773 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Int32);
2774 add(ins);
2776 pushResult(ins);
2777 return true;
2780 bool WarpCacheIRTranspiler::emitDoubleDecResult(NumberOperandId inputId) {
2781 MDefinition* input = getOperand(inputId);
2783 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2784 add(constOne);
2786 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Double);
2787 add(ins);
2789 pushResult(ins);
2790 return true;
2793 bool WarpCacheIRTranspiler::emitInt32NegationResult(Int32OperandId inputId) {
2794 MDefinition* input = getOperand(inputId);
2796 auto* constNegOne = MConstant::New(alloc(), Int32Value(-1));
2797 add(constNegOne);
2799 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Int32);
2800 add(ins);
2802 pushResult(ins);
2803 return true;
2806 bool WarpCacheIRTranspiler::emitDoubleNegationResult(NumberOperandId inputId) {
2807 MDefinition* input = getOperand(inputId);
2809 auto* constNegOne = MConstant::New(alloc(), DoubleValue(-1.0));
2810 add(constNegOne);
2812 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Double);
2813 add(ins);
2815 pushResult(ins);
2816 return true;
2819 bool WarpCacheIRTranspiler::emitInt32NotResult(Int32OperandId inputId) {
2820 MDefinition* input = getOperand(inputId);
2822 auto* ins = MBitNot::New(alloc(), input);
2823 add(ins);
2825 pushResult(ins);
2826 return true;
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);
2836 add(ins);
2838 pushResult(ins);
2839 return true;
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);
2879 add(ins);
2881 pushResult(ins);
2882 return true;
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,
2942 bool forceDouble) {
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);
2948 add(ins);
2950 pushResult(ins);
2951 return true;
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);
2961 add(ins);
2963 pushResult(ins);
2964 return true;
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()) {
2991 addEffectful(ins);
2993 pushResult(ins);
2994 return resumeAfter(ins);
2997 add(ins);
2999 pushResult(ins);
3000 return true;
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);
3049 add(ins);
3051 pushResult(ins);
3052 return true;
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);
3077 add(ins);
3079 pushResult(ins);
3080 return true;
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);
3090 add(ins);
3092 pushResult(ins);
3093 return true;
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.
3157 MDefinition* cst =
3158 isUndefined ? constant(UndefinedValue()) : constant(NullValue());
3159 auto compareType =
3160 isUndefined ? MCompare::Compare_Undefined : MCompare::Compare_Null;
3161 auto* ins = MCompare::New(alloc(), input, cst, op, compareType);
3162 add(ins);
3164 pushResult(ins);
3165 return true;
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);
3174 add(sameValue);
3176 pushResult(sameValue);
3177 return true;
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);
3186 add(sameValue);
3188 pushResult(sameValue);
3189 return true;
3192 bool WarpCacheIRTranspiler::emitIndirectTruncateInt32Result(
3193 Int32OperandId valId) {
3194 MDefinition* val = getOperand(valId);
3195 MOZ_ASSERT(val->type() == MIRType::Int32);
3197 auto* truncate =
3198 MLimitedTruncate::New(alloc(), val, TruncateKind::IndirectTruncate);
3199 add(truncate);
3201 pushResult(truncate);
3202 return true;
3205 bool WarpCacheIRTranspiler::emitMathHypot2NumberResult(
3206 NumberOperandId firstId, NumberOperandId secondId) {
3207 MDefinitionVector vector(alloc());
3208 if (!vector.reserve(2)) {
3209 return false;
3212 vector.infallibleAppend(getOperand(firstId));
3213 vector.infallibleAppend(getOperand(secondId));
3215 auto* ins = MHypot::New(alloc(), vector);
3216 if (!ins) {
3217 return false;
3219 add(ins);
3221 pushResult(ins);
3222 return true;
3225 bool WarpCacheIRTranspiler::emitMathHypot3NumberResult(
3226 NumberOperandId firstId, NumberOperandId secondId,
3227 NumberOperandId thirdId) {
3228 MDefinitionVector vector(alloc());
3229 if (!vector.reserve(3)) {
3230 return false;
3233 vector.infallibleAppend(getOperand(firstId));
3234 vector.infallibleAppend(getOperand(secondId));
3235 vector.infallibleAppend(getOperand(thirdId));
3237 auto* ins = MHypot::New(alloc(), vector);
3238 if (!ins) {
3239 return false;
3241 add(ins);
3243 pushResult(ins);
3244 return true;
3247 bool WarpCacheIRTranspiler::emitMathHypot4NumberResult(
3248 NumberOperandId firstId, NumberOperandId secondId, NumberOperandId thirdId,
3249 NumberOperandId fourthId) {
3250 MDefinitionVector vector(alloc());
3251 if (!vector.reserve(4)) {
3252 return false;
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);
3261 if (!ins) {
3262 return false;
3264 add(ins);
3266 pushResult(ins);
3267 return true;
3270 bool WarpCacheIRTranspiler::emitMathRandomResult(uint32_t rngOffset) {
3271 #ifdef DEBUG
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());
3276 #endif
3278 auto* ins = MRandom::New(alloc());
3279 addEffectful(ins);
3281 pushResult(ins);
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);
3292 add(ins);
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);
3305 add(ins);
3307 return defineOperand(resultId, ins);
3310 bool WarpCacheIRTranspiler::emitInt32MinMaxArrayResult(ObjOperandId arrayId,
3311 bool isMax) {
3312 MDefinition* array = getOperand(arrayId);
3314 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Int32, isMax);
3315 add(ins);
3317 pushResult(ins);
3318 return true;
3321 bool WarpCacheIRTranspiler::emitNumberMinMaxArrayResult(ObjOperandId arrayId,
3322 bool isMax) {
3323 MDefinition* array = getOperand(arrayId);
3325 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Double, isMax);
3326 add(ins);
3328 pushResult(ins);
3329 return true;
3332 bool WarpCacheIRTranspiler::emitMathAbsInt32Result(Int32OperandId inputId) {
3333 MDefinition* input = getOperand(inputId);
3335 auto* ins = MAbs::New(alloc(), input, MIRType::Int32);
3336 add(ins);
3338 pushResult(ins);
3339 return true;
3342 bool WarpCacheIRTranspiler::emitMathAbsNumberResult(NumberOperandId inputId) {
3343 MDefinition* input = getOperand(inputId);
3345 auto* ins = MAbs::New(alloc(), input, MIRType::Double);
3346 add(ins);
3348 pushResult(ins);
3349 return true;
3352 bool WarpCacheIRTranspiler::emitMathClz32Result(Int32OperandId inputId) {
3353 MDefinition* input = getOperand(inputId);
3355 auto* ins = MClz::New(alloc(), input, MIRType::Int32);
3356 add(ins);
3358 pushResult(ins);
3359 return true;
3362 bool WarpCacheIRTranspiler::emitMathSignInt32Result(Int32OperandId inputId) {
3363 MDefinition* input = getOperand(inputId);
3365 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3366 add(ins);
3368 pushResult(ins);
3369 return true;
3372 bool WarpCacheIRTranspiler::emitMathSignNumberResult(NumberOperandId inputId) {
3373 MDefinition* input = getOperand(inputId);
3375 auto* ins = MSign::New(alloc(), input, MIRType::Double);
3376 add(ins);
3378 pushResult(ins);
3379 return true;
3382 bool WarpCacheIRTranspiler::emitMathSignNumberToInt32Result(
3383 NumberOperandId inputId) {
3384 MDefinition* input = getOperand(inputId);
3386 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3387 add(ins);
3389 pushResult(ins);
3390 return true;
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);
3399 add(ins);
3401 pushResult(ins);
3402 return true;
3405 bool WarpCacheIRTranspiler::emitMathFloorToInt32Result(
3406 NumberOperandId inputId) {
3407 MDefinition* input = getOperand(inputId);
3409 auto* ins = MFloor::New(alloc(), input);
3410 add(ins);
3412 pushResult(ins);
3413 return true;
3416 bool WarpCacheIRTranspiler::emitMathCeilToInt32Result(NumberOperandId inputId) {
3417 MDefinition* input = getOperand(inputId);
3419 auto* ins = MCeil::New(alloc(), input);
3420 add(ins);
3422 pushResult(ins);
3423 return true;
3426 bool WarpCacheIRTranspiler::emitMathTruncToInt32Result(
3427 NumberOperandId inputId) {
3428 MDefinition* input = getOperand(inputId);
3430 auto* ins = MTrunc::New(alloc(), input);
3431 add(ins);
3433 pushResult(ins);
3434 return true;
3437 bool WarpCacheIRTranspiler::emitMathRoundToInt32Result(
3438 NumberOperandId inputId) {
3439 MDefinition* input = getOperand(inputId);
3441 auto* ins = MRound::New(alloc(), input);
3442 add(ins);
3444 pushResult(ins);
3445 return true;
3448 bool WarpCacheIRTranspiler::emitMathSqrtNumberResult(NumberOperandId inputId) {
3449 MDefinition* input = getOperand(inputId);
3451 auto* ins = MSqrt::New(alloc(), input, MIRType::Double);
3452 add(ins);
3454 pushResult(ins);
3455 return true;
3458 bool WarpCacheIRTranspiler::emitMathFRoundNumberResult(
3459 NumberOperandId inputId) {
3460 MDefinition* input = getOperand(inputId);
3462 auto* ins = MToFloat32::New(alloc(), input);
3463 add(ins);
3465 pushResult(ins);
3466 return true;
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);
3475 add(ins);
3477 pushResult(ins);
3478 return true;
3481 bool WarpCacheIRTranspiler::emitMathFunctionNumberResult(
3482 NumberOperandId inputId, UnaryMathFunction fun) {
3483 MDefinition* input = getOperand(inputId);
3485 auto* ins = MMathFunction::New(alloc(), input, fun);
3486 add(ins);
3488 pushResult(ins);
3489 return true;
3492 bool WarpCacheIRTranspiler::emitMathFloorNumberResult(NumberOperandId inputId) {
3493 MDefinition* input = getOperand(inputId);
3495 MInstruction* ins;
3496 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
3497 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Down);
3498 } else {
3499 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Floor);
3501 add(ins);
3503 pushResult(ins);
3504 return true;
3507 bool WarpCacheIRTranspiler::emitMathCeilNumberResult(NumberOperandId inputId) {
3508 MDefinition* input = getOperand(inputId);
3510 MInstruction* ins;
3511 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
3512 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Up);
3513 } else {
3514 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Ceil);
3516 add(ins);
3518 pushResult(ins);
3519 return true;
3522 bool WarpCacheIRTranspiler::emitMathTruncNumberResult(NumberOperandId inputId) {
3523 MDefinition* input = getOperand(inputId);
3525 MInstruction* ins;
3526 if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
3527 ins = MNearbyInt::New(alloc(), input, MIRType::Double,
3528 RoundingMode::TowardsZero);
3529 } else {
3530 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Trunc);
3532 add(ins);
3534 pushResult(ins);
3535 return true;
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);
3544 add(ins);
3546 pushResult(ins);
3547 return true;
3550 bool WarpCacheIRTranspiler::emitDoubleParseIntResult(NumberOperandId numId) {
3551 MDefinition* num = getOperand(numId);
3553 auto* ins = MDoubleParseInt::New(alloc(), num);
3554 add(ins);
3556 pushResult(ins);
3557 return true;
3560 bool WarpCacheIRTranspiler::emitObjectToStringResult(ObjOperandId objId) {
3561 MDefinition* obj = getOperand(objId);
3563 auto* ins = MObjectClassToString::New(alloc(), obj);
3564 add(ins);
3566 pushResult(ins);
3567 return true;
3570 bool WarpCacheIRTranspiler::emitReflectGetPrototypeOfResult(
3571 ObjOperandId objId) {
3572 MDefinition* obj = getOperand(objId);
3574 auto* ins = MGetPrototypeOf::New(alloc(), obj);
3575 addEffectful(ins);
3576 pushResult(ins);
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);
3587 addEffectful(ins);
3588 pushResult(ins);
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);
3599 addEffectful(join);
3601 pushResult(join);
3602 return resumeAfter(join);
3605 bool WarpCacheIRTranspiler::emitObjectKeysResult(ObjOperandId objId) {
3606 MDefinition* obj = getOperand(objId);
3608 auto* join = MObjectKeys::New(alloc(), obj);
3609 addEffectful(join);
3611 pushResult(join);
3612 return resumeAfter(join);
3615 bool WarpCacheIRTranspiler::emitPackedArrayPopResult(ObjOperandId arrayId) {
3616 MDefinition* array = getOperand(arrayId);
3618 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Pop);
3619 addEffectful(ins);
3621 pushResult(ins);
3622 return resumeAfter(ins);
3625 bool WarpCacheIRTranspiler::emitPackedArrayShiftResult(ObjOperandId arrayId) {
3626 MDefinition* array = getOperand(arrayId);
3628 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Shift);
3629 addEffectful(ins);
3631 pushResult(ins);
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);
3648 addEffectful(ins);
3650 pushResult(ins);
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;
3666 auto* ins =
3667 MArgumentsSlice::New(alloc(), args, begin, end, templateObj, heap);
3668 addEffectful(ins);
3670 pushResult(ins);
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);
3680 add(hasClass);
3682 pushResult(hasClass);
3683 return true;
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);
3717 pushResult(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);
3728 addEffectful(ins);
3729 pushResult(ins);
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);
3740 addEffectful(ins);
3741 pushResult(ins);
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);
3753 pushResult(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);
3767 add(result);
3769 return result;
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);
3778 add(flags);
3780 auto* mask = MConstant::New(alloc(), Int32Value(flagsMask));
3781 add(mask);
3783 auto* maskedFlag = MBitAnd::New(alloc(), flags, mask, MIRType::Int32);
3784 add(maskedFlag);
3786 auto* result = convertToBoolean(maskedFlag);
3788 pushResult(result);
3789 return true;
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);
3799 add(substr);
3801 pushResult(substr);
3802 return true;
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);
3813 add(replace);
3815 pushResult(replace);
3816 return true;
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);
3825 add(split);
3827 pushResult(split);
3828 return true;
3831 bool WarpCacheIRTranspiler::emitRegExpPrototypeOptimizableResult(
3832 ObjOperandId protoId) {
3833 MDefinition* proto = getOperand(protoId);
3835 auto* optimizable = MRegExpPrototypeOptimizable::New(alloc(), proto);
3836 add(optimizable);
3838 pushResult(optimizable);
3839 return true;
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);
3848 add(optimizable);
3850 pushResult(optimizable);
3851 return true;
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);
3862 return true;
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)));
3880 } else {
3881 auto* isObject = MIsObject::New(alloc(), value);
3882 add(isObject);
3883 pushResult(isObject);
3886 return true;
3889 bool WarpCacheIRTranspiler::emitIsPackedArrayResult(ObjOperandId objId) {
3890 MDefinition* obj = getOperand(objId);
3892 auto* isPackedArray = MIsPackedArray::New(alloc(), obj);
3893 add(isPackedArray);
3895 pushResult(isPackedArray);
3896 return true;
3899 bool WarpCacheIRTranspiler::emitIsCallableResult(ValOperandId inputId) {
3900 MDefinition* value = getOperand(inputId);
3902 auto* isCallable = MIsCallable::New(alloc(), value);
3903 add(isCallable);
3905 pushResult(isCallable);
3906 return true;
3909 bool WarpCacheIRTranspiler::emitIsConstructorResult(ObjOperandId objId) {
3910 MDefinition* obj = getOperand(objId);
3912 auto* isConstructor = MIsConstructor::New(alloc(), obj);
3913 add(isConstructor);
3915 pushResult(isConstructor);
3916 return true;
3919 bool WarpCacheIRTranspiler::emitIsCrossRealmArrayConstructorResult(
3920 ObjOperandId objId) {
3921 MDefinition* obj = getOperand(objId);
3923 auto* ins = MIsCrossRealmArrayConstructor::New(alloc(), obj);
3924 add(ins);
3926 pushResult(ins);
3927 return true;
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) {
3936 addEffectful(ins);
3937 } else {
3938 add(ins);
3941 pushResult(ins);
3943 if (isPossiblyWrapped) {
3944 if (!resumeAfter(ins)) {
3945 return false;
3949 return true;
3952 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetInt32Result(
3953 ObjOperandId objId) {
3954 MDefinition* obj = getOperand(objId);
3956 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
3957 add(byteOffset);
3959 auto* byteOffsetInt32 = MNonNegativeIntPtrToInt32::New(alloc(), byteOffset);
3960 add(byteOffsetInt32);
3962 pushResult(byteOffsetInt32);
3963 return true;
3966 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetDoubleResult(
3967 ObjOperandId objId) {
3968 MDefinition* obj = getOperand(objId);
3970 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
3971 add(byteOffset);
3973 auto* byteOffsetDouble = MIntPtrToDouble::New(alloc(), byteOffset);
3974 add(byteOffsetDouble);
3976 pushResult(byteOffsetDouble);
3977 return true;
3980 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthInt32Result(
3981 ObjOperandId objId) {
3982 MDefinition* obj = getOperand(objId);
3984 auto* length = MArrayBufferViewLength::New(alloc(), obj);
3985 add(length);
3987 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
3988 add(lengthInt32);
3990 auto* size = MTypedArrayElementSize::New(alloc(), obj);
3991 add(size);
3993 auto* mul = MMul::New(alloc(), lengthInt32, size, MIRType::Int32);
3994 mul->setCanBeNegativeZero(false);
3995 add(mul);
3997 pushResult(mul);
3998 return true;
4001 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthDoubleResult(
4002 ObjOperandId objId) {
4003 MDefinition* obj = getOperand(objId);
4005 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4006 add(length);
4008 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4009 add(lengthDouble);
4011 auto* size = MTypedArrayElementSize::New(alloc(), obj);
4012 add(size);
4014 auto* sizeDouble = MToDouble::New(alloc(), size);
4015 add(sizeDouble);
4017 auto* mul = MMul::New(alloc(), lengthDouble, sizeDouble, MIRType::Double);
4018 mul->setCanBeNegativeZero(false);
4019 add(mul);
4021 pushResult(mul);
4022 return true;
4025 bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult(
4026 ObjOperandId objId) {
4027 MDefinition* obj = getOperand(objId);
4029 auto* ins = MTypedArrayElementSize::New(alloc(), obj);
4030 add(ins);
4032 pushResult(ins);
4033 return true;
4036 bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer(
4037 ObjOperandId objId) {
4038 MDefinition* obj = getOperand(objId);
4040 auto* ins = MGuardHasAttachedArrayBuffer::New(alloc(), obj);
4041 add(ins);
4043 setOperand(objId, ins);
4044 return true;
4047 bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult(
4048 ObjOperandId objId) {
4049 MDefinition* obj = getOperand(objId);
4051 auto* ins = MIsTypedArrayConstructor::New(alloc(), obj);
4052 add(ins);
4054 pushResult(ins);
4055 return true;
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);
4066 addEffectful(ins);
4067 pushResult(ins);
4069 return resumeAfter(ins);
4072 bool WarpCacheIRTranspiler::emitFrameIsConstructingResult() {
4073 if (const CallInfo* callInfo = builder_->inlineCallInfo()) {
4074 auto* ins = constant(BooleanValue(callInfo->constructing()));
4075 pushResult(ins);
4076 return true;
4079 auto* ins = MIsConstructing::New(alloc());
4080 add(ins);
4081 pushResult(ins);
4082 return true;
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);
4091 add(iter);
4093 pushResult(iter);
4094 return true;
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;
4123 auto* obj =
4124 MNewObject::New(alloc(), templateConst, heap, MNewObject::ObjectCreate);
4125 addEffectful(obj);
4127 pushResult(obj);
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;
4150 MNewArray* obj;
4151 if (len > inlineLength) {
4152 obj = MNewArray::NewVM(alloc(), len, templateConst, heap);
4153 } else {
4154 obj = MNewArray::New(alloc(), len, templateConst, heap);
4156 add(obj);
4157 pushResult(obj);
4158 return true;
4162 auto* obj = MNewArrayDynamicLength::New(alloc(), length, templateObj, heap);
4163 addEffectful(obj);
4164 pushResult(obj);
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();
4178 if (len > 0 &&
4179 uint32_t(len) == templateObj->as<TypedArrayObject>().length()) {
4180 auto* templateConst = constant(ObjectValue(*templateObj));
4181 auto* obj = MNewTypedArray::New(alloc(), templateConst, heap);
4182 add(obj);
4183 pushResult(obj);
4184 return true;
4188 auto* obj =
4189 MNewTypedArrayDynamicLength::New(alloc(), length, templateObj, heap);
4190 addEffectful(obj);
4191 pushResult(obj);
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);
4208 addEffectful(obj);
4210 pushResult(obj);
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);
4223 addEffectful(obj);
4225 pushResult(obj);
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);
4238 add(length);
4240 index = addBoundsCheck(index, length);
4242 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4243 add(elements);
4245 bool forceDoubleForUint32 = true;
4246 MIRType knownType =
4247 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4249 auto* cas = MCompareExchangeTypedArrayElement::New(
4250 alloc(), elements, index, elementType, expected, replacement);
4251 cas->setResultType(knownType);
4252 addEffectful(cas);
4254 pushResult(cas);
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);
4266 add(length);
4268 index = addBoundsCheck(index, length);
4270 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4271 add(elements);
4273 bool forceDoubleForUint32 = true;
4274 MIRType knownType =
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,
4288 uint32_t valueId,
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);
4296 add(length);
4298 index = addBoundsCheck(index, length);
4300 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4301 add(elements);
4303 bool forceDoubleForUint32 = true;
4304 MIRType knownType =
4305 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4307 auto* binop = MAtomicTypedArrayElementBinop::New(
4308 alloc(), op, elements, index, elementType, value, forEffect);
4309 if (!forEffect) {
4310 binop->setResultType(knownType);
4312 addEffectful(binop);
4314 if (!forEffect) {
4315 pushResult(binop);
4316 } else {
4317 pushResult(constant(UndefinedValue()));
4319 return resumeAfter(binop);
4322 bool WarpCacheIRTranspiler::emitAtomicsAddResult(ObjOperandId objId,
4323 IntPtrOperandId indexId,
4324 uint32_t valueId,
4325 Scalar::Type elementType,
4326 bool forEffect) {
4327 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4328 AtomicFetchAddOp);
4331 bool WarpCacheIRTranspiler::emitAtomicsSubResult(ObjOperandId objId,
4332 IntPtrOperandId indexId,
4333 uint32_t valueId,
4334 Scalar::Type elementType,
4335 bool forEffect) {
4336 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4337 AtomicFetchSubOp);
4340 bool WarpCacheIRTranspiler::emitAtomicsAndResult(ObjOperandId objId,
4341 IntPtrOperandId indexId,
4342 uint32_t valueId,
4343 Scalar::Type elementType,
4344 bool forEffect) {
4345 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4346 AtomicFetchAndOp);
4349 bool WarpCacheIRTranspiler::emitAtomicsOrResult(ObjOperandId objId,
4350 IntPtrOperandId indexId,
4351 uint32_t valueId,
4352 Scalar::Type elementType,
4353 bool forEffect) {
4354 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4355 AtomicFetchOrOp);
4358 bool WarpCacheIRTranspiler::emitAtomicsXorResult(ObjOperandId objId,
4359 IntPtrOperandId indexId,
4360 uint32_t valueId,
4361 Scalar::Type elementType,
4362 bool forEffect) {
4363 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4364 AtomicFetchXorOp);
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);
4374 add(length);
4376 index = addBoundsCheck(index, length);
4378 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4379 add(elements);
4381 bool forceDoubleForUint32 = true;
4382 MIRType knownType =
4383 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4385 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType,
4386 DoesRequireMemoryBarrier);
4387 load->setResultType(knownType);
4388 addEffectful(load);
4390 pushResult(load);
4391 return resumeAfter(load);
4394 bool WarpCacheIRTranspiler::emitAtomicsStoreResult(ObjOperandId objId,
4395 IntPtrOperandId indexId,
4396 uint32_t valueId,
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);
4403 add(length);
4405 index = addBoundsCheck(index, length);
4407 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4408 add(elements);
4410 auto* store = MStoreUnboxedScalar::New(alloc(), elements, index, value,
4411 elementType, DoesRequireMemoryBarrier);
4412 addEffectful(store);
4414 pushResult(value);
4415 return resumeAfter(store);
4418 bool WarpCacheIRTranspiler::emitAtomicsIsLockFreeResult(
4419 Int32OperandId valueId) {
4420 MDefinition* value = getOperand(valueId);
4422 auto* ilf = MAtomicIsLockFree::New(alloc(), value);
4423 add(ilf);
4425 pushResult(ilf);
4426 return true;
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);
4435 add(ins);
4437 pushResult(ins);
4438 return true;
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);
4447 add(ins);
4449 pushResult(ins);
4450 return true;
4453 bool WarpCacheIRTranspiler::emitGuardToNonGCThing(ValOperandId inputId) {
4454 MDefinition* def = getOperand(inputId);
4455 if (IsNonGCThing(def->type())) {
4456 return true;
4459 auto* ins = MGuardNonGCThing::New(alloc(), def);
4460 add(ins);
4462 setOperand(inputId, ins);
4463 return true;
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);
4472 add(hashValue);
4474 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4475 add(hash);
4477 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4478 add(ins);
4480 pushResult(ins);
4481 return true;
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);
4490 add(hashValue);
4492 auto* hash = MHashString::New(alloc(), hashValue);
4493 add(hash);
4495 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4496 add(ins);
4498 pushResult(ins);
4499 return true;
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);
4508 add(hash);
4510 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, sym, hash);
4511 add(ins);
4513 pushResult(ins);
4514 return true;
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);
4523 add(hash);
4525 auto* ins = MSetObjectHasBigInt::New(alloc(), set, bigInt, hash);
4526 add(ins);
4528 pushResult(ins);
4529 return true;
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);
4538 add(hash);
4540 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, obj, hash);
4541 add(ins);
4543 pushResult(ins);
4544 return true;
4547 bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId,
4548 ValOperandId valId) {
4549 MDefinition* set = getOperand(setId);
4550 MDefinition* val = getOperand(valId);
4552 #ifdef JS_PUNBOX64
4553 auto* hashValue = MToHashableValue::New(alloc(), val);
4554 add(hashValue);
4556 auto* hash = MHashValue::New(alloc(), set, hashValue);
4557 add(hash);
4559 auto* ins = MSetObjectHasValue::New(alloc(), set, hashValue, hash);
4560 add(ins);
4561 #else
4562 auto* ins = MSetObjectHasValueVMCall::New(alloc(), set, val);
4563 add(ins);
4564 #endif
4566 pushResult(ins);
4567 return true;
4570 bool WarpCacheIRTranspiler::emitSetSizeResult(ObjOperandId setId) {
4571 MDefinition* set = getOperand(setId);
4573 auto* ins = MSetObjectSize::New(alloc(), set);
4574 add(ins);
4576 pushResult(ins);
4577 return true;
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);
4586 add(hashValue);
4588 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4589 add(hash);
4591 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4592 add(ins);
4594 pushResult(ins);
4595 return true;
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);
4604 add(hashValue);
4606 auto* hash = MHashString::New(alloc(), hashValue);
4607 add(hash);
4609 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4610 add(ins);
4612 pushResult(ins);
4613 return true;
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);
4622 add(hash);
4624 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, sym, hash);
4625 add(ins);
4627 pushResult(ins);
4628 return true;
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);
4637 add(hash);
4639 auto* ins = MMapObjectHasBigInt::New(alloc(), map, bigInt, hash);
4640 add(ins);
4642 pushResult(ins);
4643 return true;
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);
4652 add(hash);
4654 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, obj, hash);
4655 add(ins);
4657 pushResult(ins);
4658 return true;
4661 bool WarpCacheIRTranspiler::emitMapHasResult(ObjOperandId mapId,
4662 ValOperandId valId) {
4663 MDefinition* map = getOperand(mapId);
4664 MDefinition* val = getOperand(valId);
4666 #ifdef JS_PUNBOX64
4667 auto* hashValue = MToHashableValue::New(alloc(), val);
4668 add(hashValue);
4670 auto* hash = MHashValue::New(alloc(), map, hashValue);
4671 add(hash);
4673 auto* ins = MMapObjectHasValue::New(alloc(), map, hashValue, hash);
4674 add(ins);
4675 #else
4676 auto* ins = MMapObjectHasValueVMCall::New(alloc(), map, val);
4677 add(ins);
4678 #endif
4680 pushResult(ins);
4681 return true;
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);
4690 add(hashValue);
4692 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4693 add(hash);
4695 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
4696 add(ins);
4698 pushResult(ins);
4699 return true;
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);
4708 add(hashValue);
4710 auto* hash = MHashString::New(alloc(), hashValue);
4711 add(hash);
4713 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
4714 add(ins);
4716 pushResult(ins);
4717 return true;
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);
4726 add(hash);
4728 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, sym, hash);
4729 add(ins);
4731 pushResult(ins);
4732 return true;
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);
4741 add(hash);
4743 auto* ins = MMapObjectGetBigInt::New(alloc(), map, bigInt, hash);
4744 add(ins);
4746 pushResult(ins);
4747 return true;
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);
4756 add(hash);
4758 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, obj, hash);
4759 add(ins);
4761 pushResult(ins);
4762 return true;
4765 bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId,
4766 ValOperandId valId) {
4767 MDefinition* map = getOperand(mapId);
4768 MDefinition* val = getOperand(valId);
4770 #ifdef JS_PUNBOX64
4771 auto* hashValue = MToHashableValue::New(alloc(), val);
4772 add(hashValue);
4774 auto* hash = MHashValue::New(alloc(), map, hashValue);
4775 add(hash);
4777 auto* ins = MMapObjectGetValue::New(alloc(), map, hashValue, hash);
4778 add(ins);
4779 #else
4780 auto* ins = MMapObjectGetValueVMCall::New(alloc(), map, val);
4781 add(ins);
4782 #endif
4784 pushResult(ins);
4785 return true;
4788 bool WarpCacheIRTranspiler::emitMapSizeResult(ObjOperandId mapId) {
4789 MDefinition* map = getOperand(mapId);
4791 auto* ins = MMapObjectSize::New(alloc(), map);
4792 add(ins);
4794 pushResult(ins);
4795 return true;
4798 bool WarpCacheIRTranspiler::emitTruthyResult(OperandId inputId) {
4799 MDefinition* input = getOperand(inputId);
4801 auto* result = convertToBoolean(input);
4803 pushResult(result);
4804 return true;
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);
4836 pushResult(input);
4837 return true;
4840 bool WarpCacheIRTranspiler::emitLoadWrapperTarget(ObjOperandId objId,
4841 ObjOperandId resultId) {
4842 MDefinition* obj = getOperand(objId);
4844 auto* ins = MLoadWrapperTarget::New(alloc(), obj);
4845 add(ins);
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];
4862 if (id.valid()) {
4863 switch (kind) {
4864 case ArgumentKind::This:
4865 callInfo_->setThis(getOperand(id));
4866 break;
4867 case ArgumentKind::NewTarget:
4868 callInfo_->setNewTarget(getOperand(id));
4869 break;
4870 case ArgumentKind::Arg0:
4871 callInfo_->setArg(0, getOperand(id));
4872 break;
4873 case ArgumentKind::Arg1:
4874 callInfo_->setArg(1, getOperand(id));
4875 break;
4876 case ArgumentKind::Arg2:
4877 callInfo_->setArg(2, getOperand(id));
4878 break;
4879 case ArgumentKind::Arg3:
4880 callInfo_->setArg(3, getOperand(id));
4881 break;
4882 case ArgumentKind::Arg4:
4883 callInfo_->setArg(4, getOperand(id));
4884 break;
4885 case ArgumentKind::Arg5:
4886 callInfo_->setArg(5, getOperand(id));
4887 break;
4888 case ArgumentKind::Arg6:
4889 callInfo_->setArg(6, getOperand(id));
4890 break;
4891 case ArgumentKind::Arg7:
4892 callInfo_->setArg(7, getOperand(id));
4893 break;
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.
4906 // Layout:
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.
4921 // Args..
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));
4930 // ThisValue
4931 if (slotIndex == callInfo_->argc()) {
4932 setArgumentId(ArgumentKind::This, resultId);
4933 return defineOperand(resultId, callInfo_->thisArg());
4936 // Callee
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) {
4949 #ifdef DEBUG
4950 MDefinition* argc = getOperand(argcId);
4951 MOZ_ASSERT(argc->toConstant()->toInt32() ==
4952 static_cast<int32_t>(callInfo_->argc()));
4953 #endif
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()) {
4968 return nullptr;
4971 JSFunction* nativeTarget = nullptr;
4972 if (isNative) {
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,
4985 CallKind kind) {
4986 // CacheIR emits the following for specialized calls:
4987 // GuardSpecificFunction <callee> <func> ..
4988 // Call(Native|Scripted)Function <callee> ..
4989 // or:
4990 // GuardClass <callee> ..
4991 // GuardFunctionScript <callee> <script> ..
4992 // CallScriptedFunction <callee> ..
4994 // We can use the <func> JSFunction or <script> BaseScript to specialize this
4995 // call.
4996 if (callee->isGuardSpecificFunction()) {
4997 auto* guard = callee->toGuardSpecificFunction();
4998 return maybeWrappedFunction(guard->expected(), kind, guard->nargs(),
4999 guard->flags());
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;
5009 return nullptr;
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,
5015 CallFlags flags) {
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);
5028 break;
5029 case CallFlags::Spread:
5030 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Array);
5031 break;
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);
5042 } else {
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);
5049 break;
5050 case CallFlags::FunApplyArgsObj:
5051 MOZ_ASSERT(!callInfo_->constructing());
5052 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5054 callInfo_->setArgFormat(CallInfo::ArgFormat::FunApplyArgsObj);
5055 break;
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);
5062 break;
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);
5074 break;
5075 default:
5076 MOZ_CRASH("Unsupported arg format");
5078 return true;
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
5091 // MIRType::Value.
5092 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
5093 thisArg->isPhi());
5094 return false;
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.
5101 return false;
5103 if (flags.needsUninitializedThis()) {
5104 MConstant* uninit = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
5105 thisArg->setImplicitlyUsedUnchecked();
5106 callInfo_->setThis(uninit);
5107 return false;
5109 // See the Native case above.
5110 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
5111 thisArg->isPhi());
5113 auto* newTarget = unboxObjectInfallible(callInfo_->getNewTarget());
5114 auto* createThis = MCreateThis::New(alloc(), callee, newTarget);
5115 add(createThis);
5117 thisArg->setImplicitlyUsedUnchecked();
5118 callInfo_->setThis(createThis);
5120 return true;
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)) {
5133 return false;
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);
5150 } else {
5151 MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
5152 callInfo_->setInliningResumeMode(ResumeMode::InlinedStandardCall);
5155 switch (callInfo_->argFormat()) {
5156 case CallInfo::ArgFormat::Standard:
5157 break;
5158 default:
5159 MOZ_CRASH("Unsupported arg format");
5161 return true;
5164 #ifdef DEBUG
5165 MDefinition* argc = getOperand(argcId);
5166 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5167 static_cast<int32_t>(callInfo_->argc()));
5168 #endif
5170 if (!updateCallInfo(callee, flags)) {
5171 return false;
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);
5196 if (!call) {
5197 return false;
5200 if (flags.isSameRealm()) {
5201 call->setNotCrossRealm();
5204 if (call->isEffectful()) {
5205 addEffectful(call);
5206 pushResult(call);
5207 return resumeAfter(call);
5210 MOZ_ASSERT(kind == CallKind::DOM);
5211 add(call);
5212 pushResult(call);
5213 return true;
5215 case CallInfo::ArgFormat::Array: {
5216 MInstruction* call = makeSpreadCall(*callInfo_, needsThisCheck,
5217 flags.isSameRealm(), wrappedTarget);
5218 if (!call) {
5219 return false;
5221 addEffectful(call);
5222 pushResult(call);
5224 return resumeAfter(call);
5226 case CallInfo::ArgFormat::FunApplyArgsObj: {
5227 return emitFunApplyArgsObj(wrappedTarget, flags);
5230 MOZ_CRASH("unreachable");
5233 bool WarpCacheIRTranspiler::emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
5234 CallFlags flags) {
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);
5252 pushResult(apply);
5254 return resumeAfter(apply);
5257 #ifndef JS_SIMULATOR
5258 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5259 Int32OperandId argcId,
5260 CallFlags flags,
5261 uint32_t argcFixed,
5262 bool ignoresReturnValue) {
5263 // Instead of ignoresReturnValue we use CallInfo::ignoresReturnValue.
5264 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5265 CallKind::Native);
5268 bool WarpCacheIRTranspiler::emitCallDOMFunction(ObjOperandId calleeId,
5269 Int32OperandId argcId,
5270 ObjOperandId thisObjId,
5271 CallFlags flags,
5272 uint32_t argcFixed) {
5273 return emitCallFunction(calleeId, argcId, mozilla::Some(thisObjId), flags,
5274 CallKind::DOM);
5276 #else
5277 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5278 Int32OperandId argcId,
5279 CallFlags flags,
5280 uint32_t argcFixed,
5281 uint32_t targetOffset) {
5282 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5283 CallKind::Native);
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,
5290 CallKind::DOM);
5292 #endif
5294 bool WarpCacheIRTranspiler::emitCallScriptedFunction(ObjOperandId calleeId,
5295 Int32OperandId argcId,
5296 CallFlags flags,
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,
5305 CallFlags flags,
5306 uint32_t argcFixed) {
5307 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5308 CallKind::Scripted);
5311 #ifdef JS_PUNBOX64
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);
5320 if (!call) {
5321 return false;
5324 addEffectful(call);
5326 if (!current->ensureHasSlots(3)) {
5327 return false;
5329 current->push(call);
5330 current->push(id);
5331 current->push(target);
5333 MResumePoint* resumePoint =
5334 MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
5335 ResumeMode::ResumeAfterCheckProxyGetResult);
5336 if (!resumePoint) {
5337 return false;
5339 call->setResumePoint(resumePoint);
5341 current->pop();
5342 current->pop();
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,
5365 wrappedTarget);
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,
5381 wrappedTarget);
5383 #endif
5385 bool WarpCacheIRTranspiler::emitCallClassHook(ObjOperandId calleeId,
5386 Int32OperandId argcId,
5387 CallFlags flags,
5388 uint32_t argcFixed,
5389 uint32_t targetOffset) {
5390 MDefinition* callee = getOperand(calleeId);
5391 JSNative target = jsnativeStubField(targetOffset);
5393 #ifdef DEBUG
5394 MDefinition* argc = getOperand(argcId);
5395 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5396 static_cast<int32_t>(callInfo_->argc()));
5397 #endif
5399 if (!updateCallInfo(callee, flags)) {
5400 return false;
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());
5412 if (!call) {
5413 return false;
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());
5431 addEffectful(call);
5432 pushResult(call);
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;
5458 } else {
5459 auto* thisv = MLoadFixedSlot::New(alloc(), callee,
5460 BoundFunctionObject::boundThisSlot());
5461 add(thisv);
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());
5473 add(boundArgs);
5474 auto* boundArgsObj = unboxObjectInfallible(boundArgs, IsMovable::Yes);
5475 elements = MElements::New(alloc(), boundArgsObj);
5476 add(elements);
5479 auto loadBoundArg = [&](size_t index) {
5480 MInstruction* arg;
5481 if (usingInlineBoundArgs) {
5482 size_t slot = BoundFunctionObject::firstInlineBoundArgSlot() + index;
5483 arg = MLoadFixedSlot::New(alloc(), callee, slot);
5484 } else {
5485 arg = MLoadElement::New(alloc(), elements, constant(Int32Value(index)));
5487 add(arg);
5488 return arg;
5490 if (!callInfo_->prependArgs(numBoundArgs, loadBoundArg)) {
5491 return false;
5494 MCall* call = makeCall(*callInfo_, needsThisCheck, wrappedTarget);
5495 if (!call) {
5496 return false;
5499 if (flags.isSameRealm()) {
5500 call->setNotCrossRealm();
5503 addEffectful(call);
5504 pushResult(call);
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);
5516 if (!bound) {
5517 return false;
5519 addEffectful(bound);
5521 for (uint32_t i = 0; i < argc; i++) {
5522 bound->initArg(i, callInfo_->getArg(i));
5525 pushResult(bound);
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);
5537 add(bound);
5539 size_t numBoundArgs = argc > 0 ? argc - 1 : 0;
5540 MOZ_ASSERT(numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs);
5542 auto initSlot = [&](size_t slot, MDefinition* value) {
5543 #ifdef DEBUG
5544 // Assert we can elide the post write barrier. See also the comment in
5545 // WarpBuilder::buildNamedLambdaEnv.
5546 add(MAssertCanElidePostWriteBarrier::New(alloc(), bound, value));
5547 #endif
5548 addUnchecked(MStoreFixedSlot::NewUnbarriered(alloc(), bound, slot, value));
5551 initSlot(BoundFunctionObject::targetSlot(), target);
5552 if (argc > 0) {
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));
5560 pushResult(bound);
5561 return true;
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);
5568 #ifdef DEBUG
5569 MDefinition* argc = getOperand(argcId);
5570 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5571 static_cast<int32_t>(callInfo_->argc()));
5572 #endif
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)) {
5580 return false;
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);
5590 if (!call) {
5591 return false;
5594 mozilla::Maybe<MDefinition*> undefined;
5595 for (size_t i = 0; i < sig.args().length(); i++) {
5596 if (!alloc().ensureBallast()) {
5597 return false;
5600 MDefinition* arg;
5601 if (i < callInfo_->argc()) {
5602 arg = callInfo_->getArg(i);
5603 } else {
5604 if (!undefined) {
5605 undefined.emplace(constant(UndefinedValue()));
5607 arg = convertWasmArg(*undefined, sig.args()[i].kind());
5609 call->initArg(i, arg);
5612 addEffectful(call);
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.
5620 } else {
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);
5630 break;
5631 default:
5632 // No spectre.index_masking of i32 results required, as the generated
5633 // stub takes care of that.
5634 break;
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
5643 // instruction.
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;
5655 switch (kind) {
5656 case wasm::ValType::I32:
5657 conversion = MTruncateToInt32::New(alloc(), arg);
5658 break;
5659 case wasm::ValType::I64:
5660 conversion = MToInt64::New(alloc(), arg);
5661 break;
5662 case wasm::ValType::F32:
5663 conversion = MToFloat32::New(alloc(), arg);
5664 break;
5665 case wasm::ValType::F64:
5666 conversion = MToDouble::New(alloc(), arg);
5667 break;
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
5673 // effect-free.
5674 switch (arg->type()) {
5675 case MIRType::Object:
5676 conversion = MWasmAnyRefFromJSObject::New(alloc(), arg);
5677 break;
5678 case MIRType::String:
5679 conversion = MWasmAnyRefFromJSString::New(alloc(), arg);
5680 break;
5681 case MIRType::Null:
5682 arg->setImplicitlyUsedUnchecked();
5683 conversion = MWasmNullConstant::New(alloc());
5684 break;
5685 default:
5686 conversion = MWasmAnyRefFromJSValue::New(alloc(), arg);
5687 break;
5689 break;
5692 add(conversion);
5693 return conversion;
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);
5702 return true;
5705 bool WarpCacheIRTranspiler::emitCallGetterResult(CallKind kind,
5706 ValOperandId receiverId,
5707 uint32_t getterOffset,
5708 bool sameRealm,
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)) {
5721 return false;
5724 return true;
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);
5738 if (!call) {
5739 return false;
5742 if (sameRealm) {
5743 call->setNotCrossRealm();
5746 addEffectful(call);
5747 pushResult(call);
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)) {
5790 return false;
5793 return true;
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);
5807 if (!call) {
5808 return false;
5811 if (sameRealm) {
5812 call->setNotCrossRealm();
5815 addEffectful(call);
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,
5835 ValOperandId rhsId,
5836 bool sameRealm,
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);
5848 add(shapeConst);
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);
5861 add(createThis);
5863 callInfo_->thisArg()->setImplicitlyUsedUnchecked();
5864 callInfo_->setThis(createThis);
5865 return true;
5868 bool WarpCacheIRTranspiler::emitReturnFromIC() { return true; }
5870 bool WarpCacheIRTranspiler::emitBailout() {
5871 auto* bail = MBail::New(alloc());
5872 add(bail);
5874 return true;
5877 bool WarpCacheIRTranspiler::emitAssertPropertyLookup(ObjOperandId objId,
5878 uint32_t idOffset,
5879 uint32_t slotOffset) {
5880 // We currently only emit checks in baseline.
5881 return true;
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()));
5891 return true;
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()));
5899 return true;
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
5908 // Snapshot.
5909 auto* nop = MNop::New(alloc());
5910 add(nop);
5912 auto* resumePoint = MResumePoint::New(
5913 alloc(), nop->block(), loc_.toRawBytecode(), ResumeMode::ResumeAfter);
5914 if (!resumePoint) {
5915 return false;
5917 nop->setResumePoint(resumePoint);
5919 auto* encode = MEncodeSnapshot::New(alloc());
5920 addEffectfulUnsafe(encode);
5922 current->pop();
5924 pushResult(constant(UndefinedValue()));
5925 return true;
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.
5932 return true;
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);
5944 add(shapeConstant);
5946 auto* obj = MNewPlainObject::New(alloc(), shapeConstant, numFixedSlots,
5947 numDynamicSlots, allocKind, heap);
5948 add(obj);
5950 pushResult(obj);
5951 return true;
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);
5961 add(shapeConstant);
5963 auto* obj = MNewArrayObject::New(alloc(), shapeConstant, length, heap);
5964 add(obj);
5966 pushResult(obj);
5967 return true;
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);
5989 if (!call) {
5990 return false;
5992 addEffectful(call);
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
6003 // bailing out.
6004 current->push(call);
6005 MResumePoint* resumePoint =
6006 MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
6007 ResumeMode::ResumeAfterCheckIsObject);
6008 if (!resumePoint) {
6009 return false;
6011 call->setResumePoint(resumePoint);
6012 current->pop();
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);
6027 add(guard);
6029 return true;
6032 #ifdef FUZZING_JS_FUZZILLI
6033 bool WarpCacheIRTranspiler::emitFuzzilliHashResult(ValOperandId valId) {
6034 MDefinition* input = getOperand(valId);
6036 auto* hash = MFuzzilliHash::New(alloc(), input);
6037 add(hash);
6039 auto* store = MFuzzilliHashStore::New(alloc(), hash);
6040 addEffectful(store);
6041 pushResult(constant(UndefinedValue()));
6043 return resumeAfter(store);
6045 #endif
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
6053 // a bailout.
6055 // WarpBuilder uses WarpPoppedValueUseChecker to assert this invariant in
6056 // debug builds.
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()) {
6064 // Nothing to do.
6065 return;
6068 // If the most recent use of 'input' is an instruction we just added, there is
6069 // nothing to do.
6070 MDefinition* inputUse = input->maybeMostRecentlyAddedDefUse();
6071 if (inputUse && inputUse->id() >= numInstructionIdsBefore) {
6072 return;
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,
6087 cacheIRSnapshot);
6088 if (!transpiler.transpile(inputs)) {
6089 return false;
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);
6103 return true;