Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / js / src / jit / WarpCacheIRTranspiler.cpp
blobfdaafd00b39fb83a8505bb8a54749d863d40a5cc
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 "jit/AtomicOp.h"
15 #include "jit/CacheIR.h"
16 #include "jit/CacheIRCompiler.h"
17 #include "jit/CacheIROpsGenerated.h"
18 #include "jit/CacheIRReader.h"
19 #include "jit/LIR.h"
20 #include "jit/MIR.h"
21 #include "jit/MIRGenerator.h"
22 #include "jit/MIRGraph.h"
23 #include "jit/WarpBuilder.h"
24 #include "jit/WarpBuilderShared.h"
25 #include "jit/WarpSnapshot.h"
26 #include "js/ScalarType.h" // js::Scalar::Type
27 #include "vm/BytecodeLocation.h"
28 #include "wasm/WasmCode.h"
30 #include "gc/ObjectKind-inl.h"
31 #include "vm/NativeObject-inl.h"
32 #include "wasm/WasmInstance-inl.h"
34 using namespace js;
35 using namespace js::jit;
37 // The CacheIR transpiler generates MIR from Baseline CacheIR.
38 class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
39 WarpBuilder* builder_;
40 BytecodeLocation loc_;
41 const CacheIRStubInfo* stubInfo_;
42 const uint8_t* stubData_;
44 // Vector mapping OperandId to corresponding MDefinition.
45 using MDefinitionStackVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
46 MDefinitionStackVector operands_;
48 CallInfo* callInfo_;
50 // Array mapping call arguments to OperandId.
51 using ArgumentKindArray =
52 mozilla::EnumeratedArray<ArgumentKind, OperandId,
53 size_t(ArgumentKind::NumKinds)>;
54 ArgumentKindArray argumentOperandIds_;
56 void setArgumentId(ArgumentKind kind, OperandId id) {
57 MOZ_ASSERT(kind != ArgumentKind::Callee);
58 MOZ_ASSERT(!argumentOperandIds_[kind].valid());
59 argumentOperandIds_[kind] = id;
62 void updateArgumentsFromOperands();
64 #ifdef DEBUG
65 // Used to assert that there is only one effectful instruction
66 // per stub. And that this instruction has a resume point.
67 MInstruction* effectful_ = nullptr;
68 bool pushedResult_ = false;
69 #endif
71 inline void addUnchecked(MInstruction* ins) {
72 current->add(ins);
74 // If we have not set a more specific bailout kind, mark this instruction
75 // as transpiled CacheIR. If one of these instructions bails out, we
76 // expect to hit the baseline fallback stub and invalidate the Warp script
77 // in tryAttach.
78 if (ins->bailoutKind() == BailoutKind::Unknown) {
79 ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
83 inline void add(MInstruction* ins) {
84 MOZ_ASSERT(!ins->isEffectful());
85 addUnchecked(ins);
88 inline void addEffectful(MInstruction* ins) {
89 MOZ_ASSERT(ins->isEffectful());
90 MOZ_ASSERT(!effectful_, "Can only have one effectful instruction");
91 addUnchecked(ins);
92 #ifdef DEBUG
93 effectful_ = ins;
94 #endif
97 // Bypasses all checks in addEffectful. Only used for testing functions.
98 inline void addEffectfulUnsafe(MInstruction* ins) {
99 MOZ_ASSERT(ins->isEffectful());
100 addUnchecked(ins);
103 [[nodiscard]] bool resumeAfterUnchecked(MInstruction* ins) {
104 return WarpBuilderShared::resumeAfter(ins, loc_);
106 [[nodiscard]] bool resumeAfter(MInstruction* ins) {
107 MOZ_ASSERT(effectful_ == ins);
108 return resumeAfterUnchecked(ins);
111 // CacheIR instructions writing to the IC's result register (the *Result
112 // instructions) must call this to push the result onto the virtual stack.
113 void pushResult(MDefinition* result) {
114 MOZ_ASSERT(!pushedResult_, "Can't have more than one result");
115 current->push(result);
116 #ifdef DEBUG
117 pushedResult_ = true;
118 #endif
121 MDefinition* getOperand(OperandId id) const { return operands_[id.id()]; }
123 void setOperand(OperandId id, MDefinition* def) { operands_[id.id()] = def; }
125 [[nodiscard]] bool defineOperand(OperandId id, MDefinition* def) {
126 MOZ_ASSERT(id.id() == operands_.length());
127 return operands_.append(def);
130 uintptr_t readStubWord(uint32_t offset) {
131 return stubInfo_->getStubRawWord(stubData_, offset);
134 Shape* shapeStubField(uint32_t offset) {
135 return reinterpret_cast<Shape*>(readStubWord(offset));
137 GetterSetter* getterSetterStubField(uint32_t offset) {
138 return reinterpret_cast<GetterSetter*>(readStubWord(offset));
140 const JSClass* classStubField(uint32_t offset) {
141 return reinterpret_cast<const JSClass*>(readStubWord(offset));
143 JSString* stringStubField(uint32_t offset) {
144 return reinterpret_cast<JSString*>(readStubWord(offset));
146 JS::Symbol* symbolStubField(uint32_t offset) {
147 return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
149 BaseScript* baseScriptStubField(uint32_t offset) {
150 return reinterpret_cast<BaseScript*>(readStubWord(offset));
152 const JSJitInfo* jitInfoStubField(uint32_t offset) {
153 return reinterpret_cast<const JSJitInfo*>(readStubWord(offset));
155 JSNative jsnativeStubField(uint32_t offset) {
156 return reinterpret_cast<JSNative>(readStubWord(offset));
158 JS::ExpandoAndGeneration* expandoAndGenerationField(uint32_t offset) {
159 return reinterpret_cast<JS::ExpandoAndGeneration*>(readStubWord(offset));
161 const wasm::FuncExport* wasmFuncExportField(uint32_t offset) {
162 return reinterpret_cast<const wasm::FuncExport*>(readStubWord(offset));
164 NativeIteratorListHead* nativeIteratorListHeadStubField(uint32_t offset) {
165 return reinterpret_cast<NativeIteratorListHead*>(readStubWord(offset));
167 size_t* fuseStubField(uint32_t offset) {
168 return reinterpret_cast<size_t*>(readStubWord(offset));
170 gc::Heap allocSiteInitialHeapField(uint32_t offset) {
171 uintptr_t word = readStubWord(offset);
172 MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) ||
173 word == uintptr_t(gc::Heap::Tenured));
174 return gc::Heap(word);
176 const void* rawPointerField(uint32_t offset) {
177 return reinterpret_cast<const void*>(readStubWord(offset));
179 jsid idStubField(uint32_t offset) {
180 return jsid::fromRawBits(readStubWord(offset));
182 int32_t int32StubField(uint32_t offset) {
183 return static_cast<int32_t>(readStubWord(offset));
185 uint32_t uint32StubField(uint32_t offset) {
186 return static_cast<uint32_t>(readStubWord(offset));
188 uint64_t uint64StubField(uint32_t offset) {
189 return static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
191 Value valueStubField(uint32_t offset) {
192 uint64_t raw =
193 static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
194 Value val = Value::fromRawBits(raw);
195 MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
196 return val;
198 double doubleStubField(uint32_t offset) {
199 uint64_t raw =
200 static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
201 return mozilla::BitwiseCast<double>(raw);
204 // This must only be called when the caller knows the object is tenured and
205 // not a nursery index.
206 JSObject* tenuredObjectStubField(uint32_t offset) {
207 WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
208 return field.toObject();
211 // Returns either MConstant or MNurseryIndex. See WarpObjectField.
212 MInstruction* objectStubField(uint32_t offset);
214 const JSClass* classForGuardClassKind(GuardClassKind kind);
216 [[nodiscard]] bool emitGuardTo(ValOperandId inputId, MIRType type);
218 [[nodiscard]] bool emitToString(OperandId inputId, StringOperandId resultId);
220 template <typename T>
221 [[nodiscard]] bool emitDoubleBinaryArithResult(NumberOperandId lhsId,
222 NumberOperandId rhsId);
224 template <typename T>
225 [[nodiscard]] bool emitInt32BinaryArithResult(Int32OperandId lhsId,
226 Int32OperandId rhsId);
228 template <typename T>
229 [[nodiscard]] bool emitBigIntBinaryArithResult(BigIntOperandId lhsId,
230 BigIntOperandId rhsId);
232 template <typename T>
233 [[nodiscard]] bool emitBigIntBinaryArithEffectfulResult(
234 BigIntOperandId lhsId, BigIntOperandId rhsId);
236 template <typename T>
237 [[nodiscard]] bool emitBigIntUnaryArithResult(BigIntOperandId inputId);
239 [[nodiscard]] bool emitCompareResult(JSOp op, OperandId lhsId,
240 OperandId rhsId,
241 MCompare::CompareType compareType);
243 [[nodiscard]] bool emitTruthyResult(OperandId inputId);
245 [[nodiscard]] bool emitNewIteratorResult(MNewIterator::Type type,
246 uint32_t templateObjectOffset);
248 MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
250 [[nodiscard]] MInstruction* convertToBoolean(MDefinition* input);
252 bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind,
253 ObjOperandId objId, uint32_t offsetOffset,
254 ValOperandId rhsId, uint32_t newShapeOffset);
256 MInstruction* emitTypedArrayLength(ArrayBufferViewKind viewKind,
257 MDefinition* obj);
259 MInstruction* emitDataViewLength(ArrayBufferViewKind viewKind,
260 MDefinition* obj);
262 void addDataViewData(ArrayBufferViewKind viewKind, MDefinition* obj,
263 Scalar::Type type, MDefinition** offset,
264 MInstruction** elements);
266 [[nodiscard]] bool emitAtomicsBinaryOp(
267 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
268 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind,
269 AtomicOp op);
271 [[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId,
272 uint32_t slotIndex);
274 // Calls are either Native (native function without a JitEntry),
275 // a DOM Native (native function with a JitInfo OpType::Method),
276 // or Scripted (scripted function or native function with a JitEntry).
277 enum class CallKind { Native, DOM, Scripted };
279 [[nodiscard]] bool updateCallInfo(MDefinition* callee, CallFlags flags);
281 [[nodiscard]] bool emitCallFunction(ObjOperandId calleeId,
282 Int32OperandId argcId,
283 mozilla::Maybe<ObjOperandId> thisObjId,
284 CallFlags flags, CallKind kind);
285 [[nodiscard]] bool emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
286 CallFlags flags);
288 MDefinition* convertWasmArg(MDefinition* arg, wasm::ValType::Kind kind);
290 WrappedFunction* maybeWrappedFunction(MDefinition* callee, CallKind kind,
291 uint16_t nargs, FunctionFlags flags);
292 WrappedFunction* maybeCallTarget(MDefinition* callee, CallKind kind);
294 bool maybeCreateThis(MDefinition* callee, CallFlags flags, CallKind kind);
296 [[nodiscard]] bool emitCallGetterResult(CallKind kind,
297 ValOperandId receiverId,
298 uint32_t getterOffset, bool sameRealm,
299 uint32_t nargsAndFlagsOffset);
300 [[nodiscard]] bool emitCallSetter(CallKind kind, ObjOperandId receiverId,
301 uint32_t setterOffset, ValOperandId rhsId,
302 bool sameRealm,
303 uint32_t nargsAndFlagsOffset);
305 #ifndef JS_CODEGEN_X86
306 [[nodiscard]] bool emitCallScriptedProxyGetShared(
307 MDefinition* target, MDefinition* receiver, MDefinition* handler,
308 MDefinition* id, MDefinition* trapDef, WrappedFunction* trap);
309 #endif
311 CACHE_IR_TRANSPILER_GENERATED
313 public:
314 WarpCacheIRTranspiler(WarpBuilder* builder, BytecodeLocation loc,
315 CallInfo* callInfo, const WarpCacheIR* cacheIRSnapshot)
316 : WarpBuilderShared(builder->snapshot(), builder->mirGen(),
317 builder->currentBlock()),
318 builder_(builder),
319 loc_(loc),
320 stubInfo_(cacheIRSnapshot->stubInfo()),
321 stubData_(cacheIRSnapshot->stubData()),
322 callInfo_(callInfo) {}
324 [[nodiscard]] bool transpile(std::initializer_list<MDefinition*> inputs);
327 bool WarpCacheIRTranspiler::transpile(
328 std::initializer_list<MDefinition*> inputs) {
329 if (!operands_.append(inputs.begin(), inputs.end())) {
330 return false;
333 CacheIRReader reader(stubInfo_);
334 do {
335 CacheOp op = reader.readOp();
336 switch (op) {
337 #define DEFINE_OP(op, ...) \
338 case CacheOp::op: \
339 if (!emit##op(reader)) { \
340 return false; \
342 break;
343 CACHE_IR_TRANSPILER_OPS(DEFINE_OP)
344 #undef DEFINE_OP
346 default:
347 fprintf(stderr, "Unsupported op: %s\n", CacheIROpNames[size_t(op)]);
348 MOZ_CRASH("Unsupported op");
350 } while (reader.more());
352 // Effectful instructions should have a resume point. MIonToWasmCall is an
353 // exception: we can attach the resume point to the MInt64ToBigInt instruction
354 // instead. Other exceptions are MResizableTypedArrayLength and
355 // MResizableDataViewByteLength, and MGrowableSharedArrayBufferByteLength,
356 // which add the resume point to MPostIntPtrConversion.
357 MOZ_ASSERT_IF(effectful_,
358 effectful_->resumePoint() || effectful_->isIonToWasmCall() ||
359 effectful_->isResizableTypedArrayLength() ||
360 effectful_->isResizableDataViewByteLength() ||
361 effectful_->isGrowableSharedArrayBufferByteLength());
362 return true;
365 MInstruction* WarpCacheIRTranspiler::objectStubField(uint32_t offset) {
366 WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
368 if (field.isNurseryIndex()) {
369 auto* ins = MNurseryObject::New(alloc(), field.toNurseryIndex());
370 add(ins);
371 return ins;
374 auto* ins = MConstant::NewObject(alloc(), field.toObject());
375 add(ins);
376 return ins;
379 bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
380 GuardClassKind kind) {
381 MDefinition* def = getOperand(objId);
383 MInstruction* ins;
384 if (kind == GuardClassKind::JSFunction) {
385 ins = MGuardToFunction::New(alloc(), def);
386 } else {
387 const JSClass* classp = classForGuardClassKind(kind);
388 ins = MGuardToClass::New(alloc(), def, classp);
391 add(ins);
393 setOperand(objId, ins);
394 return true;
397 bool WarpCacheIRTranspiler::emitGuardEitherClass(ObjOperandId objId,
398 GuardClassKind kind1,
399 GuardClassKind kind2) {
400 MDefinition* def = getOperand(objId);
402 // We don't yet need this case, so it's unsupported for now.
403 MOZ_ASSERT(kind1 != GuardClassKind::JSFunction &&
404 kind2 != GuardClassKind::JSFunction);
406 const JSClass* classp1 = classForGuardClassKind(kind1);
407 const JSClass* classp2 = classForGuardClassKind(kind2);
408 auto* ins = MGuardToEitherClass::New(alloc(), def, classp1, classp2);
410 add(ins);
412 setOperand(objId, ins);
413 return true;
416 const JSClass* WarpCacheIRTranspiler::classForGuardClassKind(
417 GuardClassKind kind) {
418 switch (kind) {
419 case GuardClassKind::Array:
420 case GuardClassKind::PlainObject:
421 case GuardClassKind::FixedLengthArrayBuffer:
422 case GuardClassKind::ResizableArrayBuffer:
423 case GuardClassKind::FixedLengthSharedArrayBuffer:
424 case GuardClassKind::GrowableSharedArrayBuffer:
425 case GuardClassKind::FixedLengthDataView:
426 case GuardClassKind::ResizableDataView:
427 case GuardClassKind::MappedArguments:
428 case GuardClassKind::UnmappedArguments:
429 case GuardClassKind::Set:
430 case GuardClassKind::Map:
431 case GuardClassKind::BoundFunction:
432 return ClassFor(kind);
433 case GuardClassKind::WindowProxy:
434 return mirGen().runtime->maybeWindowProxyClass();
435 case GuardClassKind::JSFunction:
436 break;
438 MOZ_CRASH("unexpected kind");
441 bool WarpCacheIRTranspiler::emitGuardAnyClass(ObjOperandId objId,
442 uint32_t claspOffset) {
443 MDefinition* def = getOperand(objId);
444 const JSClass* classp = classStubField(claspOffset);
446 auto* ins = MGuardToClass::New(alloc(), def, classp);
447 add(ins);
449 setOperand(objId, ins);
450 return true;
453 bool WarpCacheIRTranspiler::emitGuardShape(ObjOperandId objId,
454 uint32_t shapeOffset) {
455 MDefinition* def = getOperand(objId);
457 // No read barrier is required because snapshot data is not weak and is traced
458 // as part of IonCompileTask.
459 Shape* shape = shapeStubField(shapeOffset);
461 auto* ins = MGuardShape::New(alloc(), def, shape);
462 add(ins);
464 setOperand(objId, ins);
465 return true;
468 bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
469 auto* ins = MGuardFuse::New(alloc(), fuseIndex);
470 add(ins);
472 return true;
475 bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId,
476 uint32_t shapesOffset) {
477 MDefinition* def = getOperand(objId);
478 MInstruction* shapeList = objectStubField(shapesOffset);
480 auto* ins = MGuardMultipleShapes::New(alloc(), def, shapeList);
481 if (builder_->info().inlineScriptTree()->hasSharedICScript()) {
482 ins->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding);
484 add(ins);
486 setOperand(objId, ins);
487 return true;
490 bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
491 MDefinition* def = getOperand(objId);
493 auto* ins = MGuardNullProto::New(alloc(), def);
494 add(ins);
496 setOperand(objId, ins);
497 return true;
500 bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId) {
501 MDefinition* obj = getOperand(objId);
503 auto* ins = MGuardIsNativeObject::New(alloc(), obj);
504 add(ins);
506 setOperand(objId, ins);
507 return true;
510 bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId) {
511 MDefinition* obj = getOperand(objId);
513 auto* ins = MGuardIsProxy::New(alloc(), obj);
514 add(ins);
516 setOperand(objId, ins);
517 return true;
520 bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId) {
521 MDefinition* obj = getOperand(objId);
523 auto* ins = MGuardIsNotProxy::New(alloc(), obj);
524 add(ins);
526 setOperand(objId, ins);
527 return true;
530 bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
531 MDefinition* obj = getOperand(objId);
533 auto* ins = MGuardIsNotDOMProxy::New(alloc(), obj);
534 add(ins);
536 setOperand(objId, ins);
537 return true;
540 bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
541 ObjOperandId objId, uint32_t idOffset, uint32_t getterSetterOffset) {
542 MDefinition* obj = getOperand(objId);
543 jsid id = idStubField(idOffset);
544 GetterSetter* gs = getterSetterStubField(getterSetterOffset);
546 auto* ins = MGuardHasGetterSetter::New(alloc(), obj, id, gs);
547 add(ins);
549 setOperand(objId, ins);
550 return true;
553 bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId,
554 uint32_t idOffset) {
555 MDefinition* obj = getOperand(objId);
556 jsid id = idStubField(idOffset);
558 auto* ins = MProxyGet::New(alloc(), obj, id);
559 addEffectful(ins);
561 pushResult(ins);
562 return resumeAfter(ins);
565 bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId,
566 ValOperandId idId) {
567 MDefinition* obj = getOperand(objId);
568 MDefinition* id = getOperand(idId);
570 auto* ins = MProxyGetByValue::New(alloc(), obj, id);
571 addEffectful(ins);
573 pushResult(ins);
574 return resumeAfter(ins);
577 bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId,
578 ValOperandId idId,
579 bool hasOwn) {
580 MDefinition* obj = getOperand(objId);
581 MDefinition* id = getOperand(idId);
583 auto* ins = MProxyHasProp::New(alloc(), obj, id, hasOwn);
584 addEffectful(ins);
586 pushResult(ins);
587 return resumeAfter(ins);
590 bool WarpCacheIRTranspiler::emitProxySet(ObjOperandId objId, uint32_t idOffset,
591 ValOperandId rhsId, bool strict) {
592 MDefinition* obj = getOperand(objId);
593 jsid id = idStubField(idOffset);
594 MDefinition* rhs = getOperand(rhsId);
596 auto* ins = MProxySet::New(alloc(), obj, rhs, id, strict);
597 addEffectful(ins);
599 return resumeAfter(ins);
602 bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId,
603 ValOperandId idId,
604 ValOperandId rhsId,
605 bool strict) {
606 MDefinition* obj = getOperand(objId);
607 MDefinition* id = getOperand(idId);
608 MDefinition* rhs = getOperand(rhsId);
610 auto* ins = MProxySetByValue::New(alloc(), obj, id, rhs, strict);
611 addEffectful(ins);
613 return resumeAfter(ins);
616 bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId,
617 bool strict,
618 ValOperandId rhsId) {
619 MDefinition* obj = getOperand(objId);
620 MDefinition* rhs = getOperand(rhsId);
622 auto* ins = MCallSetArrayLength::New(alloc(), obj, rhs, strict);
623 addEffectful(ins);
625 return resumeAfter(ins);
628 bool WarpCacheIRTranspiler::emitCallDOMGetterResult(ObjOperandId objId,
629 uint32_t jitInfoOffset) {
630 MDefinition* obj = getOperand(objId);
631 const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
633 MInstruction* ins;
634 if (jitInfo->isAlwaysInSlot) {
635 ins = MGetDOMMember::New(alloc(), jitInfo, obj, nullptr, nullptr);
636 } else {
637 // TODO(post-Warp): realms, guard operands (movable?).
638 ins = MGetDOMProperty::New(alloc(), jitInfo, DOMObjectKind::Native,
639 (JS::Realm*)mirGen().realm->realmPtr(), obj,
640 nullptr, nullptr);
643 if (!ins) {
644 return false;
647 if (ins->isEffectful()) {
648 addEffectful(ins);
649 pushResult(ins);
650 return resumeAfter(ins);
653 add(ins);
654 pushResult(ins);
655 return true;
658 bool WarpCacheIRTranspiler::emitCallDOMSetter(ObjOperandId objId,
659 uint32_t jitInfoOffset,
660 ValOperandId rhsId) {
661 MDefinition* obj = getOperand(objId);
662 const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
663 MDefinition* value = getOperand(rhsId);
665 MOZ_ASSERT(jitInfo->type() == JSJitInfo::Setter);
666 auto* set =
667 MSetDOMProperty::New(alloc(), jitInfo->setter, DOMObjectKind::Native,
668 (JS::Realm*)mirGen().realm->realmPtr(), obj, value);
669 addEffectful(set);
670 return resumeAfter(set);
673 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValue(ObjOperandId objId,
674 ValOperandId resultId) {
675 MDefinition* proxy = getOperand(objId);
677 auto* ins = MLoadDOMExpandoValue::New(alloc(), proxy);
678 add(ins);
680 return defineOperand(resultId, ins);
683 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueGuardGeneration(
684 ObjOperandId objId, uint32_t expandoAndGenerationOffset,
685 uint32_t generationOffset, ValOperandId resultId) {
686 MDefinition* proxy = getOperand(objId);
687 JS::ExpandoAndGeneration* expandoAndGeneration =
688 expandoAndGenerationField(expandoAndGenerationOffset);
689 uint64_t generation = uint64StubField(generationOffset);
691 auto* ins = MLoadDOMExpandoValueGuardGeneration::New(
692 alloc(), proxy, expandoAndGeneration, generation);
693 add(ins);
695 return defineOperand(resultId, ins);
698 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueIgnoreGeneration(
699 ObjOperandId objId, ValOperandId resultId) {
700 MDefinition* proxy = getOperand(objId);
702 auto* ins = MLoadDOMExpandoValueIgnoreGeneration::New(alloc(), proxy);
703 add(ins);
705 return defineOperand(resultId, ins);
708 bool WarpCacheIRTranspiler::emitGuardDOMExpandoMissingOrGuardShape(
709 ValOperandId expandoId, uint32_t shapeOffset) {
710 MDefinition* expando = getOperand(expandoId);
711 Shape* shape = shapeStubField(shapeOffset);
713 auto* ins = MGuardDOMExpandoMissingOrGuardShape::New(alloc(), expando, shape);
714 add(ins);
716 setOperand(expandoId, ins);
717 return true;
720 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotResult(ObjOperandId objId,
721 uint32_t nameOffset) {
722 MDefinition* obj = getOperand(objId);
723 PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();
725 auto* ins = MMegamorphicLoadSlot::New(alloc(), obj, NameToId(name));
726 add(ins);
728 pushResult(ins);
729 return true;
732 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValueResult(
733 ObjOperandId objId, ValOperandId idId) {
734 MDefinition* obj = getOperand(objId);
735 MDefinition* id = getOperand(idId);
737 auto* ins = MMegamorphicLoadSlotByValue::New(alloc(), obj, id);
738 add(ins);
740 pushResult(ins);
741 return true;
744 bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId,
745 uint32_t idOffset,
746 ValOperandId rhsId,
747 bool strict) {
748 MDefinition* obj = getOperand(objId);
749 jsid id = idStubField(idOffset);
750 MDefinition* rhs = getOperand(rhsId);
752 auto* ins = MMegamorphicStoreSlot::New(alloc(), obj, rhs, id, strict);
753 addEffectful(ins);
755 return resumeAfter(ins);
758 bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId,
759 ValOperandId idId,
760 bool hasOwn) {
761 MDefinition* obj = getOperand(objId);
762 MDefinition* id = getOperand(idId);
764 auto* ins = MMegamorphicHasProp::New(alloc(), obj, id, hasOwn);
765 add(ins);
767 pushResult(ins);
768 return true;
771 bool WarpCacheIRTranspiler::emitSmallObjectVariableKeyHasOwnResult(
772 StringOperandId idId, uint32_t propNamesOffset, uint32_t shapeOffset) {
773 MDefinition* id = getOperand(idId);
774 SharedShape* shape = &shapeStubField(shapeOffset)->asShared();
776 auto* ins = MSmallObjectVariableKeyHasProp::New(alloc(), id, shape);
777 add(ins);
779 pushResult(ins);
780 return true;
783 bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId,
784 ValOperandId idId,
785 ValOperandId rhsId,
786 bool strict) {
787 MDefinition* obj = getOperand(objId);
788 MDefinition* id = getOperand(idId);
789 MDefinition* rhs = getOperand(rhsId);
791 auto* ins = MMegamorphicSetElement::New(alloc(), obj, id, rhs, strict);
792 addEffectful(ins);
794 return resumeAfter(ins);
797 bool WarpCacheIRTranspiler::emitObjectToIteratorResult(
798 ObjOperandId objId, uint32_t enumeratorsAddrOffset) {
799 MDefinition* obj = getOperand(objId);
800 NativeIteratorListHead* enumeratorsAddr =
801 nativeIteratorListHeadStubField(enumeratorsAddrOffset);
803 auto* ins = MObjectToIterator::New(alloc(), obj, enumeratorsAddr);
804 addEffectful(ins);
805 pushResult(ins);
806 if (!resumeAfter(ins)) {
807 return false;
810 return true;
813 bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId) {
814 MDefinition* val = getOperand(valId);
816 auto* ins = MValueToIterator::New(alloc(), val);
817 addEffectful(ins);
819 pushResult(ins);
820 return resumeAfter(ins);
823 bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
824 ObjOperandId objId) {
825 MDefinition* obj = getOperand(objId);
827 auto* ins = MGuardIsNotArrayBufferMaybeShared::New(alloc(), obj);
828 add(ins);
830 setOperand(objId, ins);
831 return true;
834 bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
835 MDefinition* obj = getOperand(objId);
837 auto* ins = MGuardIsTypedArray::New(alloc(), obj);
838 add(ins);
840 setOperand(objId, ins);
841 return true;
844 bool WarpCacheIRTranspiler::emitGuardIsFixedLengthTypedArray(
845 ObjOperandId objId) {
846 MDefinition* obj = getOperand(objId);
848 auto* ins = MGuardIsFixedLengthTypedArray::New(alloc(), obj);
849 add(ins);
851 setOperand(objId, ins);
852 return true;
855 bool WarpCacheIRTranspiler::emitGuardIsResizableTypedArray(ObjOperandId objId) {
856 MDefinition* obj = getOperand(objId);
858 auto* ins = MGuardIsResizableTypedArray::New(alloc(), obj);
859 add(ins);
861 setOperand(objId, ins);
862 return true;
865 bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId,
866 uint32_t handlerOffset) {
867 MDefinition* obj = getOperand(objId);
868 const void* handler = rawPointerField(handlerOffset);
870 auto* ins = MGuardHasProxyHandler::New(alloc(), obj, handler);
871 add(ins);
873 setOperand(objId, ins);
874 return true;
877 bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
878 uint32_t protoOffset) {
879 MDefinition* def = getOperand(objId);
880 MDefinition* proto = objectStubField(protoOffset);
882 auto* ins = MGuardProto::New(alloc(), def, proto);
883 add(ins);
885 setOperand(objId, ins);
886 return true;
889 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsSpecificObject(
890 ObjOperandId objId, ObjOperandId expectedId, uint32_t slotOffset) {
891 size_t slotIndex = int32StubField(slotOffset);
892 MDefinition* obj = getOperand(objId);
893 MDefinition* expected = getOperand(expectedId);
895 auto* slots = MSlots::New(alloc(), obj);
896 add(slots);
898 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
899 add(load);
901 auto* unbox = MUnbox::New(alloc(), load, MIRType::Object, MUnbox::Fallible);
902 add(unbox);
904 auto* guard = MGuardObjectIdentity::New(alloc(), unbox, expected,
905 /* bailOnEquality = */ false);
906 add(guard);
907 return true;
910 bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId,
911 ObjOperandId objId,
912 uint32_t slotOffset) {
913 size_t slotIndex = int32StubField(slotOffset);
914 MDefinition* obj = getOperand(objId);
916 auto* slots = MSlots::New(alloc(), obj);
917 add(slots);
919 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
920 add(load);
922 return defineOperand(resultId, load);
925 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject(
926 ObjOperandId objId, uint32_t slotOffset) {
927 size_t slotIndex = int32StubField(slotOffset);
928 MDefinition* obj = getOperand(objId);
930 auto* slots = MSlots::New(alloc(), obj);
931 add(slots);
933 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
934 add(load);
936 auto* guard = MGuardIsNotObject::New(alloc(), load);
937 add(guard);
938 return true;
941 bool WarpCacheIRTranspiler::emitGuardFixedSlotValue(ObjOperandId objId,
942 uint32_t offsetOffset,
943 uint32_t valOffset) {
944 MDefinition* obj = getOperand(objId);
946 size_t offset = int32StubField(offsetOffset);
947 Value val = valueStubField(valOffset);
949 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
951 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
952 add(load);
954 auto* guard = MGuardValue::New(alloc(), load, val);
955 add(guard);
956 return true;
959 bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId,
960 uint32_t offsetOffset,
961 uint32_t valOffset) {
962 MDefinition* obj = getOperand(objId);
964 size_t offset = int32StubField(offsetOffset);
965 Value val = valueStubField(valOffset);
967 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
969 auto* slots = MSlots::New(alloc(), obj);
970 add(slots);
972 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
973 add(load);
975 auto* guard = MGuardValue::New(alloc(), load, val);
976 add(guard);
977 return true;
980 bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ObjOperandId resultId,
981 ObjOperandId objId) {
982 MDefinition* obj = getOperand(objId);
984 auto* load = MLoadScriptedProxyHandler::New(alloc(), obj);
985 add(load);
987 return defineOperand(resultId, load);
990 bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId,
991 ValOperandId idId) {
992 MDefinition* id = getOperand(idId);
994 auto* ins = MIdToStringOrSymbol::New(alloc(), id);
995 add(ins);
997 return defineOperand(resultId, ins);
1000 bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId,
1001 uint32_t expectedOffset) {
1002 MDefinition* str = getOperand(strId);
1003 JSString* expected = stringStubField(expectedOffset);
1005 auto* ins = MGuardSpecificAtom::New(alloc(), str, &expected->asAtom());
1006 add(ins);
1008 setOperand(strId, ins);
1009 return true;
1012 bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId,
1013 uint32_t expectedOffset) {
1014 MDefinition* symbol = getOperand(symId);
1015 JS::Symbol* expected = symbolStubField(expectedOffset);
1017 auto* ins = MGuardSpecificSymbol::New(alloc(), symbol, expected);
1018 add(ins);
1020 setOperand(symId, ins);
1021 return true;
1024 bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId,
1025 int32_t expected) {
1026 MDefinition* num = getOperand(numId);
1028 auto* ins = MGuardSpecificInt32::New(alloc(), num, expected);
1029 add(ins);
1031 setOperand(numId, ins);
1032 return true;
1035 bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
1036 uint32_t expectedOffset) {
1037 MDefinition* obj = getOperand(objId);
1038 MDefinition* expected = objectStubField(expectedOffset);
1040 auto* ins = MGuardObjectIdentity::New(alloc(), obj, expected,
1041 /* bailOnEquality = */ false);
1042 add(ins);
1044 setOperand(objId, ins);
1045 return true;
1048 bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
1049 ObjOperandId objId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
1050 MDefinition* obj = getOperand(objId);
1051 MDefinition* expected = objectStubField(expectedOffset);
1052 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
1054 uint16_t nargs = nargsAndFlags >> 16;
1055 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
1057 auto* ins = MGuardSpecificFunction::New(alloc(), obj, expected, nargs, flags);
1058 add(ins);
1060 setOperand(objId, ins);
1061 return true;
1064 bool WarpCacheIRTranspiler::emitGuardFunctionScript(
1065 ObjOperandId funId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
1066 MDefinition* fun = getOperand(funId);
1067 BaseScript* expected = baseScriptStubField(expectedOffset);
1068 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
1070 uint16_t nargs = nargsAndFlags >> 16;
1071 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
1073 auto* ins = MGuardFunctionScript::New(alloc(), fun, expected, nargs, flags);
1074 add(ins);
1076 setOperand(funId, ins);
1077 return true;
1080 bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId,
1081 Int32OperandId resultId) {
1082 MDefinition* str = getOperand(strId);
1084 auto* ins = MGuardStringToIndex::New(alloc(), str);
1085 add(ins);
1087 return defineOperand(resultId, ins);
1090 bool WarpCacheIRTranspiler::emitGuardStringToInt32(StringOperandId strId,
1091 Int32OperandId resultId) {
1092 MDefinition* str = getOperand(strId);
1094 auto* ins = MGuardStringToInt32::New(alloc(), str);
1095 add(ins);
1097 return defineOperand(resultId, ins);
1100 bool WarpCacheIRTranspiler::emitGuardStringToNumber(StringOperandId strId,
1101 NumberOperandId resultId) {
1102 MDefinition* str = getOperand(strId);
1104 auto* ins = MGuardStringToDouble::New(alloc(), str);
1105 add(ins);
1107 return defineOperand(resultId, ins);
1110 bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId) {
1111 MDefinition* obj = getOperand(objId);
1113 auto* ins = MGuardNoDenseElements::New(alloc(), obj);
1114 add(ins);
1116 setOperand(objId, ins);
1117 return true;
1120 bool WarpCacheIRTranspiler::emitGuardFunctionHasJitEntry(ObjOperandId funId,
1121 bool constructing) {
1122 MDefinition* fun = getOperand(funId);
1123 uint16_t expectedFlags = FunctionFlags::HasJitEntryFlags(constructing);
1124 uint16_t unexpectedFlags = 0;
1126 auto* ins =
1127 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1128 add(ins);
1130 setOperand(funId, ins);
1131 return true;
1134 bool WarpCacheIRTranspiler::emitGuardFunctionHasNoJitEntry(ObjOperandId funId) {
1135 MDefinition* fun = getOperand(funId);
1136 uint16_t expectedFlags = 0;
1137 uint16_t unexpectedFlags =
1138 FunctionFlags::HasJitEntryFlags(/*isConstructing=*/false);
1140 auto* ins =
1141 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1142 add(ins);
1144 setOperand(funId, ins);
1145 return true;
1148 bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
1149 ObjOperandId funId) {
1150 MDefinition* fun = getOperand(funId);
1152 auto* ins = MGuardFunctionIsNonBuiltinCtor::New(alloc(), fun);
1153 add(ins);
1155 setOperand(funId, ins);
1156 return true;
1159 bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId) {
1160 MDefinition* fun = getOperand(funId);
1161 uint16_t expectedFlags = FunctionFlags::CONSTRUCTOR;
1162 uint16_t unexpectedFlags = 0;
1164 auto* ins =
1165 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1166 add(ins);
1168 setOperand(funId, ins);
1169 return true;
1172 bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId) {
1173 MDefinition* fun = getOperand(funId);
1175 auto* ins =
1176 MGuardFunctionKind::New(alloc(), fun, FunctionFlags::ClassConstructor,
1177 /*bailOnEquality=*/true);
1178 add(ins);
1180 setOperand(funId, ins);
1181 return true;
1184 bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId) {
1185 MDefinition* array = getOperand(arrayId);
1187 auto* ins = MGuardArrayIsPacked::New(alloc(), array);
1188 add(ins);
1190 setOperand(arrayId, ins);
1191 return true;
1194 bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId,
1195 uint8_t flags) {
1196 MDefinition* obj = getOperand(objId);
1198 auto* ins = MGuardArgumentsObjectFlags::New(alloc(), obj, flags);
1199 add(ins);
1201 setOperand(objId, ins);
1202 return true;
1205 bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId,
1206 ValueType type) {
1207 switch (type) {
1208 case ValueType::String:
1209 case ValueType::Symbol:
1210 case ValueType::BigInt:
1211 case ValueType::Int32:
1212 case ValueType::Boolean:
1213 return emitGuardTo(inputId, MIRTypeFromValueType(JSValueType(type)));
1214 case ValueType::Undefined:
1215 return emitGuardIsUndefined(inputId);
1216 case ValueType::Null:
1217 return emitGuardIsNull(inputId);
1218 case ValueType::Double:
1219 case ValueType::Magic:
1220 case ValueType::PrivateGCThing:
1221 case ValueType::Object:
1222 #ifdef ENABLE_RECORD_TUPLE
1223 case ValueType::ExtendedPrimitive:
1224 #endif
1225 break;
1228 MOZ_CRASH("unexpected type");
1231 bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId, MIRType type) {
1232 MDefinition* def = getOperand(inputId);
1233 if (def->type() == type) {
1234 return true;
1237 auto* ins = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
1238 add(ins);
1240 setOperand(inputId, ins);
1241 return true;
1244 bool WarpCacheIRTranspiler::emitGuardToObject(ValOperandId inputId) {
1245 return emitGuardTo(inputId, MIRType::Object);
1248 bool WarpCacheIRTranspiler::emitGuardToString(ValOperandId inputId) {
1249 return emitGuardTo(inputId, MIRType::String);
1252 bool WarpCacheIRTranspiler::emitGuardToSymbol(ValOperandId inputId) {
1253 return emitGuardTo(inputId, MIRType::Symbol);
1256 bool WarpCacheIRTranspiler::emitGuardToBigInt(ValOperandId inputId) {
1257 return emitGuardTo(inputId, MIRType::BigInt);
1260 bool WarpCacheIRTranspiler::emitGuardToBoolean(ValOperandId inputId) {
1261 return emitGuardTo(inputId, MIRType::Boolean);
1264 bool WarpCacheIRTranspiler::emitGuardToInt32(ValOperandId inputId) {
1265 return emitGuardTo(inputId, MIRType::Int32);
1268 bool WarpCacheIRTranspiler::emitGuardBooleanToInt32(ValOperandId inputId,
1269 Int32OperandId resultId) {
1270 if (!emitGuardTo(inputId, MIRType::Boolean)) {
1271 return false;
1274 MDefinition* input = getOperand(inputId);
1275 MOZ_ASSERT(input->type() == MIRType::Boolean);
1277 auto* ins = MBooleanToInt32::New(alloc(), input);
1278 add(ins);
1280 return defineOperand(resultId, ins);
1283 bool WarpCacheIRTranspiler::emitGuardIsNumber(ValOperandId inputId) {
1284 // Prefer MToDouble because it gets further optimizations downstream.
1285 MDefinition* def = getOperand(inputId);
1286 if (def->type() == MIRType::Int32) {
1287 auto* ins = MToDouble::New(alloc(), def);
1288 add(ins);
1290 setOperand(inputId, ins);
1291 return true;
1294 // MIRType::Double also implies int32 in Ion.
1295 return emitGuardTo(inputId, MIRType::Double);
1298 bool WarpCacheIRTranspiler::emitGuardIsNullOrUndefined(ValOperandId inputId) {
1299 MDefinition* input = getOperand(inputId);
1300 if (input->type() == MIRType::Null || input->type() == MIRType::Undefined) {
1301 return true;
1304 auto* ins = MGuardNullOrUndefined::New(alloc(), input);
1305 add(ins);
1307 setOperand(inputId, ins);
1308 return true;
1311 bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId) {
1312 MDefinition* input = getOperand(inputId);
1313 if (input->type() == MIRType::Null) {
1314 return true;
1317 auto* ins = MGuardValue::New(alloc(), input, NullValue());
1318 add(ins);
1319 setOperand(inputId, ins);
1320 return true;
1323 bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId) {
1324 MDefinition* input = getOperand(inputId);
1325 if (input->type() == MIRType::Undefined) {
1326 return true;
1329 auto* ins = MGuardValue::New(alloc(), input, UndefinedValue());
1330 add(ins);
1331 setOperand(inputId, ins);
1332 return true;
1335 bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId) {
1336 MDefinition* obj = getOperand(objId);
1338 auto* ins = MGuardIsExtensible::New(alloc(), obj);
1339 add(ins);
1340 setOperand(objId, ins);
1341 return true;
1344 bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
1345 Int32OperandId indexId) {
1346 MDefinition* index = getOperand(indexId);
1348 auto* ins = MGuardInt32IsNonNegative::New(alloc(), index);
1349 add(ins);
1350 setOperand(indexId, ins);
1351 return true;
1354 bool WarpCacheIRTranspiler::emitGuardIndexIsNotDenseElement(
1355 ObjOperandId objId, Int32OperandId indexId) {
1356 MDefinition* obj = getOperand(objId);
1357 MDefinition* index = getOperand(indexId);
1359 auto* ins = MGuardIndexIsNotDenseElement::New(alloc(), obj, index);
1360 add(ins);
1361 setOperand(indexId, ins);
1362 return true;
1365 bool WarpCacheIRTranspiler::emitGuardIndexIsValidUpdateOrAdd(
1366 ObjOperandId objId, Int32OperandId indexId) {
1367 MDefinition* obj = getOperand(objId);
1368 MDefinition* index = getOperand(indexId);
1370 auto* ins = MGuardIndexIsValidUpdateOrAdd::New(alloc(), obj, index);
1371 add(ins);
1372 setOperand(indexId, ins);
1373 return true;
1376 bool WarpCacheIRTranspiler::emitCallAddOrUpdateSparseElementHelper(
1377 ObjOperandId objId, Int32OperandId idId, ValOperandId rhsId, bool strict) {
1378 MDefinition* obj = getOperand(objId);
1379 MDefinition* id = getOperand(idId);
1380 MDefinition* rhs = getOperand(rhsId);
1382 auto* ins = MCallAddOrUpdateSparseElement::New(alloc(), obj, id, rhs, strict);
1383 addEffectful(ins);
1385 return resumeAfter(ins);
1388 bool WarpCacheIRTranspiler::emitGuardTagNotEqual(ValueTagOperandId lhsId,
1389 ValueTagOperandId rhsId) {
1390 MDefinition* lhs = getOperand(lhsId);
1391 MDefinition* rhs = getOperand(rhsId);
1393 auto* ins = MGuardTagNotEqual::New(alloc(), lhs, rhs);
1394 add(ins);
1396 return true;
1399 bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId,
1400 Int32OperandId resultId) {
1401 MDefinition* input = getOperand(inputId);
1402 auto* ins =
1403 MToNumberInt32::New(alloc(), input, IntConversionInputKind::NumbersOnly);
1405 // ToPropertyKey(-0) is "0", so we can silently convert -0 to 0 here.
1406 ins->setNeedsNegativeZeroCheck(false);
1407 add(ins);
1409 return defineOperand(resultId, ins);
1412 bool WarpCacheIRTranspiler::emitTruncateDoubleToUInt32(
1413 NumberOperandId inputId, Int32OperandId resultId) {
1414 MDefinition* input = getOperand(inputId);
1415 auto* ins = MTruncateToInt32::New(alloc(), input);
1416 add(ins);
1418 return defineOperand(resultId, ins);
1421 bool WarpCacheIRTranspiler::emitDoubleToUint8Clamped(NumberOperandId inputId,
1422 Int32OperandId resultId) {
1423 MDefinition* input = getOperand(inputId);
1424 auto* ins = MClampToUint8::New(alloc(), input);
1425 add(ins);
1427 return defineOperand(resultId, ins);
1430 bool WarpCacheIRTranspiler::emitGuardToInt32ModUint32(ValOperandId valId,
1431 Int32OperandId resultId) {
1432 MDefinition* input = getOperand(valId);
1433 auto* ins = MTruncateToInt32::New(alloc(), input);
1434 add(ins);
1436 return defineOperand(resultId, ins);
1439 bool WarpCacheIRTranspiler::emitGuardToUint8Clamped(ValOperandId valId,
1440 Int32OperandId resultId) {
1441 MDefinition* input = getOperand(valId);
1442 auto* ins = MClampToUint8::New(alloc(), input);
1443 add(ins);
1445 return defineOperand(resultId, ins);
1448 bool WarpCacheIRTranspiler::emitToString(OperandId inputId,
1449 StringOperandId resultId) {
1450 MDefinition* input = getOperand(inputId);
1451 auto* ins =
1452 MToString::New(alloc(), input, MToString::SideEffectHandling::Bailout);
1453 add(ins);
1455 return defineOperand(resultId, ins);
1458 bool WarpCacheIRTranspiler::emitInt32ToIntPtr(Int32OperandId inputId,
1459 IntPtrOperandId resultId) {
1460 MDefinition* input = getOperand(inputId);
1461 auto* ins = MInt32ToIntPtr::New(alloc(), input);
1462 add(ins);
1463 return defineOperand(resultId, ins);
1466 bool WarpCacheIRTranspiler::emitGuardNumberToIntPtrIndex(
1467 NumberOperandId inputId, bool supportOOB, IntPtrOperandId resultId) {
1468 MDefinition* input = getOperand(inputId);
1469 auto* ins = MGuardNumberToIntPtrIndex::New(alloc(), input, supportOOB);
1470 add(ins);
1471 return defineOperand(resultId, ins);
1474 bool WarpCacheIRTranspiler::emitCallInt32ToString(Int32OperandId inputId,
1475 StringOperandId resultId) {
1476 return emitToString(inputId, resultId);
1479 bool WarpCacheIRTranspiler::emitCallNumberToString(NumberOperandId inputId,
1480 StringOperandId resultId) {
1481 return emitToString(inputId, resultId);
1484 bool WarpCacheIRTranspiler::emitInt32ToStringWithBaseResult(
1485 Int32OperandId inputId, Int32OperandId baseId) {
1486 MDefinition* input = getOperand(inputId);
1487 MDefinition* base = getOperand(baseId);
1489 auto* guardedBase = MGuardInt32Range::New(alloc(), base, 2, 36);
1490 add(guardedBase);
1492 // Use lower-case characters by default.
1493 constexpr bool lower = true;
1495 auto* ins = MInt32ToStringWithBase::New(alloc(), input, guardedBase, lower);
1496 add(ins);
1498 pushResult(ins);
1499 return true;
1502 bool WarpCacheIRTranspiler::emitBooleanToString(BooleanOperandId inputId,
1503 StringOperandId resultId) {
1504 return emitToString(inputId, resultId);
1507 bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId,
1508 NumberOperandId resultId) {
1509 MDefinition* input = getOperand(inputId);
1511 auto* ins = MToDouble::New(alloc(), input);
1512 add(ins);
1514 return defineOperand(resultId, ins);
1517 bool WarpCacheIRTranspiler::emitStringToAtom(StringOperandId strId) {
1518 MDefinition* str = getOperand(strId);
1520 auto* ins = MToHashableString::New(alloc(), str);
1521 add(ins);
1523 setOperand(strId, ins);
1524 return true;
1527 bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
1528 MDefinition* val = getOperand(valId);
1529 MOZ_ASSERT(val->type() == MIRType::Int32);
1530 pushResult(val);
1531 return true;
1534 bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId) {
1535 MDefinition* val = getOperand(valId);
1536 MOZ_ASSERT(val->type() == MIRType::Double);
1537 pushResult(val);
1538 return true;
1541 bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId) {
1542 MDefinition* val = getOperand(valId);
1543 MOZ_ASSERT(val->type() == MIRType::BigInt);
1544 pushResult(val);
1545 return true;
1548 bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId) {
1549 MDefinition* obj = getOperand(objId);
1550 MOZ_ASSERT(obj->type() == MIRType::Object);
1551 pushResult(obj);
1552 return true;
1555 bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId) {
1556 MDefinition* str = getOperand(strId);
1557 MOZ_ASSERT(str->type() == MIRType::String);
1558 pushResult(str);
1559 return true;
1562 bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId) {
1563 MDefinition* sym = getOperand(symId);
1564 MOZ_ASSERT(sym->type() == MIRType::Symbol);
1565 pushResult(sym);
1566 return true;
1569 bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
1570 pushResult(constant(UndefinedValue()));
1571 return true;
1574 bool WarpCacheIRTranspiler::emitLoadBooleanResult(bool val) {
1575 pushResult(constant(BooleanValue(val)));
1576 return true;
1579 bool WarpCacheIRTranspiler::emitLoadInt32Constant(uint32_t valOffset,
1580 Int32OperandId resultId) {
1581 int32_t val = int32StubField(valOffset);
1582 auto* valConst = constant(Int32Value(val));
1583 return defineOperand(resultId, valConst);
1586 bool WarpCacheIRTranspiler::emitLoadDoubleConstant(uint32_t valOffset,
1587 NumberOperandId resultId) {
1588 double val = doubleStubField(valOffset);
1589 auto* valConst = constant(DoubleValue(val));
1590 return defineOperand(resultId, valConst);
1593 bool WarpCacheIRTranspiler::emitLoadBooleanConstant(bool val,
1594 BooleanOperandId resultId) {
1595 auto* valConst = constant(BooleanValue(val));
1596 return defineOperand(resultId, valConst);
1599 bool WarpCacheIRTranspiler::emitLoadUndefined(ValOperandId resultId) {
1600 auto* valConst = constant(UndefinedValue());
1601 return defineOperand(resultId, valConst);
1604 bool WarpCacheIRTranspiler::emitLoadConstantString(uint32_t strOffset,
1605 StringOperandId resultId) {
1606 JSString* val = stringStubField(strOffset);
1607 auto* valConst = constant(StringValue(val));
1608 return defineOperand(resultId, valConst);
1611 bool WarpCacheIRTranspiler::emitLoadConstantStringResult(uint32_t strOffset) {
1612 JSString* val = stringStubField(strOffset);
1613 auto* valConst = constant(StringValue(val));
1614 pushResult(valConst);
1615 return true;
1618 bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId) {
1619 MDefinition* obj = getOperand(objId);
1620 auto* typeOf = MTypeOf::New(alloc(), obj);
1621 add(typeOf);
1623 auto* ins = MTypeOfName::New(alloc(), typeOf);
1624 add(ins);
1625 pushResult(ins);
1626 return true;
1629 bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
1630 ObjOperandId objId, ObjOperandId resultId) {
1631 MDefinition* env = getOperand(objId);
1632 auto* ins = MEnclosingEnvironment::New(alloc(), env);
1633 add(ins);
1635 return defineOperand(resultId, ins);
1638 bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId,
1639 uint32_t objOffset) {
1640 MInstruction* ins = objectStubField(objOffset);
1642 return defineOperand(resultId, ins);
1645 bool WarpCacheIRTranspiler::emitLoadProtoObject(ObjOperandId resultId,
1646 uint32_t objOffset,
1647 ObjOperandId receiverObjId) {
1648 MInstruction* ins = objectStubField(objOffset);
1649 if (ins->isConstant()) {
1650 MDefinition* receiverObj = getOperand(receiverObjId);
1652 ins = MConstantProto::New(alloc(), ins, receiverObj->skipObjectGuards());
1653 add(ins);
1655 return defineOperand(resultId, ins);
1658 bool WarpCacheIRTranspiler::emitLoadProto(ObjOperandId objId,
1659 ObjOperandId resultId) {
1660 MDefinition* obj = getOperand(objId);
1662 auto* ins = MObjectStaticProto::New(alloc(), obj);
1663 add(ins);
1665 return defineOperand(resultId, ins);
1668 bool WarpCacheIRTranspiler::emitLoadInstanceOfObjectResult(
1669 ValOperandId lhsId, ObjOperandId protoId) {
1670 MDefinition* lhs = getOperand(lhsId);
1671 MDefinition* proto = getOperand(protoId);
1673 auto* instanceOf = MInstanceOf::New(alloc(), lhs, proto);
1674 addEffectful(instanceOf);
1676 pushResult(instanceOf);
1677 return resumeAfter(instanceOf);
1680 bool WarpCacheIRTranspiler::emitLoadValueTag(ValOperandId valId,
1681 ValueTagOperandId resultId) {
1682 MDefinition* val = getOperand(valId);
1684 auto* ins = MLoadValueTag::New(alloc(), val);
1685 add(ins);
1687 return defineOperand(resultId, ins);
1690 bool WarpCacheIRTranspiler::emitLoadDynamicSlotResult(ObjOperandId objId,
1691 uint32_t offsetOffset) {
1692 int32_t offset = int32StubField(offsetOffset);
1694 MDefinition* obj = getOperand(objId);
1695 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
1697 auto* slots = MSlots::New(alloc(), obj);
1698 add(slots);
1700 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
1701 add(load);
1703 pushResult(load);
1704 return true;
1707 bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId,
1708 ObjOperandId objId,
1709 uint32_t offsetOffset) {
1710 MDefinition* obj = getOperand(objId);
1712 size_t offset = int32StubField(offsetOffset);
1713 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1715 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1716 add(load);
1718 return defineOperand(resultId, load);
1721 bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId,
1722 uint32_t offsetOffset) {
1723 int32_t offset = int32StubField(offsetOffset);
1725 MDefinition* obj = getOperand(objId);
1726 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1728 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1729 add(load);
1731 pushResult(load);
1732 return true;
1735 bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId,
1736 uint32_t offsetOffset,
1737 ValueType type) {
1738 int32_t offset = int32StubField(offsetOffset);
1740 MDefinition* obj = getOperand(objId);
1741 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1743 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1744 load->setResultType(MIRTypeFromValueType(JSValueType(type)));
1745 add(load);
1747 pushResult(load);
1748 return true;
1751 bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
1752 ValOperandId valId) {
1753 MDefinition* val = getOperand(valId);
1755 auto* lexicalCheck = MLexicalCheck::New(alloc(), val);
1756 add(lexicalCheck);
1758 if (snapshot().bailoutInfo().failedLexicalCheck()) {
1759 lexicalCheck->setNotMovable();
1762 setOperand(valId, lexicalCheck);
1763 return true;
1766 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId) {
1767 MDefinition* obj = getOperand(objId);
1769 auto* elements = MElements::New(alloc(), obj);
1770 add(elements);
1772 auto* length = MArrayLength::New(alloc(), elements);
1773 add(length);
1775 pushResult(length);
1776 return true;
1779 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId,
1780 Int32OperandId resultId) {
1781 MDefinition* obj = getOperand(objId);
1783 auto* elements = MElements::New(alloc(), obj);
1784 add(elements);
1786 auto* length = MArrayLength::New(alloc(), elements);
1787 add(length);
1789 return defineOperand(resultId, length);
1792 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgResult(
1793 ObjOperandId objId, Int32OperandId indexId) {
1794 MDefinition* obj = getOperand(objId);
1795 MDefinition* index = getOperand(indexId);
1797 auto* load = MLoadArgumentsObjectArg::New(alloc(), obj, index);
1798 add(load);
1800 pushResult(load);
1801 return true;
1804 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgHoleResult(
1805 ObjOperandId objId, Int32OperandId indexId) {
1806 MDefinition* obj = getOperand(objId);
1807 MDefinition* index = getOperand(indexId);
1809 auto* load = MLoadArgumentsObjectArgHole::New(alloc(), obj, index);
1810 add(load);
1812 pushResult(load);
1813 return true;
1816 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgExistsResult(
1817 ObjOperandId objId, Int32OperandId indexId) {
1818 MDefinition* obj = getOperand(objId);
1819 MDefinition* index = getOperand(indexId);
1821 auto* ins = MInArgumentsObjectArg::New(alloc(), obj, index);
1822 add(ins);
1824 pushResult(ins);
1825 return true;
1828 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
1829 ObjOperandId objId) {
1830 MDefinition* obj = getOperand(objId);
1832 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1833 add(length);
1835 pushResult(length);
1836 return true;
1839 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
1840 ObjOperandId objId, Int32OperandId resultId) {
1841 MDefinition* obj = getOperand(objId);
1843 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1844 add(length);
1846 return defineOperand(resultId, length);
1849 bool WarpCacheIRTranspiler::emitLoadBoundFunctionNumArgs(
1850 ObjOperandId objId, Int32OperandId resultId) {
1851 MDefinition* obj = getOperand(objId);
1853 auto* numArgs = MBoundFunctionNumArgs::New(alloc(), obj);
1854 add(numArgs);
1856 return defineOperand(resultId, numArgs);
1859 bool WarpCacheIRTranspiler::emitLoadBoundFunctionTarget(ObjOperandId objId,
1860 ObjOperandId resultId) {
1861 MDefinition* obj = getOperand(objId);
1863 auto* target = MLoadFixedSlotAndUnbox::New(
1864 alloc(), obj, BoundFunctionObject::targetSlot(), MUnbox::Mode::Infallible,
1865 MIRType::Object);
1866 add(target);
1868 return defineOperand(resultId, target);
1871 bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
1872 ObjOperandId objId) {
1873 MDefinition* obj = getOperand(objId);
1875 auto* guard = MGuardBoundFunctionIsConstructor::New(alloc(), obj);
1876 add(guard);
1878 setOperand(objId, guard);
1879 return true;
1882 bool WarpCacheIRTranspiler::emitGuardObjectIdentity(ObjOperandId obj1Id,
1883 ObjOperandId obj2Id) {
1884 MDefinition* obj1 = getOperand(obj1Id);
1885 MDefinition* obj2 = getOperand(obj2Id);
1887 auto* guard = MGuardObjectIdentity::New(alloc(), obj1, obj2,
1888 /* bailOnEquality = */ false);
1889 add(guard);
1890 return true;
1893 bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
1894 ObjOperandId objId, uint32_t shapeOffset) {
1895 MDefinition* obj = getOperand(objId);
1896 Shape* shape = shapeStubField(shapeOffset);
1897 MOZ_ASSERT(shape);
1899 auto* array = MArrayFromArgumentsObject::New(alloc(), obj, shape);
1900 addEffectful(array);
1902 pushResult(array);
1903 return resumeAfter(array);
1906 bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId) {
1907 MDefinition* obj = getOperand(objId);
1909 auto* length = MFunctionLength::New(alloc(), obj);
1910 add(length);
1912 pushResult(length);
1913 return true;
1916 bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId) {
1917 MDefinition* obj = getOperand(objId);
1919 auto* name = MFunctionName::New(alloc(), obj);
1920 add(name);
1922 pushResult(name);
1923 return true;
1926 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
1927 ObjOperandId objId) {
1928 MDefinition* obj = getOperand(objId);
1930 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1931 add(length);
1933 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1934 add(lengthInt32);
1936 pushResult(lengthInt32);
1937 return true;
1940 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
1941 ObjOperandId objId) {
1942 MDefinition* obj = getOperand(objId);
1944 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1945 add(length);
1947 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1948 add(lengthDouble);
1950 pushResult(lengthDouble);
1951 return true;
1954 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthInt32Result(
1955 ObjOperandId objId) {
1956 MDefinition* obj = getOperand(objId);
1958 // Use a separate instruction for converting the length to Int32, so that we
1959 // can fold the MArrayBufferViewLength instruction with length instructions
1960 // added for bounds checks.
1962 auto* length = MArrayBufferViewLength::New(alloc(), obj);
1963 add(length);
1965 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1966 add(lengthInt32);
1968 pushResult(lengthInt32);
1969 return true;
1972 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
1973 ObjOperandId objId) {
1974 MDefinition* obj = getOperand(objId);
1976 auto* length = MArrayBufferViewLength::New(alloc(), obj);
1977 add(length);
1979 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1980 add(lengthDouble);
1982 pushResult(lengthDouble);
1983 return true;
1986 bool WarpCacheIRTranspiler::emitLoadStringLengthResult(StringOperandId strId) {
1987 MDefinition* str = getOperand(strId);
1989 auto* length = MStringLength::New(alloc(), str);
1990 add(length);
1992 pushResult(length);
1993 return true;
1996 MInstruction* WarpCacheIRTranspiler::addBoundsCheck(MDefinition* index,
1997 MDefinition* length) {
1998 MInstruction* check = MBoundsCheck::New(alloc(), index, length);
1999 add(check);
2001 if (snapshot().bailoutInfo().failedBoundsCheck()) {
2002 check->setNotMovable();
2005 if (JitOptions.spectreIndexMasking) {
2006 // Use a separate MIR instruction for the index masking. Doing this as
2007 // part of MBoundsCheck would be unsound because bounds checks can be
2008 // optimized or eliminated completely. Consider this:
2010 // for (var i = 0; i < x; i++)
2011 // res = arr[i];
2013 // If we can prove |x < arr.length|, we are able to eliminate the bounds
2014 // check, but we should not get rid of the index masking because the
2015 // |i < x| branch could still be mispredicted.
2017 // Using a separate instruction lets us eliminate the bounds check
2018 // without affecting the index masking.
2019 check = MSpectreMaskIndex::New(alloc(), check, length);
2020 add(check);
2023 return check;
2026 bool WarpCacheIRTranspiler::emitLoadDenseElementResult(ObjOperandId objId,
2027 Int32OperandId indexId) {
2028 MDefinition* obj = getOperand(objId);
2029 MDefinition* index = getOperand(indexId);
2031 auto* elements = MElements::New(alloc(), obj);
2032 add(elements);
2034 auto* length = MInitializedLength::New(alloc(), elements);
2035 add(length);
2037 index = addBoundsCheck(index, length);
2039 auto* load = MLoadElement::New(alloc(), elements, index);
2040 add(load);
2042 pushResult(load);
2043 return true;
2046 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleResult(
2047 ObjOperandId objId, Int32OperandId indexId) {
2048 MDefinition* obj = getOperand(objId);
2049 MDefinition* index = getOperand(indexId);
2051 auto* elements = MElements::New(alloc(), obj);
2052 add(elements);
2054 auto* length = MInitializedLength::New(alloc(), elements);
2055 add(length);
2057 auto* load = MLoadElementHole::New(alloc(), elements, index, length);
2058 add(load);
2060 pushResult(load);
2061 return true;
2064 bool WarpCacheIRTranspiler::emitCallGetSparseElementResult(
2065 ObjOperandId objId, Int32OperandId indexId) {
2066 MDefinition* obj = getOperand(objId);
2067 MDefinition* index = getOperand(indexId);
2069 auto* call = MCallGetSparseElement::New(alloc(), obj, index);
2070 addEffectful(call);
2072 pushResult(call);
2073 return resumeAfter(call);
2076 bool WarpCacheIRTranspiler::emitCallNativeGetElementResult(
2077 ObjOperandId objId, Int32OperandId indexId) {
2078 MDefinition* obj = getOperand(objId);
2079 MDefinition* index = getOperand(indexId);
2081 auto* call = MCallNativeGetElement::New(alloc(), obj, index);
2082 addEffectful(call);
2084 pushResult(call);
2085 return resumeAfter(call);
2088 bool WarpCacheIRTranspiler::emitCallNativeGetElementSuperResult(
2089 ObjOperandId objId, Int32OperandId indexId, ValOperandId receiverId) {
2090 MDefinition* obj = getOperand(objId);
2091 MDefinition* index = getOperand(indexId);
2092 MDefinition* receiver = getOperand(receiverId);
2094 auto* call = MCallNativeGetElementSuper::New(alloc(), obj, index, receiver);
2095 addEffectful(call);
2097 pushResult(call);
2098 return resumeAfter(call);
2101 bool WarpCacheIRTranspiler::emitLoadDenseElementExistsResult(
2102 ObjOperandId objId, Int32OperandId indexId) {
2103 MDefinition* obj = getOperand(objId);
2104 MDefinition* index = getOperand(indexId);
2106 // Get the elements vector.
2107 auto* elements = MElements::New(alloc(), obj);
2108 add(elements);
2110 auto* length = MInitializedLength::New(alloc(), elements);
2111 add(length);
2113 // Check if id < initLength.
2114 index = addBoundsCheck(index, length);
2116 // And check elem[id] is not a hole.
2117 auto* guard = MGuardElementNotHole::New(alloc(), elements, index);
2118 add(guard);
2120 pushResult(constant(BooleanValue(true)));
2121 return true;
2124 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleExistsResult(
2125 ObjOperandId objId, Int32OperandId indexId) {
2126 MDefinition* obj = getOperand(objId);
2127 MDefinition* index = getOperand(indexId);
2129 // Get the elements vector.
2130 auto* elements = MElements::New(alloc(), obj);
2131 add(elements);
2133 auto* length = MInitializedLength::New(alloc(), elements);
2134 add(length);
2136 // Check if id < initLength and elem[id] not a hole.
2137 auto* ins = MInArray::New(alloc(), elements, index, length);
2138 add(ins);
2140 pushResult(ins);
2141 return true;
2144 bool WarpCacheIRTranspiler::emitCallObjectHasSparseElementResult(
2145 ObjOperandId objId, Int32OperandId indexId) {
2146 MDefinition* obj = getOperand(objId);
2147 MDefinition* index = getOperand(indexId);
2149 auto* ins = MCallObjectHasSparseElement::New(alloc(), obj, index);
2150 add(ins);
2152 pushResult(ins);
2153 return true;
2156 MInstruction* WarpCacheIRTranspiler::emitTypedArrayLength(
2157 ArrayBufferViewKind viewKind, MDefinition* obj) {
2158 if (viewKind == ArrayBufferViewKind::FixedLength) {
2159 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2160 add(length);
2162 return length;
2165 // Bounds check doesn't require a memory barrier. See IsValidIntegerIndex
2166 // abstract operation which reads the underlying buffer byte length using
2167 // "unordered" memory order.
2168 auto barrier = MemoryBarrierRequirement::NotRequired;
2170 // Movable and removable because no memory barrier is needed.
2171 auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier);
2172 length->setMovable();
2173 length->setNotGuard();
2174 add(length);
2176 return length;
2179 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementExistsResult(
2180 ObjOperandId objId, IntPtrOperandId indexId, ArrayBufferViewKind viewKind) {
2181 MDefinition* obj = getOperand(objId);
2182 MDefinition* index = getOperand(indexId);
2184 auto* length = emitTypedArrayLength(viewKind, obj);
2186 // Unsigned comparison to catch negative indices.
2187 auto* ins = MCompare::New(alloc(), index, length, JSOp::Lt,
2188 MCompare::Compare_UIntPtr);
2189 add(ins);
2191 pushResult(ins);
2192 return true;
2195 static MIRType MIRTypeForArrayBufferViewRead(Scalar::Type arrayType,
2196 bool forceDoubleForUint32) {
2197 switch (arrayType) {
2198 case Scalar::Int8:
2199 case Scalar::Uint8:
2200 case Scalar::Uint8Clamped:
2201 case Scalar::Int16:
2202 case Scalar::Uint16:
2203 case Scalar::Int32:
2204 return MIRType::Int32;
2205 case Scalar::Uint32:
2206 return forceDoubleForUint32 ? MIRType::Double : MIRType::Int32;
2207 case Scalar::Float32:
2208 return MIRType::Float32;
2209 case Scalar::Float64:
2210 return MIRType::Double;
2211 case Scalar::BigInt64:
2212 case Scalar::BigUint64:
2213 return MIRType::BigInt;
2214 default:
2215 break;
2217 MOZ_CRASH("Unknown typed array type");
2220 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementResult(
2221 ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType,
2222 bool handleOOB, bool forceDoubleForUint32, ArrayBufferViewKind viewKind) {
2223 MDefinition* obj = getOperand(objId);
2224 MDefinition* index = getOperand(indexId);
2226 auto* length = emitTypedArrayLength(viewKind, obj);
2228 if (!handleOOB) {
2229 // MLoadTypedArrayElementHole does the bounds checking.
2230 index = addBoundsCheck(index, length);
2233 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2234 add(elements);
2236 if (handleOOB) {
2237 auto* load = MLoadTypedArrayElementHole::New(
2238 alloc(), elements, index, length, elementType, forceDoubleForUint32);
2239 add(load);
2241 pushResult(load);
2242 return true;
2245 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType);
2246 load->setResultType(
2247 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32));
2248 add(load);
2250 pushResult(load);
2251 return true;
2254 bool WarpCacheIRTranspiler::emitLinearizeForCharAccess(
2255 StringOperandId strId, Int32OperandId indexId, StringOperandId resultId) {
2256 MDefinition* str = getOperand(strId);
2257 MDefinition* index = getOperand(indexId);
2259 auto* ins = MLinearizeForCharAccess::New(alloc(), str, index);
2260 add(ins);
2262 return defineOperand(resultId, ins);
2265 bool WarpCacheIRTranspiler::emitLinearizeForCodePointAccess(
2266 StringOperandId strId, Int32OperandId indexId, StringOperandId resultId) {
2267 MDefinition* str = getOperand(strId);
2268 MDefinition* index = getOperand(indexId);
2270 auto* ins = MLinearizeForCodePointAccess::New(alloc(), str, index);
2271 add(ins);
2273 return defineOperand(resultId, ins);
2276 bool WarpCacheIRTranspiler::emitToRelativeStringIndex(Int32OperandId indexId,
2277 StringOperandId strId,
2278 Int32OperandId resultId) {
2279 MDefinition* str = getOperand(strId);
2280 MDefinition* index = getOperand(indexId);
2282 auto* length = MStringLength::New(alloc(), str);
2283 add(length);
2285 auto* ins = MToRelativeStringIndex::New(alloc(), index, length);
2286 add(ins);
2288 return defineOperand(resultId, ins);
2291 bool WarpCacheIRTranspiler::emitLoadStringCharResult(StringOperandId strId,
2292 Int32OperandId indexId,
2293 bool handleOOB) {
2294 MDefinition* str = getOperand(strId);
2295 MDefinition* index = getOperand(indexId);
2297 if (handleOOB) {
2298 auto* charCode = MCharCodeAtOrNegative::New(alloc(), str, index);
2299 add(charCode);
2301 auto* fromCharCode = MFromCharCodeEmptyIfNegative::New(alloc(), charCode);
2302 add(fromCharCode);
2304 pushResult(fromCharCode);
2305 return true;
2308 auto* length = MStringLength::New(alloc(), str);
2309 add(length);
2311 index = addBoundsCheck(index, length);
2313 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2314 add(charCode);
2316 auto* fromCharCode = MFromCharCode::New(alloc(), charCode);
2317 add(fromCharCode);
2319 pushResult(fromCharCode);
2320 return true;
2323 bool WarpCacheIRTranspiler::emitLoadStringAtResult(StringOperandId strId,
2324 Int32OperandId indexId,
2325 bool handleOOB) {
2326 MDefinition* str = getOperand(strId);
2327 MDefinition* index = getOperand(indexId);
2329 if (handleOOB) {
2330 auto* charCode = MCharCodeAtOrNegative::New(alloc(), str, index);
2331 add(charCode);
2333 auto* fromCharCode =
2334 MFromCharCodeUndefinedIfNegative::New(alloc(), charCode);
2335 add(fromCharCode);
2337 pushResult(fromCharCode);
2338 return true;
2341 auto* length = MStringLength::New(alloc(), str);
2342 add(length);
2344 index = addBoundsCheck(index, length);
2346 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2347 add(charCode);
2349 auto* fromCharCode = MFromCharCode::New(alloc(), charCode);
2350 add(fromCharCode);
2352 pushResult(fromCharCode);
2353 return true;
2356 bool WarpCacheIRTranspiler::emitLoadStringCharCodeResult(StringOperandId strId,
2357 Int32OperandId indexId,
2358 bool handleOOB) {
2359 MDefinition* str = getOperand(strId);
2360 MDefinition* index = getOperand(indexId);
2362 if (handleOOB) {
2363 auto* charCode = MCharCodeAtOrNegative::New(alloc(), str, index);
2364 add(charCode);
2366 auto* ins = MNegativeToNaN::New(alloc(), charCode);
2367 add(ins);
2369 pushResult(ins);
2370 return true;
2373 auto* length = MStringLength::New(alloc(), str);
2374 add(length);
2376 index = addBoundsCheck(index, length);
2378 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2379 add(charCode);
2381 pushResult(charCode);
2382 return true;
2385 bool WarpCacheIRTranspiler::emitLoadStringCodePointResult(
2386 StringOperandId strId, Int32OperandId indexId, bool handleOOB) {
2387 MDefinition* str = getOperand(strId);
2388 MDefinition* index = getOperand(indexId);
2390 if (handleOOB) {
2391 auto* codePoint = MCodePointAtOrNegative::New(alloc(), str, index);
2392 add(codePoint);
2394 auto* ins = MNegativeToUndefined::New(alloc(), codePoint);
2395 add(ins);
2397 pushResult(ins);
2398 return true;
2401 auto* length = MStringLength::New(alloc(), str);
2402 add(length);
2404 index = addBoundsCheck(index, length);
2406 auto* codePoint = MCodePointAt::New(alloc(), str, index);
2407 add(codePoint);
2409 pushResult(codePoint);
2410 return true;
2413 bool WarpCacheIRTranspiler::emitNewStringObjectResult(
2414 uint32_t templateObjectOffset, StringOperandId strId) {
2415 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
2416 MDefinition* string = getOperand(strId);
2418 auto* obj = MNewStringObject::New(alloc(), string, templateObj);
2419 addEffectful(obj);
2421 pushResult(obj);
2422 return resumeAfter(obj);
2425 bool WarpCacheIRTranspiler::emitStringFromCharCodeResult(
2426 Int32OperandId codeId) {
2427 MDefinition* code = getOperand(codeId);
2429 auto* fromCharCode = MFromCharCode::New(alloc(), code);
2430 add(fromCharCode);
2432 pushResult(fromCharCode);
2433 return true;
2436 bool WarpCacheIRTranspiler::emitStringFromCodePointResult(
2437 Int32OperandId codeId) {
2438 MDefinition* code = getOperand(codeId);
2440 auto* fromCodePoint = MFromCodePoint::New(alloc(), code);
2441 add(fromCodePoint);
2443 pushResult(fromCodePoint);
2444 return true;
2447 bool WarpCacheIRTranspiler::emitStringIncludesResult(
2448 StringOperandId strId, StringOperandId searchStrId) {
2449 MDefinition* str = getOperand(strId);
2450 MDefinition* searchStr = getOperand(searchStrId);
2452 auto* includes = MStringIncludes::New(alloc(), str, searchStr);
2453 add(includes);
2455 pushResult(includes);
2456 return true;
2459 bool WarpCacheIRTranspiler::emitStringIndexOfResult(
2460 StringOperandId strId, StringOperandId searchStrId) {
2461 MDefinition* str = getOperand(strId);
2462 MDefinition* searchStr = getOperand(searchStrId);
2464 auto* indexOf = MStringIndexOf::New(alloc(), str, searchStr);
2465 add(indexOf);
2467 pushResult(indexOf);
2468 return true;
2471 bool WarpCacheIRTranspiler::emitStringLastIndexOfResult(
2472 StringOperandId strId, StringOperandId searchStrId) {
2473 MDefinition* str = getOperand(strId);
2474 MDefinition* searchStr = getOperand(searchStrId);
2476 auto* lastIndexOf = MStringLastIndexOf::New(alloc(), str, searchStr);
2477 add(lastIndexOf);
2479 pushResult(lastIndexOf);
2480 return true;
2483 bool WarpCacheIRTranspiler::emitStringStartsWithResult(
2484 StringOperandId strId, StringOperandId searchStrId) {
2485 MDefinition* str = getOperand(strId);
2486 MDefinition* searchStr = getOperand(searchStrId);
2488 auto* startsWith = MStringStartsWith::New(alloc(), str, searchStr);
2489 add(startsWith);
2491 pushResult(startsWith);
2492 return true;
2495 bool WarpCacheIRTranspiler::emitStringEndsWithResult(
2496 StringOperandId strId, StringOperandId searchStrId) {
2497 MDefinition* str = getOperand(strId);
2498 MDefinition* searchStr = getOperand(searchStrId);
2500 auto* endsWith = MStringEndsWith::New(alloc(), str, searchStr);
2501 add(endsWith);
2503 pushResult(endsWith);
2504 return true;
2507 bool WarpCacheIRTranspiler::emitStringToLowerCaseResult(StringOperandId strId) {
2508 MDefinition* str = getOperand(strId);
2510 auto* convert =
2511 MStringConvertCase::New(alloc(), str, MStringConvertCase::LowerCase);
2512 add(convert);
2514 pushResult(convert);
2515 return true;
2518 bool WarpCacheIRTranspiler::emitStringToUpperCaseResult(StringOperandId strId) {
2519 MDefinition* str = getOperand(strId);
2521 auto* convert =
2522 MStringConvertCase::New(alloc(), str, MStringConvertCase::UpperCase);
2523 add(convert);
2525 pushResult(convert);
2526 return true;
2529 bool WarpCacheIRTranspiler::emitStringTrimResult(StringOperandId strId) {
2530 MDefinition* str = getOperand(strId);
2532 auto* linear = MLinearizeString::New(alloc(), str);
2533 add(linear);
2535 auto* start = MStringTrimStartIndex::New(alloc(), linear);
2536 add(start);
2538 auto* end = MStringTrimEndIndex::New(alloc(), linear, start);
2539 add(end);
2541 // Safe to truncate because both operands are positive and end >= start.
2542 auto* length = MSub::New(alloc(), end, start, MIRType::Int32);
2543 length->setTruncateKind(TruncateKind::Truncate);
2544 add(length);
2546 auto* substr = MSubstr::New(alloc(), linear, start, length);
2547 add(substr);
2549 pushResult(substr);
2550 return true;
2553 bool WarpCacheIRTranspiler::emitStringTrimStartResult(StringOperandId strId) {
2554 MDefinition* str = getOperand(strId);
2556 auto* linear = MLinearizeString::New(alloc(), str);
2557 add(linear);
2559 auto* start = MStringTrimStartIndex::New(alloc(), linear);
2560 add(start);
2562 auto* end = MStringLength::New(alloc(), linear);
2563 add(end);
2565 // Safe to truncate because both operands are positive and end >= start.
2566 auto* length = MSub::New(alloc(), end, start, MIRType::Int32);
2567 length->setTruncateKind(TruncateKind::Truncate);
2568 add(length);
2570 auto* substr = MSubstr::New(alloc(), linear, start, length);
2571 add(substr);
2573 pushResult(substr);
2574 return true;
2577 bool WarpCacheIRTranspiler::emitStringTrimEndResult(StringOperandId strId) {
2578 MDefinition* str = getOperand(strId);
2580 auto* linear = MLinearizeString::New(alloc(), str);
2581 add(linear);
2583 auto* start = constant(Int32Value(0));
2585 auto* length = MStringTrimEndIndex::New(alloc(), linear, start);
2586 add(length);
2588 auto* substr = MSubstr::New(alloc(), linear, start, length);
2589 add(substr);
2591 pushResult(substr);
2592 return true;
2595 bool WarpCacheIRTranspiler::emitStoreDynamicSlot(ObjOperandId objId,
2596 uint32_t offsetOffset,
2597 ValOperandId rhsId) {
2598 int32_t offset = int32StubField(offsetOffset);
2600 MDefinition* obj = getOperand(objId);
2601 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
2602 MDefinition* rhs = getOperand(rhsId);
2604 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2605 add(barrier);
2607 auto* slots = MSlots::New(alloc(), obj);
2608 add(slots);
2610 auto* store = MStoreDynamicSlot::NewBarriered(alloc(), slots, slotIndex, rhs);
2611 addEffectful(store);
2612 return resumeAfter(store);
2615 bool WarpCacheIRTranspiler::emitStoreFixedSlot(ObjOperandId objId,
2616 uint32_t offsetOffset,
2617 ValOperandId rhsId) {
2618 int32_t offset = int32StubField(offsetOffset);
2620 MDefinition* obj = getOperand(objId);
2621 size_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
2622 MDefinition* rhs = getOperand(rhsId);
2624 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2625 add(barrier);
2627 auto* store = MStoreFixedSlot::NewBarriered(alloc(), obj, slotIndex, rhs);
2628 addEffectful(store);
2629 return resumeAfter(store);
2632 bool WarpCacheIRTranspiler::emitStoreFixedSlotUndefinedResult(
2633 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId) {
2634 int32_t offset = int32StubField(offsetOffset);
2636 MDefinition* obj = getOperand(objId);
2637 size_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
2638 MDefinition* rhs = getOperand(rhsId);
2640 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2641 add(barrier);
2643 auto* store = MStoreFixedSlot::NewBarriered(alloc(), obj, slotIndex, rhs);
2644 addEffectful(store);
2646 auto* undef = constant(UndefinedValue());
2647 pushResult(undef);
2649 return resumeAfter(store);
2652 bool WarpCacheIRTranspiler::emitAddAndStoreSlotShared(
2653 MAddAndStoreSlot::Kind kind, ObjOperandId objId, uint32_t offsetOffset,
2654 ValOperandId rhsId, uint32_t newShapeOffset) {
2655 int32_t offset = int32StubField(offsetOffset);
2656 Shape* shape = shapeStubField(newShapeOffset);
2658 MDefinition* obj = getOperand(objId);
2659 MDefinition* rhs = getOperand(rhsId);
2661 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2662 add(barrier);
2664 auto* addAndStore =
2665 MAddAndStoreSlot::New(alloc(), obj, rhs, kind, offset, shape);
2666 addEffectful(addAndStore);
2668 return resumeAfter(addAndStore);
2671 bool WarpCacheIRTranspiler::emitAddAndStoreFixedSlot(ObjOperandId objId,
2672 uint32_t offsetOffset,
2673 ValOperandId rhsId,
2674 uint32_t newShapeOffset) {
2675 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::FixedSlot, objId,
2676 offsetOffset, rhsId, newShapeOffset);
2679 bool WarpCacheIRTranspiler::emitAddAndStoreDynamicSlot(
2680 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId,
2681 uint32_t newShapeOffset) {
2682 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::DynamicSlot, objId,
2683 offsetOffset, rhsId, newShapeOffset);
2686 bool WarpCacheIRTranspiler::emitAllocateAndStoreDynamicSlot(
2687 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId,
2688 uint32_t newShapeOffset, uint32_t numNewSlotsOffset) {
2689 int32_t offset = int32StubField(offsetOffset);
2690 Shape* shape = shapeStubField(newShapeOffset);
2691 uint32_t numNewSlots = uint32StubField(numNewSlotsOffset);
2693 MDefinition* obj = getOperand(objId);
2694 MDefinition* rhs = getOperand(rhsId);
2696 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2697 add(barrier);
2699 auto* allocateAndStore =
2700 MAllocateAndStoreSlot::New(alloc(), obj, rhs, offset, shape, numNewSlots);
2701 addEffectful(allocateAndStore);
2703 return resumeAfter(allocateAndStore);
2706 bool WarpCacheIRTranspiler::emitAddSlotAndCallAddPropHook(
2707 ObjOperandId objId, ValOperandId rhsId, uint32_t newShapeOffset) {
2708 Shape* shape = shapeStubField(newShapeOffset);
2709 MDefinition* obj = getOperand(objId);
2710 MDefinition* rhs = getOperand(rhsId);
2712 auto* addProp = MAddSlotAndCallAddPropHook::New(alloc(), obj, rhs, shape);
2713 addEffectful(addProp);
2715 return resumeAfter(addProp);
2718 bool WarpCacheIRTranspiler::emitStoreDenseElement(ObjOperandId objId,
2719 Int32OperandId indexId,
2720 ValOperandId rhsId) {
2721 MDefinition* obj = getOperand(objId);
2722 MDefinition* index = getOperand(indexId);
2723 MDefinition* rhs = getOperand(rhsId);
2725 auto* elements = MElements::New(alloc(), obj);
2726 add(elements);
2728 auto* length = MInitializedLength::New(alloc(), elements);
2729 add(length);
2731 index = addBoundsCheck(index, length);
2733 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2734 add(barrier);
2736 bool needsHoleCheck = true;
2737 auto* store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2738 needsHoleCheck);
2739 addEffectful(store);
2740 return resumeAfter(store);
2743 bool WarpCacheIRTranspiler::emitStoreDenseElementHole(ObjOperandId objId,
2744 Int32OperandId indexId,
2745 ValOperandId rhsId,
2746 bool handleAdd) {
2747 MDefinition* obj = getOperand(objId);
2748 MDefinition* index = getOperand(indexId);
2749 MDefinition* rhs = getOperand(rhsId);
2751 auto* elements = MElements::New(alloc(), obj);
2752 add(elements);
2754 MInstruction* store;
2755 if (handleAdd) {
2756 // TODO(post-Warp): Consider changing MStoreElementHole to match IC code.
2757 store = MStoreElementHole::New(alloc(), obj, elements, index, rhs);
2758 } else {
2759 auto* length = MInitializedLength::New(alloc(), elements);
2760 add(length);
2762 index = addBoundsCheck(index, length);
2764 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2765 add(barrier);
2767 bool needsHoleCheck = false;
2768 store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2769 needsHoleCheck);
2771 addEffectful(store);
2773 return resumeAfter(store);
2776 bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(
2777 ObjOperandId objId, Scalar::Type elementType, IntPtrOperandId indexId,
2778 uint32_t rhsId, bool handleOOB, ArrayBufferViewKind viewKind) {
2779 MDefinition* obj = getOperand(objId);
2780 MDefinition* index = getOperand(indexId);
2781 MDefinition* rhs = getOperand(ValOperandId(rhsId));
2783 auto* length = emitTypedArrayLength(viewKind, obj);
2785 if (!handleOOB) {
2786 // MStoreTypedArrayElementHole does the bounds checking.
2787 index = addBoundsCheck(index, length);
2790 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2791 add(elements);
2793 MInstruction* store;
2794 if (handleOOB) {
2795 store = MStoreTypedArrayElementHole::New(alloc(), elements, length, index,
2796 rhs, elementType);
2797 } else {
2798 store =
2799 MStoreUnboxedScalar::New(alloc(), elements, index, rhs, elementType);
2801 addEffectful(store);
2802 return resumeAfter(store);
2805 MInstruction* WarpCacheIRTranspiler::emitDataViewLength(
2806 ArrayBufferViewKind viewKind, MDefinition* obj) {
2807 if (viewKind == ArrayBufferViewKind::FixedLength) {
2808 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2809 add(length);
2811 return length;
2814 // Bounds check doesn't require a memory barrier. See GetViewValue and
2815 // SetViewValue abstract operations which read the underlying buffer byte
2816 // length using "unordered" memory order.
2817 auto barrier = MemoryBarrierRequirement::NotRequired;
2819 // Movable and removable because no memory barrier is needed.
2820 auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier);
2821 length->setMovable();
2822 length->setNotGuard();
2823 add(length);
2825 return length;
2828 void WarpCacheIRTranspiler::addDataViewData(ArrayBufferViewKind viewKind,
2829 MDefinition* obj, Scalar::Type type,
2830 MDefinition** offset,
2831 MInstruction** elements) {
2832 auto* length = emitDataViewLength(viewKind, obj);
2834 // Adjust the length to account for accesses near the end of the dataview.
2835 if (size_t byteSize = Scalar::byteSize(type); byteSize > 1) {
2836 // To ensure |0 <= offset && offset + byteSize <= length|, first adjust the
2837 // length by subtracting |byteSize - 1| (bailing out if that becomes
2838 // negative).
2839 length = MAdjustDataViewLength::New(alloc(), length, byteSize);
2840 add(length);
2843 *offset = addBoundsCheck(*offset, length);
2845 *elements = MArrayBufferViewElements::New(alloc(), obj);
2846 add(*elements);
2849 bool WarpCacheIRTranspiler::emitLoadDataViewValueResult(
2850 ObjOperandId objId, IntPtrOperandId offsetId,
2851 BooleanOperandId littleEndianId, Scalar::Type elementType,
2852 bool forceDoubleForUint32, ArrayBufferViewKind viewKind) {
2853 MDefinition* obj = getOperand(objId);
2854 MDefinition* offset = getOperand(offsetId);
2855 MDefinition* littleEndian = getOperand(littleEndianId);
2857 // Add bounds check and get the DataViewObject's elements.
2858 MInstruction* elements;
2859 addDataViewData(viewKind, obj, elementType, &offset, &elements);
2861 // Load the element.
2862 MInstruction* load;
2863 if (Scalar::byteSize(elementType) == 1) {
2864 load = MLoadUnboxedScalar::New(alloc(), elements, offset, elementType);
2865 } else {
2866 load = MLoadDataViewElement::New(alloc(), elements, offset, littleEndian,
2867 elementType);
2869 add(load);
2871 MIRType knownType =
2872 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
2873 load->setResultType(knownType);
2875 pushResult(load);
2876 return true;
2879 bool WarpCacheIRTranspiler::emitStoreDataViewValueResult(
2880 ObjOperandId objId, IntPtrOperandId offsetId, uint32_t valueId,
2881 BooleanOperandId littleEndianId, Scalar::Type elementType,
2882 ArrayBufferViewKind viewKind) {
2883 MDefinition* obj = getOperand(objId);
2884 MDefinition* offset = getOperand(offsetId);
2885 MDefinition* value = getOperand(ValOperandId(valueId));
2886 MDefinition* littleEndian = getOperand(littleEndianId);
2888 // Add bounds check and get the DataViewObject's elements.
2889 MInstruction* elements;
2890 addDataViewData(viewKind, obj, elementType, &offset, &elements);
2892 // Store the element.
2893 MInstruction* store;
2894 if (Scalar::byteSize(elementType) == 1) {
2895 store =
2896 MStoreUnboxedScalar::New(alloc(), elements, offset, value, elementType);
2897 } else {
2898 store = MStoreDataViewElement::New(alloc(), elements, offset, value,
2899 littleEndian, elementType);
2901 addEffectful(store);
2903 pushResult(constant(UndefinedValue()));
2905 return resumeAfter(store);
2908 bool WarpCacheIRTranspiler::emitInt32IncResult(Int32OperandId inputId) {
2909 MDefinition* input = getOperand(inputId);
2911 auto* constOne = MConstant::New(alloc(), Int32Value(1));
2912 add(constOne);
2914 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Int32);
2915 add(ins);
2917 pushResult(ins);
2918 return true;
2921 bool WarpCacheIRTranspiler::emitDoubleIncResult(NumberOperandId inputId) {
2922 MDefinition* input = getOperand(inputId);
2924 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2925 add(constOne);
2927 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Double);
2928 add(ins);
2930 pushResult(ins);
2931 return true;
2934 bool WarpCacheIRTranspiler::emitInt32DecResult(Int32OperandId inputId) {
2935 MDefinition* input = getOperand(inputId);
2937 auto* constOne = MConstant::New(alloc(), Int32Value(1));
2938 add(constOne);
2940 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Int32);
2941 add(ins);
2943 pushResult(ins);
2944 return true;
2947 bool WarpCacheIRTranspiler::emitDoubleDecResult(NumberOperandId inputId) {
2948 MDefinition* input = getOperand(inputId);
2950 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2951 add(constOne);
2953 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Double);
2954 add(ins);
2956 pushResult(ins);
2957 return true;
2960 bool WarpCacheIRTranspiler::emitInt32NegationResult(Int32OperandId inputId) {
2961 MDefinition* input = getOperand(inputId);
2963 auto* constNegOne = MConstant::New(alloc(), Int32Value(-1));
2964 add(constNegOne);
2966 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Int32);
2967 add(ins);
2969 pushResult(ins);
2970 return true;
2973 bool WarpCacheIRTranspiler::emitDoubleNegationResult(NumberOperandId inputId) {
2974 MDefinition* input = getOperand(inputId);
2976 auto* constNegOne = MConstant::New(alloc(), DoubleValue(-1.0));
2977 add(constNegOne);
2979 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Double);
2980 add(ins);
2982 pushResult(ins);
2983 return true;
2986 bool WarpCacheIRTranspiler::emitInt32NotResult(Int32OperandId inputId) {
2987 MDefinition* input = getOperand(inputId);
2989 auto* ins = MBitNot::New(alloc(), input);
2990 add(ins);
2992 pushResult(ins);
2993 return true;
2996 template <typename T>
2997 bool WarpCacheIRTranspiler::emitDoubleBinaryArithResult(NumberOperandId lhsId,
2998 NumberOperandId rhsId) {
2999 MDefinition* lhs = getOperand(lhsId);
3000 MDefinition* rhs = getOperand(rhsId);
3002 auto* ins = T::New(alloc(), lhs, rhs, MIRType::Double);
3003 add(ins);
3005 pushResult(ins);
3006 return true;
3009 bool WarpCacheIRTranspiler::emitDoubleAddResult(NumberOperandId lhsId,
3010 NumberOperandId rhsId) {
3011 return emitDoubleBinaryArithResult<MAdd>(lhsId, rhsId);
3014 bool WarpCacheIRTranspiler::emitDoubleSubResult(NumberOperandId lhsId,
3015 NumberOperandId rhsId) {
3016 return emitDoubleBinaryArithResult<MSub>(lhsId, rhsId);
3019 bool WarpCacheIRTranspiler::emitDoubleMulResult(NumberOperandId lhsId,
3020 NumberOperandId rhsId) {
3021 return emitDoubleBinaryArithResult<MMul>(lhsId, rhsId);
3024 bool WarpCacheIRTranspiler::emitDoubleDivResult(NumberOperandId lhsId,
3025 NumberOperandId rhsId) {
3026 return emitDoubleBinaryArithResult<MDiv>(lhsId, rhsId);
3029 bool WarpCacheIRTranspiler::emitDoubleModResult(NumberOperandId lhsId,
3030 NumberOperandId rhsId) {
3031 return emitDoubleBinaryArithResult<MMod>(lhsId, rhsId);
3034 bool WarpCacheIRTranspiler::emitDoublePowResult(NumberOperandId lhsId,
3035 NumberOperandId rhsId) {
3036 return emitDoubleBinaryArithResult<MPow>(lhsId, rhsId);
3039 template <typename T>
3040 bool WarpCacheIRTranspiler::emitInt32BinaryArithResult(Int32OperandId lhsId,
3041 Int32OperandId rhsId) {
3042 MDefinition* lhs = getOperand(lhsId);
3043 MDefinition* rhs = getOperand(rhsId);
3045 auto* ins = T::New(alloc(), lhs, rhs, MIRType::Int32);
3046 add(ins);
3048 pushResult(ins);
3049 return true;
3052 bool WarpCacheIRTranspiler::emitInt32AddResult(Int32OperandId lhsId,
3053 Int32OperandId rhsId) {
3054 return emitInt32BinaryArithResult<MAdd>(lhsId, rhsId);
3057 bool WarpCacheIRTranspiler::emitInt32SubResult(Int32OperandId lhsId,
3058 Int32OperandId rhsId) {
3059 return emitInt32BinaryArithResult<MSub>(lhsId, rhsId);
3062 bool WarpCacheIRTranspiler::emitInt32MulResult(Int32OperandId lhsId,
3063 Int32OperandId rhsId) {
3064 return emitInt32BinaryArithResult<MMul>(lhsId, rhsId);
3067 bool WarpCacheIRTranspiler::emitInt32DivResult(Int32OperandId lhsId,
3068 Int32OperandId rhsId) {
3069 return emitInt32BinaryArithResult<MDiv>(lhsId, rhsId);
3072 bool WarpCacheIRTranspiler::emitInt32ModResult(Int32OperandId lhsId,
3073 Int32OperandId rhsId) {
3074 return emitInt32BinaryArithResult<MMod>(lhsId, rhsId);
3077 bool WarpCacheIRTranspiler::emitInt32PowResult(Int32OperandId lhsId,
3078 Int32OperandId rhsId) {
3079 return emitInt32BinaryArithResult<MPow>(lhsId, rhsId);
3082 bool WarpCacheIRTranspiler::emitInt32BitOrResult(Int32OperandId lhsId,
3083 Int32OperandId rhsId) {
3084 return emitInt32BinaryArithResult<MBitOr>(lhsId, rhsId);
3087 bool WarpCacheIRTranspiler::emitInt32BitXorResult(Int32OperandId lhsId,
3088 Int32OperandId rhsId) {
3089 return emitInt32BinaryArithResult<MBitXor>(lhsId, rhsId);
3092 bool WarpCacheIRTranspiler::emitInt32BitAndResult(Int32OperandId lhsId,
3093 Int32OperandId rhsId) {
3094 return emitInt32BinaryArithResult<MBitAnd>(lhsId, rhsId);
3097 bool WarpCacheIRTranspiler::emitInt32LeftShiftResult(Int32OperandId lhsId,
3098 Int32OperandId rhsId) {
3099 return emitInt32BinaryArithResult<MLsh>(lhsId, rhsId);
3102 bool WarpCacheIRTranspiler::emitInt32RightShiftResult(Int32OperandId lhsId,
3103 Int32OperandId rhsId) {
3104 return emitInt32BinaryArithResult<MRsh>(lhsId, rhsId);
3107 bool WarpCacheIRTranspiler::emitInt32URightShiftResult(Int32OperandId lhsId,
3108 Int32OperandId rhsId,
3109 bool forceDouble) {
3110 MDefinition* lhs = getOperand(lhsId);
3111 MDefinition* rhs = getOperand(rhsId);
3113 MIRType specialization = forceDouble ? MIRType::Double : MIRType::Int32;
3114 auto* ins = MUrsh::New(alloc(), lhs, rhs, specialization);
3115 add(ins);
3117 pushResult(ins);
3118 return true;
3121 template <typename T>
3122 bool WarpCacheIRTranspiler::emitBigIntBinaryArithResult(BigIntOperandId lhsId,
3123 BigIntOperandId rhsId) {
3124 MDefinition* lhs = getOperand(lhsId);
3125 MDefinition* rhs = getOperand(rhsId);
3127 auto* ins = T::New(alloc(), lhs, rhs);
3128 add(ins);
3130 pushResult(ins);
3131 return true;
3134 bool WarpCacheIRTranspiler::emitBigIntAddResult(BigIntOperandId lhsId,
3135 BigIntOperandId rhsId) {
3136 return emitBigIntBinaryArithResult<MBigIntAdd>(lhsId, rhsId);
3139 bool WarpCacheIRTranspiler::emitBigIntSubResult(BigIntOperandId lhsId,
3140 BigIntOperandId rhsId) {
3141 return emitBigIntBinaryArithResult<MBigIntSub>(lhsId, rhsId);
3144 bool WarpCacheIRTranspiler::emitBigIntMulResult(BigIntOperandId lhsId,
3145 BigIntOperandId rhsId) {
3146 return emitBigIntBinaryArithResult<MBigIntMul>(lhsId, rhsId);
3149 template <typename T>
3150 bool WarpCacheIRTranspiler::emitBigIntBinaryArithEffectfulResult(
3151 BigIntOperandId lhsId, BigIntOperandId rhsId) {
3152 MDefinition* lhs = getOperand(lhsId);
3153 MDefinition* rhs = getOperand(rhsId);
3155 auto* ins = T::New(alloc(), lhs, rhs);
3157 if (ins->isEffectful()) {
3158 addEffectful(ins);
3160 pushResult(ins);
3161 return resumeAfter(ins);
3164 add(ins);
3166 pushResult(ins);
3167 return true;
3170 bool WarpCacheIRTranspiler::emitBigIntDivResult(BigIntOperandId lhsId,
3171 BigIntOperandId rhsId) {
3172 return emitBigIntBinaryArithEffectfulResult<MBigIntDiv>(lhsId, rhsId);
3175 bool WarpCacheIRTranspiler::emitBigIntModResult(BigIntOperandId lhsId,
3176 BigIntOperandId rhsId) {
3177 return emitBigIntBinaryArithEffectfulResult<MBigIntMod>(lhsId, rhsId);
3180 bool WarpCacheIRTranspiler::emitBigIntPowResult(BigIntOperandId lhsId,
3181 BigIntOperandId rhsId) {
3182 return emitBigIntBinaryArithEffectfulResult<MBigIntPow>(lhsId, rhsId);
3185 bool WarpCacheIRTranspiler::emitBigIntBitAndResult(BigIntOperandId lhsId,
3186 BigIntOperandId rhsId) {
3187 return emitBigIntBinaryArithResult<MBigIntBitAnd>(lhsId, rhsId);
3190 bool WarpCacheIRTranspiler::emitBigIntBitOrResult(BigIntOperandId lhsId,
3191 BigIntOperandId rhsId) {
3192 return emitBigIntBinaryArithResult<MBigIntBitOr>(lhsId, rhsId);
3195 bool WarpCacheIRTranspiler::emitBigIntBitXorResult(BigIntOperandId lhsId,
3196 BigIntOperandId rhsId) {
3197 return emitBigIntBinaryArithResult<MBigIntBitXor>(lhsId, rhsId);
3200 bool WarpCacheIRTranspiler::emitBigIntLeftShiftResult(BigIntOperandId lhsId,
3201 BigIntOperandId rhsId) {
3202 return emitBigIntBinaryArithResult<MBigIntLsh>(lhsId, rhsId);
3205 bool WarpCacheIRTranspiler::emitBigIntRightShiftResult(BigIntOperandId lhsId,
3206 BigIntOperandId rhsId) {
3207 return emitBigIntBinaryArithResult<MBigIntRsh>(lhsId, rhsId);
3210 template <typename T>
3211 bool WarpCacheIRTranspiler::emitBigIntUnaryArithResult(
3212 BigIntOperandId inputId) {
3213 MDefinition* input = getOperand(inputId);
3215 auto* ins = T::New(alloc(), input);
3216 add(ins);
3218 pushResult(ins);
3219 return true;
3222 bool WarpCacheIRTranspiler::emitBigIntIncResult(BigIntOperandId inputId) {
3223 return emitBigIntUnaryArithResult<MBigIntIncrement>(inputId);
3226 bool WarpCacheIRTranspiler::emitBigIntDecResult(BigIntOperandId inputId) {
3227 return emitBigIntUnaryArithResult<MBigIntDecrement>(inputId);
3230 bool WarpCacheIRTranspiler::emitBigIntNegationResult(BigIntOperandId inputId) {
3231 return emitBigIntUnaryArithResult<MBigIntNegate>(inputId);
3234 bool WarpCacheIRTranspiler::emitBigIntNotResult(BigIntOperandId inputId) {
3235 return emitBigIntUnaryArithResult<MBigIntBitNot>(inputId);
3238 bool WarpCacheIRTranspiler::emitCallStringConcatResult(StringOperandId lhsId,
3239 StringOperandId rhsId) {
3240 MDefinition* lhs = getOperand(lhsId);
3241 MDefinition* rhs = getOperand(rhsId);
3243 auto* ins = MConcat::New(alloc(), lhs, rhs);
3244 add(ins);
3246 pushResult(ins);
3247 return true;
3250 bool WarpCacheIRTranspiler::emitCompareResult(
3251 JSOp op, OperandId lhsId, OperandId rhsId,
3252 MCompare::CompareType compareType) {
3253 MDefinition* lhs = getOperand(lhsId);
3254 MDefinition* rhs = getOperand(rhsId);
3256 auto* ins = MCompare::New(alloc(), lhs, rhs, op, compareType);
3257 add(ins);
3259 pushResult(ins);
3260 return true;
3263 bool WarpCacheIRTranspiler::emitCompareInt32Result(JSOp op,
3264 Int32OperandId lhsId,
3265 Int32OperandId rhsId) {
3266 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Int32);
3269 bool WarpCacheIRTranspiler::emitCompareDoubleResult(JSOp op,
3270 NumberOperandId lhsId,
3271 NumberOperandId rhsId) {
3272 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Double);
3275 bool WarpCacheIRTranspiler::emitCompareObjectResult(JSOp op, ObjOperandId lhsId,
3276 ObjOperandId rhsId) {
3277 MOZ_ASSERT(IsEqualityOp(op));
3278 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Object);
3281 bool WarpCacheIRTranspiler::emitCompareStringResult(JSOp op,
3282 StringOperandId lhsId,
3283 StringOperandId rhsId) {
3284 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_String);
3287 bool WarpCacheIRTranspiler::emitCompareSymbolResult(JSOp op,
3288 SymbolOperandId lhsId,
3289 SymbolOperandId rhsId) {
3290 MOZ_ASSERT(IsEqualityOp(op));
3291 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Symbol);
3294 bool WarpCacheIRTranspiler::emitCompareBigIntResult(JSOp op,
3295 BigIntOperandId lhsId,
3296 BigIntOperandId rhsId) {
3297 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt);
3300 bool WarpCacheIRTranspiler::emitCompareBigIntInt32Result(JSOp op,
3301 BigIntOperandId lhsId,
3302 Int32OperandId rhsId) {
3303 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_Int32);
3306 bool WarpCacheIRTranspiler::emitCompareBigIntNumberResult(
3307 JSOp op, BigIntOperandId lhsId, NumberOperandId rhsId) {
3308 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_Double);
3311 bool WarpCacheIRTranspiler::emitCompareBigIntStringResult(
3312 JSOp op, BigIntOperandId lhsId, StringOperandId rhsId) {
3313 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_String);
3316 bool WarpCacheIRTranspiler::emitCompareNullUndefinedResult(
3317 JSOp op, bool isUndefined, ValOperandId inputId) {
3318 MDefinition* input = getOperand(inputId);
3320 MOZ_ASSERT(IsEqualityOp(op));
3322 // A previously emitted guard ensures that one side of the comparison
3323 // is null or undefined.
3324 MDefinition* cst =
3325 isUndefined ? constant(UndefinedValue()) : constant(NullValue());
3326 auto compareType =
3327 isUndefined ? MCompare::Compare_Undefined : MCompare::Compare_Null;
3328 auto* ins = MCompare::New(alloc(), input, cst, op, compareType);
3329 add(ins);
3331 pushResult(ins);
3332 return true;
3335 bool WarpCacheIRTranspiler::emitCompareDoubleSameValueResult(
3336 NumberOperandId lhsId, NumberOperandId rhsId) {
3337 MDefinition* lhs = getOperand(lhsId);
3338 MDefinition* rhs = getOperand(rhsId);
3340 auto* sameValue = MSameValueDouble::New(alloc(), lhs, rhs);
3341 add(sameValue);
3343 pushResult(sameValue);
3344 return true;
3347 bool WarpCacheIRTranspiler::emitSameValueResult(ValOperandId lhsId,
3348 ValOperandId rhsId) {
3349 MDefinition* lhs = getOperand(lhsId);
3350 MDefinition* rhs = getOperand(rhsId);
3352 auto* sameValue = MSameValue::New(alloc(), lhs, rhs);
3353 add(sameValue);
3355 pushResult(sameValue);
3356 return true;
3359 bool WarpCacheIRTranspiler::emitIndirectTruncateInt32Result(
3360 Int32OperandId valId) {
3361 MDefinition* val = getOperand(valId);
3362 MOZ_ASSERT(val->type() == MIRType::Int32);
3364 auto* truncate =
3365 MLimitedTruncate::New(alloc(), val, TruncateKind::IndirectTruncate);
3366 add(truncate);
3368 pushResult(truncate);
3369 return true;
3372 bool WarpCacheIRTranspiler::emitMathHypot2NumberResult(
3373 NumberOperandId firstId, NumberOperandId secondId) {
3374 MDefinitionVector vector(alloc());
3375 if (!vector.reserve(2)) {
3376 return false;
3379 vector.infallibleAppend(getOperand(firstId));
3380 vector.infallibleAppend(getOperand(secondId));
3382 auto* ins = MHypot::New(alloc(), vector);
3383 if (!ins) {
3384 return false;
3386 add(ins);
3388 pushResult(ins);
3389 return true;
3392 bool WarpCacheIRTranspiler::emitMathHypot3NumberResult(
3393 NumberOperandId firstId, NumberOperandId secondId,
3394 NumberOperandId thirdId) {
3395 MDefinitionVector vector(alloc());
3396 if (!vector.reserve(3)) {
3397 return false;
3400 vector.infallibleAppend(getOperand(firstId));
3401 vector.infallibleAppend(getOperand(secondId));
3402 vector.infallibleAppend(getOperand(thirdId));
3404 auto* ins = MHypot::New(alloc(), vector);
3405 if (!ins) {
3406 return false;
3408 add(ins);
3410 pushResult(ins);
3411 return true;
3414 bool WarpCacheIRTranspiler::emitMathHypot4NumberResult(
3415 NumberOperandId firstId, NumberOperandId secondId, NumberOperandId thirdId,
3416 NumberOperandId fourthId) {
3417 MDefinitionVector vector(alloc());
3418 if (!vector.reserve(4)) {
3419 return false;
3422 vector.infallibleAppend(getOperand(firstId));
3423 vector.infallibleAppend(getOperand(secondId));
3424 vector.infallibleAppend(getOperand(thirdId));
3425 vector.infallibleAppend(getOperand(fourthId));
3427 auto* ins = MHypot::New(alloc(), vector);
3428 if (!ins) {
3429 return false;
3431 add(ins);
3433 pushResult(ins);
3434 return true;
3437 bool WarpCacheIRTranspiler::emitMathRandomResult(uint32_t rngOffset) {
3438 #ifdef DEBUG
3439 // CodeGenerator uses CompileRealm::addressOfRandomNumberGenerator. Assert it
3440 // matches the RNG pointer stored in the stub field.
3441 const void* rng = rawPointerField(rngOffset);
3442 MOZ_ASSERT(rng == mirGen().realm->addressOfRandomNumberGenerator());
3443 #endif
3445 auto* ins = MRandom::New(alloc());
3446 addEffectful(ins);
3448 pushResult(ins);
3449 return resumeAfter(ins);
3452 bool WarpCacheIRTranspiler::emitInt32MinMax(bool isMax, Int32OperandId firstId,
3453 Int32OperandId secondId,
3454 Int32OperandId resultId) {
3455 MDefinition* first = getOperand(firstId);
3456 MDefinition* second = getOperand(secondId);
3458 auto* ins = MMinMax::New(alloc(), first, second, MIRType::Int32, isMax);
3459 add(ins);
3461 return defineOperand(resultId, ins);
3464 bool WarpCacheIRTranspiler::emitNumberMinMax(bool isMax,
3465 NumberOperandId firstId,
3466 NumberOperandId secondId,
3467 NumberOperandId resultId) {
3468 MDefinition* first = getOperand(firstId);
3469 MDefinition* second = getOperand(secondId);
3471 auto* ins = MMinMax::New(alloc(), first, second, MIRType::Double, isMax);
3472 add(ins);
3474 return defineOperand(resultId, ins);
3477 bool WarpCacheIRTranspiler::emitInt32MinMaxArrayResult(ObjOperandId arrayId,
3478 bool isMax) {
3479 MDefinition* array = getOperand(arrayId);
3481 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Int32, isMax);
3482 add(ins);
3484 pushResult(ins);
3485 return true;
3488 bool WarpCacheIRTranspiler::emitNumberMinMaxArrayResult(ObjOperandId arrayId,
3489 bool isMax) {
3490 MDefinition* array = getOperand(arrayId);
3492 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Double, isMax);
3493 add(ins);
3495 pushResult(ins);
3496 return true;
3499 bool WarpCacheIRTranspiler::emitMathAbsInt32Result(Int32OperandId inputId) {
3500 MDefinition* input = getOperand(inputId);
3502 auto* ins = MAbs::New(alloc(), input, MIRType::Int32);
3503 add(ins);
3505 pushResult(ins);
3506 return true;
3509 bool WarpCacheIRTranspiler::emitMathAbsNumberResult(NumberOperandId inputId) {
3510 MDefinition* input = getOperand(inputId);
3512 auto* ins = MAbs::New(alloc(), input, MIRType::Double);
3513 add(ins);
3515 pushResult(ins);
3516 return true;
3519 bool WarpCacheIRTranspiler::emitMathClz32Result(Int32OperandId inputId) {
3520 MDefinition* input = getOperand(inputId);
3522 auto* ins = MClz::New(alloc(), input, MIRType::Int32);
3523 add(ins);
3525 pushResult(ins);
3526 return true;
3529 bool WarpCacheIRTranspiler::emitMathSignInt32Result(Int32OperandId inputId) {
3530 MDefinition* input = getOperand(inputId);
3532 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3533 add(ins);
3535 pushResult(ins);
3536 return true;
3539 bool WarpCacheIRTranspiler::emitMathSignNumberResult(NumberOperandId inputId) {
3540 MDefinition* input = getOperand(inputId);
3542 auto* ins = MSign::New(alloc(), input, MIRType::Double);
3543 add(ins);
3545 pushResult(ins);
3546 return true;
3549 bool WarpCacheIRTranspiler::emitMathSignNumberToInt32Result(
3550 NumberOperandId inputId) {
3551 MDefinition* input = getOperand(inputId);
3553 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3554 add(ins);
3556 pushResult(ins);
3557 return true;
3560 bool WarpCacheIRTranspiler::emitMathImulResult(Int32OperandId lhsId,
3561 Int32OperandId rhsId) {
3562 MDefinition* lhs = getOperand(lhsId);
3563 MDefinition* rhs = getOperand(rhsId);
3565 auto* ins = MMul::New(alloc(), lhs, rhs, MIRType::Int32, MMul::Integer);
3566 add(ins);
3568 pushResult(ins);
3569 return true;
3572 bool WarpCacheIRTranspiler::emitMathFloorToInt32Result(
3573 NumberOperandId inputId) {
3574 MDefinition* input = getOperand(inputId);
3576 auto* ins = MFloor::New(alloc(), input);
3577 add(ins);
3579 pushResult(ins);
3580 return true;
3583 bool WarpCacheIRTranspiler::emitMathCeilToInt32Result(NumberOperandId inputId) {
3584 MDefinition* input = getOperand(inputId);
3586 auto* ins = MCeil::New(alloc(), input);
3587 add(ins);
3589 pushResult(ins);
3590 return true;
3593 bool WarpCacheIRTranspiler::emitMathTruncToInt32Result(
3594 NumberOperandId inputId) {
3595 MDefinition* input = getOperand(inputId);
3597 auto* ins = MTrunc::New(alloc(), input);
3598 add(ins);
3600 pushResult(ins);
3601 return true;
3604 bool WarpCacheIRTranspiler::emitMathRoundToInt32Result(
3605 NumberOperandId inputId) {
3606 MDefinition* input = getOperand(inputId);
3608 auto* ins = MRound::New(alloc(), input);
3609 add(ins);
3611 pushResult(ins);
3612 return true;
3615 bool WarpCacheIRTranspiler::emitMathSqrtNumberResult(NumberOperandId inputId) {
3616 MDefinition* input = getOperand(inputId);
3618 auto* ins = MSqrt::New(alloc(), input, MIRType::Double);
3619 add(ins);
3621 pushResult(ins);
3622 return true;
3625 bool WarpCacheIRTranspiler::emitMathFRoundNumberResult(
3626 NumberOperandId inputId) {
3627 MDefinition* input = getOperand(inputId);
3629 auto* ins = MToFloat32::New(alloc(), input);
3630 add(ins);
3632 pushResult(ins);
3633 return true;
3636 bool WarpCacheIRTranspiler::emitMathAtan2NumberResult(NumberOperandId yId,
3637 NumberOperandId xId) {
3638 MDefinition* y = getOperand(yId);
3639 MDefinition* x = getOperand(xId);
3641 auto* ins = MAtan2::New(alloc(), y, x);
3642 add(ins);
3644 pushResult(ins);
3645 return true;
3648 bool WarpCacheIRTranspiler::emitMathFunctionNumberResult(
3649 NumberOperandId inputId, UnaryMathFunction fun) {
3650 MDefinition* input = getOperand(inputId);
3652 auto* ins = MMathFunction::New(alloc(), input, fun);
3653 add(ins);
3655 pushResult(ins);
3656 return true;
3659 bool WarpCacheIRTranspiler::emitMathFloorNumberResult(NumberOperandId inputId) {
3660 MDefinition* input = getOperand(inputId);
3662 MInstruction* ins;
3663 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
3664 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Down);
3665 } else {
3666 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Floor);
3668 add(ins);
3670 pushResult(ins);
3671 return true;
3674 bool WarpCacheIRTranspiler::emitMathCeilNumberResult(NumberOperandId inputId) {
3675 MDefinition* input = getOperand(inputId);
3677 MInstruction* ins;
3678 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
3679 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Up);
3680 } else {
3681 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Ceil);
3683 add(ins);
3685 pushResult(ins);
3686 return true;
3689 bool WarpCacheIRTranspiler::emitMathTruncNumberResult(NumberOperandId inputId) {
3690 MDefinition* input = getOperand(inputId);
3692 MInstruction* ins;
3693 if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
3694 ins = MNearbyInt::New(alloc(), input, MIRType::Double,
3695 RoundingMode::TowardsZero);
3696 } else {
3697 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Trunc);
3699 add(ins);
3701 pushResult(ins);
3702 return true;
3705 bool WarpCacheIRTranspiler::emitNumberParseIntResult(StringOperandId strId,
3706 Int32OperandId radixId) {
3707 MDefinition* str = getOperand(strId);
3708 MDefinition* radix = getOperand(radixId);
3710 auto* ins = MNumberParseInt::New(alloc(), str, radix);
3711 add(ins);
3713 pushResult(ins);
3714 return true;
3717 bool WarpCacheIRTranspiler::emitDoubleParseIntResult(NumberOperandId numId) {
3718 MDefinition* num = getOperand(numId);
3720 auto* ins = MDoubleParseInt::New(alloc(), num);
3721 add(ins);
3723 pushResult(ins);
3724 return true;
3727 bool WarpCacheIRTranspiler::emitObjectToStringResult(ObjOperandId objId) {
3728 MDefinition* obj = getOperand(objId);
3730 auto* ins = MObjectClassToString::New(alloc(), obj);
3731 add(ins);
3733 pushResult(ins);
3734 return true;
3737 bool WarpCacheIRTranspiler::emitReflectGetPrototypeOfResult(
3738 ObjOperandId objId) {
3739 MDefinition* obj = getOperand(objId);
3741 auto* ins = MGetPrototypeOf::New(alloc(), obj);
3742 addEffectful(ins);
3743 pushResult(ins);
3745 return resumeAfter(ins);
3748 bool WarpCacheIRTranspiler::emitArrayPush(ObjOperandId objId,
3749 ValOperandId rhsId) {
3750 MDefinition* obj = getOperand(objId);
3751 MDefinition* value = getOperand(rhsId);
3753 auto* ins = MArrayPush::New(alloc(), obj, value);
3754 addEffectful(ins);
3755 pushResult(ins);
3757 return resumeAfter(ins);
3760 bool WarpCacheIRTranspiler::emitArrayJoinResult(ObjOperandId objId,
3761 StringOperandId sepId) {
3762 MDefinition* obj = getOperand(objId);
3763 MDefinition* sep = getOperand(sepId);
3765 auto* join = MArrayJoin::New(alloc(), obj, sep);
3766 addEffectful(join);
3768 pushResult(join);
3769 return resumeAfter(join);
3772 bool WarpCacheIRTranspiler::emitObjectKeysResult(ObjOperandId objId) {
3773 MDefinition* obj = getOperand(objId);
3775 auto* join = MObjectKeys::New(alloc(), obj);
3776 addEffectful(join);
3778 pushResult(join);
3779 return resumeAfter(join);
3782 bool WarpCacheIRTranspiler::emitPackedArrayPopResult(ObjOperandId arrayId) {
3783 MDefinition* array = getOperand(arrayId);
3785 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Pop);
3786 addEffectful(ins);
3788 pushResult(ins);
3789 return resumeAfter(ins);
3792 bool WarpCacheIRTranspiler::emitPackedArrayShiftResult(ObjOperandId arrayId) {
3793 MDefinition* array = getOperand(arrayId);
3795 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Shift);
3796 addEffectful(ins);
3798 pushResult(ins);
3799 return resumeAfter(ins);
3802 bool WarpCacheIRTranspiler::emitPackedArraySliceResult(
3803 uint32_t templateObjectOffset, ObjOperandId arrayId, Int32OperandId beginId,
3804 Int32OperandId endId) {
3805 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3807 MDefinition* array = getOperand(arrayId);
3808 MDefinition* begin = getOperand(beginId);
3809 MDefinition* end = getOperand(endId);
3811 // TODO: support pre-tenuring.
3812 gc::Heap heap = gc::Heap::Default;
3814 auto* ins = MArraySlice::New(alloc(), array, begin, end, templateObj, heap);
3815 addEffectful(ins);
3817 pushResult(ins);
3818 return resumeAfter(ins);
3821 bool WarpCacheIRTranspiler::emitArgumentsSliceResult(
3822 uint32_t templateObjectOffset, ObjOperandId argsId, Int32OperandId beginId,
3823 Int32OperandId endId) {
3824 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3826 MDefinition* args = getOperand(argsId);
3827 MDefinition* begin = getOperand(beginId);
3828 MDefinition* end = getOperand(endId);
3830 // TODO: support pre-tenuring.
3831 gc::Heap heap = gc::Heap::Default;
3833 auto* ins =
3834 MArgumentsSlice::New(alloc(), args, begin, end, templateObj, heap);
3835 addEffectful(ins);
3837 pushResult(ins);
3838 return resumeAfter(ins);
3841 bool WarpCacheIRTranspiler::emitHasClassResult(ObjOperandId objId,
3842 uint32_t claspOffset) {
3843 MDefinition* obj = getOperand(objId);
3844 const JSClass* clasp = classStubField(claspOffset);
3846 auto* hasClass = MHasClass::New(alloc(), obj, clasp);
3847 add(hasClass);
3849 pushResult(hasClass);
3850 return true;
3853 bool WarpCacheIRTranspiler::emitCallRegExpMatcherResult(
3854 ObjOperandId regexpId, StringOperandId inputId, Int32OperandId lastIndexId,
3855 uint32_t stubOffset) {
3856 MDefinition* regexp = getOperand(regexpId);
3857 MDefinition* input = getOperand(inputId);
3858 MDefinition* lastIndex = getOperand(lastIndexId);
3860 auto* matcher = MRegExpMatcher::New(alloc(), regexp, input, lastIndex);
3861 addEffectful(matcher);
3862 pushResult(matcher);
3864 return resumeAfter(matcher);
3867 bool WarpCacheIRTranspiler::emitCallRegExpSearcherResult(
3868 ObjOperandId regexpId, StringOperandId inputId, Int32OperandId lastIndexId,
3869 uint32_t stubOffset) {
3870 MDefinition* regexp = getOperand(regexpId);
3871 MDefinition* input = getOperand(inputId);
3872 MDefinition* lastIndex = getOperand(lastIndexId);
3874 auto* searcher = MRegExpSearcher::New(alloc(), regexp, input, lastIndex);
3875 addEffectful(searcher);
3876 pushResult(searcher);
3878 return resumeAfter(searcher);
3881 bool WarpCacheIRTranspiler::emitRegExpSearcherLastLimitResult() {
3882 auto* limit = MRegExpSearcherLastLimit::New(alloc());
3883 addEffectful(limit);
3884 pushResult(limit);
3886 return resumeAfter(limit);
3889 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecMatchResult(
3890 ObjOperandId regexpId, StringOperandId inputId, uint32_t stubOffset) {
3891 MDefinition* regexp = getOperand(regexpId);
3892 MDefinition* input = getOperand(inputId);
3894 auto* ins = MRegExpExecMatch::New(alloc(), regexp, input);
3895 addEffectful(ins);
3896 pushResult(ins);
3898 return resumeAfter(ins);
3901 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecTestResult(
3902 ObjOperandId regexpId, StringOperandId inputId, uint32_t stubOffset) {
3903 MDefinition* regexp = getOperand(regexpId);
3904 MDefinition* input = getOperand(inputId);
3906 auto* ins = MRegExpExecTest::New(alloc(), regexp, input);
3907 addEffectful(ins);
3908 pushResult(ins);
3910 return resumeAfter(ins);
3913 bool WarpCacheIRTranspiler::emitRegExpHasCaptureGroupsResult(
3914 ObjOperandId regexpId, StringOperandId inputId) {
3915 MDefinition* regexp = getOperand(regexpId);
3916 MDefinition* input = getOperand(inputId);
3918 auto* result = MRegExpHasCaptureGroups::New(alloc(), regexp, input);
3919 addEffectful(result);
3920 pushResult(result);
3922 return resumeAfter(result);
3925 MInstruction* WarpCacheIRTranspiler::convertToBoolean(MDefinition* input) {
3926 // Convert to bool with the '!!' idiom.
3928 // The FoldTests and GVN passes both specifically handle this pattern. If you
3929 // change this code, make sure to update FoldTests and GVN, too.
3931 auto* resultInverted = MNot::New(alloc(), input);
3932 add(resultInverted);
3933 auto* result = MNot::New(alloc(), resultInverted);
3934 add(result);
3936 return result;
3939 bool WarpCacheIRTranspiler::emitRegExpFlagResult(ObjOperandId regexpId,
3940 int32_t flagsMask) {
3941 MDefinition* regexp = getOperand(regexpId);
3943 auto* flags = MLoadFixedSlot::New(alloc(), regexp, RegExpObject::flagsSlot());
3944 flags->setResultType(MIRType::Int32);
3945 add(flags);
3947 auto* mask = MConstant::New(alloc(), Int32Value(flagsMask));
3948 add(mask);
3950 auto* maskedFlag = MBitAnd::New(alloc(), flags, mask, MIRType::Int32);
3951 add(maskedFlag);
3953 auto* result = convertToBoolean(maskedFlag);
3955 pushResult(result);
3956 return true;
3959 bool WarpCacheIRTranspiler::emitCallSubstringKernelResult(
3960 StringOperandId strId, Int32OperandId beginId, Int32OperandId lengthId) {
3961 MDefinition* str = getOperand(strId);
3962 MDefinition* begin = getOperand(beginId);
3963 MDefinition* length = getOperand(lengthId);
3965 auto* substr = MSubstr::New(alloc(), str, begin, length);
3966 add(substr);
3968 pushResult(substr);
3969 return true;
3972 bool WarpCacheIRTranspiler::emitStringReplaceStringResult(
3973 StringOperandId strId, StringOperandId patternId,
3974 StringOperandId replacementId) {
3975 MDefinition* str = getOperand(strId);
3976 MDefinition* pattern = getOperand(patternId);
3977 MDefinition* replacement = getOperand(replacementId);
3979 auto* replace = MStringReplace::New(alloc(), str, pattern, replacement);
3980 add(replace);
3982 pushResult(replace);
3983 return true;
3986 bool WarpCacheIRTranspiler::emitStringSplitStringResult(
3987 StringOperandId strId, StringOperandId separatorId) {
3988 MDefinition* str = getOperand(strId);
3989 MDefinition* separator = getOperand(separatorId);
3991 auto* split = MStringSplit::New(alloc(), str, separator);
3992 add(split);
3994 pushResult(split);
3995 return true;
3998 bool WarpCacheIRTranspiler::emitRegExpPrototypeOptimizableResult(
3999 ObjOperandId protoId) {
4000 MDefinition* proto = getOperand(protoId);
4002 auto* optimizable = MRegExpPrototypeOptimizable::New(alloc(), proto);
4003 add(optimizable);
4005 pushResult(optimizable);
4006 return true;
4009 bool WarpCacheIRTranspiler::emitRegExpInstanceOptimizableResult(
4010 ObjOperandId regexpId, ObjOperandId protoId) {
4011 MDefinition* regexp = getOperand(regexpId);
4012 MDefinition* proto = getOperand(protoId);
4014 auto* optimizable = MRegExpInstanceOptimizable::New(alloc(), regexp, proto);
4015 add(optimizable);
4017 pushResult(optimizable);
4018 return true;
4021 bool WarpCacheIRTranspiler::emitGetFirstDollarIndexResult(
4022 StringOperandId strId) {
4023 MDefinition* str = getOperand(strId);
4025 auto* firstDollarIndex = MGetFirstDollarIndex::New(alloc(), str);
4026 add(firstDollarIndex);
4028 pushResult(firstDollarIndex);
4029 return true;
4032 bool WarpCacheIRTranspiler::emitIsArrayResult(ValOperandId inputId) {
4033 MDefinition* value = getOperand(inputId);
4035 auto* isArray = MIsArray::New(alloc(), value);
4036 addEffectful(isArray);
4037 pushResult(isArray);
4039 return resumeAfter(isArray);
4042 bool WarpCacheIRTranspiler::emitIsObjectResult(ValOperandId inputId) {
4043 MDefinition* value = getOperand(inputId);
4045 if (value->type() == MIRType::Object) {
4046 pushResult(constant(BooleanValue(true)));
4047 } else {
4048 auto* isObject = MIsObject::New(alloc(), value);
4049 add(isObject);
4050 pushResult(isObject);
4053 return true;
4056 bool WarpCacheIRTranspiler::emitIsPackedArrayResult(ObjOperandId objId) {
4057 MDefinition* obj = getOperand(objId);
4059 auto* isPackedArray = MIsPackedArray::New(alloc(), obj);
4060 add(isPackedArray);
4062 pushResult(isPackedArray);
4063 return true;
4066 bool WarpCacheIRTranspiler::emitIsCallableResult(ValOperandId inputId) {
4067 MDefinition* value = getOperand(inputId);
4069 auto* isCallable = MIsCallable::New(alloc(), value);
4070 add(isCallable);
4072 pushResult(isCallable);
4073 return true;
4076 bool WarpCacheIRTranspiler::emitIsConstructorResult(ObjOperandId objId) {
4077 MDefinition* obj = getOperand(objId);
4079 auto* isConstructor = MIsConstructor::New(alloc(), obj);
4080 add(isConstructor);
4082 pushResult(isConstructor);
4083 return true;
4086 bool WarpCacheIRTranspiler::emitIsCrossRealmArrayConstructorResult(
4087 ObjOperandId objId) {
4088 MDefinition* obj = getOperand(objId);
4090 auto* ins = MIsCrossRealmArrayConstructor::New(alloc(), obj);
4091 add(ins);
4093 pushResult(ins);
4094 return true;
4097 bool WarpCacheIRTranspiler::emitIsTypedArrayResult(ObjOperandId objId,
4098 bool isPossiblyWrapped) {
4099 MDefinition* obj = getOperand(objId);
4101 auto* ins = MIsTypedArray::New(alloc(), obj, isPossiblyWrapped);
4102 if (isPossiblyWrapped) {
4103 addEffectful(ins);
4104 } else {
4105 add(ins);
4108 pushResult(ins);
4110 if (isPossiblyWrapped) {
4111 if (!resumeAfter(ins)) {
4112 return false;
4116 return true;
4119 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetInt32Result(
4120 ObjOperandId objId) {
4121 MDefinition* obj = getOperand(objId);
4123 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
4124 add(byteOffset);
4126 auto* byteOffsetInt32 = MNonNegativeIntPtrToInt32::New(alloc(), byteOffset);
4127 add(byteOffsetInt32);
4129 pushResult(byteOffsetInt32);
4130 return true;
4133 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetDoubleResult(
4134 ObjOperandId objId) {
4135 MDefinition* obj = getOperand(objId);
4137 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
4138 add(byteOffset);
4140 auto* byteOffsetDouble = MIntPtrToDouble::New(alloc(), byteOffset);
4141 add(byteOffsetDouble);
4143 pushResult(byteOffsetDouble);
4144 return true;
4147 bool WarpCacheIRTranspiler::
4148 emitResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result(
4149 ObjOperandId objId) {
4150 MDefinition* obj = getOperand(objId);
4152 auto* byteOffset =
4153 MResizableTypedArrayByteOffsetMaybeOutOfBounds::New(alloc(), obj);
4154 add(byteOffset);
4156 auto* byteOffsetInt32 = MNonNegativeIntPtrToInt32::New(alloc(), byteOffset);
4157 add(byteOffsetInt32);
4159 pushResult(byteOffsetInt32);
4160 return true;
4163 bool WarpCacheIRTranspiler::
4164 emitResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult(
4165 ObjOperandId objId) {
4166 MDefinition* obj = getOperand(objId);
4168 auto* byteOffset =
4169 MResizableTypedArrayByteOffsetMaybeOutOfBounds::New(alloc(), obj);
4170 add(byteOffset);
4172 auto* byteOffsetDouble = MIntPtrToDouble::New(alloc(), byteOffset);
4173 add(byteOffsetDouble);
4175 pushResult(byteOffsetDouble);
4176 return true;
4179 bool WarpCacheIRTranspiler::emitResizableTypedArrayLengthInt32Result(
4180 ObjOperandId objId) {
4181 MDefinition* obj = getOperand(objId);
4183 // Explicit |length| accesses are seq-consistent atomic loads.
4184 auto barrier = MemoryBarrierRequirement::Required;
4186 auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier);
4187 addEffectful(length);
4189 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
4190 add(lengthInt32);
4192 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32);
4193 add(postConversion);
4195 pushResult(postConversion);
4196 return resumeAfterUnchecked(postConversion);
4199 bool WarpCacheIRTranspiler::emitResizableTypedArrayLengthDoubleResult(
4200 ObjOperandId objId) {
4201 MDefinition* obj = getOperand(objId);
4203 // Explicit |length| accesses are seq-consistent atomic loads.
4204 auto barrier = MemoryBarrierRequirement::Required;
4206 auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier);
4207 addEffectful(length);
4209 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4210 add(lengthDouble);
4212 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble);
4213 add(postConversion);
4215 pushResult(postConversion);
4216 return resumeAfterUnchecked(postConversion);
4219 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthInt32Result(
4220 ObjOperandId objId) {
4221 MDefinition* obj = getOperand(objId);
4223 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4224 add(length);
4226 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
4227 add(lengthInt32);
4229 auto* size = MTypedArrayElementSize::New(alloc(), obj);
4230 add(size);
4232 auto* mul = MMul::New(alloc(), lengthInt32, size, MIRType::Int32);
4233 mul->setCanBeNegativeZero(false);
4234 add(mul);
4236 pushResult(mul);
4237 return true;
4240 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthDoubleResult(
4241 ObjOperandId objId) {
4242 MDefinition* obj = getOperand(objId);
4244 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4245 add(length);
4247 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4248 add(lengthDouble);
4250 auto* size = MTypedArrayElementSize::New(alloc(), obj);
4251 add(size);
4253 auto* sizeDouble = MToDouble::New(alloc(), size);
4254 add(sizeDouble);
4256 auto* mul = MMul::New(alloc(), lengthDouble, sizeDouble, MIRType::Double);
4257 mul->setCanBeNegativeZero(false);
4258 add(mul);
4260 pushResult(mul);
4261 return true;
4264 bool WarpCacheIRTranspiler::emitResizableTypedArrayByteLengthInt32Result(
4265 ObjOperandId objId) {
4266 MDefinition* obj = getOperand(objId);
4268 // Explicit |byteLength| accesses are seq-consistent atomic loads.
4269 auto barrier = MemoryBarrierRequirement::Required;
4271 auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier);
4272 addEffectful(length);
4274 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
4275 add(lengthInt32);
4277 auto* size = MTypedArrayElementSize::New(alloc(), obj);
4278 add(size);
4280 auto* mul = MMul::New(alloc(), lengthInt32, size, MIRType::Int32);
4281 mul->setCanBeNegativeZero(false);
4282 add(mul);
4284 auto* postConversion = MPostIntPtrConversion::New(alloc(), mul);
4285 add(postConversion);
4287 pushResult(postConversion);
4288 return resumeAfterUnchecked(postConversion);
4291 bool WarpCacheIRTranspiler::emitResizableTypedArrayByteLengthDoubleResult(
4292 ObjOperandId objId) {
4293 MDefinition* obj = getOperand(objId);
4295 // Explicit |byteLength| accesses are seq-consistent atomic loads.
4296 auto barrier = MemoryBarrierRequirement::Required;
4298 auto* length = MResizableTypedArrayLength::New(alloc(), obj, barrier);
4299 addEffectful(length);
4301 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4302 add(lengthDouble);
4304 auto* size = MTypedArrayElementSize::New(alloc(), obj);
4305 add(size);
4307 auto* sizeDouble = MToDouble::New(alloc(), size);
4308 add(sizeDouble);
4310 auto* mul = MMul::New(alloc(), lengthDouble, sizeDouble, MIRType::Double);
4311 mul->setCanBeNegativeZero(false);
4312 add(mul);
4314 auto* postConversion = MPostIntPtrConversion::New(alloc(), mul);
4315 add(postConversion);
4317 pushResult(postConversion);
4318 return resumeAfterUnchecked(postConversion);
4321 bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult(
4322 ObjOperandId objId) {
4323 MDefinition* obj = getOperand(objId);
4325 auto* ins = MTypedArrayElementSize::New(alloc(), obj);
4326 add(ins);
4328 pushResult(ins);
4329 return true;
4332 bool WarpCacheIRTranspiler::emitResizableDataViewByteLengthInt32Result(
4333 ObjOperandId objId) {
4334 MDefinition* obj = getOperand(objId);
4336 // Explicit |byteLength| accesses are seq-consistent atomic loads.
4337 auto barrier = MemoryBarrierRequirement::Required;
4339 auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier);
4340 addEffectful(length);
4342 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
4343 add(lengthInt32);
4345 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32);
4346 add(postConversion);
4348 pushResult(postConversion);
4349 return resumeAfterUnchecked(postConversion);
4352 bool WarpCacheIRTranspiler::emitResizableDataViewByteLengthDoubleResult(
4353 ObjOperandId objId) {
4354 MDefinition* obj = getOperand(objId);
4356 // Explicit |byteLength| accesses are seq-consistent atomic loads.
4357 auto barrier = MemoryBarrierRequirement::Required;
4359 auto* length = MResizableDataViewByteLength::New(alloc(), obj, barrier);
4360 addEffectful(length);
4362 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4363 add(lengthDouble);
4365 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble);
4366 add(postConversion);
4368 pushResult(postConversion);
4369 return resumeAfterUnchecked(postConversion);
4372 bool WarpCacheIRTranspiler::emitGrowableSharedArrayBufferByteLengthInt32Result(
4373 ObjOperandId objId) {
4374 MDefinition* obj = getOperand(objId);
4376 auto* length = MGrowableSharedArrayBufferByteLength::New(alloc(), obj);
4377 addEffectful(length);
4379 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
4380 add(lengthInt32);
4382 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthInt32);
4383 add(postConversion);
4385 pushResult(postConversion);
4386 return resumeAfterUnchecked(postConversion);
4389 bool WarpCacheIRTranspiler::emitGrowableSharedArrayBufferByteLengthDoubleResult(
4390 ObjOperandId objId) {
4391 MDefinition* obj = getOperand(objId);
4393 auto* length = MGrowableSharedArrayBufferByteLength::New(alloc(), obj);
4394 addEffectful(length);
4396 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
4397 add(lengthDouble);
4399 auto* postConversion = MPostIntPtrConversion::New(alloc(), lengthDouble);
4400 add(postConversion);
4402 pushResult(postConversion);
4403 return resumeAfterUnchecked(postConversion);
4406 bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer(
4407 ObjOperandId objId) {
4408 MDefinition* obj = getOperand(objId);
4410 auto* ins = MGuardHasAttachedArrayBuffer::New(alloc(), obj);
4411 add(ins);
4413 setOperand(objId, ins);
4414 return true;
4417 bool WarpCacheIRTranspiler::emitGuardResizableArrayBufferViewInBounds(
4418 ObjOperandId objId) {
4419 MDefinition* obj = getOperand(objId);
4421 auto* ins = MGuardResizableArrayBufferViewInBounds::New(alloc(), obj);
4422 add(ins);
4424 setOperand(objId, ins);
4425 return true;
4428 bool WarpCacheIRTranspiler::emitGuardResizableArrayBufferViewInBoundsOrDetached(
4429 ObjOperandId objId) {
4430 MDefinition* obj = getOperand(objId);
4432 auto* ins =
4433 MGuardResizableArrayBufferViewInBoundsOrDetached::New(alloc(), obj);
4434 add(ins);
4436 setOperand(objId, ins);
4437 return true;
4440 bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult(
4441 ObjOperandId objId) {
4442 MDefinition* obj = getOperand(objId);
4444 auto* ins = MIsTypedArrayConstructor::New(alloc(), obj);
4445 add(ins);
4447 pushResult(ins);
4448 return true;
4451 bool WarpCacheIRTranspiler::emitGetNextMapSetEntryForIteratorResult(
4452 ObjOperandId iterId, ObjOperandId resultArrId, bool isMap) {
4453 MDefinition* iter = getOperand(iterId);
4454 MDefinition* resultArr = getOperand(resultArrId);
4456 MGetNextEntryForIterator::Mode mode =
4457 isMap ? MGetNextEntryForIterator::Map : MGetNextEntryForIterator::Set;
4458 auto* ins = MGetNextEntryForIterator::New(alloc(), iter, resultArr, mode);
4459 addEffectful(ins);
4460 pushResult(ins);
4462 return resumeAfter(ins);
4465 bool WarpCacheIRTranspiler::emitFrameIsConstructingResult() {
4466 if (const CallInfo* callInfo = builder_->inlineCallInfo()) {
4467 auto* ins = constant(BooleanValue(callInfo->constructing()));
4468 pushResult(ins);
4469 return true;
4472 auto* ins = MIsConstructing::New(alloc());
4473 add(ins);
4474 pushResult(ins);
4475 return true;
4478 bool WarpCacheIRTranspiler::emitNewIteratorResult(
4479 MNewIterator::Type type, uint32_t templateObjectOffset) {
4480 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4482 auto* templateConst = constant(ObjectValue(*templateObj));
4483 auto* iter = MNewIterator::New(alloc(), templateConst, type);
4484 add(iter);
4486 pushResult(iter);
4487 return true;
4490 bool WarpCacheIRTranspiler::emitNewArrayIteratorResult(
4491 uint32_t templateObjectOffset) {
4492 return emitNewIteratorResult(MNewIterator::ArrayIterator,
4493 templateObjectOffset);
4496 bool WarpCacheIRTranspiler::emitNewStringIteratorResult(
4497 uint32_t templateObjectOffset) {
4498 return emitNewIteratorResult(MNewIterator::StringIterator,
4499 templateObjectOffset);
4502 bool WarpCacheIRTranspiler::emitNewRegExpStringIteratorResult(
4503 uint32_t templateObjectOffset) {
4504 return emitNewIteratorResult(MNewIterator::RegExpStringIterator,
4505 templateObjectOffset);
4508 bool WarpCacheIRTranspiler::emitObjectCreateResult(
4509 uint32_t templateObjectOffset) {
4510 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4512 auto* templateConst = constant(ObjectValue(*templateObj));
4514 // TODO: support pre-tenuring.
4515 gc::Heap heap = gc::Heap::Default;
4516 auto* obj =
4517 MNewObject::New(alloc(), templateConst, heap, MNewObject::ObjectCreate);
4518 addEffectful(obj);
4520 pushResult(obj);
4521 return resumeAfter(obj);
4524 bool WarpCacheIRTranspiler::emitNewArrayFromLengthResult(
4525 uint32_t templateObjectOffset, Int32OperandId lengthId) {
4526 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4527 MDefinition* length = getOperand(lengthId);
4529 // TODO: support pre-tenuring.
4530 gc::Heap heap = gc::Heap::Default;
4532 if (length->isConstant()) {
4533 int32_t lenInt32 = length->toConstant()->toInt32();
4534 if (lenInt32 >= 0 &&
4535 uint32_t(lenInt32) == templateObj->as<ArrayObject>().length()) {
4536 uint32_t len = uint32_t(lenInt32);
4537 auto* templateConst = constant(ObjectValue(*templateObj));
4539 size_t inlineLength =
4540 gc::GetGCKindSlots(templateObj->asTenured().getAllocKind()) -
4541 ObjectElements::VALUES_PER_HEADER;
4543 MNewArray* obj;
4544 if (len > inlineLength) {
4545 obj = MNewArray::NewVM(alloc(), len, templateConst, heap);
4546 } else {
4547 obj = MNewArray::New(alloc(), len, templateConst, heap);
4549 add(obj);
4550 pushResult(obj);
4551 return true;
4555 auto* obj = MNewArrayDynamicLength::New(alloc(), length, templateObj, heap);
4556 addEffectful(obj);
4557 pushResult(obj);
4558 return resumeAfter(obj);
4561 bool WarpCacheIRTranspiler::emitNewTypedArrayFromLengthResult(
4562 uint32_t templateObjectOffset, Int32OperandId lengthId) {
4563 auto* templateObj = &tenuredObjectStubField(templateObjectOffset)
4564 ->as<FixedLengthTypedArrayObject>();
4565 MDefinition* length = getOperand(lengthId);
4567 // TODO: support pre-tenuring.
4568 gc::Heap heap = gc::Heap::Default;
4570 if (length->isConstant()) {
4571 int32_t len = length->toConstant()->toInt32();
4572 if (len > 0 && uint32_t(len) == templateObj->length()) {
4573 auto* templateConst = constant(ObjectValue(*templateObj));
4574 auto* obj = MNewTypedArray::New(alloc(), templateConst, heap);
4575 add(obj);
4576 pushResult(obj);
4577 return true;
4581 auto* obj =
4582 MNewTypedArrayDynamicLength::New(alloc(), length, templateObj, heap);
4583 addEffectful(obj);
4584 pushResult(obj);
4585 return resumeAfter(obj);
4588 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayBufferResult(
4589 uint32_t templateObjectOffset, ObjOperandId bufferId,
4590 ValOperandId byteOffsetId, ValOperandId lengthId) {
4591 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4592 MDefinition* buffer = getOperand(bufferId);
4593 MDefinition* byteOffset = getOperand(byteOffsetId);
4594 MDefinition* length = getOperand(lengthId);
4596 // TODO: support pre-tenuring.
4597 gc::Heap heap = gc::Heap::Default;
4599 auto* obj = MNewTypedArrayFromArrayBuffer::New(alloc(), buffer, byteOffset,
4600 length, templateObj, heap);
4601 addEffectful(obj);
4603 pushResult(obj);
4604 return resumeAfter(obj);
4607 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayResult(
4608 uint32_t templateObjectOffset, ObjOperandId arrayId) {
4609 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4610 MDefinition* array = getOperand(arrayId);
4612 // TODO: support pre-tenuring.
4613 gc::Heap heap = gc::Heap::Default;
4615 auto* obj = MNewTypedArrayFromArray::New(alloc(), array, templateObj, heap);
4616 addEffectful(obj);
4618 pushResult(obj);
4619 return resumeAfter(obj);
4622 bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult(
4623 ObjOperandId objId, IntPtrOperandId indexId, uint32_t expectedId,
4624 uint32_t replacementId, Scalar::Type elementType,
4625 ArrayBufferViewKind viewKind) {
4626 MDefinition* obj = getOperand(objId);
4627 MDefinition* index = getOperand(indexId);
4628 MDefinition* expected = getOperand(ValOperandId(expectedId));
4629 MDefinition* replacement = getOperand(ValOperandId(replacementId));
4631 auto* length = emitTypedArrayLength(viewKind, obj);
4633 index = addBoundsCheck(index, length);
4635 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4636 add(elements);
4638 bool forceDoubleForUint32 = true;
4639 MIRType knownType =
4640 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4642 auto* cas = MCompareExchangeTypedArrayElement::New(
4643 alloc(), elements, index, elementType, expected, replacement);
4644 cas->setResultType(knownType);
4645 addEffectful(cas);
4647 pushResult(cas);
4648 return resumeAfter(cas);
4651 bool WarpCacheIRTranspiler::emitAtomicsExchangeResult(
4652 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4653 Scalar::Type elementType, ArrayBufferViewKind viewKind) {
4654 MDefinition* obj = getOperand(objId);
4655 MDefinition* index = getOperand(indexId);
4656 MDefinition* value = getOperand(ValOperandId(valueId));
4658 auto* length = emitTypedArrayLength(viewKind, obj);
4660 index = addBoundsCheck(index, length);
4662 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4663 add(elements);
4665 bool forceDoubleForUint32 = true;
4666 MIRType knownType =
4667 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4669 auto* exchange = MAtomicExchangeTypedArrayElement::New(
4670 alloc(), elements, index, value, elementType);
4671 exchange->setResultType(knownType);
4672 addEffectful(exchange);
4674 pushResult(exchange);
4675 return resumeAfter(exchange);
4678 bool WarpCacheIRTranspiler::emitAtomicsBinaryOp(
4679 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4680 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind,
4681 AtomicOp op) {
4682 MDefinition* obj = getOperand(objId);
4683 MDefinition* index = getOperand(indexId);
4684 MDefinition* value = getOperand(ValOperandId(valueId));
4686 auto* length = emitTypedArrayLength(viewKind, obj);
4688 index = addBoundsCheck(index, length);
4690 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4691 add(elements);
4693 bool forceDoubleForUint32 = true;
4694 MIRType knownType =
4695 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4697 auto* binop = MAtomicTypedArrayElementBinop::New(
4698 alloc(), op, elements, index, elementType, value, forEffect);
4699 if (!forEffect) {
4700 binop->setResultType(knownType);
4702 addEffectful(binop);
4704 if (!forEffect) {
4705 pushResult(binop);
4706 } else {
4707 pushResult(constant(UndefinedValue()));
4709 return resumeAfter(binop);
4712 bool WarpCacheIRTranspiler::emitAtomicsAddResult(
4713 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4714 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) {
4715 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4716 viewKind, AtomicOp::Add);
4719 bool WarpCacheIRTranspiler::emitAtomicsSubResult(
4720 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4721 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) {
4722 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4723 viewKind, AtomicOp::Sub);
4726 bool WarpCacheIRTranspiler::emitAtomicsAndResult(
4727 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4728 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) {
4729 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4730 viewKind, AtomicOp::And);
4733 bool WarpCacheIRTranspiler::emitAtomicsOrResult(
4734 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4735 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) {
4736 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4737 viewKind, AtomicOp::Or);
4740 bool WarpCacheIRTranspiler::emitAtomicsXorResult(
4741 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4742 Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind) {
4743 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4744 viewKind, AtomicOp::Xor);
4747 bool WarpCacheIRTranspiler::emitAtomicsLoadResult(
4748 ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType,
4749 ArrayBufferViewKind viewKind) {
4750 MDefinition* obj = getOperand(objId);
4751 MDefinition* index = getOperand(indexId);
4753 auto* length = emitTypedArrayLength(viewKind, obj);
4755 index = addBoundsCheck(index, length);
4757 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4758 add(elements);
4760 bool forceDoubleForUint32 = true;
4761 MIRType knownType =
4762 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4764 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType,
4765 MemoryBarrierRequirement::Required);
4766 load->setResultType(knownType);
4767 addEffectful(load);
4769 pushResult(load);
4770 return resumeAfter(load);
4773 bool WarpCacheIRTranspiler::emitAtomicsStoreResult(
4774 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4775 Scalar::Type elementType, ArrayBufferViewKind viewKind) {
4776 MDefinition* obj = getOperand(objId);
4777 MDefinition* index = getOperand(indexId);
4778 MDefinition* value = getOperand(ValOperandId(valueId));
4780 auto* length = emitTypedArrayLength(viewKind, obj);
4782 index = addBoundsCheck(index, length);
4784 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4785 add(elements);
4787 auto* store =
4788 MStoreUnboxedScalar::New(alloc(), elements, index, value, elementType,
4789 MemoryBarrierRequirement::Required);
4790 addEffectful(store);
4792 pushResult(value);
4793 return resumeAfter(store);
4796 bool WarpCacheIRTranspiler::emitAtomicsIsLockFreeResult(
4797 Int32OperandId valueId) {
4798 MDefinition* value = getOperand(valueId);
4800 auto* ilf = MAtomicIsLockFree::New(alloc(), value);
4801 add(ilf);
4803 pushResult(ilf);
4804 return true;
4807 bool WarpCacheIRTranspiler::emitBigIntAsIntNResult(Int32OperandId bitsId,
4808 BigIntOperandId bigIntId) {
4809 MDefinition* bits = getOperand(bitsId);
4810 MDefinition* bigInt = getOperand(bigIntId);
4812 auto* ins = MBigIntAsIntN::New(alloc(), bits, bigInt);
4813 add(ins);
4815 pushResult(ins);
4816 return true;
4819 bool WarpCacheIRTranspiler::emitBigIntAsUintNResult(Int32OperandId bitsId,
4820 BigIntOperandId bigIntId) {
4821 MDefinition* bits = getOperand(bitsId);
4822 MDefinition* bigInt = getOperand(bigIntId);
4824 auto* ins = MBigIntAsUintN::New(alloc(), bits, bigInt);
4825 add(ins);
4827 pushResult(ins);
4828 return true;
4831 bool WarpCacheIRTranspiler::emitGuardToNonGCThing(ValOperandId inputId) {
4832 MDefinition* def = getOperand(inputId);
4833 if (IsNonGCThing(def->type())) {
4834 return true;
4837 auto* ins = MGuardNonGCThing::New(alloc(), def);
4838 add(ins);
4840 setOperand(inputId, ins);
4841 return true;
4844 bool WarpCacheIRTranspiler::emitSetHasNonGCThingResult(ObjOperandId setId,
4845 ValOperandId valId) {
4846 MDefinition* set = getOperand(setId);
4847 MDefinition* val = getOperand(valId);
4849 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
4850 add(hashValue);
4852 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4853 add(hash);
4855 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4856 add(ins);
4858 pushResult(ins);
4859 return true;
4862 bool WarpCacheIRTranspiler::emitSetHasStringResult(ObjOperandId setId,
4863 StringOperandId strId) {
4864 MDefinition* set = getOperand(setId);
4865 MDefinition* str = getOperand(strId);
4867 auto* hashValue = MToHashableString::New(alloc(), str);
4868 add(hashValue);
4870 auto* hash = MHashString::New(alloc(), hashValue);
4871 add(hash);
4873 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4874 add(ins);
4876 pushResult(ins);
4877 return true;
4880 bool WarpCacheIRTranspiler::emitSetHasSymbolResult(ObjOperandId setId,
4881 SymbolOperandId symId) {
4882 MDefinition* set = getOperand(setId);
4883 MDefinition* sym = getOperand(symId);
4885 auto* hash = MHashSymbol::New(alloc(), sym);
4886 add(hash);
4888 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, sym, hash);
4889 add(ins);
4891 pushResult(ins);
4892 return true;
4895 bool WarpCacheIRTranspiler::emitSetHasBigIntResult(ObjOperandId setId,
4896 BigIntOperandId bigIntId) {
4897 MDefinition* set = getOperand(setId);
4898 MDefinition* bigInt = getOperand(bigIntId);
4900 auto* hash = MHashBigInt::New(alloc(), bigInt);
4901 add(hash);
4903 auto* ins = MSetObjectHasBigInt::New(alloc(), set, bigInt, hash);
4904 add(ins);
4906 pushResult(ins);
4907 return true;
4910 bool WarpCacheIRTranspiler::emitSetHasObjectResult(ObjOperandId setId,
4911 ObjOperandId objId) {
4912 MDefinition* set = getOperand(setId);
4913 MDefinition* obj = getOperand(objId);
4915 auto* hash = MHashObject::New(alloc(), set, obj);
4916 add(hash);
4918 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, obj, hash);
4919 add(ins);
4921 pushResult(ins);
4922 return true;
4925 bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId,
4926 ValOperandId valId) {
4927 MDefinition* set = getOperand(setId);
4928 MDefinition* val = getOperand(valId);
4930 #ifdef JS_PUNBOX64
4931 auto* hashValue = MToHashableValue::New(alloc(), val);
4932 add(hashValue);
4934 auto* hash = MHashValue::New(alloc(), set, hashValue);
4935 add(hash);
4937 auto* ins = MSetObjectHasValue::New(alloc(), set, hashValue, hash);
4938 add(ins);
4939 #else
4940 auto* ins = MSetObjectHasValueVMCall::New(alloc(), set, val);
4941 add(ins);
4942 #endif
4944 pushResult(ins);
4945 return true;
4948 bool WarpCacheIRTranspiler::emitSetSizeResult(ObjOperandId setId) {
4949 MDefinition* set = getOperand(setId);
4951 auto* ins = MSetObjectSize::New(alloc(), set);
4952 add(ins);
4954 pushResult(ins);
4955 return true;
4958 bool WarpCacheIRTranspiler::emitMapHasNonGCThingResult(ObjOperandId mapId,
4959 ValOperandId valId) {
4960 MDefinition* map = getOperand(mapId);
4961 MDefinition* val = getOperand(valId);
4963 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
4964 add(hashValue);
4966 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4967 add(hash);
4969 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4970 add(ins);
4972 pushResult(ins);
4973 return true;
4976 bool WarpCacheIRTranspiler::emitMapHasStringResult(ObjOperandId mapId,
4977 StringOperandId strId) {
4978 MDefinition* map = getOperand(mapId);
4979 MDefinition* str = getOperand(strId);
4981 auto* hashValue = MToHashableString::New(alloc(), str);
4982 add(hashValue);
4984 auto* hash = MHashString::New(alloc(), hashValue);
4985 add(hash);
4987 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4988 add(ins);
4990 pushResult(ins);
4991 return true;
4994 bool WarpCacheIRTranspiler::emitMapHasSymbolResult(ObjOperandId mapId,
4995 SymbolOperandId symId) {
4996 MDefinition* map = getOperand(mapId);
4997 MDefinition* sym = getOperand(symId);
4999 auto* hash = MHashSymbol::New(alloc(), sym);
5000 add(hash);
5002 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, sym, hash);
5003 add(ins);
5005 pushResult(ins);
5006 return true;
5009 bool WarpCacheIRTranspiler::emitMapHasBigIntResult(ObjOperandId mapId,
5010 BigIntOperandId bigIntId) {
5011 MDefinition* map = getOperand(mapId);
5012 MDefinition* bigInt = getOperand(bigIntId);
5014 auto* hash = MHashBigInt::New(alloc(), bigInt);
5015 add(hash);
5017 auto* ins = MMapObjectHasBigInt::New(alloc(), map, bigInt, hash);
5018 add(ins);
5020 pushResult(ins);
5021 return true;
5024 bool WarpCacheIRTranspiler::emitMapHasObjectResult(ObjOperandId mapId,
5025 ObjOperandId objId) {
5026 MDefinition* map = getOperand(mapId);
5027 MDefinition* obj = getOperand(objId);
5029 auto* hash = MHashObject::New(alloc(), map, obj);
5030 add(hash);
5032 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, obj, hash);
5033 add(ins);
5035 pushResult(ins);
5036 return true;
5039 bool WarpCacheIRTranspiler::emitMapHasResult(ObjOperandId mapId,
5040 ValOperandId valId) {
5041 MDefinition* map = getOperand(mapId);
5042 MDefinition* val = getOperand(valId);
5044 #ifdef JS_PUNBOX64
5045 auto* hashValue = MToHashableValue::New(alloc(), val);
5046 add(hashValue);
5048 auto* hash = MHashValue::New(alloc(), map, hashValue);
5049 add(hash);
5051 auto* ins = MMapObjectHasValue::New(alloc(), map, hashValue, hash);
5052 add(ins);
5053 #else
5054 auto* ins = MMapObjectHasValueVMCall::New(alloc(), map, val);
5055 add(ins);
5056 #endif
5058 pushResult(ins);
5059 return true;
5062 bool WarpCacheIRTranspiler::emitMapGetNonGCThingResult(ObjOperandId mapId,
5063 ValOperandId valId) {
5064 MDefinition* map = getOperand(mapId);
5065 MDefinition* val = getOperand(valId);
5067 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
5068 add(hashValue);
5070 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
5071 add(hash);
5073 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
5074 add(ins);
5076 pushResult(ins);
5077 return true;
5080 bool WarpCacheIRTranspiler::emitMapGetStringResult(ObjOperandId mapId,
5081 StringOperandId strId) {
5082 MDefinition* map = getOperand(mapId);
5083 MDefinition* str = getOperand(strId);
5085 auto* hashValue = MToHashableString::New(alloc(), str);
5086 add(hashValue);
5088 auto* hash = MHashString::New(alloc(), hashValue);
5089 add(hash);
5091 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
5092 add(ins);
5094 pushResult(ins);
5095 return true;
5098 bool WarpCacheIRTranspiler::emitMapGetSymbolResult(ObjOperandId mapId,
5099 SymbolOperandId symId) {
5100 MDefinition* map = getOperand(mapId);
5101 MDefinition* sym = getOperand(symId);
5103 auto* hash = MHashSymbol::New(alloc(), sym);
5104 add(hash);
5106 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, sym, hash);
5107 add(ins);
5109 pushResult(ins);
5110 return true;
5113 bool WarpCacheIRTranspiler::emitMapGetBigIntResult(ObjOperandId mapId,
5114 BigIntOperandId bigIntId) {
5115 MDefinition* map = getOperand(mapId);
5116 MDefinition* bigInt = getOperand(bigIntId);
5118 auto* hash = MHashBigInt::New(alloc(), bigInt);
5119 add(hash);
5121 auto* ins = MMapObjectGetBigInt::New(alloc(), map, bigInt, hash);
5122 add(ins);
5124 pushResult(ins);
5125 return true;
5128 bool WarpCacheIRTranspiler::emitMapGetObjectResult(ObjOperandId mapId,
5129 ObjOperandId objId) {
5130 MDefinition* map = getOperand(mapId);
5131 MDefinition* obj = getOperand(objId);
5133 auto* hash = MHashObject::New(alloc(), map, obj);
5134 add(hash);
5136 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, obj, hash);
5137 add(ins);
5139 pushResult(ins);
5140 return true;
5143 bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId,
5144 ValOperandId valId) {
5145 MDefinition* map = getOperand(mapId);
5146 MDefinition* val = getOperand(valId);
5148 #ifdef JS_PUNBOX64
5149 auto* hashValue = MToHashableValue::New(alloc(), val);
5150 add(hashValue);
5152 auto* hash = MHashValue::New(alloc(), map, hashValue);
5153 add(hash);
5155 auto* ins = MMapObjectGetValue::New(alloc(), map, hashValue, hash);
5156 add(ins);
5157 #else
5158 auto* ins = MMapObjectGetValueVMCall::New(alloc(), map, val);
5159 add(ins);
5160 #endif
5162 pushResult(ins);
5163 return true;
5166 bool WarpCacheIRTranspiler::emitMapSizeResult(ObjOperandId mapId) {
5167 MDefinition* map = getOperand(mapId);
5169 auto* ins = MMapObjectSize::New(alloc(), map);
5170 add(ins);
5172 pushResult(ins);
5173 return true;
5176 bool WarpCacheIRTranspiler::emitTruthyResult(OperandId inputId) {
5177 MDefinition* input = getOperand(inputId);
5179 auto* result = convertToBoolean(input);
5181 pushResult(result);
5182 return true;
5185 bool WarpCacheIRTranspiler::emitLoadInt32TruthyResult(ValOperandId inputId) {
5186 return emitTruthyResult(inputId);
5189 bool WarpCacheIRTranspiler::emitLoadDoubleTruthyResult(
5190 NumberOperandId inputId) {
5191 return emitTruthyResult(inputId);
5194 bool WarpCacheIRTranspiler::emitLoadStringTruthyResult(
5195 StringOperandId inputId) {
5196 return emitTruthyResult(inputId);
5199 bool WarpCacheIRTranspiler::emitLoadObjectTruthyResult(ObjOperandId inputId) {
5200 return emitTruthyResult(inputId);
5203 bool WarpCacheIRTranspiler::emitLoadBigIntTruthyResult(
5204 BigIntOperandId inputId) {
5205 return emitTruthyResult(inputId);
5208 bool WarpCacheIRTranspiler::emitLoadValueTruthyResult(ValOperandId inputId) {
5209 return emitTruthyResult(inputId);
5212 bool WarpCacheIRTranspiler::emitLoadOperandResult(ValOperandId inputId) {
5213 MDefinition* input = getOperand(inputId);
5214 pushResult(input);
5215 return true;
5218 bool WarpCacheIRTranspiler::emitLoadWrapperTarget(ObjOperandId objId,
5219 ObjOperandId resultId,
5220 bool fallible) {
5221 MDefinition* obj = getOperand(objId);
5223 auto* ins = MLoadWrapperTarget::New(alloc(), obj, fallible);
5224 if (fallible) {
5225 ins->setGuard();
5227 add(ins);
5229 return defineOperand(resultId, ins);
5232 // When we transpile a call, we may generate guards for some
5233 // arguments. To make sure the call instruction depends on those
5234 // guards, when the transpiler creates an operand for an argument, we
5235 // register the OperandId of that argument in argumentIds_. (See
5236 // emitLoadArgumentSlot.) Before generating the call, we update the
5237 // CallInfo to use the appropriate value from operands_.
5238 // Note: The callee is an explicit argument to the call op, and is
5239 // tracked separately.
5240 void WarpCacheIRTranspiler::updateArgumentsFromOperands() {
5241 for (uint32_t i = 0; i < uint32_t(ArgumentKind::NumKinds); i++) {
5242 ArgumentKind kind = ArgumentKind(i);
5243 OperandId id = argumentOperandIds_[kind];
5244 if (id.valid()) {
5245 switch (kind) {
5246 case ArgumentKind::This:
5247 callInfo_->setThis(getOperand(id));
5248 break;
5249 case ArgumentKind::NewTarget:
5250 callInfo_->setNewTarget(getOperand(id));
5251 break;
5252 case ArgumentKind::Arg0:
5253 callInfo_->setArg(0, getOperand(id));
5254 break;
5255 case ArgumentKind::Arg1:
5256 callInfo_->setArg(1, getOperand(id));
5257 break;
5258 case ArgumentKind::Arg2:
5259 callInfo_->setArg(2, getOperand(id));
5260 break;
5261 case ArgumentKind::Arg3:
5262 callInfo_->setArg(3, getOperand(id));
5263 break;
5264 case ArgumentKind::Arg4:
5265 callInfo_->setArg(4, getOperand(id));
5266 break;
5267 case ArgumentKind::Arg5:
5268 callInfo_->setArg(5, getOperand(id));
5269 break;
5270 case ArgumentKind::Arg6:
5271 callInfo_->setArg(6, getOperand(id));
5272 break;
5273 case ArgumentKind::Arg7:
5274 callInfo_->setArg(7, getOperand(id));
5275 break;
5276 case ArgumentKind::Callee:
5277 case ArgumentKind::NumKinds:
5278 MOZ_CRASH("Unexpected argument kind");
5284 bool WarpCacheIRTranspiler::emitLoadArgumentSlot(ValOperandId resultId,
5285 uint32_t slotIndex) {
5286 // Reverse of GetIndexOfArgument.
5288 // Layout:
5289 // NewTarget | Args.. (reversed) | ThisValue | Callee
5290 // 0 | ArgC .. Arg1 Arg0 (+1) | argc (+1) | argc + 1 (+ 1)
5291 // ^ (if constructing)
5293 // NewTarget (optional)
5294 if (callInfo_->constructing()) {
5295 if (slotIndex == 0) {
5296 setArgumentId(ArgumentKind::NewTarget, resultId);
5297 return defineOperand(resultId, callInfo_->getNewTarget());
5300 slotIndex -= 1; // Adjust slot index to match non-constructing calls.
5303 // Args..
5304 if (slotIndex < callInfo_->argc()) {
5305 uint32_t arg = callInfo_->argc() - 1 - slotIndex;
5306 ArgumentKind kind = ArgumentKindForArgIndex(arg);
5307 MOZ_ASSERT(kind < ArgumentKind::NumKinds);
5308 setArgumentId(kind, resultId);
5309 return defineOperand(resultId, callInfo_->getArg(arg));
5312 // ThisValue
5313 if (slotIndex == callInfo_->argc()) {
5314 setArgumentId(ArgumentKind::This, resultId);
5315 return defineOperand(resultId, callInfo_->thisArg());
5318 // Callee
5319 MOZ_ASSERT(slotIndex == callInfo_->argc() + 1);
5320 return defineOperand(resultId, callInfo_->callee());
5323 bool WarpCacheIRTranspiler::emitLoadArgumentFixedSlot(ValOperandId resultId,
5324 uint8_t slotIndex) {
5325 return emitLoadArgumentSlot(resultId, slotIndex);
5328 bool WarpCacheIRTranspiler::emitLoadArgumentDynamicSlot(ValOperandId resultId,
5329 Int32OperandId argcId,
5330 uint8_t slotIndex) {
5331 #ifdef DEBUG
5332 MDefinition* argc = getOperand(argcId);
5333 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5334 static_cast<int32_t>(callInfo_->argc()));
5335 #endif
5337 return emitLoadArgumentSlot(resultId, callInfo_->argc() + slotIndex);
5340 WrappedFunction* WarpCacheIRTranspiler::maybeWrappedFunction(
5341 MDefinition* callee, CallKind kind, uint16_t nargs, FunctionFlags flags) {
5342 MOZ_ASSERT(callee->isConstant() || callee->isNurseryObject());
5344 // If this is a native without a JitEntry, WrappedFunction needs to know the
5345 // target JSFunction.
5346 // TODO: support nursery-allocated natives with WrappedFunction, maybe by
5347 // storing the JSNative in the Baseline stub like flags/nargs.
5348 bool isNative = flags.isNativeWithoutJitEntry();
5349 if (isNative && !callee->isConstant()) {
5350 return nullptr;
5353 JSFunction* nativeTarget = nullptr;
5354 if (isNative) {
5355 nativeTarget = &callee->toConstant()->toObject().as<JSFunction>();
5358 WrappedFunction* wrappedTarget =
5359 new (alloc()) WrappedFunction(nativeTarget, nargs, flags);
5360 MOZ_ASSERT_IF(kind == CallKind::Native || kind == CallKind::DOM,
5361 wrappedTarget->isNativeWithoutJitEntry());
5362 MOZ_ASSERT_IF(kind == CallKind::Scripted, wrappedTarget->hasJitEntry());
5363 return wrappedTarget;
5366 WrappedFunction* WarpCacheIRTranspiler::maybeCallTarget(MDefinition* callee,
5367 CallKind kind) {
5368 // CacheIR emits the following for specialized calls:
5369 // GuardSpecificFunction <callee> <func> ..
5370 // Call(Native|Scripted)Function <callee> ..
5371 // or:
5372 // GuardClass <callee> ..
5373 // GuardFunctionScript <callee> <script> ..
5374 // CallScriptedFunction <callee> ..
5376 // We can use the <func> JSFunction or <script> BaseScript to specialize this
5377 // call.
5378 if (callee->isGuardSpecificFunction()) {
5379 auto* guard = callee->toGuardSpecificFunction();
5380 return maybeWrappedFunction(guard->expected(), kind, guard->nargs(),
5381 guard->flags());
5383 if (callee->isGuardFunctionScript()) {
5384 MOZ_ASSERT(kind == CallKind::Scripted);
5385 auto* guard = callee->toGuardFunctionScript();
5386 WrappedFunction* wrappedTarget = new (alloc()) WrappedFunction(
5387 /* nativeFun = */ nullptr, guard->nargs(), guard->flags());
5388 MOZ_ASSERT(wrappedTarget->hasJitEntry());
5389 return wrappedTarget;
5391 return nullptr;
5394 // If it is possible to use MCall for this call, update callInfo_ to use
5395 // the correct arguments. Otherwise, update the ArgFormat of callInfo_.
5396 bool WarpCacheIRTranspiler::updateCallInfo(MDefinition* callee,
5397 CallFlags flags) {
5398 // The transpilation will add various guards to the callee.
5399 // We replace the callee referenced by the CallInfo, so that
5400 // the resulting call instruction depends on these guards.
5401 callInfo_->setCallee(callee);
5403 // The transpilation may also add guards to other arguments.
5404 // We replace those arguments in the CallInfo here.
5405 updateArgumentsFromOperands();
5407 switch (flags.getArgFormat()) {
5408 case CallFlags::Standard:
5409 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5410 break;
5411 case CallFlags::Spread:
5412 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Array);
5413 break;
5414 case CallFlags::FunCall:
5415 // Note: We already changed the callee to the target
5416 // function instead of the |call| function.
5417 MOZ_ASSERT(!callInfo_->constructing());
5418 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5420 if (callInfo_->argc() == 0) {
5421 // Special case for fun.call() with no arguments.
5422 auto* undef = constant(UndefinedValue());
5423 callInfo_->setThis(undef);
5424 } else {
5425 // The first argument for |call| is the new this value.
5426 callInfo_->setThis(callInfo_->getArg(0));
5428 // Shift down all other arguments by removing the first.
5429 callInfo_->removeArg(0);
5431 break;
5432 case CallFlags::FunApplyArgsObj:
5433 MOZ_ASSERT(!callInfo_->constructing());
5434 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5436 callInfo_->setArgFormat(CallInfo::ArgFormat::FunApplyArgsObj);
5437 break;
5438 case CallFlags::FunApplyArray: {
5439 MDefinition* argFunc = callInfo_->thisArg();
5440 MDefinition* argThis = callInfo_->getArg(0);
5441 callInfo_->setCallee(argFunc);
5442 callInfo_->setThis(argThis);
5443 callInfo_->setArgFormat(CallInfo::ArgFormat::Array);
5444 break;
5446 case CallFlags::FunApplyNullUndefined:
5447 // Note: We already changed the callee to the target
5448 // function instead of the |apply| function.
5449 MOZ_ASSERT(callInfo_->argc() == 2);
5450 MOZ_ASSERT(!callInfo_->constructing());
5451 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5452 callInfo_->setThis(callInfo_->getArg(0));
5453 callInfo_->getArg(1)->setImplicitlyUsedUnchecked();
5454 callInfo_->removeArg(1);
5455 callInfo_->removeArg(0);
5456 break;
5457 default:
5458 MOZ_CRASH("Unsupported arg format");
5460 return true;
5463 // Returns true if we are generating a call to CreateThisFromIon and
5464 // must check its return value.
5465 bool WarpCacheIRTranspiler::maybeCreateThis(MDefinition* callee,
5466 CallFlags flags, CallKind kind) {
5467 MOZ_ASSERT(kind != CallKind::DOM, "DOM functions are not constructors");
5468 MDefinition* thisArg = callInfo_->thisArg();
5470 if (kind == CallKind::Native) {
5471 // Native functions keep the is-constructing MagicValue as |this|.
5472 // If one of the arguments uses spread syntax this can be a loop phi with
5473 // MIRType::Value.
5474 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
5475 thisArg->isPhi());
5476 return false;
5478 MOZ_ASSERT(kind == CallKind::Scripted);
5480 if (thisArg->isNewPlainObject()) {
5481 // We have already updated |this| based on MetaScriptedThisShape. We do
5482 // not need to generate a check.
5483 return false;
5485 if (flags.needsUninitializedThis()) {
5486 MConstant* uninit = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
5487 thisArg->setImplicitlyUsedUnchecked();
5488 callInfo_->setThis(uninit);
5489 return false;
5491 // See the Native case above.
5492 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
5493 thisArg->isPhi());
5495 auto* newTarget = unboxObjectInfallible(callInfo_->getNewTarget());
5496 auto* createThis = MCreateThis::New(alloc(), callee, newTarget);
5497 add(createThis);
5499 thisArg->setImplicitlyUsedUnchecked();
5500 callInfo_->setThis(createThis);
5502 return true;
5505 bool WarpCacheIRTranspiler::emitCallFunction(
5506 ObjOperandId calleeId, Int32OperandId argcId,
5507 mozilla::Maybe<ObjOperandId> thisObjId, CallFlags flags, CallKind kind) {
5508 MDefinition* callee = getOperand(calleeId);
5509 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
5510 // We are transpiling to generate the correct guards. We also
5511 // update the CallInfo to use the correct arguments. Code for the
5512 // inlined function itself will be generated in
5513 // WarpBuilder::buildInlinedCall.
5514 if (!updateCallInfo(callee, flags)) {
5515 return false;
5517 if (callInfo_->constructing()) {
5518 MOZ_ASSERT(flags.isConstructing());
5520 // We call maybeCreateThis to update |this|, but inlined constructors
5521 // never need a VM call. CallIRGenerator::getThisForScripted ensures that
5522 // we don't attach a specialized stub unless we have a template object or
5523 // know that the constructor needs uninitialized this.
5524 MOZ_ALWAYS_FALSE(maybeCreateThis(callee, flags, CallKind::Scripted));
5525 mozilla::DebugOnly<MDefinition*> thisArg = callInfo_->thisArg();
5526 MOZ_ASSERT(thisArg->isNewPlainObject() ||
5527 thisArg->type() == MIRType::MagicUninitializedLexical);
5530 if (flags.getArgFormat() == CallFlags::FunCall) {
5531 callInfo_->setInliningResumeMode(ResumeMode::InlinedFunCall);
5532 } else {
5533 MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
5534 callInfo_->setInliningResumeMode(ResumeMode::InlinedStandardCall);
5537 switch (callInfo_->argFormat()) {
5538 case CallInfo::ArgFormat::Standard:
5539 break;
5540 default:
5541 MOZ_CRASH("Unsupported arg format");
5543 return true;
5546 #ifdef DEBUG
5547 MDefinition* argc = getOperand(argcId);
5548 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5549 static_cast<int32_t>(callInfo_->argc()));
5550 #endif
5552 if (!updateCallInfo(callee, flags)) {
5553 return false;
5556 if (kind == CallKind::DOM) {
5557 MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
5558 // For DOM calls |this| has a class guard.
5559 MDefinition* thisObj = getOperand(*thisObjId);
5560 callInfo_->setThis(thisObj);
5563 WrappedFunction* wrappedTarget = maybeCallTarget(callee, kind);
5565 bool needsThisCheck = false;
5566 if (callInfo_->constructing()) {
5567 MOZ_ASSERT(flags.isConstructing());
5568 needsThisCheck = maybeCreateThis(callee, flags, kind);
5569 if (needsThisCheck) {
5570 wrappedTarget = nullptr;
5574 switch (callInfo_->argFormat()) {
5575 case CallInfo::ArgFormat::Standard: {
5576 MCall* call = makeCall(*callInfo_, needsThisCheck, wrappedTarget,
5577 kind == CallKind::DOM);
5578 if (!call) {
5579 return false;
5582 if (flags.isSameRealm()) {
5583 call->setNotCrossRealm();
5586 if (call->isEffectful()) {
5587 addEffectful(call);
5588 pushResult(call);
5589 return resumeAfter(call);
5592 MOZ_ASSERT(kind == CallKind::DOM);
5593 add(call);
5594 pushResult(call);
5595 return true;
5597 case CallInfo::ArgFormat::Array: {
5598 MInstruction* call = makeSpreadCall(*callInfo_, needsThisCheck,
5599 flags.isSameRealm(), wrappedTarget);
5600 if (!call) {
5601 return false;
5603 addEffectful(call);
5604 pushResult(call);
5606 return resumeAfter(call);
5608 case CallInfo::ArgFormat::FunApplyArgsObj: {
5609 return emitFunApplyArgsObj(wrappedTarget, flags);
5612 MOZ_CRASH("unreachable");
5615 bool WarpCacheIRTranspiler::emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
5616 CallFlags flags) {
5617 MOZ_ASSERT(!callInfo_->constructing());
5619 MDefinition* callee = callInfo_->thisArg();
5620 MDefinition* thisArg = callInfo_->getArg(0);
5621 MDefinition* argsObj = callInfo_->getArg(1);
5623 MApplyArgsObj* apply =
5624 MApplyArgsObj::New(alloc(), wrappedTarget, callee, argsObj, thisArg);
5626 if (flags.isSameRealm()) {
5627 apply->setNotCrossRealm();
5629 if (callInfo_->ignoresReturnValue()) {
5630 apply->setIgnoresReturnValue();
5633 addEffectful(apply);
5634 pushResult(apply);
5636 return resumeAfter(apply);
5639 #ifndef JS_SIMULATOR
5640 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5641 Int32OperandId argcId,
5642 CallFlags flags,
5643 uint32_t argcFixed,
5644 bool ignoresReturnValue) {
5645 // Instead of ignoresReturnValue we use CallInfo::ignoresReturnValue.
5646 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5647 CallKind::Native);
5650 bool WarpCacheIRTranspiler::emitCallDOMFunction(ObjOperandId calleeId,
5651 Int32OperandId argcId,
5652 ObjOperandId thisObjId,
5653 CallFlags flags,
5654 uint32_t argcFixed) {
5655 return emitCallFunction(calleeId, argcId, mozilla::Some(thisObjId), flags,
5656 CallKind::DOM);
5658 #else
5659 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5660 Int32OperandId argcId,
5661 CallFlags flags,
5662 uint32_t argcFixed,
5663 uint32_t targetOffset) {
5664 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5665 CallKind::Native);
5668 bool WarpCacheIRTranspiler::emitCallDOMFunction(
5669 ObjOperandId calleeId, Int32OperandId argcId, ObjOperandId thisObjId,
5670 CallFlags flags, uint32_t argcFixed, uint32_t targetOffset) {
5671 return emitCallFunction(calleeId, argcId, mozilla::Some(thisObjId), flags,
5672 CallKind::DOM);
5674 #endif
5676 bool WarpCacheIRTranspiler::emitCallScriptedFunction(ObjOperandId calleeId,
5677 Int32OperandId argcId,
5678 CallFlags flags,
5679 uint32_t argcFixed) {
5680 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5681 CallKind::Scripted);
5684 bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId,
5685 Int32OperandId argcId,
5686 uint32_t icScriptOffset,
5687 CallFlags flags,
5688 uint32_t argcFixed) {
5689 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5690 CallKind::Scripted);
5693 #ifdef JS_PUNBOX64
5694 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetShared(
5695 MDefinition* target, MDefinition* receiver, MDefinition* handler,
5696 MDefinition* id, MDefinition* trapDef, WrappedFunction* trap) {
5697 CallInfo callInfo(alloc(), /* constructing = */ false,
5698 /* ignoresRval = */ false);
5699 callInfo.initForProxyGet(trapDef, handler, target, id, receiver);
5701 MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, trap);
5702 if (!call) {
5703 return false;
5706 addEffectful(call);
5708 if (!current->ensureHasSlots(3)) {
5709 return false;
5711 current->push(call);
5712 current->push(id);
5713 current->push(target);
5715 MResumePoint* resumePoint =
5716 MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
5717 ResumeMode::ResumeAfterCheckProxyGetResult);
5718 if (!resumePoint) {
5719 return false;
5721 call->setResumePoint(resumePoint);
5723 current->pop();
5724 current->pop();
5726 MCheckScriptedProxyGetResult* check =
5727 MCheckScriptedProxyGetResult::New(alloc(), target, id, call);
5728 addEffectfulUnsafe(check);
5730 return resumeAfterUnchecked(check);
5733 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetResult(
5734 ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
5735 uint32_t trapOffset, uint32_t idOffset, uint32_t nargsAndFlags) {
5736 MDefinition* target = getOperand(targetId);
5737 MDefinition* receiver = getOperand(receiverId);
5738 MDefinition* handler = getOperand(handlerId);
5739 MDefinition* trap = objectStubField(trapOffset);
5740 jsid id = idStubField(idOffset);
5741 MDefinition* idDef = constant(StringValue(id.toAtom()));
5742 uint16_t nargs = nargsAndFlags >> 16;
5743 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
5744 WrappedFunction* wrappedTarget =
5745 maybeWrappedFunction(trap, CallKind::Scripted, nargs, flags);
5746 return emitCallScriptedProxyGetShared(target, receiver, handler, idDef, trap,
5747 wrappedTarget);
5750 bool WarpCacheIRTranspiler::emitCallScriptedProxyGetByValueResult(
5751 ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
5752 ValOperandId idId, uint32_t trapOffset, uint32_t nargsAndFlags) {
5753 MDefinition* target = getOperand(targetId);
5754 MDefinition* receiver = getOperand(receiverId);
5755 MDefinition* handler = getOperand(handlerId);
5756 MDefinition* trap = objectStubField(trapOffset);
5757 MDefinition* idDef = getOperand(idId);
5758 uint16_t nargs = nargsAndFlags >> 16;
5759 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
5760 WrappedFunction* wrappedTarget =
5761 maybeWrappedFunction(trap, CallKind::Scripted, nargs, flags);
5762 return emitCallScriptedProxyGetShared(target, receiver, handler, idDef, trap,
5763 wrappedTarget);
5765 #endif
5767 bool WarpCacheIRTranspiler::emitCallClassHook(ObjOperandId calleeId,
5768 Int32OperandId argcId,
5769 CallFlags flags,
5770 uint32_t argcFixed,
5771 uint32_t targetOffset) {
5772 MDefinition* callee = getOperand(calleeId);
5773 JSNative target = jsnativeStubField(targetOffset);
5775 #ifdef DEBUG
5776 MDefinition* argc = getOperand(argcId);
5777 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5778 static_cast<int32_t>(callInfo_->argc()));
5779 #endif
5781 if (!updateCallInfo(callee, flags)) {
5782 return false;
5785 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5786 MOZ_ASSERT(flags.getArgFormat() == CallFlags::ArgFormat::Standard);
5788 // Callees can be from any realm. If this changes, we should update
5789 // MCallClassHook::maybeCrossRealm.
5790 MOZ_ASSERT(!flags.isSameRealm());
5792 auto* call = MCallClassHook::New(alloc(), target, callInfo_->argc(),
5793 callInfo_->constructing());
5794 if (!call) {
5795 return false;
5798 if (callInfo_->ignoresReturnValue()) {
5799 call->setIgnoresReturnValue();
5802 call->initCallee(callInfo_->callee());
5803 call->addArg(0, callInfo_->thisArg());
5805 for (uint32_t i = 0; i < callInfo_->argc(); i++) {
5806 call->addArg(i + 1, callInfo_->getArg(i));
5809 if (callInfo_->constructing()) {
5810 call->addArg(1 + callInfo_->argc(), callInfo_->getNewTarget());
5813 addEffectful(call);
5814 pushResult(call);
5816 return resumeAfter(call);
5819 bool WarpCacheIRTranspiler::emitCallBoundScriptedFunction(
5820 ObjOperandId calleeId, ObjOperandId targetId, Int32OperandId argcId,
5821 CallFlags flags, uint32_t numBoundArgs) {
5822 MDefinition* callee = getOperand(calleeId);
5823 MDefinition* target = getOperand(targetId);
5825 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5826 MOZ_ASSERT(callInfo_->constructing() == flags.isConstructing());
5828 callInfo_->setCallee(target);
5829 updateArgumentsFromOperands();
5831 WrappedFunction* wrappedTarget = maybeCallTarget(target, CallKind::Scripted);
5833 bool needsThisCheck = false;
5834 if (callInfo_->constructing()) {
5835 callInfo_->setNewTarget(target);
5836 needsThisCheck = maybeCreateThis(target, flags, CallKind::Scripted);
5837 if (needsThisCheck) {
5838 wrappedTarget = nullptr;
5840 } else {
5841 auto* thisv = MLoadFixedSlot::New(alloc(), callee,
5842 BoundFunctionObject::boundThisSlot());
5843 add(thisv);
5844 callInfo_->thisArg()->setImplicitlyUsedUnchecked();
5845 callInfo_->setThis(thisv);
5848 bool usingInlineBoundArgs =
5849 numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs;
5851 MElements* elements = nullptr;
5852 if (!usingInlineBoundArgs) {
5853 auto* boundArgs = MLoadFixedSlot::New(
5854 alloc(), callee, BoundFunctionObject::firstInlineBoundArgSlot());
5855 add(boundArgs);
5856 auto* boundArgsObj = unboxObjectInfallible(boundArgs, IsMovable::Yes);
5857 elements = MElements::New(alloc(), boundArgsObj);
5858 add(elements);
5861 auto loadBoundArg = [&](size_t index) {
5862 MInstruction* arg;
5863 if (usingInlineBoundArgs) {
5864 size_t slot = BoundFunctionObject::firstInlineBoundArgSlot() + index;
5865 arg = MLoadFixedSlot::New(alloc(), callee, slot);
5866 } else {
5867 arg = MLoadElement::New(alloc(), elements, constant(Int32Value(index)));
5869 add(arg);
5870 return arg;
5872 if (!callInfo_->prependArgs(numBoundArgs, loadBoundArg)) {
5873 return false;
5876 MCall* call = makeCall(*callInfo_, needsThisCheck, wrappedTarget);
5877 if (!call) {
5878 return false;
5881 if (flags.isSameRealm()) {
5882 call->setNotCrossRealm();
5885 addEffectful(call);
5886 pushResult(call);
5887 return resumeAfter(call);
5890 bool WarpCacheIRTranspiler::emitBindFunctionResult(
5891 ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) {
5892 MDefinition* target = getOperand(targetId);
5893 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
5895 MOZ_ASSERT(callInfo_->argc() == argc);
5897 auto* bound = MBindFunction::New(alloc(), target, argc, templateObj);
5898 if (!bound) {
5899 return false;
5901 addEffectful(bound);
5903 for (uint32_t i = 0; i < argc; i++) {
5904 bound->initArg(i, callInfo_->getArg(i));
5907 pushResult(bound);
5908 return resumeAfter(bound);
5911 bool WarpCacheIRTranspiler::emitSpecializedBindFunctionResult(
5912 ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) {
5913 MDefinition* target = getOperand(targetId);
5914 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
5916 MOZ_ASSERT(callInfo_->argc() == argc);
5918 auto* bound = MNewBoundFunction::New(alloc(), templateObj);
5919 add(bound);
5921 size_t numBoundArgs = argc > 0 ? argc - 1 : 0;
5922 MOZ_ASSERT(numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs);
5924 auto initSlot = [&](size_t slot, MDefinition* value) {
5925 #ifdef DEBUG
5926 // Assert we can elide the post write barrier. See also the comment in
5927 // WarpBuilder::buildNamedLambdaEnv.
5928 add(MAssertCanElidePostWriteBarrier::New(alloc(), bound, value));
5929 #endif
5930 addUnchecked(MStoreFixedSlot::NewUnbarriered(alloc(), bound, slot, value));
5933 initSlot(BoundFunctionObject::targetSlot(), target);
5934 if (argc > 0) {
5935 initSlot(BoundFunctionObject::boundThisSlot(), callInfo_->getArg(0));
5937 for (size_t i = 0; i < numBoundArgs; i++) {
5938 size_t slot = BoundFunctionObject::firstInlineBoundArgSlot() + i;
5939 initSlot(slot, callInfo_->getArg(1 + i));
5942 pushResult(bound);
5943 return true;
5946 bool WarpCacheIRTranspiler::emitCallWasmFunction(
5947 ObjOperandId calleeId, Int32OperandId argcId, CallFlags flags,
5948 uint32_t argcFixed, uint32_t funcExportOffset, uint32_t instanceOffset) {
5949 MDefinition* callee = getOperand(calleeId);
5950 #ifdef DEBUG
5951 MDefinition* argc = getOperand(argcId);
5952 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5953 static_cast<int32_t>(callInfo_->argc()));
5954 #endif
5955 JSObject* instanceObject = tenuredObjectStubField(instanceOffset);
5956 auto* wasmInstanceObj = &instanceObject->as<WasmInstanceObject>();
5957 const wasm::FuncExport* funcExport = wasmFuncExportField(funcExportOffset);
5958 const wasm::FuncType& sig =
5959 wasmInstanceObj->instance().metadata().getFuncExportType(*funcExport);
5961 if (!updateCallInfo(callee, flags)) {
5962 return false;
5965 static_assert(wasm::MaxArgsForJitInlineCall <= MaxNumLInstructionOperands,
5966 "arguments must fit in LIR operands");
5967 MOZ_ASSERT(sig.args().length() <= wasm::MaxArgsForJitInlineCall);
5969 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5971 auto* call = MIonToWasmCall::New(alloc(), wasmInstanceObj, *funcExport);
5972 if (!call) {
5973 return false;
5976 mozilla::Maybe<MDefinition*> undefined;
5977 for (size_t i = 0; i < sig.args().length(); i++) {
5978 if (!alloc().ensureBallast()) {
5979 return false;
5982 MDefinition* arg;
5983 if (i < callInfo_->argc()) {
5984 arg = callInfo_->getArg(i);
5985 } else {
5986 if (!undefined) {
5987 undefined.emplace(constant(UndefinedValue()));
5989 arg = convertWasmArg(*undefined, sig.args()[i].kind());
5991 call->initArg(i, arg);
5994 addEffectful(call);
5996 // Add any post-function call conversions that are necessary.
5997 MInstruction* postConversion = call;
5998 const wasm::ValTypeVector& results = sig.results();
5999 MOZ_ASSERT(results.length() <= 1, "Multi-value returns not supported.");
6000 if (results.length() == 0) {
6001 // No results to convert.
6002 } else {
6003 switch (results[0].kind()) {
6004 case wasm::ValType::I64:
6005 // JS expects a BigInt from I64 types.
6006 postConversion = MInt64ToBigInt::New(alloc(), call);
6008 // Make non-movable so we can attach a resume point.
6009 postConversion->setNotMovable();
6011 add(postConversion);
6012 break;
6013 default:
6014 // No spectre.index_masking of i32 results required, as the generated
6015 // stub takes care of that.
6016 break;
6020 // The resume point has to be attached to the post-conversion instruction
6021 // (if present) instead of to the call. This way, if the call triggers an
6022 // invalidation bailout, we will have the BigInt value on the Baseline stack.
6023 // Potential alternative solution: attach the resume point to the call and
6024 // have bailouts turn the Int64 value into a BigInt, maybe with a recover
6025 // instruction.
6026 pushResult(postConversion);
6027 return resumeAfterUnchecked(postConversion);
6030 MDefinition* WarpCacheIRTranspiler::convertWasmArg(MDefinition* arg,
6031 wasm::ValType::Kind kind) {
6032 // An invariant in this code is that any type conversion operation that has
6033 // externally visible effects, such as invoking valueOf on an object argument,
6034 // must bailout so that we don't have to worry about replaying effects during
6035 // argument conversion.
6036 MInstruction* conversion = nullptr;
6037 switch (kind) {
6038 case wasm::ValType::I32:
6039 conversion = MTruncateToInt32::New(alloc(), arg);
6040 break;
6041 case wasm::ValType::I64:
6042 conversion = MToInt64::New(alloc(), arg);
6043 break;
6044 case wasm::ValType::F32:
6045 conversion = MToFloat32::New(alloc(), arg);
6046 break;
6047 case wasm::ValType::F64:
6048 conversion = MToDouble::New(alloc(), arg);
6049 break;
6050 case wasm::ValType::V128:
6051 MOZ_CRASH("Unexpected type for Wasm JitEntry");
6052 case wasm::ValType::Ref:
6053 // Transform the JS representation into an AnyRef representation.
6054 // The resulting type is MIRType::WasmAnyRef. These cases are all
6055 // effect-free.
6056 switch (arg->type()) {
6057 case MIRType::Object:
6058 conversion = MWasmAnyRefFromJSObject::New(alloc(), arg);
6059 break;
6060 case MIRType::String:
6061 conversion = MWasmAnyRefFromJSString::New(alloc(), arg);
6062 break;
6063 case MIRType::Null:
6064 arg->setImplicitlyUsedUnchecked();
6065 conversion = MWasmNullConstant::New(alloc());
6066 break;
6067 default:
6068 conversion = MWasmAnyRefFromJSValue::New(alloc(), arg);
6069 break;
6071 break;
6074 add(conversion);
6075 return conversion;
6078 bool WarpCacheIRTranspiler::emitGuardWasmArg(ValOperandId argId,
6079 wasm::ValType::Kind kind) {
6080 MDefinition* arg = getOperand(argId);
6081 MDefinition* conversion = convertWasmArg(arg, kind);
6083 setOperand(argId, conversion);
6084 return true;
6087 bool WarpCacheIRTranspiler::emitCallGetterResult(CallKind kind,
6088 ValOperandId receiverId,
6089 uint32_t getterOffset,
6090 bool sameRealm,
6091 uint32_t nargsAndFlagsOffset) {
6092 MDefinition* receiver = getOperand(receiverId);
6093 MDefinition* getter = objectStubField(getterOffset);
6094 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
6095 // We are transpiling to generate the correct guards. We also update the
6096 // CallInfo to use the correct arguments. Code for the inlined getter
6097 // itself will be generated in WarpBuilder::buildInlinedCall.
6098 callInfo_->initForGetterCall(getter, receiver);
6099 callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
6101 // Make sure there's enough room to push the arguments on the stack.
6102 if (!current->ensureHasSlots(2)) {
6103 return false;
6106 return true;
6109 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
6110 uint16_t nargs = nargsAndFlags >> 16;
6111 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
6112 WrappedFunction* wrappedTarget =
6113 maybeWrappedFunction(getter, kind, nargs, flags);
6115 bool ignoresRval = loc_.resultIsPopped();
6116 CallInfo callInfo(alloc(), /* constructing = */ false, ignoresRval);
6117 callInfo.initForGetterCall(getter, receiver);
6119 MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, wrappedTarget);
6120 if (!call) {
6121 return false;
6124 if (sameRealm) {
6125 call->setNotCrossRealm();
6128 addEffectful(call);
6129 pushResult(call);
6131 return resumeAfter(call);
6134 bool WarpCacheIRTranspiler::emitCallScriptedGetterResult(
6135 ValOperandId receiverId, uint32_t getterOffset, bool sameRealm,
6136 uint32_t nargsAndFlagsOffset) {
6137 return emitCallGetterResult(CallKind::Scripted, receiverId, getterOffset,
6138 sameRealm, nargsAndFlagsOffset);
6141 bool WarpCacheIRTranspiler::emitCallInlinedGetterResult(
6142 ValOperandId receiverId, uint32_t getterOffset, uint32_t icScriptOffset,
6143 bool sameRealm, uint32_t nargsAndFlagsOffset) {
6144 return emitCallGetterResult(CallKind::Scripted, receiverId, getterOffset,
6145 sameRealm, nargsAndFlagsOffset);
6148 bool WarpCacheIRTranspiler::emitCallNativeGetterResult(
6149 ValOperandId receiverId, uint32_t getterOffset, bool sameRealm,
6150 uint32_t nargsAndFlagsOffset) {
6151 return emitCallGetterResult(CallKind::Native, receiverId, getterOffset,
6152 sameRealm, nargsAndFlagsOffset);
6155 bool WarpCacheIRTranspiler::emitCallSetter(CallKind kind,
6156 ObjOperandId receiverId,
6157 uint32_t setterOffset,
6158 ValOperandId rhsId, bool sameRealm,
6159 uint32_t nargsAndFlagsOffset) {
6160 MDefinition* receiver = getOperand(receiverId);
6161 MDefinition* setter = objectStubField(setterOffset);
6162 MDefinition* rhs = getOperand(rhsId);
6163 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
6164 // We are transpiling to generate the correct guards. We also update the
6165 // CallInfo to use the correct arguments. Code for the inlined setter
6166 // itself will be generated in WarpBuilder::buildInlinedCall.
6167 callInfo_->initForSetterCall(setter, receiver, rhs);
6168 callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
6170 // Make sure there's enough room to push the arguments on the stack.
6171 if (!current->ensureHasSlots(3)) {
6172 return false;
6175 return true;
6178 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
6179 uint16_t nargs = nargsAndFlags >> 16;
6180 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
6181 WrappedFunction* wrappedTarget =
6182 maybeWrappedFunction(setter, kind, nargs, flags);
6184 CallInfo callInfo(alloc(), /* constructing = */ false,
6185 /* ignoresReturnValue = */ true);
6186 callInfo.initForSetterCall(setter, receiver, rhs);
6188 MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, wrappedTarget);
6189 if (!call) {
6190 return false;
6193 if (sameRealm) {
6194 call->setNotCrossRealm();
6197 addEffectful(call);
6198 return resumeAfter(call);
6201 bool WarpCacheIRTranspiler::emitCallScriptedSetter(
6202 ObjOperandId receiverId, uint32_t setterOffset, ValOperandId rhsId,
6203 bool sameRealm, uint32_t nargsAndFlagsOffset) {
6204 return emitCallSetter(CallKind::Scripted, receiverId, setterOffset, rhsId,
6205 sameRealm, nargsAndFlagsOffset);
6208 bool WarpCacheIRTranspiler::emitCallInlinedSetter(
6209 ObjOperandId receiverId, uint32_t setterOffset, ValOperandId rhsId,
6210 uint32_t icScriptOffset, bool sameRealm, uint32_t nargsAndFlagsOffset) {
6211 return emitCallSetter(CallKind::Scripted, receiverId, setterOffset, rhsId,
6212 sameRealm, nargsAndFlagsOffset);
6215 bool WarpCacheIRTranspiler::emitCallNativeSetter(ObjOperandId receiverId,
6216 uint32_t setterOffset,
6217 ValOperandId rhsId,
6218 bool sameRealm,
6219 uint32_t nargsAndFlagsOffset) {
6220 return emitCallSetter(CallKind::Native, receiverId, setterOffset, rhsId,
6221 sameRealm, nargsAndFlagsOffset);
6224 bool WarpCacheIRTranspiler::emitMetaScriptedThisShape(
6225 uint32_t thisShapeOffset) {
6226 SharedShape* shape = &shapeStubField(thisShapeOffset)->asShared();
6227 MOZ_ASSERT(shape->getObjectClass() == &PlainObject::class_);
6229 MConstant* shapeConst = MConstant::NewShape(alloc(), shape);
6230 add(shapeConst);
6232 // TODO: support pre-tenuring.
6233 gc::Heap heap = gc::Heap::Default;
6235 uint32_t numFixedSlots = shape->numFixedSlots();
6236 uint32_t numDynamicSlots = NativeObject::calculateDynamicSlots(shape);
6237 gc::AllocKind kind = gc::GetGCObjectKind(numFixedSlots);
6238 MOZ_ASSERT(gc::CanChangeToBackgroundAllocKind(kind, &PlainObject::class_));
6239 kind = gc::ForegroundToBackgroundAllocKind(kind);
6241 auto* createThis = MNewPlainObject::New(alloc(), shapeConst, numFixedSlots,
6242 numDynamicSlots, kind, heap);
6243 add(createThis);
6245 callInfo_->thisArg()->setImplicitlyUsedUnchecked();
6246 callInfo_->setThis(createThis);
6247 return true;
6250 bool WarpCacheIRTranspiler::emitReturnFromIC() { return true; }
6252 bool WarpCacheIRTranspiler::emitBailout() {
6253 auto* bail = MBail::New(alloc());
6254 add(bail);
6256 return true;
6259 bool WarpCacheIRTranspiler::emitAssertPropertyLookup(ObjOperandId objId,
6260 uint32_t idOffset,
6261 uint32_t slotOffset) {
6262 // We currently only emit checks in baseline.
6263 return true;
6266 bool WarpCacheIRTranspiler::emitAssertRecoveredOnBailoutResult(
6267 ValOperandId valId, bool mustBeRecovered) {
6268 MDefinition* val = getOperand(valId);
6270 // Don't assert for recovered instructions when recovering is disabled.
6271 if (JitOptions.disableRecoverIns) {
6272 pushResult(constant(UndefinedValue()));
6273 return true;
6276 if (JitOptions.checkRangeAnalysis) {
6277 // If we are checking the range of all instructions, then the guards
6278 // inserted by Range Analysis prevent the use of recover instruction. Thus,
6279 // we just disable these checks.
6280 pushResult(constant(UndefinedValue()));
6281 return true;
6284 auto* assert = MAssertRecoveredOnBailout::New(alloc(), val, mustBeRecovered);
6285 addEffectfulUnsafe(assert);
6286 current->push(assert);
6288 // Create an instruction sequence which implies that the argument of the
6289 // assertRecoveredOnBailout function would be encoded at least in one
6290 // Snapshot.
6291 auto* nop = MNop::New(alloc());
6292 add(nop);
6294 auto* resumePoint = MResumePoint::New(
6295 alloc(), nop->block(), loc_.toRawBytecode(), ResumeMode::ResumeAfter);
6296 if (!resumePoint) {
6297 return false;
6299 nop->setResumePoint(resumePoint);
6301 auto* encode = MEncodeSnapshot::New(alloc());
6302 addEffectfulUnsafe(encode);
6304 current->pop();
6306 pushResult(constant(UndefinedValue()));
6307 return true;
6310 bool WarpCacheIRTranspiler::emitGuardNoAllocationMetadataBuilder(
6311 uint32_t builderAddrOffset) {
6312 // This is a no-op because we discard all JIT code when set an allocation
6313 // metadata callback.
6314 return true;
6317 bool WarpCacheIRTranspiler::emitNewPlainObjectResult(uint32_t numFixedSlots,
6318 uint32_t numDynamicSlots,
6319 gc::AllocKind allocKind,
6320 uint32_t shapeOffset,
6321 uint32_t siteOffset) {
6322 Shape* shape = shapeStubField(shapeOffset);
6323 gc::Heap heap = allocSiteInitialHeapField(siteOffset);
6325 auto* shapeConstant = MConstant::NewShape(alloc(), shape);
6326 add(shapeConstant);
6328 auto* obj = MNewPlainObject::New(alloc(), shapeConstant, numFixedSlots,
6329 numDynamicSlots, allocKind, heap);
6330 add(obj);
6332 pushResult(obj);
6333 return true;
6336 bool WarpCacheIRTranspiler::emitNewArrayObjectResult(uint32_t length,
6337 uint32_t shapeOffset,
6338 uint32_t siteOffset) {
6339 Shape* shape = shapeStubField(shapeOffset);
6340 gc::Heap heap = allocSiteInitialHeapField(siteOffset);
6342 auto* shapeConstant = MConstant::NewShape(alloc(), shape);
6343 add(shapeConstant);
6345 auto* obj = MNewArrayObject::New(alloc(), shapeConstant, length, heap);
6346 add(obj);
6348 pushResult(obj);
6349 return true;
6352 bool WarpCacheIRTranspiler::emitCloseIterScriptedResult(ObjOperandId iterId,
6353 ObjOperandId calleeId,
6354 CompletionKind kind,
6355 uint32_t calleeNargs) {
6356 MDefinition* iter = getOperand(iterId);
6357 MDefinition* callee = getOperand(calleeId);
6359 WrappedFunction* wrappedTarget = maybeCallTarget(callee, CallKind::Scripted);
6360 MOZ_ASSERT(wrappedTarget);
6361 MOZ_ASSERT(wrappedTarget->nargs() == calleeNargs);
6362 MOZ_ASSERT(wrappedTarget->hasJitEntry());
6364 bool constructing = false;
6365 bool ignoresRval = false;
6366 bool needsThisCheck = false;
6367 bool isDOMCall = false;
6368 CallInfo callInfo(alloc(), constructing, ignoresRval);
6369 callInfo.initForCloseIter(iter, callee);
6370 MCall* call = makeCall(callInfo, needsThisCheck, wrappedTarget, isDOMCall);
6371 if (!call) {
6372 return false;
6374 addEffectful(call);
6375 if (kind == CompletionKind::Throw) {
6376 return resumeAfter(call);
6379 // If we bail out here, after the call but before the CheckIsObj, we
6380 // can't simply resume in the baseline interpreter. If we resume
6381 // after the CloseIter, we won't check the return value. If we
6382 // resume at the CloseIter, we will call the |return| method twice.
6383 // Instead, we use a special resume mode that captures the
6384 // intermediate value, and then checks that it's an object while
6385 // bailing out.
6386 current->push(call);
6387 MResumePoint* resumePoint =
6388 MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
6389 ResumeMode::ResumeAfterCheckIsObject);
6390 if (!resumePoint) {
6391 return false;
6393 call->setResumePoint(resumePoint);
6394 current->pop();
6396 MCheckIsObj* check = MCheckIsObj::New(
6397 alloc(), call, uint8_t(CheckIsObjectKind::IteratorReturn));
6398 addEffectfulUnsafe(check);
6400 return resumeAfterUnchecked(check);
6403 bool WarpCacheIRTranspiler::emitGuardGlobalGeneration(
6404 uint32_t expectedOffset, uint32_t generationAddrOffset) {
6405 uint32_t expected = uint32StubField(expectedOffset);
6406 const void* generationAddr = rawPointerField(generationAddrOffset);
6408 auto guard = MGuardGlobalGeneration::New(alloc(), expected, generationAddr);
6409 add(guard);
6411 return true;
6414 #ifdef FUZZING_JS_FUZZILLI
6415 bool WarpCacheIRTranspiler::emitFuzzilliHashResult(ValOperandId valId) {
6416 MDefinition* input = getOperand(valId);
6418 auto* hash = MFuzzilliHash::New(alloc(), input);
6419 add(hash);
6421 auto* store = MFuzzilliHashStore::New(alloc(), hash);
6422 addEffectful(store);
6423 pushResult(constant(UndefinedValue()));
6425 return resumeAfter(store);
6427 #endif
6429 static void MaybeSetImplicitlyUsed(uint32_t numInstructionIdsBefore,
6430 MDefinition* input) {
6431 // When building MIR from bytecode, for each MDefinition that's an operand to
6432 // a bytecode instruction, we must either add an SSA use or set the
6433 // ImplicitlyUsed flag on that definition. The ImplicitlyUsed flag prevents
6434 // the backend from optimizing-out values that will be used by Baseline after
6435 // a bailout.
6437 // WarpBuilder uses WarpPoppedValueUseChecker to assert this invariant in
6438 // debug builds.
6440 // This function is responsible for setting the ImplicitlyUsed flag for an
6441 // input when using the transpiler. It looks at the input's most recent use
6442 // and if that's an instruction that was added while transpiling this JSOp
6443 // (based on the MIR instruction id) we don't set the ImplicitlyUsed flag.
6445 if (input->isImplicitlyUsed()) {
6446 // Nothing to do.
6447 return;
6450 // If the most recent use of 'input' is an instruction we just added, there is
6451 // nothing to do.
6452 MDefinition* inputUse = input->maybeMostRecentlyAddedDefUse();
6453 if (inputUse && inputUse->id() >= numInstructionIdsBefore) {
6454 return;
6457 // The transpiler didn't add a use for 'input'.
6458 input->setImplicitlyUsed();
6461 bool jit::TranspileCacheIRToMIR(WarpBuilder* builder, BytecodeLocation loc,
6462 const WarpCacheIR* cacheIRSnapshot,
6463 std::initializer_list<MDefinition*> inputs,
6464 CallInfo* maybeCallInfo) {
6465 uint32_t numInstructionIdsBefore =
6466 builder->mirGen().graph().getNumInstructionIds();
6468 WarpCacheIRTranspiler transpiler(builder, loc, maybeCallInfo,
6469 cacheIRSnapshot);
6470 if (!transpiler.transpile(inputs)) {
6471 return false;
6474 for (MDefinition* input : inputs) {
6475 MaybeSetImplicitlyUsed(numInstructionIdsBefore, input);
6478 if (maybeCallInfo) {
6479 auto maybeSetFlag = [numInstructionIdsBefore](MDefinition* def) {
6480 MaybeSetImplicitlyUsed(numInstructionIdsBefore, def);
6482 maybeCallInfo->forEachCallOperand(maybeSetFlag);
6485 return true;