Bug 1837620 - Part 1: Remove baseline ICs that guard shapes when the shape becomes...
[gecko.git] / js / src / jit / WarpCacheIRTranspiler.cpp
blob49687d30981937c3671253c994bbf252d9c0e23e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "jit/WarpCacheIRTranspiler.h"
9 #include "mozilla/Casting.h"
10 #include "mozilla/Maybe.h"
12 #include "jsmath.h"
14 #include "builtin/DataViewObject.h"
15 #include "builtin/MapObject.h"
16 #include "jit/AtomicOp.h"
17 #include "jit/CacheIR.h"
18 #include "jit/CacheIRCompiler.h"
19 #include "jit/CacheIROpsGenerated.h"
20 #include "jit/CacheIRReader.h"
21 #include "jit/LIR.h"
22 #include "jit/MIR.h"
23 #include "jit/MIRGenerator.h"
24 #include "jit/MIRGraph.h"
25 #include "jit/WarpBuilder.h"
26 #include "jit/WarpBuilderShared.h"
27 #include "jit/WarpSnapshot.h"
28 #include "js/ScalarType.h" // js::Scalar::Type
29 #include "vm/ArgumentsObject.h"
30 #include "vm/BytecodeLocation.h"
31 #include "wasm/WasmCode.h"
33 #include "gc/ObjectKind-inl.h"
34 #include "vm/NativeObject-inl.h"
35 #include "wasm/WasmInstance-inl.h"
37 using namespace js;
38 using namespace js::jit;
40 // The CacheIR transpiler generates MIR from Baseline CacheIR.
41 class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
42 WarpBuilder* builder_;
43 BytecodeLocation loc_;
44 const CacheIRStubInfo* stubInfo_;
45 const uint8_t* stubData_;
47 // Vector mapping OperandId to corresponding MDefinition.
48 using MDefinitionStackVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
49 MDefinitionStackVector operands_;
51 CallInfo* callInfo_;
53 // Array mapping call arguments to OperandId.
54 using ArgumentKindArray =
55 mozilla::EnumeratedArray<ArgumentKind, ArgumentKind::NumKinds, OperandId>;
56 ArgumentKindArray argumentOperandIds_;
58 void setArgumentId(ArgumentKind kind, OperandId id) {
59 MOZ_ASSERT(kind != ArgumentKind::Callee);
60 MOZ_ASSERT(!argumentOperandIds_[kind].valid());
61 argumentOperandIds_[kind] = id;
64 void updateArgumentsFromOperands();
66 #ifdef DEBUG
67 // Used to assert that there is only one effectful instruction
68 // per stub. And that this instruction has a resume point.
69 MInstruction* effectful_ = nullptr;
70 bool pushedResult_ = false;
71 #endif
73 inline void addUnchecked(MInstruction* ins) {
74 current->add(ins);
76 // If we have not set a more specific bailout kind, mark this instruction
77 // as transpiled CacheIR. If one of these instructions bails out, we
78 // expect to hit the baseline fallback stub and invalidate the Warp script
79 // in tryAttach.
80 if (ins->bailoutKind() == BailoutKind::Unknown) {
81 ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
85 inline void add(MInstruction* ins) {
86 MOZ_ASSERT(!ins->isEffectful());
87 addUnchecked(ins);
90 inline void addEffectful(MInstruction* ins) {
91 MOZ_ASSERT(ins->isEffectful());
92 MOZ_ASSERT(!effectful_, "Can only have one effectful instruction");
93 addUnchecked(ins);
94 #ifdef DEBUG
95 effectful_ = ins;
96 #endif
99 // Bypasses all checks in addEffectful. Only used for testing functions.
100 inline void addEffectfulUnsafe(MInstruction* ins) {
101 MOZ_ASSERT(ins->isEffectful());
102 addUnchecked(ins);
105 [[nodiscard]] bool resumeAfterUnchecked(MInstruction* ins) {
106 return WarpBuilderShared::resumeAfter(ins, loc_);
108 [[nodiscard]] bool resumeAfter(MInstruction* ins) {
109 MOZ_ASSERT(effectful_ == ins);
110 return resumeAfterUnchecked(ins);
113 // CacheIR instructions writing to the IC's result register (the *Result
114 // instructions) must call this to push the result onto the virtual stack.
115 void pushResult(MDefinition* result) {
116 MOZ_ASSERT(!pushedResult_, "Can't have more than one result");
117 current->push(result);
118 #ifdef DEBUG
119 pushedResult_ = true;
120 #endif
123 MDefinition* getOperand(OperandId id) const { return operands_[id.id()]; }
125 void setOperand(OperandId id, MDefinition* def) { operands_[id.id()] = def; }
127 [[nodiscard]] bool defineOperand(OperandId id, MDefinition* def) {
128 MOZ_ASSERT(id.id() == operands_.length());
129 return operands_.append(def);
132 uintptr_t readStubWord(uint32_t offset) {
133 return stubInfo_->getStubRawWord(stubData_, offset);
136 Shape* shapeStubField(uint32_t offset) {
137 return reinterpret_cast<Shape*>(readStubWord(offset));
139 GetterSetter* getterSetterStubField(uint32_t offset) {
140 return reinterpret_cast<GetterSetter*>(readStubWord(offset));
142 const JSClass* classStubField(uint32_t offset) {
143 return reinterpret_cast<const JSClass*>(readStubWord(offset));
145 JSString* stringStubField(uint32_t offset) {
146 return reinterpret_cast<JSString*>(readStubWord(offset));
148 JS::Symbol* symbolStubField(uint32_t offset) {
149 return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
151 BaseScript* baseScriptStubField(uint32_t offset) {
152 return reinterpret_cast<BaseScript*>(readStubWord(offset));
154 const JSJitInfo* jitInfoStubField(uint32_t offset) {
155 return reinterpret_cast<const JSJitInfo*>(readStubWord(offset));
157 JSNative jsnativeStubField(uint32_t offset) {
158 return reinterpret_cast<JSNative>(readStubWord(offset));
160 JS::ExpandoAndGeneration* expandoAndGenerationField(uint32_t offset) {
161 return reinterpret_cast<JS::ExpandoAndGeneration*>(readStubWord(offset));
163 const wasm::FuncExport* wasmFuncExportField(uint32_t offset) {
164 return reinterpret_cast<const wasm::FuncExport*>(readStubWord(offset));
166 NativeIteratorListHead* nativeIteratorListHeadStubField(uint32_t offset) {
167 return reinterpret_cast<NativeIteratorListHead*>(readStubWord(offset));
169 gc::Heap allocSiteInitialHeapField(uint32_t offset) {
170 uintptr_t word = readStubWord(offset);
171 MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) ||
172 word == uintptr_t(gc::Heap::Tenured));
173 return gc::Heap(word);
175 const void* rawPointerField(uint32_t offset) {
176 return reinterpret_cast<const void*>(readStubWord(offset));
178 jsid idStubField(uint32_t offset) {
179 return jsid::fromRawBits(readStubWord(offset));
181 int32_t int32StubField(uint32_t offset) {
182 return static_cast<int32_t>(readStubWord(offset));
184 uint32_t uint32StubField(uint32_t offset) {
185 return static_cast<uint32_t>(readStubWord(offset));
187 uint64_t uint64StubField(uint32_t offset) {
188 return static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
190 Value valueStubField(uint32_t offset) {
191 uint64_t raw =
192 static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
193 Value val = Value::fromRawBits(raw);
194 MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
195 return val;
197 double doubleStubField(uint32_t offset) {
198 uint64_t raw =
199 static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
200 return mozilla::BitwiseCast<double>(raw);
203 // This must only be called when the caller knows the object is tenured and
204 // not a nursery index.
205 JSObject* tenuredObjectStubField(uint32_t offset) {
206 WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
207 return field.toObject();
210 // Returns either MConstant or MNurseryIndex. See WarpObjectField.
211 MInstruction* objectStubField(uint32_t offset);
213 const JSClass* classForGuardClassKind(GuardClassKind kind);
215 [[nodiscard]] bool emitGuardTo(ValOperandId inputId, MIRType type);
217 [[nodiscard]] bool emitToString(OperandId inputId, StringOperandId resultId);
219 template <typename T>
220 [[nodiscard]] bool emitDoubleBinaryArithResult(NumberOperandId lhsId,
221 NumberOperandId rhsId);
223 template <typename T>
224 [[nodiscard]] bool emitInt32BinaryArithResult(Int32OperandId lhsId,
225 Int32OperandId rhsId);
227 template <typename T>
228 [[nodiscard]] bool emitBigIntBinaryArithResult(BigIntOperandId lhsId,
229 BigIntOperandId rhsId);
231 template <typename T>
232 [[nodiscard]] bool emitBigIntBinaryArithEffectfulResult(
233 BigIntOperandId lhsId, BigIntOperandId rhsId);
235 template <typename T>
236 [[nodiscard]] bool emitBigIntUnaryArithResult(BigIntOperandId inputId);
238 [[nodiscard]] bool emitCompareResult(JSOp op, OperandId lhsId,
239 OperandId rhsId,
240 MCompare::CompareType compareType);
242 [[nodiscard]] bool emitTruthyResult(OperandId inputId);
244 [[nodiscard]] bool emitNewIteratorResult(MNewIterator::Type type,
245 uint32_t templateObjectOffset);
247 MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
249 [[nodiscard]] MInstruction* convertToBoolean(MDefinition* input);
251 bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind,
252 ObjOperandId objId, uint32_t offsetOffset,
253 ValOperandId rhsId, uint32_t newShapeOffset);
255 void addDataViewData(MDefinition* obj, Scalar::Type type,
256 MDefinition** offset, MInstruction** elements);
258 [[nodiscard]] bool emitAtomicsBinaryOp(ObjOperandId objId,
259 IntPtrOperandId indexId,
260 uint32_t valueId,
261 Scalar::Type elementType,
262 bool forEffect, AtomicOp op);
264 [[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId,
265 uint32_t slotIndex);
267 // Calls are either Native (native function without a JitEntry),
268 // a DOM Native (native function with a JitInfo OpType::Method),
269 // or Scripted (scripted function or native function with a JitEntry).
270 enum class CallKind { Native, DOM, Scripted };
272 [[nodiscard]] bool updateCallInfo(MDefinition* callee, CallFlags flags);
274 [[nodiscard]] bool emitCallFunction(ObjOperandId calleeId,
275 Int32OperandId argcId,
276 mozilla::Maybe<ObjOperandId> thisObjId,
277 CallFlags flags, CallKind kind);
278 [[nodiscard]] bool emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
279 CallFlags flags);
281 MDefinition* convertWasmArg(MDefinition* arg, wasm::ValType::Kind kind);
283 WrappedFunction* maybeWrappedFunction(MDefinition* callee, CallKind kind,
284 uint16_t nargs, FunctionFlags flags);
285 WrappedFunction* maybeCallTarget(MDefinition* callee, CallKind kind);
287 bool maybeCreateThis(MDefinition* callee, CallFlags flags, CallKind kind);
289 [[nodiscard]] bool emitCallGetterResult(CallKind kind,
290 ValOperandId receiverId,
291 uint32_t getterOffset, bool sameRealm,
292 uint32_t nargsAndFlagsOffset);
293 [[nodiscard]] bool emitCallSetter(CallKind kind, ObjOperandId receiverId,
294 uint32_t setterOffset, ValOperandId rhsId,
295 bool sameRealm,
296 uint32_t nargsAndFlagsOffset);
298 CACHE_IR_TRANSPILER_GENERATED
300 public:
301 WarpCacheIRTranspiler(WarpBuilder* builder, BytecodeLocation loc,
302 CallInfo* callInfo, const WarpCacheIR* cacheIRSnapshot)
303 : WarpBuilderShared(builder->snapshot(), builder->mirGen(),
304 builder->currentBlock()),
305 builder_(builder),
306 loc_(loc),
307 stubInfo_(cacheIRSnapshot->stubInfo()),
308 stubData_(cacheIRSnapshot->stubData()),
309 callInfo_(callInfo) {}
311 [[nodiscard]] bool transpile(std::initializer_list<MDefinition*> inputs);
314 bool WarpCacheIRTranspiler::transpile(
315 std::initializer_list<MDefinition*> inputs) {
316 if (!operands_.append(inputs.begin(), inputs.end())) {
317 return false;
320 CacheIRReader reader(stubInfo_);
321 do {
322 CacheOp op = reader.readOp();
323 switch (op) {
324 #define DEFINE_OP(op, ...) \
325 case CacheOp::op: \
326 if (!emit##op(reader)) { \
327 return false; \
329 break;
330 CACHE_IR_TRANSPILER_OPS(DEFINE_OP)
331 #undef DEFINE_OP
333 default:
334 fprintf(stderr, "Unsupported op: %s\n", CacheIROpNames[size_t(op)]);
335 MOZ_CRASH("Unsupported op");
337 } while (reader.more());
339 // Effectful instructions should have a resume point. MIonToWasmCall is an
340 // exception: we can attach the resume point to the MInt64ToBigInt instruction
341 // instead.
342 MOZ_ASSERT_IF(effectful_,
343 effectful_->resumePoint() || effectful_->isIonToWasmCall());
344 return true;
347 MInstruction* WarpCacheIRTranspiler::objectStubField(uint32_t offset) {
348 WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
350 if (field.isNurseryIndex()) {
351 auto* ins = MNurseryObject::New(alloc(), field.toNurseryIndex());
352 add(ins);
353 return ins;
356 auto* ins = MConstant::NewObject(alloc(), field.toObject());
357 add(ins);
358 return ins;
361 bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
362 GuardClassKind kind) {
363 MDefinition* def = getOperand(objId);
365 MInstruction* ins;
366 if (kind == GuardClassKind::JSFunction) {
367 ins = MGuardToFunction::New(alloc(), def);
368 } else {
369 const JSClass* classp = classForGuardClassKind(kind);
370 ins = MGuardToClass::New(alloc(), def, classp);
373 add(ins);
375 setOperand(objId, ins);
376 return true;
379 const JSClass* WarpCacheIRTranspiler::classForGuardClassKind(
380 GuardClassKind kind) {
381 switch (kind) {
382 case GuardClassKind::Array:
383 return &ArrayObject::class_;
384 case GuardClassKind::PlainObject:
385 return &PlainObject::class_;
386 case GuardClassKind::ArrayBuffer:
387 return &ArrayBufferObject::class_;
388 case GuardClassKind::SharedArrayBuffer:
389 return &SharedArrayBufferObject::class_;
390 case GuardClassKind::DataView:
391 return &DataViewObject::class_;
392 case GuardClassKind::MappedArguments:
393 return &MappedArgumentsObject::class_;
394 case GuardClassKind::UnmappedArguments:
395 return &UnmappedArgumentsObject::class_;
396 case GuardClassKind::WindowProxy:
397 return mirGen().runtime->maybeWindowProxyClass();
398 case GuardClassKind::Set:
399 return &SetObject::class_;
400 case GuardClassKind::Map:
401 return &MapObject::class_;
402 case GuardClassKind::BoundFunction:
403 return &BoundFunctionObject::class_;
404 case GuardClassKind::JSFunction:
405 break;
407 MOZ_CRASH("unexpected kind");
410 bool WarpCacheIRTranspiler::emitGuardAnyClass(ObjOperandId objId,
411 uint32_t claspOffset) {
412 MDefinition* def = getOperand(objId);
413 const JSClass* classp = classStubField(claspOffset);
415 auto* ins = MGuardToClass::New(alloc(), def, classp);
416 add(ins);
418 setOperand(objId, ins);
419 return true;
422 bool WarpCacheIRTranspiler::emitGuardShape(ObjOperandId objId,
423 uint32_t shapeOffset) {
424 MDefinition* def = getOperand(objId);
426 // No read barrier is required because snapshot data is not weak and is traced
427 // as part of IonCompileTask.
428 Shape* shape = shapeStubField(shapeOffset);
430 auto* ins = MGuardShape::New(alloc(), def, shape);
431 add(ins);
433 setOperand(objId, ins);
434 return true;
437 bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId,
438 uint32_t shapesOffset) {
439 MDefinition* def = getOperand(objId);
440 MInstruction* shapeList = objectStubField(shapesOffset);
442 auto* ins = MGuardMultipleShapes::New(alloc(), def, shapeList);
443 if (builder_->isMonomorphicInlined()) {
444 ins->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding);
446 add(ins);
448 setOperand(objId, ins);
449 return true;
452 bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
453 MDefinition* def = getOperand(objId);
455 auto* ins = MGuardNullProto::New(alloc(), def);
456 add(ins);
458 setOperand(objId, ins);
459 return true;
462 bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId) {
463 MDefinition* obj = getOperand(objId);
465 auto* ins = MGuardIsNativeObject::New(alloc(), obj);
466 add(ins);
468 setOperand(objId, ins);
469 return true;
472 bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId) {
473 MDefinition* obj = getOperand(objId);
475 auto* ins = MGuardIsProxy::New(alloc(), obj);
476 add(ins);
478 setOperand(objId, ins);
479 return true;
482 bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId) {
483 MDefinition* obj = getOperand(objId);
485 auto* ins = MGuardIsNotProxy::New(alloc(), obj);
486 add(ins);
488 setOperand(objId, ins);
489 return true;
492 bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
493 MDefinition* obj = getOperand(objId);
495 auto* ins = MGuardIsNotDOMProxy::New(alloc(), obj);
496 add(ins);
498 setOperand(objId, ins);
499 return true;
502 bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
503 ObjOperandId objId, uint32_t idOffset, uint32_t getterSetterOffset) {
504 MDefinition* obj = getOperand(objId);
505 jsid id = idStubField(idOffset);
506 GetterSetter* gs = getterSetterStubField(getterSetterOffset);
508 auto* ins = MGuardHasGetterSetter::New(alloc(), obj, id, gs);
509 add(ins);
511 setOperand(objId, ins);
512 return true;
515 bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId,
516 uint32_t idOffset) {
517 MDefinition* obj = getOperand(objId);
518 jsid id = idStubField(idOffset);
520 auto* ins = MProxyGet::New(alloc(), obj, id);
521 addEffectful(ins);
523 pushResult(ins);
524 return resumeAfter(ins);
527 bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId,
528 ValOperandId idId) {
529 MDefinition* obj = getOperand(objId);
530 MDefinition* id = getOperand(idId);
532 auto* ins = MProxyGetByValue::New(alloc(), obj, id);
533 addEffectful(ins);
535 pushResult(ins);
536 return resumeAfter(ins);
539 bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId,
540 ValOperandId idId,
541 bool hasOwn) {
542 MDefinition* obj = getOperand(objId);
543 MDefinition* id = getOperand(idId);
545 auto* ins = MProxyHasProp::New(alloc(), obj, id, hasOwn);
546 addEffectful(ins);
548 pushResult(ins);
549 return resumeAfter(ins);
552 bool WarpCacheIRTranspiler::emitProxySet(ObjOperandId objId, uint32_t idOffset,
553 ValOperandId rhsId, bool strict) {
554 MDefinition* obj = getOperand(objId);
555 jsid id = idStubField(idOffset);
556 MDefinition* rhs = getOperand(rhsId);
558 auto* ins = MProxySet::New(alloc(), obj, rhs, id, strict);
559 addEffectful(ins);
561 return resumeAfter(ins);
564 bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId,
565 ValOperandId idId,
566 ValOperandId rhsId,
567 bool strict) {
568 MDefinition* obj = getOperand(objId);
569 MDefinition* id = getOperand(idId);
570 MDefinition* rhs = getOperand(rhsId);
572 auto* ins = MProxySetByValue::New(alloc(), obj, id, rhs, strict);
573 addEffectful(ins);
575 return resumeAfter(ins);
578 bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId,
579 bool strict,
580 ValOperandId rhsId) {
581 MDefinition* obj = getOperand(objId);
582 MDefinition* rhs = getOperand(rhsId);
584 auto* ins = MCallSetArrayLength::New(alloc(), obj, rhs, strict);
585 addEffectful(ins);
587 return resumeAfter(ins);
590 bool WarpCacheIRTranspiler::emitCallDOMGetterResult(ObjOperandId objId,
591 uint32_t jitInfoOffset) {
592 MDefinition* obj = getOperand(objId);
593 const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
595 MInstruction* ins;
596 if (jitInfo->isAlwaysInSlot) {
597 ins = MGetDOMMember::New(alloc(), jitInfo, obj, nullptr, nullptr);
598 } else {
599 // TODO(post-Warp): realms, guard operands (movable?).
600 ins = MGetDOMProperty::New(alloc(), jitInfo, DOMObjectKind::Native,
601 (JS::Realm*)mirGen().realm->realmPtr(), obj,
602 nullptr, nullptr);
605 if (!ins) {
606 return false;
609 if (ins->isEffectful()) {
610 addEffectful(ins);
611 pushResult(ins);
612 return resumeAfter(ins);
615 add(ins);
616 pushResult(ins);
617 return true;
620 bool WarpCacheIRTranspiler::emitCallDOMSetter(ObjOperandId objId,
621 uint32_t jitInfoOffset,
622 ValOperandId rhsId) {
623 MDefinition* obj = getOperand(objId);
624 const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
625 MDefinition* value = getOperand(rhsId);
627 MOZ_ASSERT(jitInfo->type() == JSJitInfo::Setter);
628 auto* set =
629 MSetDOMProperty::New(alloc(), jitInfo->setter, DOMObjectKind::Native,
630 (JS::Realm*)mirGen().realm->realmPtr(), obj, value);
631 addEffectful(set);
632 return resumeAfter(set);
635 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValue(ObjOperandId objId,
636 ValOperandId resultId) {
637 MDefinition* proxy = getOperand(objId);
639 auto* ins = MLoadDOMExpandoValue::New(alloc(), proxy);
640 add(ins);
642 return defineOperand(resultId, ins);
645 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueGuardGeneration(
646 ObjOperandId objId, uint32_t expandoAndGenerationOffset,
647 uint32_t generationOffset, ValOperandId resultId) {
648 MDefinition* proxy = getOperand(objId);
649 JS::ExpandoAndGeneration* expandoAndGeneration =
650 expandoAndGenerationField(expandoAndGenerationOffset);
651 uint64_t generation = uint64StubField(generationOffset);
653 auto* ins = MLoadDOMExpandoValueGuardGeneration::New(
654 alloc(), proxy, expandoAndGeneration, generation);
655 add(ins);
657 return defineOperand(resultId, ins);
660 bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueIgnoreGeneration(
661 ObjOperandId objId, ValOperandId resultId) {
662 MDefinition* proxy = getOperand(objId);
664 auto* ins = MLoadDOMExpandoValueIgnoreGeneration::New(alloc(), proxy);
665 add(ins);
667 return defineOperand(resultId, ins);
670 bool WarpCacheIRTranspiler::emitGuardDOMExpandoMissingOrGuardShape(
671 ValOperandId expandoId, uint32_t shapeOffset) {
672 MDefinition* expando = getOperand(expandoId);
673 Shape* shape = shapeStubField(shapeOffset);
675 auto* ins = MGuardDOMExpandoMissingOrGuardShape::New(alloc(), expando, shape);
676 add(ins);
678 setOperand(expandoId, ins);
679 return true;
682 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotResult(ObjOperandId objId,
683 uint32_t nameOffset) {
684 MDefinition* obj = getOperand(objId);
685 PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();
687 auto* ins = MMegamorphicLoadSlot::New(alloc(), obj, NameToId(name));
688 add(ins);
690 pushResult(ins);
691 return true;
694 bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValueResult(
695 ObjOperandId objId, ValOperandId idId) {
696 MDefinition* obj = getOperand(objId);
697 MDefinition* id = getOperand(idId);
699 auto* ins = MMegamorphicLoadSlotByValue::New(alloc(), obj, id);
700 add(ins);
702 pushResult(ins);
703 return true;
706 bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId,
707 uint32_t idOffset,
708 ValOperandId rhsId,
709 bool strict) {
710 MDefinition* obj = getOperand(objId);
711 jsid id = idStubField(idOffset);
712 MDefinition* rhs = getOperand(rhsId);
714 auto* ins = MMegamorphicStoreSlot::New(alloc(), obj, rhs, id, strict);
715 addEffectful(ins);
717 return resumeAfter(ins);
720 bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId,
721 ValOperandId idId,
722 bool hasOwn) {
723 MDefinition* obj = getOperand(objId);
724 MDefinition* id = getOperand(idId);
726 auto* ins = MMegamorphicHasProp::New(alloc(), obj, id, hasOwn);
727 add(ins);
729 pushResult(ins);
730 return true;
733 bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId,
734 ValOperandId idId,
735 ValOperandId rhsId,
736 bool strict) {
737 MDefinition* obj = getOperand(objId);
738 MDefinition* id = getOperand(idId);
739 MDefinition* rhs = getOperand(rhsId);
741 auto* ins = MMegamorphicSetElement::New(alloc(), obj, id, rhs, strict);
742 addEffectful(ins);
744 return resumeAfter(ins);
747 bool WarpCacheIRTranspiler::emitObjectToIteratorResult(
748 ObjOperandId objId, uint32_t enumeratorsAddrOffset) {
749 MDefinition* obj = getOperand(objId);
750 NativeIteratorListHead* enumeratorsAddr =
751 nativeIteratorListHeadStubField(enumeratorsAddrOffset);
753 auto* ins = MObjectToIterator::New(alloc(), obj, enumeratorsAddr);
754 addEffectful(ins);
755 pushResult(ins);
756 if (!resumeAfter(ins)) {
757 return false;
760 return true;
763 bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId) {
764 MDefinition* val = getOperand(valId);
766 auto* ins = MValueToIterator::New(alloc(), val);
767 addEffectful(ins);
769 pushResult(ins);
770 return resumeAfter(ins);
773 bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
774 ObjOperandId objId) {
775 MDefinition* obj = getOperand(objId);
777 auto* ins = MGuardIsNotArrayBufferMaybeShared::New(alloc(), obj);
778 add(ins);
780 setOperand(objId, ins);
781 return true;
784 bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
785 MDefinition* obj = getOperand(objId);
787 auto* ins = MGuardIsTypedArray::New(alloc(), obj);
788 add(ins);
790 setOperand(objId, ins);
791 return true;
794 bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
795 uint32_t protoOffset) {
796 MDefinition* def = getOperand(objId);
797 MDefinition* proto = objectStubField(protoOffset);
799 auto* ins = MGuardProto::New(alloc(), def, proto);
800 add(ins);
802 setOperand(objId, ins);
803 return true;
806 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsSpecificObject(
807 ObjOperandId objId, ObjOperandId expectedId, uint32_t slotOffset) {
808 size_t slotIndex = int32StubField(slotOffset);
809 MDefinition* obj = getOperand(objId);
810 MDefinition* expected = getOperand(expectedId);
812 auto* slots = MSlots::New(alloc(), obj);
813 add(slots);
815 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
816 add(load);
818 auto* unbox = MUnbox::New(alloc(), load, MIRType::Object, MUnbox::Fallible);
819 add(unbox);
821 auto* guard = MGuardObjectIdentity::New(alloc(), unbox, expected,
822 /* bailOnEquality = */ false);
823 add(guard);
824 return true;
827 bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId,
828 ObjOperandId objId,
829 uint32_t slotOffset) {
830 size_t slotIndex = int32StubField(slotOffset);
831 MDefinition* obj = getOperand(objId);
833 auto* slots = MSlots::New(alloc(), obj);
834 add(slots);
836 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
837 add(load);
839 return defineOperand(resultId, load);
842 bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject(
843 ObjOperandId objId, uint32_t slotOffset) {
844 size_t slotIndex = int32StubField(slotOffset);
845 MDefinition* obj = getOperand(objId);
847 auto* slots = MSlots::New(alloc(), obj);
848 add(slots);
850 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
851 add(load);
853 auto* guard = MGuardIsNotObject::New(alloc(), load);
854 add(guard);
855 return true;
858 bool WarpCacheIRTranspiler::emitGuardFixedSlotValue(ObjOperandId objId,
859 uint32_t offsetOffset,
860 uint32_t valOffset) {
861 MDefinition* obj = getOperand(objId);
863 size_t offset = int32StubField(offsetOffset);
864 Value val = valueStubField(valOffset);
866 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
868 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
869 add(load);
871 auto* guard = MGuardValue::New(alloc(), load, val);
872 add(guard);
873 return true;
876 bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId,
877 uint32_t offsetOffset,
878 uint32_t valOffset) {
879 MDefinition* obj = getOperand(objId);
881 size_t offset = int32StubField(offsetOffset);
882 Value val = valueStubField(valOffset);
884 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
886 auto* slots = MSlots::New(alloc(), obj);
887 add(slots);
889 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
890 add(load);
892 auto* guard = MGuardValue::New(alloc(), load, val);
893 add(guard);
894 return true;
897 bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId,
898 uint32_t expectedOffset) {
899 MDefinition* str = getOperand(strId);
900 JSString* expected = stringStubField(expectedOffset);
902 auto* ins = MGuardSpecificAtom::New(alloc(), str, &expected->asAtom());
903 add(ins);
905 setOperand(strId, ins);
906 return true;
909 bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId,
910 uint32_t expectedOffset) {
911 MDefinition* symbol = getOperand(symId);
912 JS::Symbol* expected = symbolStubField(expectedOffset);
914 auto* ins = MGuardSpecificSymbol::New(alloc(), symbol, expected);
915 add(ins);
917 setOperand(symId, ins);
918 return true;
921 bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId,
922 int32_t expected) {
923 MDefinition* num = getOperand(numId);
925 auto* ins = MGuardSpecificInt32::New(alloc(), num, expected);
926 add(ins);
928 setOperand(numId, ins);
929 return true;
932 bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
933 uint32_t expectedOffset) {
934 MDefinition* obj = getOperand(objId);
935 MDefinition* expected = objectStubField(expectedOffset);
937 auto* ins = MGuardObjectIdentity::New(alloc(), obj, expected,
938 /* bailOnEquality = */ false);
939 add(ins);
941 setOperand(objId, ins);
942 return true;
945 bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
946 ObjOperandId objId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
947 MDefinition* obj = getOperand(objId);
948 MDefinition* expected = objectStubField(expectedOffset);
949 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
951 uint16_t nargs = nargsAndFlags >> 16;
952 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
954 auto* ins = MGuardSpecificFunction::New(alloc(), obj, expected, nargs, flags);
955 add(ins);
957 setOperand(objId, ins);
958 return true;
961 bool WarpCacheIRTranspiler::emitGuardFunctionScript(
962 ObjOperandId funId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
963 MDefinition* fun = getOperand(funId);
964 BaseScript* expected = baseScriptStubField(expectedOffset);
965 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
967 uint16_t nargs = nargsAndFlags >> 16;
968 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
970 auto* ins = MGuardFunctionScript::New(alloc(), fun, expected, nargs, flags);
971 add(ins);
973 setOperand(funId, ins);
974 return true;
977 bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId,
978 Int32OperandId resultId) {
979 MDefinition* str = getOperand(strId);
981 auto* ins = MGuardStringToIndex::New(alloc(), str);
982 add(ins);
984 return defineOperand(resultId, ins);
987 bool WarpCacheIRTranspiler::emitGuardStringToInt32(StringOperandId strId,
988 Int32OperandId resultId) {
989 MDefinition* str = getOperand(strId);
991 auto* ins = MGuardStringToInt32::New(alloc(), str);
992 add(ins);
994 return defineOperand(resultId, ins);
997 bool WarpCacheIRTranspiler::emitGuardStringToNumber(StringOperandId strId,
998 NumberOperandId resultId) {
999 MDefinition* str = getOperand(strId);
1001 auto* ins = MGuardStringToDouble::New(alloc(), str);
1002 add(ins);
1004 return defineOperand(resultId, ins);
1007 bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId) {
1008 MDefinition* obj = getOperand(objId);
1010 auto* ins = MGuardNoDenseElements::New(alloc(), obj);
1011 add(ins);
1013 setOperand(objId, ins);
1014 return true;
1017 bool WarpCacheIRTranspiler::emitGuardFunctionHasJitEntry(ObjOperandId funId,
1018 bool constructing) {
1019 MDefinition* fun = getOperand(funId);
1020 uint16_t expectedFlags = FunctionFlags::HasJitEntryFlags(constructing);
1021 uint16_t unexpectedFlags = 0;
1023 auto* ins =
1024 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1025 add(ins);
1027 setOperand(funId, ins);
1028 return true;
1031 bool WarpCacheIRTranspiler::emitGuardFunctionHasNoJitEntry(ObjOperandId funId) {
1032 MDefinition* fun = getOperand(funId);
1033 uint16_t expectedFlags = 0;
1034 uint16_t unexpectedFlags =
1035 FunctionFlags::HasJitEntryFlags(/*isConstructing=*/false);
1037 auto* ins =
1038 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1039 add(ins);
1041 setOperand(funId, ins);
1042 return true;
1045 bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
1046 ObjOperandId funId) {
1047 MDefinition* fun = getOperand(funId);
1049 auto* ins = MGuardFunctionIsNonBuiltinCtor::New(alloc(), fun);
1050 add(ins);
1052 setOperand(funId, ins);
1053 return true;
1056 bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId) {
1057 MDefinition* fun = getOperand(funId);
1058 uint16_t expectedFlags = FunctionFlags::CONSTRUCTOR;
1059 uint16_t unexpectedFlags = 0;
1061 auto* ins =
1062 MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
1063 add(ins);
1065 setOperand(funId, ins);
1066 return true;
1069 bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId) {
1070 MDefinition* fun = getOperand(funId);
1072 auto* ins =
1073 MGuardFunctionKind::New(alloc(), fun, FunctionFlags::ClassConstructor,
1074 /*bailOnEquality=*/true);
1075 add(ins);
1077 setOperand(funId, ins);
1078 return true;
1081 bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId) {
1082 MDefinition* array = getOperand(arrayId);
1084 auto* ins = MGuardArrayIsPacked::New(alloc(), array);
1085 add(ins);
1087 setOperand(arrayId, ins);
1088 return true;
1091 bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId,
1092 uint8_t flags) {
1093 MDefinition* obj = getOperand(objId);
1095 auto* ins = MGuardArgumentsObjectFlags::New(alloc(), obj, flags);
1096 add(ins);
1098 setOperand(objId, ins);
1099 return true;
1102 bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId,
1103 ValueType type) {
1104 switch (type) {
1105 case ValueType::String:
1106 case ValueType::Symbol:
1107 case ValueType::BigInt:
1108 case ValueType::Int32:
1109 case ValueType::Boolean:
1110 return emitGuardTo(inputId, MIRTypeFromValueType(JSValueType(type)));
1111 case ValueType::Undefined:
1112 return emitGuardIsUndefined(inputId);
1113 case ValueType::Null:
1114 return emitGuardIsNull(inputId);
1115 case ValueType::Double:
1116 case ValueType::Magic:
1117 case ValueType::PrivateGCThing:
1118 case ValueType::Object:
1119 #ifdef ENABLE_RECORD_TUPLE
1120 case ValueType::ExtendedPrimitive:
1121 #endif
1122 break;
1125 MOZ_CRASH("unexpected type");
1128 bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId, MIRType type) {
1129 MDefinition* def = getOperand(inputId);
1130 if (def->type() == type) {
1131 return true;
1134 auto* ins = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
1135 add(ins);
1137 setOperand(inputId, ins);
1138 return true;
1141 bool WarpCacheIRTranspiler::emitGuardToObject(ValOperandId inputId) {
1142 return emitGuardTo(inputId, MIRType::Object);
1145 bool WarpCacheIRTranspiler::emitGuardToString(ValOperandId inputId) {
1146 return emitGuardTo(inputId, MIRType::String);
1149 bool WarpCacheIRTranspiler::emitGuardToSymbol(ValOperandId inputId) {
1150 return emitGuardTo(inputId, MIRType::Symbol);
1153 bool WarpCacheIRTranspiler::emitGuardToBigInt(ValOperandId inputId) {
1154 return emitGuardTo(inputId, MIRType::BigInt);
1157 bool WarpCacheIRTranspiler::emitGuardToBoolean(ValOperandId inputId) {
1158 return emitGuardTo(inputId, MIRType::Boolean);
1161 bool WarpCacheIRTranspiler::emitGuardToInt32(ValOperandId inputId) {
1162 return emitGuardTo(inputId, MIRType::Int32);
1165 bool WarpCacheIRTranspiler::emitGuardBooleanToInt32(ValOperandId inputId,
1166 Int32OperandId resultId) {
1167 MDefinition* input = getOperand(inputId);
1169 auto* ins = MBooleanToInt32::New(alloc(), input);
1170 add(ins);
1172 return defineOperand(resultId, ins);
1175 bool WarpCacheIRTranspiler::emitGuardIsNumber(ValOperandId inputId) {
1176 // Prefer MToDouble because it gets further optimizations downstream.
1177 MDefinition* def = getOperand(inputId);
1178 if (def->type() == MIRType::Int32) {
1179 auto* ins = MToDouble::New(alloc(), def);
1180 add(ins);
1182 setOperand(inputId, ins);
1183 return true;
1186 // MIRType::Double also implies int32 in Ion.
1187 return emitGuardTo(inputId, MIRType::Double);
1190 bool WarpCacheIRTranspiler::emitGuardIsNullOrUndefined(ValOperandId inputId) {
1191 MDefinition* input = getOperand(inputId);
1192 if (input->type() == MIRType::Null || input->type() == MIRType::Undefined) {
1193 return true;
1196 auto* ins = MGuardNullOrUndefined::New(alloc(), input);
1197 add(ins);
1199 setOperand(inputId, ins);
1200 return true;
1203 bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId) {
1204 MDefinition* input = getOperand(inputId);
1205 if (input->type() == MIRType::Null) {
1206 return true;
1209 auto* ins = MGuardValue::New(alloc(), input, NullValue());
1210 add(ins);
1211 setOperand(inputId, ins);
1212 return true;
1215 bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId) {
1216 MDefinition* input = getOperand(inputId);
1217 if (input->type() == MIRType::Undefined) {
1218 return true;
1221 auto* ins = MGuardValue::New(alloc(), input, UndefinedValue());
1222 add(ins);
1223 setOperand(inputId, ins);
1224 return true;
1227 bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId) {
1228 MDefinition* obj = getOperand(objId);
1230 auto* ins = MGuardIsExtensible::New(alloc(), obj);
1231 add(ins);
1232 setOperand(objId, ins);
1233 return true;
1236 bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
1237 Int32OperandId indexId) {
1238 MDefinition* index = getOperand(indexId);
1240 auto* ins = MGuardInt32IsNonNegative::New(alloc(), index);
1241 add(ins);
1242 setOperand(indexId, ins);
1243 return true;
1246 bool WarpCacheIRTranspiler::emitGuardIndexIsNotDenseElement(
1247 ObjOperandId objId, Int32OperandId indexId) {
1248 MDefinition* obj = getOperand(objId);
1249 MDefinition* index = getOperand(indexId);
1251 auto* ins = MGuardIndexIsNotDenseElement::New(alloc(), obj, index);
1252 add(ins);
1253 setOperand(indexId, ins);
1254 return true;
1257 bool WarpCacheIRTranspiler::emitGuardIndexIsValidUpdateOrAdd(
1258 ObjOperandId objId, Int32OperandId indexId) {
1259 MDefinition* obj = getOperand(objId);
1260 MDefinition* index = getOperand(indexId);
1262 auto* ins = MGuardIndexIsValidUpdateOrAdd::New(alloc(), obj, index);
1263 add(ins);
1264 setOperand(indexId, ins);
1265 return true;
1268 bool WarpCacheIRTranspiler::emitCallAddOrUpdateSparseElementHelper(
1269 ObjOperandId objId, Int32OperandId idId, ValOperandId rhsId, bool strict) {
1270 MDefinition* obj = getOperand(objId);
1271 MDefinition* id = getOperand(idId);
1272 MDefinition* rhs = getOperand(rhsId);
1274 auto* ins = MCallAddOrUpdateSparseElement::New(alloc(), obj, id, rhs, strict);
1275 addEffectful(ins);
1277 return resumeAfter(ins);
1280 bool WarpCacheIRTranspiler::emitGuardTagNotEqual(ValueTagOperandId lhsId,
1281 ValueTagOperandId rhsId) {
1282 MDefinition* lhs = getOperand(lhsId);
1283 MDefinition* rhs = getOperand(rhsId);
1285 auto* ins = MGuardTagNotEqual::New(alloc(), lhs, rhs);
1286 add(ins);
1288 return true;
1291 bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId,
1292 Int32OperandId resultId) {
1293 MDefinition* input = getOperand(inputId);
1294 auto* ins =
1295 MToNumberInt32::New(alloc(), input, IntConversionInputKind::NumbersOnly);
1297 // ToPropertyKey(-0) is "0", so we can silently convert -0 to 0 here.
1298 ins->setNeedsNegativeZeroCheck(false);
1299 add(ins);
1301 return defineOperand(resultId, ins);
1304 bool WarpCacheIRTranspiler::emitTruncateDoubleToUInt32(
1305 NumberOperandId inputId, Int32OperandId resultId) {
1306 MDefinition* input = getOperand(inputId);
1307 auto* ins = MTruncateToInt32::New(alloc(), input);
1308 add(ins);
1310 return defineOperand(resultId, ins);
1313 bool WarpCacheIRTranspiler::emitGuardToInt32ModUint32(ValOperandId valId,
1314 Int32OperandId resultId) {
1315 MDefinition* input = getOperand(valId);
1316 auto* ins = MTruncateToInt32::New(alloc(), input);
1317 add(ins);
1319 return defineOperand(resultId, ins);
1322 bool WarpCacheIRTranspiler::emitGuardToUint8Clamped(ValOperandId valId,
1323 Int32OperandId resultId) {
1324 MDefinition* input = getOperand(valId);
1325 auto* ins = MClampToUint8::New(alloc(), input);
1326 add(ins);
1328 return defineOperand(resultId, ins);
1331 bool WarpCacheIRTranspiler::emitToString(OperandId inputId,
1332 StringOperandId resultId) {
1333 MDefinition* input = getOperand(inputId);
1334 auto* ins =
1335 MToString::New(alloc(), input, MToString::SideEffectHandling::Bailout);
1336 add(ins);
1338 return defineOperand(resultId, ins);
1341 bool WarpCacheIRTranspiler::emitInt32ToIntPtr(Int32OperandId inputId,
1342 IntPtrOperandId resultId) {
1343 MDefinition* input = getOperand(inputId);
1344 auto* ins = MInt32ToIntPtr::New(alloc(), input);
1345 add(ins);
1346 return defineOperand(resultId, ins);
1349 bool WarpCacheIRTranspiler::emitGuardNumberToIntPtrIndex(
1350 NumberOperandId inputId, bool supportOOB, IntPtrOperandId resultId) {
1351 MDefinition* input = getOperand(inputId);
1352 auto* ins = MGuardNumberToIntPtrIndex::New(alloc(), input, supportOOB);
1353 add(ins);
1354 return defineOperand(resultId, ins);
1357 bool WarpCacheIRTranspiler::emitCallInt32ToString(Int32OperandId inputId,
1358 StringOperandId resultId) {
1359 return emitToString(inputId, resultId);
1362 bool WarpCacheIRTranspiler::emitCallNumberToString(NumberOperandId inputId,
1363 StringOperandId resultId) {
1364 return emitToString(inputId, resultId);
1367 bool WarpCacheIRTranspiler::emitInt32ToStringWithBaseResult(
1368 Int32OperandId inputId, Int32OperandId baseId) {
1369 MDefinition* input = getOperand(inputId);
1370 MDefinition* base = getOperand(baseId);
1372 auto* guardedBase = MGuardInt32Range::New(alloc(), base, 2, 36);
1373 add(guardedBase);
1375 auto* ins = MInt32ToStringWithBase::New(alloc(), input, guardedBase);
1376 add(ins);
1378 pushResult(ins);
1379 return true;
1382 bool WarpCacheIRTranspiler::emitBooleanToString(BooleanOperandId inputId,
1383 StringOperandId resultId) {
1384 return emitToString(inputId, resultId);
1387 bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId,
1388 NumberOperandId resultId) {
1389 MDefinition* input = getOperand(inputId);
1391 auto* ins = MToDouble::New(alloc(), input);
1392 add(ins);
1394 return defineOperand(resultId, ins);
1397 bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
1398 MDefinition* val = getOperand(valId);
1399 MOZ_ASSERT(val->type() == MIRType::Int32);
1400 pushResult(val);
1401 return true;
1404 bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId) {
1405 MDefinition* val = getOperand(valId);
1406 MOZ_ASSERT(val->type() == MIRType::Double);
1407 pushResult(val);
1408 return true;
1411 bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId) {
1412 MDefinition* val = getOperand(valId);
1413 MOZ_ASSERT(val->type() == MIRType::BigInt);
1414 pushResult(val);
1415 return true;
1418 bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId) {
1419 MDefinition* obj = getOperand(objId);
1420 MOZ_ASSERT(obj->type() == MIRType::Object);
1421 pushResult(obj);
1422 return true;
1425 bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId) {
1426 MDefinition* str = getOperand(strId);
1427 MOZ_ASSERT(str->type() == MIRType::String);
1428 pushResult(str);
1429 return true;
1432 bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId) {
1433 MDefinition* sym = getOperand(symId);
1434 MOZ_ASSERT(sym->type() == MIRType::Symbol);
1435 pushResult(sym);
1436 return true;
1439 bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
1440 pushResult(constant(UndefinedValue()));
1441 return true;
1444 bool WarpCacheIRTranspiler::emitLoadBooleanResult(bool val) {
1445 pushResult(constant(BooleanValue(val)));
1446 return true;
1449 bool WarpCacheIRTranspiler::emitLoadInt32Constant(uint32_t valOffset,
1450 Int32OperandId resultId) {
1451 int32_t val = int32StubField(valOffset);
1452 auto* valConst = constant(Int32Value(val));
1453 return defineOperand(resultId, valConst);
1456 bool WarpCacheIRTranspiler::emitLoadDoubleConstant(uint32_t valOffset,
1457 NumberOperandId resultId) {
1458 double val = doubleStubField(valOffset);
1459 auto* valConst = constant(DoubleValue(val));
1460 return defineOperand(resultId, valConst);
1463 bool WarpCacheIRTranspiler::emitLoadBooleanConstant(bool val,
1464 BooleanOperandId resultId) {
1465 auto* valConst = constant(BooleanValue(val));
1466 return defineOperand(resultId, valConst);
1469 bool WarpCacheIRTranspiler::emitLoadUndefined(ValOperandId resultId) {
1470 auto* valConst = constant(UndefinedValue());
1471 return defineOperand(resultId, valConst);
1474 bool WarpCacheIRTranspiler::emitLoadConstantString(uint32_t strOffset,
1475 StringOperandId resultId) {
1476 JSString* val = stringStubField(strOffset);
1477 auto* valConst = constant(StringValue(val));
1478 return defineOperand(resultId, valConst);
1481 bool WarpCacheIRTranspiler::emitLoadConstantStringResult(uint32_t strOffset) {
1482 JSString* val = stringStubField(strOffset);
1483 auto* valConst = constant(StringValue(val));
1484 pushResult(valConst);
1485 return true;
1488 bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId) {
1489 MDefinition* obj = getOperand(objId);
1490 auto* typeOf = MTypeOf::New(alloc(), obj);
1491 add(typeOf);
1493 auto* ins = MTypeOfName::New(alloc(), typeOf);
1494 add(ins);
1495 pushResult(ins);
1496 return true;
1499 bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
1500 ObjOperandId objId, ObjOperandId resultId) {
1501 MDefinition* env = getOperand(objId);
1502 auto* ins = MEnclosingEnvironment::New(alloc(), env);
1503 add(ins);
1505 return defineOperand(resultId, ins);
1508 bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId,
1509 uint32_t objOffset) {
1510 MInstruction* ins = objectStubField(objOffset);
1512 return defineOperand(resultId, ins);
1515 bool WarpCacheIRTranspiler::emitLoadProtoObject(ObjOperandId resultId,
1516 uint32_t objOffset,
1517 ObjOperandId receiverObjId) {
1518 MInstruction* ins = objectStubField(objOffset);
1519 if (ins->isConstant()) {
1520 MDefinition* receiverObj = getOperand(receiverObjId);
1522 ins = MConstantProto::New(alloc(), ins, receiverObj->skipObjectGuards());
1523 add(ins);
1525 return defineOperand(resultId, ins);
1528 bool WarpCacheIRTranspiler::emitLoadProto(ObjOperandId objId,
1529 ObjOperandId resultId) {
1530 MDefinition* obj = getOperand(objId);
1532 auto* ins = MObjectStaticProto::New(alloc(), obj);
1533 add(ins);
1535 return defineOperand(resultId, ins);
1538 bool WarpCacheIRTranspiler::emitLoadInstanceOfObjectResult(
1539 ValOperandId lhsId, ObjOperandId protoId) {
1540 MDefinition* lhs = getOperand(lhsId);
1541 MDefinition* proto = getOperand(protoId);
1543 auto* instanceOf = MInstanceOf::New(alloc(), lhs, proto);
1544 addEffectful(instanceOf);
1546 pushResult(instanceOf);
1547 return resumeAfter(instanceOf);
1550 bool WarpCacheIRTranspiler::emitLoadValueTag(ValOperandId valId,
1551 ValueTagOperandId resultId) {
1552 MDefinition* val = getOperand(valId);
1554 auto* ins = MLoadValueTag::New(alloc(), val);
1555 add(ins);
1557 return defineOperand(resultId, ins);
1560 bool WarpCacheIRTranspiler::emitLoadDynamicSlotResult(ObjOperandId objId,
1561 uint32_t offsetOffset) {
1562 int32_t offset = int32StubField(offsetOffset);
1564 MDefinition* obj = getOperand(objId);
1565 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
1567 auto* slots = MSlots::New(alloc(), obj);
1568 add(slots);
1570 auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
1571 add(load);
1573 pushResult(load);
1574 return true;
1577 bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId,
1578 ObjOperandId objId,
1579 uint32_t offsetOffset) {
1580 MDefinition* obj = getOperand(objId);
1582 size_t offset = int32StubField(offsetOffset);
1583 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1585 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1586 add(load);
1588 return defineOperand(resultId, load);
1591 bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId,
1592 uint32_t offsetOffset) {
1593 int32_t offset = int32StubField(offsetOffset);
1595 MDefinition* obj = getOperand(objId);
1596 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1598 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1599 add(load);
1601 pushResult(load);
1602 return true;
1605 bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId,
1606 uint32_t offsetOffset,
1607 ValueType type) {
1608 int32_t offset = int32StubField(offsetOffset);
1610 MDefinition* obj = getOperand(objId);
1611 uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
1613 auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
1614 load->setResultType(MIRTypeFromValueType(JSValueType(type)));
1615 add(load);
1617 pushResult(load);
1618 return true;
1621 bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
1622 ValOperandId valId) {
1623 MDefinition* val = getOperand(valId);
1625 auto* lexicalCheck = MLexicalCheck::New(alloc(), val);
1626 add(lexicalCheck);
1628 if (snapshot().bailoutInfo().failedLexicalCheck()) {
1629 lexicalCheck->setNotMovable();
1632 setOperand(valId, lexicalCheck);
1633 return true;
1636 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId) {
1637 MDefinition* obj = getOperand(objId);
1639 auto* elements = MElements::New(alloc(), obj);
1640 add(elements);
1642 auto* length = MArrayLength::New(alloc(), elements);
1643 add(length);
1645 pushResult(length);
1646 return true;
1649 bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId,
1650 Int32OperandId resultId) {
1651 MDefinition* obj = getOperand(objId);
1653 auto* elements = MElements::New(alloc(), obj);
1654 add(elements);
1656 auto* length = MArrayLength::New(alloc(), elements);
1657 add(length);
1659 return defineOperand(resultId, length);
1662 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgResult(
1663 ObjOperandId objId, Int32OperandId indexId) {
1664 MDefinition* obj = getOperand(objId);
1665 MDefinition* index = getOperand(indexId);
1667 auto* load = MLoadArgumentsObjectArg::New(alloc(), obj, index);
1668 add(load);
1670 pushResult(load);
1671 return true;
1674 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgHoleResult(
1675 ObjOperandId objId, Int32OperandId indexId) {
1676 MDefinition* obj = getOperand(objId);
1677 MDefinition* index = getOperand(indexId);
1679 auto* load = MLoadArgumentsObjectArgHole::New(alloc(), obj, index);
1680 add(load);
1682 pushResult(load);
1683 return true;
1686 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgExistsResult(
1687 ObjOperandId objId, Int32OperandId indexId) {
1688 MDefinition* obj = getOperand(objId);
1689 MDefinition* index = getOperand(indexId);
1691 auto* ins = MInArgumentsObjectArg::New(alloc(), obj, index);
1692 add(ins);
1694 pushResult(ins);
1695 return true;
1698 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
1699 ObjOperandId objId) {
1700 MDefinition* obj = getOperand(objId);
1702 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1703 add(length);
1705 pushResult(length);
1706 return true;
1709 bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
1710 ObjOperandId objId, Int32OperandId resultId) {
1711 MDefinition* obj = getOperand(objId);
1713 auto* length = MArgumentsObjectLength::New(alloc(), obj);
1714 add(length);
1716 return defineOperand(resultId, length);
1719 bool WarpCacheIRTranspiler::emitLoadBoundFunctionNumArgs(
1720 ObjOperandId objId, Int32OperandId resultId) {
1721 MDefinition* obj = getOperand(objId);
1723 auto* numArgs = MBoundFunctionNumArgs::New(alloc(), obj);
1724 add(numArgs);
1726 return defineOperand(resultId, numArgs);
1729 bool WarpCacheIRTranspiler::emitLoadBoundFunctionTarget(ObjOperandId objId,
1730 ObjOperandId resultId) {
1731 MDefinition* obj = getOperand(objId);
1733 auto* target = MLoadFixedSlotAndUnbox::New(
1734 alloc(), obj, BoundFunctionObject::targetSlot(), MUnbox::Mode::Infallible,
1735 MIRType::Object);
1736 add(target);
1738 return defineOperand(resultId, target);
1741 bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
1742 ObjOperandId objId) {
1743 MDefinition* obj = getOperand(objId);
1745 auto* guard = MGuardBoundFunctionIsConstructor::New(alloc(), obj);
1746 add(guard);
1748 setOperand(objId, guard);
1749 return true;
1752 bool WarpCacheIRTranspiler::emitGuardObjectIdentity(ObjOperandId obj1Id,
1753 ObjOperandId obj2Id) {
1754 MDefinition* obj1 = getOperand(obj1Id);
1755 MDefinition* obj2 = getOperand(obj2Id);
1757 auto* guard = MGuardObjectIdentity::New(alloc(), obj1, obj2,
1758 /* bailOnEquality = */ false);
1759 add(guard);
1760 return true;
1763 bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
1764 ObjOperandId objId, uint32_t shapeOffset) {
1765 MDefinition* obj = getOperand(objId);
1766 Shape* shape = shapeStubField(shapeOffset);
1767 MOZ_ASSERT(shape);
1769 auto* array = MArrayFromArgumentsObject::New(alloc(), obj, shape);
1770 addEffectful(array);
1772 pushResult(array);
1773 return resumeAfter(array);
1776 bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId) {
1777 MDefinition* obj = getOperand(objId);
1779 auto* length = MFunctionLength::New(alloc(), obj);
1780 add(length);
1782 pushResult(length);
1783 return true;
1786 bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId) {
1787 MDefinition* obj = getOperand(objId);
1789 auto* name = MFunctionName::New(alloc(), obj);
1790 add(name);
1792 pushResult(name);
1793 return true;
1796 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
1797 ObjOperandId objId) {
1798 MDefinition* obj = getOperand(objId);
1800 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1801 add(length);
1803 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1804 add(lengthInt32);
1806 pushResult(lengthInt32);
1807 return true;
1810 bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
1811 ObjOperandId objId) {
1812 MDefinition* obj = getOperand(objId);
1814 auto* length = MArrayBufferByteLength::New(alloc(), obj);
1815 add(length);
1817 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1818 add(lengthDouble);
1820 pushResult(lengthDouble);
1821 return true;
1824 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthInt32Result(
1825 ObjOperandId objId) {
1826 MDefinition* obj = getOperand(objId);
1828 // Use a separate instruction for converting the length to Int32, so that we
1829 // can fold the MArrayBufferViewLength instruction with length instructions
1830 // added for bounds checks.
1832 auto* length = MArrayBufferViewLength::New(alloc(), obj);
1833 add(length);
1835 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
1836 add(lengthInt32);
1838 pushResult(lengthInt32);
1839 return true;
1842 bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
1843 ObjOperandId objId) {
1844 MDefinition* obj = getOperand(objId);
1846 auto* length = MArrayBufferViewLength::New(alloc(), obj);
1847 add(length);
1849 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
1850 add(lengthDouble);
1852 pushResult(lengthDouble);
1853 return true;
1856 bool WarpCacheIRTranspiler::emitLoadStringLengthResult(StringOperandId strId) {
1857 MDefinition* str = getOperand(strId);
1859 auto* length = MStringLength::New(alloc(), str);
1860 add(length);
1862 pushResult(length);
1863 return true;
1866 MInstruction* WarpCacheIRTranspiler::addBoundsCheck(MDefinition* index,
1867 MDefinition* length) {
1868 MInstruction* check = MBoundsCheck::New(alloc(), index, length);
1869 add(check);
1871 if (snapshot().bailoutInfo().failedBoundsCheck()) {
1872 check->setNotMovable();
1875 if (JitOptions.spectreIndexMasking) {
1876 // Use a separate MIR instruction for the index masking. Doing this as
1877 // part of MBoundsCheck would be unsound because bounds checks can be
1878 // optimized or eliminated completely. Consider this:
1880 // for (var i = 0; i < x; i++)
1881 // res = arr[i];
1883 // If we can prove |x < arr.length|, we are able to eliminate the bounds
1884 // check, but we should not get rid of the index masking because the
1885 // |i < x| branch could still be mispredicted.
1887 // Using a separate instruction lets us eliminate the bounds check
1888 // without affecting the index masking.
1889 check = MSpectreMaskIndex::New(alloc(), check, length);
1890 add(check);
1893 return check;
1896 bool WarpCacheIRTranspiler::emitLoadDenseElementResult(ObjOperandId objId,
1897 Int32OperandId indexId) {
1898 MDefinition* obj = getOperand(objId);
1899 MDefinition* index = getOperand(indexId);
1901 auto* elements = MElements::New(alloc(), obj);
1902 add(elements);
1904 auto* length = MInitializedLength::New(alloc(), elements);
1905 add(length);
1907 index = addBoundsCheck(index, length);
1909 auto* load = MLoadElement::New(alloc(), elements, index);
1910 add(load);
1912 pushResult(load);
1913 return true;
1916 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleResult(
1917 ObjOperandId objId, Int32OperandId indexId) {
1918 MDefinition* obj = getOperand(objId);
1919 MDefinition* index = getOperand(indexId);
1921 auto* elements = MElements::New(alloc(), obj);
1922 add(elements);
1924 auto* length = MInitializedLength::New(alloc(), elements);
1925 add(length);
1927 auto* load = MLoadElementHole::New(alloc(), elements, index, length);
1928 add(load);
1930 pushResult(load);
1931 return true;
1934 bool WarpCacheIRTranspiler::emitCallGetSparseElementResult(
1935 ObjOperandId objId, Int32OperandId indexId) {
1936 MDefinition* obj = getOperand(objId);
1937 MDefinition* index = getOperand(indexId);
1939 auto* call = MCallGetSparseElement::New(alloc(), obj, index);
1940 addEffectful(call);
1942 pushResult(call);
1943 return resumeAfter(call);
1946 bool WarpCacheIRTranspiler::emitCallNativeGetElementResult(
1947 ObjOperandId objId, Int32OperandId indexId) {
1948 MDefinition* obj = getOperand(objId);
1949 MDefinition* index = getOperand(indexId);
1951 auto* call = MCallNativeGetElement::New(alloc(), obj, index);
1952 addEffectful(call);
1954 pushResult(call);
1955 return resumeAfter(call);
1958 bool WarpCacheIRTranspiler::emitCallNativeGetElementSuperResult(
1959 ObjOperandId objId, Int32OperandId indexId, ValOperandId receiverId) {
1960 MDefinition* obj = getOperand(objId);
1961 MDefinition* index = getOperand(indexId);
1962 MDefinition* receiver = getOperand(receiverId);
1964 auto* call = MCallNativeGetElementSuper::New(alloc(), obj, index, receiver);
1965 addEffectful(call);
1967 pushResult(call);
1968 return resumeAfter(call);
1971 bool WarpCacheIRTranspiler::emitLoadDenseElementExistsResult(
1972 ObjOperandId objId, Int32OperandId indexId) {
1973 MDefinition* obj = getOperand(objId);
1974 MDefinition* index = getOperand(indexId);
1976 // Get the elements vector.
1977 auto* elements = MElements::New(alloc(), obj);
1978 add(elements);
1980 auto* length = MInitializedLength::New(alloc(), elements);
1981 add(length);
1983 // Check if id < initLength.
1984 index = addBoundsCheck(index, length);
1986 // And check elem[id] is not a hole.
1987 auto* guard = MGuardElementNotHole::New(alloc(), elements, index);
1988 add(guard);
1990 pushResult(constant(BooleanValue(true)));
1991 return true;
1994 bool WarpCacheIRTranspiler::emitLoadDenseElementHoleExistsResult(
1995 ObjOperandId objId, Int32OperandId indexId) {
1996 MDefinition* obj = getOperand(objId);
1997 MDefinition* index = getOperand(indexId);
1999 // Get the elements vector.
2000 auto* elements = MElements::New(alloc(), obj);
2001 add(elements);
2003 auto* length = MInitializedLength::New(alloc(), elements);
2004 add(length);
2006 // Check if id < initLength and elem[id] not a hole.
2007 auto* ins = MInArray::New(alloc(), elements, index, length, obj);
2008 add(ins);
2010 pushResult(ins);
2011 return true;
2014 bool WarpCacheIRTranspiler::emitCallObjectHasSparseElementResult(
2015 ObjOperandId objId, Int32OperandId indexId) {
2016 MDefinition* obj = getOperand(objId);
2017 MDefinition* index = getOperand(indexId);
2019 auto* ins = MCallObjectHasSparseElement::New(alloc(), obj, index);
2020 add(ins);
2022 pushResult(ins);
2023 return true;
2026 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementExistsResult(
2027 ObjOperandId objId, IntPtrOperandId indexId) {
2028 MDefinition* obj = getOperand(objId);
2029 MDefinition* index = getOperand(indexId);
2031 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2032 add(length);
2034 // Unsigned comparison to catch negative indices.
2035 auto* ins = MCompare::New(alloc(), index, length, JSOp::Lt,
2036 MCompare::Compare_UIntPtr);
2037 add(ins);
2039 pushResult(ins);
2040 return true;
2043 static MIRType MIRTypeForArrayBufferViewRead(Scalar::Type arrayType,
2044 bool forceDoubleForUint32) {
2045 switch (arrayType) {
2046 case Scalar::Int8:
2047 case Scalar::Uint8:
2048 case Scalar::Uint8Clamped:
2049 case Scalar::Int16:
2050 case Scalar::Uint16:
2051 case Scalar::Int32:
2052 return MIRType::Int32;
2053 case Scalar::Uint32:
2054 return forceDoubleForUint32 ? MIRType::Double : MIRType::Int32;
2055 case Scalar::Float32:
2056 return MIRType::Float32;
2057 case Scalar::Float64:
2058 return MIRType::Double;
2059 case Scalar::BigInt64:
2060 case Scalar::BigUint64:
2061 return MIRType::BigInt;
2062 default:
2063 break;
2065 MOZ_CRASH("Unknown typed array type");
2068 bool WarpCacheIRTranspiler::emitLoadTypedArrayElementResult(
2069 ObjOperandId objId, IntPtrOperandId indexId, Scalar::Type elementType,
2070 bool handleOOB, bool forceDoubleForUint32) {
2071 MDefinition* obj = getOperand(objId);
2072 MDefinition* index = getOperand(indexId);
2074 if (handleOOB) {
2075 auto* load = MLoadTypedArrayElementHole::New(
2076 alloc(), obj, index, elementType, forceDoubleForUint32);
2077 add(load);
2079 pushResult(load);
2080 return true;
2083 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2084 add(length);
2086 index = addBoundsCheck(index, length);
2088 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2089 add(elements);
2091 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType);
2092 load->setResultType(
2093 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32));
2094 add(load);
2096 pushResult(load);
2097 return true;
2100 bool WarpCacheIRTranspiler::emitLinearizeForCharAccess(
2101 StringOperandId strId, Int32OperandId indexId, StringOperandId resultId) {
2102 MDefinition* str = getOperand(strId);
2103 MDefinition* index = getOperand(indexId);
2105 auto* ins = MLinearizeForCharAccess::New(alloc(), str, index);
2106 add(ins);
2108 return defineOperand(resultId, ins);
2111 bool WarpCacheIRTranspiler::emitLoadStringCharResult(StringOperandId strId,
2112 Int32OperandId indexId,
2113 bool handleOOB) {
2114 MDefinition* str = getOperand(strId);
2115 MDefinition* index = getOperand(indexId);
2117 if (handleOOB) {
2118 auto* ins = MCharAtMaybeOutOfBounds::New(alloc(), str, index);
2119 add(ins);
2121 pushResult(ins);
2122 return true;
2125 auto* length = MStringLength::New(alloc(), str);
2126 add(length);
2128 index = addBoundsCheck(index, length);
2130 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2131 add(charCode);
2133 auto* fromCharCode = MFromCharCode::New(alloc(), charCode);
2134 add(fromCharCode);
2136 pushResult(fromCharCode);
2137 return true;
2140 bool WarpCacheIRTranspiler::emitLoadStringCharCodeResult(StringOperandId strId,
2141 Int32OperandId indexId,
2142 bool handleOOB) {
2143 MDefinition* str = getOperand(strId);
2144 MDefinition* index = getOperand(indexId);
2146 if (handleOOB) {
2147 auto* ins = MCharCodeAtMaybeOutOfBounds::New(alloc(), str, index);
2148 add(ins);
2150 pushResult(ins);
2151 return true;
2154 auto* length = MStringLength::New(alloc(), str);
2155 add(length);
2157 index = addBoundsCheck(index, length);
2159 auto* charCode = MCharCodeAt::New(alloc(), str, index);
2160 add(charCode);
2162 pushResult(charCode);
2163 return true;
2166 bool WarpCacheIRTranspiler::emitNewStringObjectResult(
2167 uint32_t templateObjectOffset, StringOperandId strId) {
2168 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
2169 MDefinition* string = getOperand(strId);
2171 auto* obj = MNewStringObject::New(alloc(), string, templateObj);
2172 addEffectful(obj);
2174 pushResult(obj);
2175 return resumeAfter(obj);
2178 bool WarpCacheIRTranspiler::emitStringFromCharCodeResult(
2179 Int32OperandId codeId) {
2180 MDefinition* code = getOperand(codeId);
2182 auto* fromCharCode = MFromCharCode::New(alloc(), code);
2183 add(fromCharCode);
2185 pushResult(fromCharCode);
2186 return true;
2189 bool WarpCacheIRTranspiler::emitStringFromCodePointResult(
2190 Int32OperandId codeId) {
2191 MDefinition* code = getOperand(codeId);
2193 auto* fromCodePoint = MFromCodePoint::New(alloc(), code);
2194 add(fromCodePoint);
2196 pushResult(fromCodePoint);
2197 return true;
2200 bool WarpCacheIRTranspiler::emitStringIndexOfResult(
2201 StringOperandId strId, StringOperandId searchStrId) {
2202 MDefinition* str = getOperand(strId);
2203 MDefinition* searchStr = getOperand(searchStrId);
2205 auto* indexOf = MStringIndexOf::New(alloc(), str, searchStr);
2206 add(indexOf);
2208 pushResult(indexOf);
2209 return true;
2212 bool WarpCacheIRTranspiler::emitStringStartsWithResult(
2213 StringOperandId strId, StringOperandId searchStrId) {
2214 MDefinition* str = getOperand(strId);
2215 MDefinition* searchStr = getOperand(searchStrId);
2217 auto* startsWith = MStringStartsWith::New(alloc(), str, searchStr);
2218 add(startsWith);
2220 pushResult(startsWith);
2221 return true;
2224 bool WarpCacheIRTranspiler::emitStringEndsWithResult(
2225 StringOperandId strId, StringOperandId searchStrId) {
2226 MDefinition* str = getOperand(strId);
2227 MDefinition* searchStr = getOperand(searchStrId);
2229 auto* endsWith = MStringEndsWith::New(alloc(), str, searchStr);
2230 add(endsWith);
2232 pushResult(endsWith);
2233 return true;
2236 bool WarpCacheIRTranspiler::emitStringToLowerCaseResult(StringOperandId strId) {
2237 MDefinition* str = getOperand(strId);
2239 auto* convert =
2240 MStringConvertCase::New(alloc(), str, MStringConvertCase::LowerCase);
2241 add(convert);
2243 pushResult(convert);
2244 return true;
2247 bool WarpCacheIRTranspiler::emitStringToUpperCaseResult(StringOperandId strId) {
2248 MDefinition* str = getOperand(strId);
2250 auto* convert =
2251 MStringConvertCase::New(alloc(), str, MStringConvertCase::UpperCase);
2252 add(convert);
2254 pushResult(convert);
2255 return true;
2258 bool WarpCacheIRTranspiler::emitStoreDynamicSlot(ObjOperandId objId,
2259 uint32_t offsetOffset,
2260 ValOperandId rhsId) {
2261 int32_t offset = int32StubField(offsetOffset);
2263 MDefinition* obj = getOperand(objId);
2264 size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
2265 MDefinition* rhs = getOperand(rhsId);
2267 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2268 add(barrier);
2270 auto* slots = MSlots::New(alloc(), obj);
2271 add(slots);
2273 auto* store = MStoreDynamicSlot::NewBarriered(alloc(), slots, slotIndex, rhs);
2274 addEffectful(store);
2275 return resumeAfter(store);
2278 bool WarpCacheIRTranspiler::emitStoreFixedSlot(ObjOperandId objId,
2279 uint32_t offsetOffset,
2280 ValOperandId rhsId) {
2281 int32_t offset = int32StubField(offsetOffset);
2283 MDefinition* obj = getOperand(objId);
2284 size_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
2285 MDefinition* rhs = getOperand(rhsId);
2287 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2288 add(barrier);
2290 auto* store = MStoreFixedSlot::NewBarriered(alloc(), obj, slotIndex, rhs);
2291 addEffectful(store);
2292 return resumeAfter(store);
2295 bool WarpCacheIRTranspiler::emitStoreFixedSlotUndefinedResult(
2296 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId) {
2297 int32_t offset = int32StubField(offsetOffset);
2299 MDefinition* obj = getOperand(objId);
2300 size_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
2301 MDefinition* rhs = getOperand(rhsId);
2303 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2304 add(barrier);
2306 auto* store = MStoreFixedSlot::NewBarriered(alloc(), obj, slotIndex, rhs);
2307 addEffectful(store);
2309 auto* undef = constant(UndefinedValue());
2310 pushResult(undef);
2312 return resumeAfter(store);
2315 bool WarpCacheIRTranspiler::emitAddAndStoreSlotShared(
2316 MAddAndStoreSlot::Kind kind, ObjOperandId objId, uint32_t offsetOffset,
2317 ValOperandId rhsId, uint32_t newShapeOffset) {
2318 int32_t offset = int32StubField(offsetOffset);
2319 Shape* shape = shapeStubField(newShapeOffset);
2321 MDefinition* obj = getOperand(objId);
2322 MDefinition* rhs = getOperand(rhsId);
2324 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2325 add(barrier);
2327 auto* addAndStore =
2328 MAddAndStoreSlot::New(alloc(), obj, rhs, kind, offset, shape);
2329 addEffectful(addAndStore);
2331 return resumeAfter(addAndStore);
2334 bool WarpCacheIRTranspiler::emitAddAndStoreFixedSlot(ObjOperandId objId,
2335 uint32_t offsetOffset,
2336 ValOperandId rhsId,
2337 uint32_t newShapeOffset) {
2338 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::FixedSlot, objId,
2339 offsetOffset, rhsId, newShapeOffset);
2342 bool WarpCacheIRTranspiler::emitAddAndStoreDynamicSlot(
2343 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId,
2344 uint32_t newShapeOffset) {
2345 return emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind::DynamicSlot, objId,
2346 offsetOffset, rhsId, newShapeOffset);
2349 bool WarpCacheIRTranspiler::emitAllocateAndStoreDynamicSlot(
2350 ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId,
2351 uint32_t newShapeOffset, uint32_t numNewSlotsOffset) {
2352 int32_t offset = int32StubField(offsetOffset);
2353 Shape* shape = shapeStubField(newShapeOffset);
2354 uint32_t numNewSlots = uint32StubField(numNewSlotsOffset);
2356 MDefinition* obj = getOperand(objId);
2357 MDefinition* rhs = getOperand(rhsId);
2359 auto* barrier = MPostWriteBarrier::New(alloc(), obj, rhs);
2360 add(barrier);
2362 auto* allocateAndStore =
2363 MAllocateAndStoreSlot::New(alloc(), obj, rhs, offset, shape, numNewSlots);
2364 addEffectful(allocateAndStore);
2366 return resumeAfter(allocateAndStore);
2369 bool WarpCacheIRTranspiler::emitAddSlotAndCallAddPropHook(
2370 ObjOperandId objId, ValOperandId rhsId, uint32_t newShapeOffset) {
2371 Shape* shape = shapeStubField(newShapeOffset);
2372 MDefinition* obj = getOperand(objId);
2373 MDefinition* rhs = getOperand(rhsId);
2375 auto* addProp = MAddSlotAndCallAddPropHook::New(alloc(), obj, rhs, shape);
2376 addEffectful(addProp);
2378 return resumeAfter(addProp);
2381 bool WarpCacheIRTranspiler::emitStoreDenseElement(ObjOperandId objId,
2382 Int32OperandId indexId,
2383 ValOperandId rhsId) {
2384 MDefinition* obj = getOperand(objId);
2385 MDefinition* index = getOperand(indexId);
2386 MDefinition* rhs = getOperand(rhsId);
2388 auto* elements = MElements::New(alloc(), obj);
2389 add(elements);
2391 auto* length = MInitializedLength::New(alloc(), elements);
2392 add(length);
2394 index = addBoundsCheck(index, length);
2396 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2397 add(barrier);
2399 bool needsHoleCheck = true;
2400 auto* store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2401 needsHoleCheck);
2402 addEffectful(store);
2403 return resumeAfter(store);
2406 bool WarpCacheIRTranspiler::emitStoreDenseElementHole(ObjOperandId objId,
2407 Int32OperandId indexId,
2408 ValOperandId rhsId,
2409 bool handleAdd) {
2410 MDefinition* obj = getOperand(objId);
2411 MDefinition* index = getOperand(indexId);
2412 MDefinition* rhs = getOperand(rhsId);
2414 auto* elements = MElements::New(alloc(), obj);
2415 add(elements);
2417 auto* barrier = MPostWriteElementBarrier::New(alloc(), obj, rhs, index);
2418 add(barrier);
2420 MInstruction* store;
2421 if (handleAdd) {
2422 // TODO(post-Warp): Consider changing MStoreElementHole to match IC code.
2423 store = MStoreElementHole::New(alloc(), obj, elements, index, rhs);
2424 } else {
2425 auto* length = MInitializedLength::New(alloc(), elements);
2426 add(length);
2428 index = addBoundsCheck(index, length);
2430 bool needsHoleCheck = false;
2431 store = MStoreElement::NewBarriered(alloc(), elements, index, rhs,
2432 needsHoleCheck);
2434 addEffectful(store);
2436 return resumeAfter(store);
2439 bool WarpCacheIRTranspiler::emitStoreTypedArrayElement(ObjOperandId objId,
2440 Scalar::Type elementType,
2441 IntPtrOperandId indexId,
2442 uint32_t rhsId,
2443 bool handleOOB) {
2444 MDefinition* obj = getOperand(objId);
2445 MDefinition* index = getOperand(indexId);
2446 MDefinition* rhs = getOperand(ValOperandId(rhsId));
2448 auto* length = MArrayBufferViewLength::New(alloc(), obj);
2449 add(length);
2451 if (!handleOOB) {
2452 // MStoreTypedArrayElementHole does the bounds checking.
2453 index = addBoundsCheck(index, length);
2456 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
2457 add(elements);
2459 MInstruction* store;
2460 if (handleOOB) {
2461 store = MStoreTypedArrayElementHole::New(alloc(), elements, length, index,
2462 rhs, elementType);
2463 } else {
2464 store =
2465 MStoreUnboxedScalar::New(alloc(), elements, index, rhs, elementType);
2467 addEffectful(store);
2468 return resumeAfter(store);
2471 void WarpCacheIRTranspiler::addDataViewData(MDefinition* obj, Scalar::Type type,
2472 MDefinition** offset,
2473 MInstruction** elements) {
2474 MInstruction* length = MArrayBufferViewLength::New(alloc(), obj);
2475 add(length);
2477 // Adjust the length to account for accesses near the end of the dataview.
2478 if (size_t byteSize = Scalar::byteSize(type); byteSize > 1) {
2479 // To ensure |0 <= offset && offset + byteSize <= length|, first adjust the
2480 // length by subtracting |byteSize - 1| (bailing out if that becomes
2481 // negative).
2482 length = MAdjustDataViewLength::New(alloc(), length, byteSize);
2483 add(length);
2486 *offset = addBoundsCheck(*offset, length);
2488 *elements = MArrayBufferViewElements::New(alloc(), obj);
2489 add(*elements);
2492 bool WarpCacheIRTranspiler::emitLoadDataViewValueResult(
2493 ObjOperandId objId, IntPtrOperandId offsetId,
2494 BooleanOperandId littleEndianId, Scalar::Type elementType,
2495 bool forceDoubleForUint32) {
2496 MDefinition* obj = getOperand(objId);
2497 MDefinition* offset = getOperand(offsetId);
2498 MDefinition* littleEndian = getOperand(littleEndianId);
2500 // Add bounds check and get the DataViewObject's elements.
2501 MInstruction* elements;
2502 addDataViewData(obj, elementType, &offset, &elements);
2504 // Load the element.
2505 MInstruction* load;
2506 if (Scalar::byteSize(elementType) == 1) {
2507 load = MLoadUnboxedScalar::New(alloc(), elements, offset, elementType);
2508 } else {
2509 load = MLoadDataViewElement::New(alloc(), elements, offset, littleEndian,
2510 elementType);
2512 add(load);
2514 MIRType knownType =
2515 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
2516 load->setResultType(knownType);
2518 pushResult(load);
2519 return true;
2522 bool WarpCacheIRTranspiler::emitStoreDataViewValueResult(
2523 ObjOperandId objId, IntPtrOperandId offsetId, uint32_t valueId,
2524 BooleanOperandId littleEndianId, Scalar::Type elementType) {
2525 MDefinition* obj = getOperand(objId);
2526 MDefinition* offset = getOperand(offsetId);
2527 MDefinition* value = getOperand(ValOperandId(valueId));
2528 MDefinition* littleEndian = getOperand(littleEndianId);
2530 // Add bounds check and get the DataViewObject's elements.
2531 MInstruction* elements;
2532 addDataViewData(obj, elementType, &offset, &elements);
2534 // Store the element.
2535 MInstruction* store;
2536 if (Scalar::byteSize(elementType) == 1) {
2537 store =
2538 MStoreUnboxedScalar::New(alloc(), elements, offset, value, elementType);
2539 } else {
2540 store = MStoreDataViewElement::New(alloc(), elements, offset, value,
2541 littleEndian, elementType);
2543 addEffectful(store);
2545 pushResult(constant(UndefinedValue()));
2547 return resumeAfter(store);
2550 bool WarpCacheIRTranspiler::emitInt32IncResult(Int32OperandId inputId) {
2551 MDefinition* input = getOperand(inputId);
2553 auto* constOne = MConstant::New(alloc(), Int32Value(1));
2554 add(constOne);
2556 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Int32);
2557 add(ins);
2559 pushResult(ins);
2560 return true;
2563 bool WarpCacheIRTranspiler::emitDoubleIncResult(NumberOperandId inputId) {
2564 MDefinition* input = getOperand(inputId);
2566 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2567 add(constOne);
2569 auto* ins = MAdd::New(alloc(), input, constOne, MIRType::Double);
2570 add(ins);
2572 pushResult(ins);
2573 return true;
2576 bool WarpCacheIRTranspiler::emitInt32DecResult(Int32OperandId inputId) {
2577 MDefinition* input = getOperand(inputId);
2579 auto* constOne = MConstant::New(alloc(), Int32Value(1));
2580 add(constOne);
2582 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Int32);
2583 add(ins);
2585 pushResult(ins);
2586 return true;
2589 bool WarpCacheIRTranspiler::emitDoubleDecResult(NumberOperandId inputId) {
2590 MDefinition* input = getOperand(inputId);
2592 auto* constOne = MConstant::New(alloc(), DoubleValue(1.0));
2593 add(constOne);
2595 auto* ins = MSub::New(alloc(), input, constOne, MIRType::Double);
2596 add(ins);
2598 pushResult(ins);
2599 return true;
2602 bool WarpCacheIRTranspiler::emitInt32NegationResult(Int32OperandId inputId) {
2603 MDefinition* input = getOperand(inputId);
2605 auto* constNegOne = MConstant::New(alloc(), Int32Value(-1));
2606 add(constNegOne);
2608 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Int32);
2609 add(ins);
2611 pushResult(ins);
2612 return true;
2615 bool WarpCacheIRTranspiler::emitDoubleNegationResult(NumberOperandId inputId) {
2616 MDefinition* input = getOperand(inputId);
2618 auto* constNegOne = MConstant::New(alloc(), DoubleValue(-1.0));
2619 add(constNegOne);
2621 auto* ins = MMul::New(alloc(), input, constNegOne, MIRType::Double);
2622 add(ins);
2624 pushResult(ins);
2625 return true;
2628 bool WarpCacheIRTranspiler::emitInt32NotResult(Int32OperandId inputId) {
2629 MDefinition* input = getOperand(inputId);
2631 auto* ins = MBitNot::New(alloc(), input);
2632 add(ins);
2634 pushResult(ins);
2635 return true;
2638 template <typename T>
2639 bool WarpCacheIRTranspiler::emitDoubleBinaryArithResult(NumberOperandId lhsId,
2640 NumberOperandId rhsId) {
2641 MDefinition* lhs = getOperand(lhsId);
2642 MDefinition* rhs = getOperand(rhsId);
2644 auto* ins = T::New(alloc(), lhs, rhs, MIRType::Double);
2645 add(ins);
2647 pushResult(ins);
2648 return true;
2651 bool WarpCacheIRTranspiler::emitDoubleAddResult(NumberOperandId lhsId,
2652 NumberOperandId rhsId) {
2653 return emitDoubleBinaryArithResult<MAdd>(lhsId, rhsId);
2656 bool WarpCacheIRTranspiler::emitDoubleSubResult(NumberOperandId lhsId,
2657 NumberOperandId rhsId) {
2658 return emitDoubleBinaryArithResult<MSub>(lhsId, rhsId);
2661 bool WarpCacheIRTranspiler::emitDoubleMulResult(NumberOperandId lhsId,
2662 NumberOperandId rhsId) {
2663 return emitDoubleBinaryArithResult<MMul>(lhsId, rhsId);
2666 bool WarpCacheIRTranspiler::emitDoubleDivResult(NumberOperandId lhsId,
2667 NumberOperandId rhsId) {
2668 return emitDoubleBinaryArithResult<MDiv>(lhsId, rhsId);
2671 bool WarpCacheIRTranspiler::emitDoubleModResult(NumberOperandId lhsId,
2672 NumberOperandId rhsId) {
2673 return emitDoubleBinaryArithResult<MMod>(lhsId, rhsId);
2676 bool WarpCacheIRTranspiler::emitDoublePowResult(NumberOperandId lhsId,
2677 NumberOperandId rhsId) {
2678 return emitDoubleBinaryArithResult<MPow>(lhsId, rhsId);
2681 template <typename T>
2682 bool WarpCacheIRTranspiler::emitInt32BinaryArithResult(Int32OperandId lhsId,
2683 Int32OperandId rhsId) {
2684 MDefinition* lhs = getOperand(lhsId);
2685 MDefinition* rhs = getOperand(rhsId);
2687 auto* ins = T::New(alloc(), lhs, rhs, MIRType::Int32);
2688 add(ins);
2690 pushResult(ins);
2691 return true;
2694 bool WarpCacheIRTranspiler::emitInt32AddResult(Int32OperandId lhsId,
2695 Int32OperandId rhsId) {
2696 return emitInt32BinaryArithResult<MAdd>(lhsId, rhsId);
2699 bool WarpCacheIRTranspiler::emitInt32SubResult(Int32OperandId lhsId,
2700 Int32OperandId rhsId) {
2701 return emitInt32BinaryArithResult<MSub>(lhsId, rhsId);
2704 bool WarpCacheIRTranspiler::emitInt32MulResult(Int32OperandId lhsId,
2705 Int32OperandId rhsId) {
2706 return emitInt32BinaryArithResult<MMul>(lhsId, rhsId);
2709 bool WarpCacheIRTranspiler::emitInt32DivResult(Int32OperandId lhsId,
2710 Int32OperandId rhsId) {
2711 return emitInt32BinaryArithResult<MDiv>(lhsId, rhsId);
2714 bool WarpCacheIRTranspiler::emitInt32ModResult(Int32OperandId lhsId,
2715 Int32OperandId rhsId) {
2716 return emitInt32BinaryArithResult<MMod>(lhsId, rhsId);
2719 bool WarpCacheIRTranspiler::emitInt32PowResult(Int32OperandId lhsId,
2720 Int32OperandId rhsId) {
2721 return emitInt32BinaryArithResult<MPow>(lhsId, rhsId);
2724 bool WarpCacheIRTranspiler::emitInt32BitOrResult(Int32OperandId lhsId,
2725 Int32OperandId rhsId) {
2726 return emitInt32BinaryArithResult<MBitOr>(lhsId, rhsId);
2729 bool WarpCacheIRTranspiler::emitInt32BitXorResult(Int32OperandId lhsId,
2730 Int32OperandId rhsId) {
2731 return emitInt32BinaryArithResult<MBitXor>(lhsId, rhsId);
2734 bool WarpCacheIRTranspiler::emitInt32BitAndResult(Int32OperandId lhsId,
2735 Int32OperandId rhsId) {
2736 return emitInt32BinaryArithResult<MBitAnd>(lhsId, rhsId);
2739 bool WarpCacheIRTranspiler::emitInt32LeftShiftResult(Int32OperandId lhsId,
2740 Int32OperandId rhsId) {
2741 return emitInt32BinaryArithResult<MLsh>(lhsId, rhsId);
2744 bool WarpCacheIRTranspiler::emitInt32RightShiftResult(Int32OperandId lhsId,
2745 Int32OperandId rhsId) {
2746 return emitInt32BinaryArithResult<MRsh>(lhsId, rhsId);
2749 bool WarpCacheIRTranspiler::emitInt32URightShiftResult(Int32OperandId lhsId,
2750 Int32OperandId rhsId,
2751 bool forceDouble) {
2752 MDefinition* lhs = getOperand(lhsId);
2753 MDefinition* rhs = getOperand(rhsId);
2755 MIRType specialization = forceDouble ? MIRType::Double : MIRType::Int32;
2756 auto* ins = MUrsh::New(alloc(), lhs, rhs, specialization);
2757 add(ins);
2759 pushResult(ins);
2760 return true;
2763 template <typename T>
2764 bool WarpCacheIRTranspiler::emitBigIntBinaryArithResult(BigIntOperandId lhsId,
2765 BigIntOperandId rhsId) {
2766 MDefinition* lhs = getOperand(lhsId);
2767 MDefinition* rhs = getOperand(rhsId);
2769 auto* ins = T::New(alloc(), lhs, rhs);
2770 add(ins);
2772 pushResult(ins);
2773 return true;
2776 bool WarpCacheIRTranspiler::emitBigIntAddResult(BigIntOperandId lhsId,
2777 BigIntOperandId rhsId) {
2778 return emitBigIntBinaryArithResult<MBigIntAdd>(lhsId, rhsId);
2781 bool WarpCacheIRTranspiler::emitBigIntSubResult(BigIntOperandId lhsId,
2782 BigIntOperandId rhsId) {
2783 return emitBigIntBinaryArithResult<MBigIntSub>(lhsId, rhsId);
2786 bool WarpCacheIRTranspiler::emitBigIntMulResult(BigIntOperandId lhsId,
2787 BigIntOperandId rhsId) {
2788 return emitBigIntBinaryArithResult<MBigIntMul>(lhsId, rhsId);
2791 template <typename T>
2792 bool WarpCacheIRTranspiler::emitBigIntBinaryArithEffectfulResult(
2793 BigIntOperandId lhsId, BigIntOperandId rhsId) {
2794 MDefinition* lhs = getOperand(lhsId);
2795 MDefinition* rhs = getOperand(rhsId);
2797 auto* ins = T::New(alloc(), lhs, rhs);
2799 if (ins->isEffectful()) {
2800 addEffectful(ins);
2802 pushResult(ins);
2803 return resumeAfter(ins);
2806 add(ins);
2808 pushResult(ins);
2809 return true;
2812 bool WarpCacheIRTranspiler::emitBigIntDivResult(BigIntOperandId lhsId,
2813 BigIntOperandId rhsId) {
2814 return emitBigIntBinaryArithEffectfulResult<MBigIntDiv>(lhsId, rhsId);
2817 bool WarpCacheIRTranspiler::emitBigIntModResult(BigIntOperandId lhsId,
2818 BigIntOperandId rhsId) {
2819 return emitBigIntBinaryArithEffectfulResult<MBigIntMod>(lhsId, rhsId);
2822 bool WarpCacheIRTranspiler::emitBigIntPowResult(BigIntOperandId lhsId,
2823 BigIntOperandId rhsId) {
2824 return emitBigIntBinaryArithEffectfulResult<MBigIntPow>(lhsId, rhsId);
2827 bool WarpCacheIRTranspiler::emitBigIntBitAndResult(BigIntOperandId lhsId,
2828 BigIntOperandId rhsId) {
2829 return emitBigIntBinaryArithResult<MBigIntBitAnd>(lhsId, rhsId);
2832 bool WarpCacheIRTranspiler::emitBigIntBitOrResult(BigIntOperandId lhsId,
2833 BigIntOperandId rhsId) {
2834 return emitBigIntBinaryArithResult<MBigIntBitOr>(lhsId, rhsId);
2837 bool WarpCacheIRTranspiler::emitBigIntBitXorResult(BigIntOperandId lhsId,
2838 BigIntOperandId rhsId) {
2839 return emitBigIntBinaryArithResult<MBigIntBitXor>(lhsId, rhsId);
2842 bool WarpCacheIRTranspiler::emitBigIntLeftShiftResult(BigIntOperandId lhsId,
2843 BigIntOperandId rhsId) {
2844 return emitBigIntBinaryArithResult<MBigIntLsh>(lhsId, rhsId);
2847 bool WarpCacheIRTranspiler::emitBigIntRightShiftResult(BigIntOperandId lhsId,
2848 BigIntOperandId rhsId) {
2849 return emitBigIntBinaryArithResult<MBigIntRsh>(lhsId, rhsId);
2852 template <typename T>
2853 bool WarpCacheIRTranspiler::emitBigIntUnaryArithResult(
2854 BigIntOperandId inputId) {
2855 MDefinition* input = getOperand(inputId);
2857 auto* ins = T::New(alloc(), input);
2858 add(ins);
2860 pushResult(ins);
2861 return true;
2864 bool WarpCacheIRTranspiler::emitBigIntIncResult(BigIntOperandId inputId) {
2865 return emitBigIntUnaryArithResult<MBigIntIncrement>(inputId);
2868 bool WarpCacheIRTranspiler::emitBigIntDecResult(BigIntOperandId inputId) {
2869 return emitBigIntUnaryArithResult<MBigIntDecrement>(inputId);
2872 bool WarpCacheIRTranspiler::emitBigIntNegationResult(BigIntOperandId inputId) {
2873 return emitBigIntUnaryArithResult<MBigIntNegate>(inputId);
2876 bool WarpCacheIRTranspiler::emitBigIntNotResult(BigIntOperandId inputId) {
2877 return emitBigIntUnaryArithResult<MBigIntBitNot>(inputId);
2880 bool WarpCacheIRTranspiler::emitCallStringConcatResult(StringOperandId lhsId,
2881 StringOperandId rhsId) {
2882 MDefinition* lhs = getOperand(lhsId);
2883 MDefinition* rhs = getOperand(rhsId);
2885 auto* ins = MConcat::New(alloc(), lhs, rhs);
2886 add(ins);
2888 pushResult(ins);
2889 return true;
2892 bool WarpCacheIRTranspiler::emitCompareResult(
2893 JSOp op, OperandId lhsId, OperandId rhsId,
2894 MCompare::CompareType compareType) {
2895 MDefinition* lhs = getOperand(lhsId);
2896 MDefinition* rhs = getOperand(rhsId);
2898 auto* ins = MCompare::New(alloc(), lhs, rhs, op, compareType);
2899 add(ins);
2901 pushResult(ins);
2902 return true;
2905 bool WarpCacheIRTranspiler::emitCompareInt32Result(JSOp op,
2906 Int32OperandId lhsId,
2907 Int32OperandId rhsId) {
2908 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Int32);
2911 bool WarpCacheIRTranspiler::emitCompareDoubleResult(JSOp op,
2912 NumberOperandId lhsId,
2913 NumberOperandId rhsId) {
2914 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Double);
2917 bool WarpCacheIRTranspiler::emitCompareObjectResult(JSOp op, ObjOperandId lhsId,
2918 ObjOperandId rhsId) {
2919 MOZ_ASSERT(IsEqualityOp(op));
2920 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Object);
2923 bool WarpCacheIRTranspiler::emitCompareStringResult(JSOp op,
2924 StringOperandId lhsId,
2925 StringOperandId rhsId) {
2926 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_String);
2929 bool WarpCacheIRTranspiler::emitCompareSymbolResult(JSOp op,
2930 SymbolOperandId lhsId,
2931 SymbolOperandId rhsId) {
2932 MOZ_ASSERT(IsEqualityOp(op));
2933 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_Symbol);
2936 bool WarpCacheIRTranspiler::emitCompareBigIntResult(JSOp op,
2937 BigIntOperandId lhsId,
2938 BigIntOperandId rhsId) {
2939 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt);
2942 bool WarpCacheIRTranspiler::emitCompareBigIntInt32Result(JSOp op,
2943 BigIntOperandId lhsId,
2944 Int32OperandId rhsId) {
2945 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_Int32);
2948 bool WarpCacheIRTranspiler::emitCompareBigIntNumberResult(
2949 JSOp op, BigIntOperandId lhsId, NumberOperandId rhsId) {
2950 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_Double);
2953 bool WarpCacheIRTranspiler::emitCompareBigIntStringResult(
2954 JSOp op, BigIntOperandId lhsId, StringOperandId rhsId) {
2955 return emitCompareResult(op, lhsId, rhsId, MCompare::Compare_BigInt_String);
2958 bool WarpCacheIRTranspiler::emitCompareNullUndefinedResult(
2959 JSOp op, bool isUndefined, ValOperandId inputId) {
2960 MDefinition* input = getOperand(inputId);
2962 MOZ_ASSERT(IsEqualityOp(op));
2964 // A previously emitted guard ensures that one side of the comparison
2965 // is null or undefined.
2966 MDefinition* cst =
2967 isUndefined ? constant(UndefinedValue()) : constant(NullValue());
2968 auto compareType =
2969 isUndefined ? MCompare::Compare_Undefined : MCompare::Compare_Null;
2970 auto* ins = MCompare::New(alloc(), input, cst, op, compareType);
2971 add(ins);
2973 pushResult(ins);
2974 return true;
2977 bool WarpCacheIRTranspiler::emitCompareDoubleSameValueResult(
2978 NumberOperandId lhsId, NumberOperandId rhsId) {
2979 MDefinition* lhs = getOperand(lhsId);
2980 MDefinition* rhs = getOperand(rhsId);
2982 auto* sameValue = MSameValueDouble::New(alloc(), lhs, rhs);
2983 add(sameValue);
2985 pushResult(sameValue);
2986 return true;
2989 bool WarpCacheIRTranspiler::emitSameValueResult(ValOperandId lhsId,
2990 ValOperandId rhsId) {
2991 MDefinition* lhs = getOperand(lhsId);
2992 MDefinition* rhs = getOperand(rhsId);
2994 auto* sameValue = MSameValue::New(alloc(), lhs, rhs);
2995 add(sameValue);
2997 pushResult(sameValue);
2998 return true;
3001 bool WarpCacheIRTranspiler::emitIndirectTruncateInt32Result(
3002 Int32OperandId valId) {
3003 MDefinition* val = getOperand(valId);
3004 MOZ_ASSERT(val->type() == MIRType::Int32);
3006 auto* truncate =
3007 MLimitedTruncate::New(alloc(), val, TruncateKind::IndirectTruncate);
3008 add(truncate);
3010 pushResult(truncate);
3011 return true;
3014 bool WarpCacheIRTranspiler::emitMathHypot2NumberResult(
3015 NumberOperandId firstId, NumberOperandId secondId) {
3016 MDefinitionVector vector(alloc());
3017 if (!vector.reserve(2)) {
3018 return false;
3021 vector.infallibleAppend(getOperand(firstId));
3022 vector.infallibleAppend(getOperand(secondId));
3024 auto* ins = MHypot::New(alloc(), vector);
3025 if (!ins) {
3026 return false;
3028 add(ins);
3030 pushResult(ins);
3031 return true;
3034 bool WarpCacheIRTranspiler::emitMathHypot3NumberResult(
3035 NumberOperandId firstId, NumberOperandId secondId,
3036 NumberOperandId thirdId) {
3037 MDefinitionVector vector(alloc());
3038 if (!vector.reserve(3)) {
3039 return false;
3042 vector.infallibleAppend(getOperand(firstId));
3043 vector.infallibleAppend(getOperand(secondId));
3044 vector.infallibleAppend(getOperand(thirdId));
3046 auto* ins = MHypot::New(alloc(), vector);
3047 if (!ins) {
3048 return false;
3050 add(ins);
3052 pushResult(ins);
3053 return true;
3056 bool WarpCacheIRTranspiler::emitMathHypot4NumberResult(
3057 NumberOperandId firstId, NumberOperandId secondId, NumberOperandId thirdId,
3058 NumberOperandId fourthId) {
3059 MDefinitionVector vector(alloc());
3060 if (!vector.reserve(4)) {
3061 return false;
3064 vector.infallibleAppend(getOperand(firstId));
3065 vector.infallibleAppend(getOperand(secondId));
3066 vector.infallibleAppend(getOperand(thirdId));
3067 vector.infallibleAppend(getOperand(fourthId));
3069 auto* ins = MHypot::New(alloc(), vector);
3070 if (!ins) {
3071 return false;
3073 add(ins);
3075 pushResult(ins);
3076 return true;
3079 bool WarpCacheIRTranspiler::emitMathRandomResult(uint32_t rngOffset) {
3080 #ifdef DEBUG
3081 // CodeGenerator uses CompileRealm::addressOfRandomNumberGenerator. Assert it
3082 // matches the RNG pointer stored in the stub field.
3083 const void* rng = rawPointerField(rngOffset);
3084 MOZ_ASSERT(rng == mirGen().realm->addressOfRandomNumberGenerator());
3085 #endif
3087 auto* ins = MRandom::New(alloc());
3088 addEffectful(ins);
3090 pushResult(ins);
3091 return resumeAfter(ins);
3094 bool WarpCacheIRTranspiler::emitInt32MinMax(bool isMax, Int32OperandId firstId,
3095 Int32OperandId secondId,
3096 Int32OperandId resultId) {
3097 MDefinition* first = getOperand(firstId);
3098 MDefinition* second = getOperand(secondId);
3100 auto* ins = MMinMax::New(alloc(), first, second, MIRType::Int32, isMax);
3101 add(ins);
3103 return defineOperand(resultId, ins);
3106 bool WarpCacheIRTranspiler::emitNumberMinMax(bool isMax,
3107 NumberOperandId firstId,
3108 NumberOperandId secondId,
3109 NumberOperandId resultId) {
3110 MDefinition* first = getOperand(firstId);
3111 MDefinition* second = getOperand(secondId);
3113 auto* ins = MMinMax::New(alloc(), first, second, MIRType::Double, isMax);
3114 add(ins);
3116 return defineOperand(resultId, ins);
3119 bool WarpCacheIRTranspiler::emitInt32MinMaxArrayResult(ObjOperandId arrayId,
3120 bool isMax) {
3121 MDefinition* array = getOperand(arrayId);
3123 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Int32, isMax);
3124 add(ins);
3126 pushResult(ins);
3127 return true;
3130 bool WarpCacheIRTranspiler::emitNumberMinMaxArrayResult(ObjOperandId arrayId,
3131 bool isMax) {
3132 MDefinition* array = getOperand(arrayId);
3134 auto* ins = MMinMaxArray::New(alloc(), array, MIRType::Double, isMax);
3135 add(ins);
3137 pushResult(ins);
3138 return true;
3141 bool WarpCacheIRTranspiler::emitMathAbsInt32Result(Int32OperandId inputId) {
3142 MDefinition* input = getOperand(inputId);
3144 auto* ins = MAbs::New(alloc(), input, MIRType::Int32);
3145 add(ins);
3147 pushResult(ins);
3148 return true;
3151 bool WarpCacheIRTranspiler::emitMathAbsNumberResult(NumberOperandId inputId) {
3152 MDefinition* input = getOperand(inputId);
3154 auto* ins = MAbs::New(alloc(), input, MIRType::Double);
3155 add(ins);
3157 pushResult(ins);
3158 return true;
3161 bool WarpCacheIRTranspiler::emitMathClz32Result(Int32OperandId inputId) {
3162 MDefinition* input = getOperand(inputId);
3164 auto* ins = MClz::New(alloc(), input, MIRType::Int32);
3165 add(ins);
3167 pushResult(ins);
3168 return true;
3171 bool WarpCacheIRTranspiler::emitMathSignInt32Result(Int32OperandId inputId) {
3172 MDefinition* input = getOperand(inputId);
3174 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3175 add(ins);
3177 pushResult(ins);
3178 return true;
3181 bool WarpCacheIRTranspiler::emitMathSignNumberResult(NumberOperandId inputId) {
3182 MDefinition* input = getOperand(inputId);
3184 auto* ins = MSign::New(alloc(), input, MIRType::Double);
3185 add(ins);
3187 pushResult(ins);
3188 return true;
3191 bool WarpCacheIRTranspiler::emitMathSignNumberToInt32Result(
3192 NumberOperandId inputId) {
3193 MDefinition* input = getOperand(inputId);
3195 auto* ins = MSign::New(alloc(), input, MIRType::Int32);
3196 add(ins);
3198 pushResult(ins);
3199 return true;
3202 bool WarpCacheIRTranspiler::emitMathImulResult(Int32OperandId lhsId,
3203 Int32OperandId rhsId) {
3204 MDefinition* lhs = getOperand(lhsId);
3205 MDefinition* rhs = getOperand(rhsId);
3207 auto* ins = MMul::New(alloc(), lhs, rhs, MIRType::Int32, MMul::Integer);
3208 add(ins);
3210 pushResult(ins);
3211 return true;
3214 bool WarpCacheIRTranspiler::emitMathFloorToInt32Result(
3215 NumberOperandId inputId) {
3216 MDefinition* input = getOperand(inputId);
3218 auto* ins = MFloor::New(alloc(), input);
3219 add(ins);
3221 pushResult(ins);
3222 return true;
3225 bool WarpCacheIRTranspiler::emitMathCeilToInt32Result(NumberOperandId inputId) {
3226 MDefinition* input = getOperand(inputId);
3228 auto* ins = MCeil::New(alloc(), input);
3229 add(ins);
3231 pushResult(ins);
3232 return true;
3235 bool WarpCacheIRTranspiler::emitMathTruncToInt32Result(
3236 NumberOperandId inputId) {
3237 MDefinition* input = getOperand(inputId);
3239 auto* ins = MTrunc::New(alloc(), input);
3240 add(ins);
3242 pushResult(ins);
3243 return true;
3246 bool WarpCacheIRTranspiler::emitMathRoundToInt32Result(
3247 NumberOperandId inputId) {
3248 MDefinition* input = getOperand(inputId);
3250 auto* ins = MRound::New(alloc(), input);
3251 add(ins);
3253 pushResult(ins);
3254 return true;
3257 bool WarpCacheIRTranspiler::emitMathSqrtNumberResult(NumberOperandId inputId) {
3258 MDefinition* input = getOperand(inputId);
3260 auto* ins = MSqrt::New(alloc(), input, MIRType::Double);
3261 add(ins);
3263 pushResult(ins);
3264 return true;
3267 bool WarpCacheIRTranspiler::emitMathFRoundNumberResult(
3268 NumberOperandId inputId) {
3269 MDefinition* input = getOperand(inputId);
3271 auto* ins = MToFloat32::New(alloc(), input);
3272 add(ins);
3274 pushResult(ins);
3275 return true;
3278 bool WarpCacheIRTranspiler::emitMathAtan2NumberResult(NumberOperandId yId,
3279 NumberOperandId xId) {
3280 MDefinition* y = getOperand(yId);
3281 MDefinition* x = getOperand(xId);
3283 auto* ins = MAtan2::New(alloc(), y, x);
3284 add(ins);
3286 pushResult(ins);
3287 return true;
3290 bool WarpCacheIRTranspiler::emitMathFunctionNumberResult(
3291 NumberOperandId inputId, UnaryMathFunction fun) {
3292 MDefinition* input = getOperand(inputId);
3294 auto* ins = MMathFunction::New(alloc(), input, fun);
3295 add(ins);
3297 pushResult(ins);
3298 return true;
3301 bool WarpCacheIRTranspiler::emitMathFloorNumberResult(NumberOperandId inputId) {
3302 MDefinition* input = getOperand(inputId);
3304 MInstruction* ins;
3305 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
3306 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Down);
3307 } else {
3308 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Floor);
3310 add(ins);
3312 pushResult(ins);
3313 return true;
3316 bool WarpCacheIRTranspiler::emitMathCeilNumberResult(NumberOperandId inputId) {
3317 MDefinition* input = getOperand(inputId);
3319 MInstruction* ins;
3320 if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
3321 ins = MNearbyInt::New(alloc(), input, MIRType::Double, RoundingMode::Up);
3322 } else {
3323 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Ceil);
3325 add(ins);
3327 pushResult(ins);
3328 return true;
3331 bool WarpCacheIRTranspiler::emitMathTruncNumberResult(NumberOperandId inputId) {
3332 MDefinition* input = getOperand(inputId);
3334 MInstruction* ins;
3335 if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
3336 ins = MNearbyInt::New(alloc(), input, MIRType::Double,
3337 RoundingMode::TowardsZero);
3338 } else {
3339 ins = MMathFunction::New(alloc(), input, UnaryMathFunction::Trunc);
3341 add(ins);
3343 pushResult(ins);
3344 return true;
3347 bool WarpCacheIRTranspiler::emitNumberParseIntResult(StringOperandId strId,
3348 Int32OperandId radixId) {
3349 MDefinition* str = getOperand(strId);
3350 MDefinition* radix = getOperand(radixId);
3352 auto* ins = MNumberParseInt::New(alloc(), str, radix);
3353 add(ins);
3355 pushResult(ins);
3356 return true;
3359 bool WarpCacheIRTranspiler::emitDoubleParseIntResult(NumberOperandId numId) {
3360 MDefinition* num = getOperand(numId);
3362 auto* ins = MDoubleParseInt::New(alloc(), num);
3363 add(ins);
3365 pushResult(ins);
3366 return true;
3369 bool WarpCacheIRTranspiler::emitObjectToStringResult(ObjOperandId objId) {
3370 MDefinition* obj = getOperand(objId);
3372 auto* ins = MObjectClassToString::New(alloc(), obj);
3373 add(ins);
3375 pushResult(ins);
3376 return true;
3379 bool WarpCacheIRTranspiler::emitReflectGetPrototypeOfResult(
3380 ObjOperandId objId) {
3381 MDefinition* obj = getOperand(objId);
3383 auto* ins = MGetPrototypeOf::New(alloc(), obj);
3384 addEffectful(ins);
3385 pushResult(ins);
3387 return resumeAfter(ins);
3390 bool WarpCacheIRTranspiler::emitArrayPush(ObjOperandId objId,
3391 ValOperandId rhsId) {
3392 MDefinition* obj = getOperand(objId);
3393 MDefinition* value = getOperand(rhsId);
3395 auto* elements = MElements::New(alloc(), obj);
3396 add(elements);
3398 auto* initLength = MInitializedLength::New(alloc(), elements);
3399 add(initLength);
3401 auto* barrier =
3402 MPostWriteElementBarrier::New(alloc(), obj, value, initLength);
3403 add(barrier);
3405 auto* ins = MArrayPush::New(alloc(), obj, value);
3406 addEffectful(ins);
3407 pushResult(ins);
3409 return resumeAfter(ins);
3412 bool WarpCacheIRTranspiler::emitArrayJoinResult(ObjOperandId objId,
3413 StringOperandId sepId) {
3414 MDefinition* obj = getOperand(objId);
3415 MDefinition* sep = getOperand(sepId);
3417 auto* join = MArrayJoin::New(alloc(), obj, sep);
3418 addEffectful(join);
3420 pushResult(join);
3421 return resumeAfter(join);
3424 bool WarpCacheIRTranspiler::emitPackedArrayPopResult(ObjOperandId arrayId) {
3425 MDefinition* array = getOperand(arrayId);
3427 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Pop);
3428 addEffectful(ins);
3430 pushResult(ins);
3431 return resumeAfter(ins);
3434 bool WarpCacheIRTranspiler::emitPackedArrayShiftResult(ObjOperandId arrayId) {
3435 MDefinition* array = getOperand(arrayId);
3437 auto* ins = MArrayPopShift::New(alloc(), array, MArrayPopShift::Shift);
3438 addEffectful(ins);
3440 pushResult(ins);
3441 return resumeAfter(ins);
3444 bool WarpCacheIRTranspiler::emitPackedArraySliceResult(
3445 uint32_t templateObjectOffset, ObjOperandId arrayId, Int32OperandId beginId,
3446 Int32OperandId endId) {
3447 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3449 MDefinition* array = getOperand(arrayId);
3450 MDefinition* begin = getOperand(beginId);
3451 MDefinition* end = getOperand(endId);
3453 // TODO: support pre-tenuring.
3454 gc::Heap heap = gc::Heap::Default;
3456 auto* ins = MArraySlice::New(alloc(), array, begin, end, templateObj, heap);
3457 addEffectful(ins);
3459 pushResult(ins);
3460 return resumeAfter(ins);
3463 bool WarpCacheIRTranspiler::emitArgumentsSliceResult(
3464 uint32_t templateObjectOffset, ObjOperandId argsId, Int32OperandId beginId,
3465 Int32OperandId endId) {
3466 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3468 MDefinition* args = getOperand(argsId);
3469 MDefinition* begin = getOperand(beginId);
3470 MDefinition* end = getOperand(endId);
3472 // TODO: support pre-tenuring.
3473 gc::Heap heap = gc::Heap::Default;
3475 auto* ins =
3476 MArgumentsSlice::New(alloc(), args, begin, end, templateObj, heap);
3477 addEffectful(ins);
3479 pushResult(ins);
3480 return resumeAfter(ins);
3483 bool WarpCacheIRTranspiler::emitHasClassResult(ObjOperandId objId,
3484 uint32_t claspOffset) {
3485 MDefinition* obj = getOperand(objId);
3486 const JSClass* clasp = classStubField(claspOffset);
3488 auto* hasClass = MHasClass::New(alloc(), obj, clasp);
3489 add(hasClass);
3491 pushResult(hasClass);
3492 return true;
3495 bool WarpCacheIRTranspiler::emitCallRegExpMatcherResult(
3496 ObjOperandId regexpId, StringOperandId inputId, Int32OperandId lastIndexId,
3497 uint32_t stubOffset) {
3498 MDefinition* regexp = getOperand(regexpId);
3499 MDefinition* input = getOperand(inputId);
3500 MDefinition* lastIndex = getOperand(lastIndexId);
3502 auto* matcher = MRegExpMatcher::New(alloc(), regexp, input, lastIndex);
3503 addEffectful(matcher);
3504 pushResult(matcher);
3506 return resumeAfter(matcher);
3509 bool WarpCacheIRTranspiler::emitCallRegExpSearcherResult(
3510 ObjOperandId regexpId, StringOperandId inputId, Int32OperandId lastIndexId,
3511 uint32_t stubOffset) {
3512 MDefinition* regexp = getOperand(regexpId);
3513 MDefinition* input = getOperand(inputId);
3514 MDefinition* lastIndex = getOperand(lastIndexId);
3516 auto* searcher = MRegExpSearcher::New(alloc(), regexp, input, lastIndex);
3517 addEffectful(searcher);
3518 pushResult(searcher);
3520 return resumeAfter(searcher);
3523 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecMatchResult(
3524 ObjOperandId regexpId, StringOperandId inputId, uint32_t stubOffset) {
3525 MDefinition* regexp = getOperand(regexpId);
3526 MDefinition* input = getOperand(inputId);
3528 auto* ins = MRegExpExecMatch::New(alloc(), regexp, input);
3529 addEffectful(ins);
3530 pushResult(ins);
3532 return resumeAfter(ins);
3535 bool WarpCacheIRTranspiler::emitRegExpBuiltinExecTestResult(
3536 ObjOperandId regexpId, StringOperandId inputId, uint32_t stubOffset) {
3537 MDefinition* regexp = getOperand(regexpId);
3538 MDefinition* input = getOperand(inputId);
3540 auto* ins = MRegExpExecTest::New(alloc(), regexp, input);
3541 addEffectful(ins);
3542 pushResult(ins);
3544 return resumeAfter(ins);
3547 MInstruction* WarpCacheIRTranspiler::convertToBoolean(MDefinition* input) {
3548 // Convert to bool with the '!!' idiom.
3550 // The FoldTests and GVN passes both specifically handle this pattern. If you
3551 // change this code, make sure to update FoldTests and GVN, too.
3553 auto* resultInverted = MNot::New(alloc(), input);
3554 add(resultInverted);
3555 auto* result = MNot::New(alloc(), resultInverted);
3556 add(result);
3558 return result;
3561 bool WarpCacheIRTranspiler::emitRegExpFlagResult(ObjOperandId regexpId,
3562 int32_t flagsMask) {
3563 MDefinition* regexp = getOperand(regexpId);
3565 auto* flags = MLoadFixedSlot::New(alloc(), regexp, RegExpObject::flagsSlot());
3566 flags->setResultType(MIRType::Int32);
3567 add(flags);
3569 auto* mask = MConstant::New(alloc(), Int32Value(flagsMask));
3570 add(mask);
3572 auto* maskedFlag = MBitAnd::New(alloc(), flags, mask, MIRType::Int32);
3573 add(maskedFlag);
3575 auto* result = convertToBoolean(maskedFlag);
3577 pushResult(result);
3578 return true;
3581 bool WarpCacheIRTranspiler::emitCallSubstringKernelResult(
3582 StringOperandId strId, Int32OperandId beginId, Int32OperandId lengthId) {
3583 MDefinition* str = getOperand(strId);
3584 MDefinition* begin = getOperand(beginId);
3585 MDefinition* length = getOperand(lengthId);
3587 auto* substr = MSubstr::New(alloc(), str, begin, length);
3588 add(substr);
3590 pushResult(substr);
3591 return true;
3594 bool WarpCacheIRTranspiler::emitStringReplaceStringResult(
3595 StringOperandId strId, StringOperandId patternId,
3596 StringOperandId replacementId) {
3597 MDefinition* str = getOperand(strId);
3598 MDefinition* pattern = getOperand(patternId);
3599 MDefinition* replacement = getOperand(replacementId);
3601 auto* replace = MStringReplace::New(alloc(), str, pattern, replacement);
3602 add(replace);
3604 pushResult(replace);
3605 return true;
3608 bool WarpCacheIRTranspiler::emitStringSplitStringResult(
3609 StringOperandId strId, StringOperandId separatorId) {
3610 MDefinition* str = getOperand(strId);
3611 MDefinition* separator = getOperand(separatorId);
3613 auto* split = MStringSplit::New(alloc(), str, separator);
3614 add(split);
3616 pushResult(split);
3617 return true;
3620 bool WarpCacheIRTranspiler::emitRegExpPrototypeOptimizableResult(
3621 ObjOperandId protoId) {
3622 MDefinition* proto = getOperand(protoId);
3624 auto* optimizable = MRegExpPrototypeOptimizable::New(alloc(), proto);
3625 add(optimizable);
3627 pushResult(optimizable);
3628 return true;
3631 bool WarpCacheIRTranspiler::emitRegExpInstanceOptimizableResult(
3632 ObjOperandId regexpId, ObjOperandId protoId) {
3633 MDefinition* regexp = getOperand(regexpId);
3634 MDefinition* proto = getOperand(protoId);
3636 auto* optimizable = MRegExpInstanceOptimizable::New(alloc(), regexp, proto);
3637 add(optimizable);
3639 pushResult(optimizable);
3640 return true;
3643 bool WarpCacheIRTranspiler::emitGetFirstDollarIndexResult(
3644 StringOperandId strId) {
3645 MDefinition* str = getOperand(strId);
3647 auto* firstDollarIndex = MGetFirstDollarIndex::New(alloc(), str);
3648 add(firstDollarIndex);
3650 pushResult(firstDollarIndex);
3651 return true;
3654 bool WarpCacheIRTranspiler::emitIsArrayResult(ValOperandId inputId) {
3655 MDefinition* value = getOperand(inputId);
3657 auto* isArray = MIsArray::New(alloc(), value);
3658 addEffectful(isArray);
3659 pushResult(isArray);
3661 return resumeAfter(isArray);
3664 bool WarpCacheIRTranspiler::emitIsObjectResult(ValOperandId inputId) {
3665 MDefinition* value = getOperand(inputId);
3667 if (value->type() == MIRType::Object) {
3668 pushResult(constant(BooleanValue(true)));
3669 } else {
3670 auto* isObject = MIsObject::New(alloc(), value);
3671 add(isObject);
3672 pushResult(isObject);
3675 return true;
3678 bool WarpCacheIRTranspiler::emitIsPackedArrayResult(ObjOperandId objId) {
3679 MDefinition* obj = getOperand(objId);
3681 auto* isPackedArray = MIsPackedArray::New(alloc(), obj);
3682 add(isPackedArray);
3684 pushResult(isPackedArray);
3685 return true;
3688 bool WarpCacheIRTranspiler::emitIsCallableResult(ValOperandId inputId) {
3689 MDefinition* value = getOperand(inputId);
3691 auto* isCallable = MIsCallable::New(alloc(), value);
3692 add(isCallable);
3694 pushResult(isCallable);
3695 return true;
3698 bool WarpCacheIRTranspiler::emitIsConstructorResult(ObjOperandId objId) {
3699 MDefinition* obj = getOperand(objId);
3701 auto* isConstructor = MIsConstructor::New(alloc(), obj);
3702 add(isConstructor);
3704 pushResult(isConstructor);
3705 return true;
3708 bool WarpCacheIRTranspiler::emitIsCrossRealmArrayConstructorResult(
3709 ObjOperandId objId) {
3710 MDefinition* obj = getOperand(objId);
3712 auto* ins = MIsCrossRealmArrayConstructor::New(alloc(), obj);
3713 add(ins);
3715 pushResult(ins);
3716 return true;
3719 bool WarpCacheIRTranspiler::emitIsTypedArrayResult(ObjOperandId objId,
3720 bool isPossiblyWrapped) {
3721 MDefinition* obj = getOperand(objId);
3723 auto* ins = MIsTypedArray::New(alloc(), obj, isPossiblyWrapped);
3724 if (isPossiblyWrapped) {
3725 addEffectful(ins);
3726 } else {
3727 add(ins);
3730 pushResult(ins);
3732 if (isPossiblyWrapped) {
3733 if (!resumeAfter(ins)) {
3734 return false;
3738 return true;
3741 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetInt32Result(
3742 ObjOperandId objId) {
3743 MDefinition* obj = getOperand(objId);
3745 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
3746 add(byteOffset);
3748 auto* byteOffsetInt32 = MNonNegativeIntPtrToInt32::New(alloc(), byteOffset);
3749 add(byteOffsetInt32);
3751 pushResult(byteOffsetInt32);
3752 return true;
3755 bool WarpCacheIRTranspiler::emitArrayBufferViewByteOffsetDoubleResult(
3756 ObjOperandId objId) {
3757 MDefinition* obj = getOperand(objId);
3759 auto* byteOffset = MArrayBufferViewByteOffset::New(alloc(), obj);
3760 add(byteOffset);
3762 auto* byteOffsetDouble = MIntPtrToDouble::New(alloc(), byteOffset);
3763 add(byteOffsetDouble);
3765 pushResult(byteOffsetDouble);
3766 return true;
3769 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthInt32Result(
3770 ObjOperandId objId) {
3771 MDefinition* obj = getOperand(objId);
3773 auto* length = MArrayBufferViewLength::New(alloc(), obj);
3774 add(length);
3776 auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
3777 add(lengthInt32);
3779 auto* size = MTypedArrayElementSize::New(alloc(), obj);
3780 add(size);
3782 auto* mul = MMul::New(alloc(), lengthInt32, size, MIRType::Int32);
3783 mul->setCanBeNegativeZero(false);
3784 add(mul);
3786 pushResult(mul);
3787 return true;
3790 bool WarpCacheIRTranspiler::emitTypedArrayByteLengthDoubleResult(
3791 ObjOperandId objId) {
3792 MDefinition* obj = getOperand(objId);
3794 auto* length = MArrayBufferViewLength::New(alloc(), obj);
3795 add(length);
3797 auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
3798 add(lengthDouble);
3800 auto* size = MTypedArrayElementSize::New(alloc(), obj);
3801 add(size);
3803 auto* mul = MMul::New(alloc(), lengthDouble, size, MIRType::Double);
3804 mul->setCanBeNegativeZero(false);
3805 add(mul);
3807 pushResult(mul);
3808 return true;
3811 bool WarpCacheIRTranspiler::emitTypedArrayElementSizeResult(
3812 ObjOperandId objId) {
3813 MDefinition* obj = getOperand(objId);
3815 auto* ins = MTypedArrayElementSize::New(alloc(), obj);
3816 add(ins);
3818 pushResult(ins);
3819 return true;
3822 bool WarpCacheIRTranspiler::emitGuardHasAttachedArrayBuffer(
3823 ObjOperandId objId) {
3824 MDefinition* obj = getOperand(objId);
3826 auto* ins = MGuardHasAttachedArrayBuffer::New(alloc(), obj);
3827 add(ins);
3829 setOperand(objId, ins);
3830 return true;
3833 bool WarpCacheIRTranspiler::emitIsTypedArrayConstructorResult(
3834 ObjOperandId objId) {
3835 MDefinition* obj = getOperand(objId);
3837 auto* ins = MIsTypedArrayConstructor::New(alloc(), obj);
3838 add(ins);
3840 pushResult(ins);
3841 return true;
3844 bool WarpCacheIRTranspiler::emitGetNextMapSetEntryForIteratorResult(
3845 ObjOperandId iterId, ObjOperandId resultArrId, bool isMap) {
3846 MDefinition* iter = getOperand(iterId);
3847 MDefinition* resultArr = getOperand(resultArrId);
3849 MGetNextEntryForIterator::Mode mode =
3850 isMap ? MGetNextEntryForIterator::Map : MGetNextEntryForIterator::Set;
3851 auto* ins = MGetNextEntryForIterator::New(alloc(), iter, resultArr, mode);
3852 addEffectful(ins);
3853 pushResult(ins);
3855 return resumeAfter(ins);
3858 bool WarpCacheIRTranspiler::emitFrameIsConstructingResult() {
3859 if (const CallInfo* callInfo = builder_->inlineCallInfo()) {
3860 auto* ins = constant(BooleanValue(callInfo->constructing()));
3861 pushResult(ins);
3862 return true;
3865 auto* ins = MIsConstructing::New(alloc());
3866 add(ins);
3867 pushResult(ins);
3868 return true;
3871 bool WarpCacheIRTranspiler::emitNewIteratorResult(
3872 MNewIterator::Type type, uint32_t templateObjectOffset) {
3873 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3875 auto* templateConst = constant(ObjectValue(*templateObj));
3876 auto* iter = MNewIterator::New(alloc(), templateConst, type);
3877 add(iter);
3879 pushResult(iter);
3880 return true;
3883 bool WarpCacheIRTranspiler::emitNewArrayIteratorResult(
3884 uint32_t templateObjectOffset) {
3885 return emitNewIteratorResult(MNewIterator::ArrayIterator,
3886 templateObjectOffset);
3889 bool WarpCacheIRTranspiler::emitNewStringIteratorResult(
3890 uint32_t templateObjectOffset) {
3891 return emitNewIteratorResult(MNewIterator::StringIterator,
3892 templateObjectOffset);
3895 bool WarpCacheIRTranspiler::emitNewRegExpStringIteratorResult(
3896 uint32_t templateObjectOffset) {
3897 return emitNewIteratorResult(MNewIterator::RegExpStringIterator,
3898 templateObjectOffset);
3901 bool WarpCacheIRTranspiler::emitObjectCreateResult(
3902 uint32_t templateObjectOffset) {
3903 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3905 auto* templateConst = constant(ObjectValue(*templateObj));
3907 // TODO: support pre-tenuring.
3908 gc::Heap heap = gc::Heap::Default;
3909 auto* obj =
3910 MNewObject::New(alloc(), templateConst, heap, MNewObject::ObjectCreate);
3911 addEffectful(obj);
3913 pushResult(obj);
3914 return resumeAfter(obj);
3917 bool WarpCacheIRTranspiler::emitNewArrayFromLengthResult(
3918 uint32_t templateObjectOffset, Int32OperandId lengthId) {
3919 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3920 MDefinition* length = getOperand(lengthId);
3922 // TODO: support pre-tenuring.
3923 gc::Heap heap = gc::Heap::Default;
3925 if (length->isConstant()) {
3926 int32_t lenInt32 = length->toConstant()->toInt32();
3927 if (lenInt32 >= 0 &&
3928 uint32_t(lenInt32) == templateObj->as<ArrayObject>().length()) {
3929 uint32_t len = uint32_t(lenInt32);
3930 auto* templateConst = constant(ObjectValue(*templateObj));
3932 size_t inlineLength =
3933 gc::GetGCKindSlots(templateObj->asTenured().getAllocKind()) -
3934 ObjectElements::VALUES_PER_HEADER;
3936 MNewArray* obj;
3937 if (len > inlineLength) {
3938 obj = MNewArray::NewVM(alloc(), len, templateConst, heap);
3939 } else {
3940 obj = MNewArray::New(alloc(), len, templateConst, heap);
3942 add(obj);
3943 pushResult(obj);
3944 return true;
3948 auto* obj = MNewArrayDynamicLength::New(alloc(), length, templateObj, heap);
3949 addEffectful(obj);
3950 pushResult(obj);
3951 return resumeAfter(obj);
3954 bool WarpCacheIRTranspiler::emitNewTypedArrayFromLengthResult(
3955 uint32_t templateObjectOffset, Int32OperandId lengthId) {
3956 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3957 MDefinition* length = getOperand(lengthId);
3959 // TODO: support pre-tenuring.
3960 gc::Heap heap = gc::Heap::Default;
3962 if (length->isConstant()) {
3963 int32_t len = length->toConstant()->toInt32();
3964 if (len > 0 &&
3965 uint32_t(len) == templateObj->as<TypedArrayObject>().length()) {
3966 auto* templateConst = constant(ObjectValue(*templateObj));
3967 auto* obj = MNewTypedArray::New(alloc(), templateConst, heap);
3968 add(obj);
3969 pushResult(obj);
3970 return true;
3974 auto* obj =
3975 MNewTypedArrayDynamicLength::New(alloc(), length, templateObj, heap);
3976 addEffectful(obj);
3977 pushResult(obj);
3978 return resumeAfter(obj);
3981 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayBufferResult(
3982 uint32_t templateObjectOffset, ObjOperandId bufferId,
3983 ValOperandId byteOffsetId, ValOperandId lengthId) {
3984 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
3985 MDefinition* buffer = getOperand(bufferId);
3986 MDefinition* byteOffset = getOperand(byteOffsetId);
3987 MDefinition* length = getOperand(lengthId);
3989 // TODO: support pre-tenuring.
3990 gc::Heap heap = gc::Heap::Default;
3992 auto* obj = MNewTypedArrayFromArrayBuffer::New(alloc(), buffer, byteOffset,
3993 length, templateObj, heap);
3994 addEffectful(obj);
3996 pushResult(obj);
3997 return resumeAfter(obj);
4000 bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayResult(
4001 uint32_t templateObjectOffset, ObjOperandId arrayId) {
4002 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
4003 MDefinition* array = getOperand(arrayId);
4005 // TODO: support pre-tenuring.
4006 gc::Heap heap = gc::Heap::Default;
4008 auto* obj = MNewTypedArrayFromArray::New(alloc(), array, templateObj, heap);
4009 addEffectful(obj);
4011 pushResult(obj);
4012 return resumeAfter(obj);
4015 bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult(
4016 ObjOperandId objId, IntPtrOperandId indexId, uint32_t expectedId,
4017 uint32_t replacementId, Scalar::Type elementType) {
4018 MDefinition* obj = getOperand(objId);
4019 MDefinition* index = getOperand(indexId);
4020 MDefinition* expected = getOperand(ValOperandId(expectedId));
4021 MDefinition* replacement = getOperand(ValOperandId(replacementId));
4023 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4024 add(length);
4026 index = addBoundsCheck(index, length);
4028 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4029 add(elements);
4031 bool forceDoubleForUint32 = true;
4032 MIRType knownType =
4033 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4035 auto* cas = MCompareExchangeTypedArrayElement::New(
4036 alloc(), elements, index, elementType, expected, replacement);
4037 cas->setResultType(knownType);
4038 addEffectful(cas);
4040 pushResult(cas);
4041 return resumeAfter(cas);
4044 bool WarpCacheIRTranspiler::emitAtomicsExchangeResult(
4045 ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
4046 Scalar::Type elementType) {
4047 MDefinition* obj = getOperand(objId);
4048 MDefinition* index = getOperand(indexId);
4049 MDefinition* value = getOperand(ValOperandId(valueId));
4051 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4052 add(length);
4054 index = addBoundsCheck(index, length);
4056 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4057 add(elements);
4059 bool forceDoubleForUint32 = true;
4060 MIRType knownType =
4061 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4063 auto* exchange = MAtomicExchangeTypedArrayElement::New(
4064 alloc(), elements, index, value, elementType);
4065 exchange->setResultType(knownType);
4066 addEffectful(exchange);
4068 pushResult(exchange);
4069 return resumeAfter(exchange);
4072 bool WarpCacheIRTranspiler::emitAtomicsBinaryOp(ObjOperandId objId,
4073 IntPtrOperandId indexId,
4074 uint32_t valueId,
4075 Scalar::Type elementType,
4076 bool forEffect, AtomicOp op) {
4077 MDefinition* obj = getOperand(objId);
4078 MDefinition* index = getOperand(indexId);
4079 MDefinition* value = getOperand(ValOperandId(valueId));
4081 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4082 add(length);
4084 index = addBoundsCheck(index, length);
4086 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4087 add(elements);
4089 bool forceDoubleForUint32 = true;
4090 MIRType knownType =
4091 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4093 auto* binop = MAtomicTypedArrayElementBinop::New(
4094 alloc(), op, elements, index, elementType, value, forEffect);
4095 if (!forEffect) {
4096 binop->setResultType(knownType);
4098 addEffectful(binop);
4100 if (!forEffect) {
4101 pushResult(binop);
4102 } else {
4103 pushResult(constant(UndefinedValue()));
4105 return resumeAfter(binop);
4108 bool WarpCacheIRTranspiler::emitAtomicsAddResult(ObjOperandId objId,
4109 IntPtrOperandId indexId,
4110 uint32_t valueId,
4111 Scalar::Type elementType,
4112 bool forEffect) {
4113 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4114 AtomicFetchAddOp);
4117 bool WarpCacheIRTranspiler::emitAtomicsSubResult(ObjOperandId objId,
4118 IntPtrOperandId indexId,
4119 uint32_t valueId,
4120 Scalar::Type elementType,
4121 bool forEffect) {
4122 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4123 AtomicFetchSubOp);
4126 bool WarpCacheIRTranspiler::emitAtomicsAndResult(ObjOperandId objId,
4127 IntPtrOperandId indexId,
4128 uint32_t valueId,
4129 Scalar::Type elementType,
4130 bool forEffect) {
4131 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4132 AtomicFetchAndOp);
4135 bool WarpCacheIRTranspiler::emitAtomicsOrResult(ObjOperandId objId,
4136 IntPtrOperandId indexId,
4137 uint32_t valueId,
4138 Scalar::Type elementType,
4139 bool forEffect) {
4140 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4141 AtomicFetchOrOp);
4144 bool WarpCacheIRTranspiler::emitAtomicsXorResult(ObjOperandId objId,
4145 IntPtrOperandId indexId,
4146 uint32_t valueId,
4147 Scalar::Type elementType,
4148 bool forEffect) {
4149 return emitAtomicsBinaryOp(objId, indexId, valueId, elementType, forEffect,
4150 AtomicFetchXorOp);
4153 bool WarpCacheIRTranspiler::emitAtomicsLoadResult(ObjOperandId objId,
4154 IntPtrOperandId indexId,
4155 Scalar::Type elementType) {
4156 MDefinition* obj = getOperand(objId);
4157 MDefinition* index = getOperand(indexId);
4159 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4160 add(length);
4162 index = addBoundsCheck(index, length);
4164 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4165 add(elements);
4167 bool forceDoubleForUint32 = true;
4168 MIRType knownType =
4169 MIRTypeForArrayBufferViewRead(elementType, forceDoubleForUint32);
4171 auto* load = MLoadUnboxedScalar::New(alloc(), elements, index, elementType,
4172 DoesRequireMemoryBarrier);
4173 load->setResultType(knownType);
4174 addEffectful(load);
4176 pushResult(load);
4177 return resumeAfter(load);
4180 bool WarpCacheIRTranspiler::emitAtomicsStoreResult(ObjOperandId objId,
4181 IntPtrOperandId indexId,
4182 uint32_t valueId,
4183 Scalar::Type elementType) {
4184 MDefinition* obj = getOperand(objId);
4185 MDefinition* index = getOperand(indexId);
4186 MDefinition* value = getOperand(ValOperandId(valueId));
4188 auto* length = MArrayBufferViewLength::New(alloc(), obj);
4189 add(length);
4191 index = addBoundsCheck(index, length);
4193 auto* elements = MArrayBufferViewElements::New(alloc(), obj);
4194 add(elements);
4196 auto* store = MStoreUnboxedScalar::New(alloc(), elements, index, value,
4197 elementType, DoesRequireMemoryBarrier);
4198 addEffectful(store);
4200 pushResult(value);
4201 return resumeAfter(store);
4204 bool WarpCacheIRTranspiler::emitAtomicsIsLockFreeResult(
4205 Int32OperandId valueId) {
4206 MDefinition* value = getOperand(valueId);
4208 auto* ilf = MAtomicIsLockFree::New(alloc(), value);
4209 add(ilf);
4211 pushResult(ilf);
4212 return true;
4215 bool WarpCacheIRTranspiler::emitBigIntAsIntNResult(Int32OperandId bitsId,
4216 BigIntOperandId bigIntId) {
4217 MDefinition* bits = getOperand(bitsId);
4218 MDefinition* bigInt = getOperand(bigIntId);
4220 auto* ins = MBigIntAsIntN::New(alloc(), bits, bigInt);
4221 add(ins);
4223 pushResult(ins);
4224 return true;
4227 bool WarpCacheIRTranspiler::emitBigIntAsUintNResult(Int32OperandId bitsId,
4228 BigIntOperandId bigIntId) {
4229 MDefinition* bits = getOperand(bitsId);
4230 MDefinition* bigInt = getOperand(bigIntId);
4232 auto* ins = MBigIntAsUintN::New(alloc(), bits, bigInt);
4233 add(ins);
4235 pushResult(ins);
4236 return true;
4239 bool WarpCacheIRTranspiler::emitGuardToNonGCThing(ValOperandId inputId) {
4240 MDefinition* def = getOperand(inputId);
4241 if (IsNonGCThing(def->type())) {
4242 return true;
4245 auto* ins = MGuardNonGCThing::New(alloc(), def);
4246 add(ins);
4248 setOperand(inputId, ins);
4249 return true;
4252 bool WarpCacheIRTranspiler::emitSetHasNonGCThingResult(ObjOperandId setId,
4253 ValOperandId valId) {
4254 MDefinition* set = getOperand(setId);
4255 MDefinition* val = getOperand(valId);
4257 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
4258 add(hashValue);
4260 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4261 add(hash);
4263 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4264 add(ins);
4266 pushResult(ins);
4267 return true;
4270 bool WarpCacheIRTranspiler::emitSetHasStringResult(ObjOperandId setId,
4271 StringOperandId strId) {
4272 MDefinition* set = getOperand(setId);
4273 MDefinition* str = getOperand(strId);
4275 auto* hashValue = MToHashableString::New(alloc(), str);
4276 add(hashValue);
4278 auto* hash = MHashString::New(alloc(), hashValue);
4279 add(hash);
4281 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, hashValue, hash);
4282 add(ins);
4284 pushResult(ins);
4285 return true;
4288 bool WarpCacheIRTranspiler::emitSetHasSymbolResult(ObjOperandId setId,
4289 SymbolOperandId symId) {
4290 MDefinition* set = getOperand(setId);
4291 MDefinition* sym = getOperand(symId);
4293 auto* hash = MHashSymbol::New(alloc(), sym);
4294 add(hash);
4296 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, sym, hash);
4297 add(ins);
4299 pushResult(ins);
4300 return true;
4303 bool WarpCacheIRTranspiler::emitSetHasBigIntResult(ObjOperandId setId,
4304 BigIntOperandId bigIntId) {
4305 MDefinition* set = getOperand(setId);
4306 MDefinition* bigInt = getOperand(bigIntId);
4308 auto* hash = MHashBigInt::New(alloc(), bigInt);
4309 add(hash);
4311 auto* ins = MSetObjectHasBigInt::New(alloc(), set, bigInt, hash);
4312 add(ins);
4314 pushResult(ins);
4315 return true;
4318 bool WarpCacheIRTranspiler::emitSetHasObjectResult(ObjOperandId setId,
4319 ObjOperandId objId) {
4320 MDefinition* set = getOperand(setId);
4321 MDefinition* obj = getOperand(objId);
4323 auto* hash = MHashObject::New(alloc(), set, obj);
4324 add(hash);
4326 auto* ins = MSetObjectHasNonBigInt::New(alloc(), set, obj, hash);
4327 add(ins);
4329 pushResult(ins);
4330 return true;
4333 bool WarpCacheIRTranspiler::emitSetHasResult(ObjOperandId setId,
4334 ValOperandId valId) {
4335 MDefinition* set = getOperand(setId);
4336 MDefinition* val = getOperand(valId);
4338 #ifdef JS_PUNBOX64
4339 auto* hashValue = MToHashableValue::New(alloc(), val);
4340 add(hashValue);
4342 auto* hash = MHashValue::New(alloc(), set, hashValue);
4343 add(hash);
4345 auto* ins = MSetObjectHasValue::New(alloc(), set, hashValue, hash);
4346 add(ins);
4347 #else
4348 auto* ins = MSetObjectHasValueVMCall::New(alloc(), set, val);
4349 add(ins);
4350 #endif
4352 pushResult(ins);
4353 return true;
4356 bool WarpCacheIRTranspiler::emitSetSizeResult(ObjOperandId setId) {
4357 MDefinition* set = getOperand(setId);
4359 auto* ins = MSetObjectSize::New(alloc(), set);
4360 add(ins);
4362 pushResult(ins);
4363 return true;
4366 bool WarpCacheIRTranspiler::emitMapHasNonGCThingResult(ObjOperandId mapId,
4367 ValOperandId valId) {
4368 MDefinition* map = getOperand(mapId);
4369 MDefinition* val = getOperand(valId);
4371 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
4372 add(hashValue);
4374 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4375 add(hash);
4377 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4378 add(ins);
4380 pushResult(ins);
4381 return true;
4384 bool WarpCacheIRTranspiler::emitMapHasStringResult(ObjOperandId mapId,
4385 StringOperandId strId) {
4386 MDefinition* map = getOperand(mapId);
4387 MDefinition* str = getOperand(strId);
4389 auto* hashValue = MToHashableString::New(alloc(), str);
4390 add(hashValue);
4392 auto* hash = MHashString::New(alloc(), hashValue);
4393 add(hash);
4395 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, hashValue, hash);
4396 add(ins);
4398 pushResult(ins);
4399 return true;
4402 bool WarpCacheIRTranspiler::emitMapHasSymbolResult(ObjOperandId mapId,
4403 SymbolOperandId symId) {
4404 MDefinition* map = getOperand(mapId);
4405 MDefinition* sym = getOperand(symId);
4407 auto* hash = MHashSymbol::New(alloc(), sym);
4408 add(hash);
4410 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, sym, hash);
4411 add(ins);
4413 pushResult(ins);
4414 return true;
4417 bool WarpCacheIRTranspiler::emitMapHasBigIntResult(ObjOperandId mapId,
4418 BigIntOperandId bigIntId) {
4419 MDefinition* map = getOperand(mapId);
4420 MDefinition* bigInt = getOperand(bigIntId);
4422 auto* hash = MHashBigInt::New(alloc(), bigInt);
4423 add(hash);
4425 auto* ins = MMapObjectHasBigInt::New(alloc(), map, bigInt, hash);
4426 add(ins);
4428 pushResult(ins);
4429 return true;
4432 bool WarpCacheIRTranspiler::emitMapHasObjectResult(ObjOperandId mapId,
4433 ObjOperandId objId) {
4434 MDefinition* map = getOperand(mapId);
4435 MDefinition* obj = getOperand(objId);
4437 auto* hash = MHashObject::New(alloc(), map, obj);
4438 add(hash);
4440 auto* ins = MMapObjectHasNonBigInt::New(alloc(), map, obj, hash);
4441 add(ins);
4443 pushResult(ins);
4444 return true;
4447 bool WarpCacheIRTranspiler::emitMapHasResult(ObjOperandId mapId,
4448 ValOperandId valId) {
4449 MDefinition* map = getOperand(mapId);
4450 MDefinition* val = getOperand(valId);
4452 #ifdef JS_PUNBOX64
4453 auto* hashValue = MToHashableValue::New(alloc(), val);
4454 add(hashValue);
4456 auto* hash = MHashValue::New(alloc(), map, hashValue);
4457 add(hash);
4459 auto* ins = MMapObjectHasValue::New(alloc(), map, hashValue, hash);
4460 add(ins);
4461 #else
4462 auto* ins = MMapObjectHasValueVMCall::New(alloc(), map, val);
4463 add(ins);
4464 #endif
4466 pushResult(ins);
4467 return true;
4470 bool WarpCacheIRTranspiler::emitMapGetNonGCThingResult(ObjOperandId mapId,
4471 ValOperandId valId) {
4472 MDefinition* map = getOperand(mapId);
4473 MDefinition* val = getOperand(valId);
4475 auto* hashValue = MToHashableNonGCThing::New(alloc(), val);
4476 add(hashValue);
4478 auto* hash = MHashNonGCThing::New(alloc(), hashValue);
4479 add(hash);
4481 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
4482 add(ins);
4484 pushResult(ins);
4485 return true;
4488 bool WarpCacheIRTranspiler::emitMapGetStringResult(ObjOperandId mapId,
4489 StringOperandId strId) {
4490 MDefinition* map = getOperand(mapId);
4491 MDefinition* str = getOperand(strId);
4493 auto* hashValue = MToHashableString::New(alloc(), str);
4494 add(hashValue);
4496 auto* hash = MHashString::New(alloc(), hashValue);
4497 add(hash);
4499 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, hashValue, hash);
4500 add(ins);
4502 pushResult(ins);
4503 return true;
4506 bool WarpCacheIRTranspiler::emitMapGetSymbolResult(ObjOperandId mapId,
4507 SymbolOperandId symId) {
4508 MDefinition* map = getOperand(mapId);
4509 MDefinition* sym = getOperand(symId);
4511 auto* hash = MHashSymbol::New(alloc(), sym);
4512 add(hash);
4514 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, sym, hash);
4515 add(ins);
4517 pushResult(ins);
4518 return true;
4521 bool WarpCacheIRTranspiler::emitMapGetBigIntResult(ObjOperandId mapId,
4522 BigIntOperandId bigIntId) {
4523 MDefinition* map = getOperand(mapId);
4524 MDefinition* bigInt = getOperand(bigIntId);
4526 auto* hash = MHashBigInt::New(alloc(), bigInt);
4527 add(hash);
4529 auto* ins = MMapObjectGetBigInt::New(alloc(), map, bigInt, hash);
4530 add(ins);
4532 pushResult(ins);
4533 return true;
4536 bool WarpCacheIRTranspiler::emitMapGetObjectResult(ObjOperandId mapId,
4537 ObjOperandId objId) {
4538 MDefinition* map = getOperand(mapId);
4539 MDefinition* obj = getOperand(objId);
4541 auto* hash = MHashObject::New(alloc(), map, obj);
4542 add(hash);
4544 auto* ins = MMapObjectGetNonBigInt::New(alloc(), map, obj, hash);
4545 add(ins);
4547 pushResult(ins);
4548 return true;
4551 bool WarpCacheIRTranspiler::emitMapGetResult(ObjOperandId mapId,
4552 ValOperandId valId) {
4553 MDefinition* map = getOperand(mapId);
4554 MDefinition* val = getOperand(valId);
4556 #ifdef JS_PUNBOX64
4557 auto* hashValue = MToHashableValue::New(alloc(), val);
4558 add(hashValue);
4560 auto* hash = MHashValue::New(alloc(), map, hashValue);
4561 add(hash);
4563 auto* ins = MMapObjectGetValue::New(alloc(), map, hashValue, hash);
4564 add(ins);
4565 #else
4566 auto* ins = MMapObjectGetValueVMCall::New(alloc(), map, val);
4567 add(ins);
4568 #endif
4570 pushResult(ins);
4571 return true;
4574 bool WarpCacheIRTranspiler::emitMapSizeResult(ObjOperandId mapId) {
4575 MDefinition* map = getOperand(mapId);
4577 auto* ins = MMapObjectSize::New(alloc(), map);
4578 add(ins);
4580 pushResult(ins);
4581 return true;
4584 bool WarpCacheIRTranspiler::emitTruthyResult(OperandId inputId) {
4585 MDefinition* input = getOperand(inputId);
4587 auto* result = convertToBoolean(input);
4589 pushResult(result);
4590 return true;
4593 bool WarpCacheIRTranspiler::emitLoadInt32TruthyResult(ValOperandId inputId) {
4594 return emitTruthyResult(inputId);
4597 bool WarpCacheIRTranspiler::emitLoadDoubleTruthyResult(
4598 NumberOperandId inputId) {
4599 return emitTruthyResult(inputId);
4602 bool WarpCacheIRTranspiler::emitLoadStringTruthyResult(
4603 StringOperandId inputId) {
4604 return emitTruthyResult(inputId);
4607 bool WarpCacheIRTranspiler::emitLoadObjectTruthyResult(ObjOperandId inputId) {
4608 return emitTruthyResult(inputId);
4611 bool WarpCacheIRTranspiler::emitLoadBigIntTruthyResult(
4612 BigIntOperandId inputId) {
4613 return emitTruthyResult(inputId);
4616 bool WarpCacheIRTranspiler::emitLoadValueTruthyResult(ValOperandId inputId) {
4617 return emitTruthyResult(inputId);
4620 bool WarpCacheIRTranspiler::emitLoadOperandResult(ValOperandId inputId) {
4621 MDefinition* input = getOperand(inputId);
4622 pushResult(input);
4623 return true;
4626 bool WarpCacheIRTranspiler::emitLoadWrapperTarget(ObjOperandId objId,
4627 ObjOperandId resultId) {
4628 MDefinition* obj = getOperand(objId);
4630 auto* ins = MLoadWrapperTarget::New(alloc(), obj);
4631 add(ins);
4633 return defineOperand(resultId, ins);
4636 // When we transpile a call, we may generate guards for some
4637 // arguments. To make sure the call instruction depends on those
4638 // guards, when the transpiler creates an operand for an argument, we
4639 // register the OperandId of that argument in argumentIds_. (See
4640 // emitLoadArgumentSlot.) Before generating the call, we update the
4641 // CallInfo to use the appropriate value from operands_.
4642 // Note: The callee is an explicit argument to the call op, and is
4643 // tracked separately.
4644 void WarpCacheIRTranspiler::updateArgumentsFromOperands() {
4645 for (uint32_t i = 0; i < uint32_t(ArgumentKind::NumKinds); i++) {
4646 ArgumentKind kind = ArgumentKind(i);
4647 OperandId id = argumentOperandIds_[kind];
4648 if (id.valid()) {
4649 switch (kind) {
4650 case ArgumentKind::This:
4651 callInfo_->setThis(getOperand(id));
4652 break;
4653 case ArgumentKind::NewTarget:
4654 callInfo_->setNewTarget(getOperand(id));
4655 break;
4656 case ArgumentKind::Arg0:
4657 callInfo_->setArg(0, getOperand(id));
4658 break;
4659 case ArgumentKind::Arg1:
4660 callInfo_->setArg(1, getOperand(id));
4661 break;
4662 case ArgumentKind::Arg2:
4663 callInfo_->setArg(2, getOperand(id));
4664 break;
4665 case ArgumentKind::Arg3:
4666 callInfo_->setArg(3, getOperand(id));
4667 break;
4668 case ArgumentKind::Arg4:
4669 callInfo_->setArg(4, getOperand(id));
4670 break;
4671 case ArgumentKind::Arg5:
4672 callInfo_->setArg(5, getOperand(id));
4673 break;
4674 case ArgumentKind::Arg6:
4675 callInfo_->setArg(6, getOperand(id));
4676 break;
4677 case ArgumentKind::Arg7:
4678 callInfo_->setArg(7, getOperand(id));
4679 break;
4680 case ArgumentKind::Callee:
4681 case ArgumentKind::NumKinds:
4682 MOZ_CRASH("Unexpected argument kind");
4688 bool WarpCacheIRTranspiler::emitLoadArgumentSlot(ValOperandId resultId,
4689 uint32_t slotIndex) {
4690 // Reverse of GetIndexOfArgument.
4692 // Layout:
4693 // NewTarget | Args.. (reversed) | ThisValue | Callee
4694 // 0 | ArgC .. Arg1 Arg0 (+1) | argc (+1) | argc + 1 (+ 1)
4695 // ^ (if constructing)
4697 // NewTarget (optional)
4698 if (callInfo_->constructing()) {
4699 if (slotIndex == 0) {
4700 setArgumentId(ArgumentKind::NewTarget, resultId);
4701 return defineOperand(resultId, callInfo_->getNewTarget());
4704 slotIndex -= 1; // Adjust slot index to match non-constructing calls.
4707 // Args..
4708 if (slotIndex < callInfo_->argc()) {
4709 uint32_t arg = callInfo_->argc() - 1 - slotIndex;
4710 ArgumentKind kind = ArgumentKindForArgIndex(arg);
4711 MOZ_ASSERT(kind < ArgumentKind::NumKinds);
4712 setArgumentId(kind, resultId);
4713 return defineOperand(resultId, callInfo_->getArg(arg));
4716 // ThisValue
4717 if (slotIndex == callInfo_->argc()) {
4718 setArgumentId(ArgumentKind::This, resultId);
4719 return defineOperand(resultId, callInfo_->thisArg());
4722 // Callee
4723 MOZ_ASSERT(slotIndex == callInfo_->argc() + 1);
4724 return defineOperand(resultId, callInfo_->callee());
4727 bool WarpCacheIRTranspiler::emitLoadArgumentFixedSlot(ValOperandId resultId,
4728 uint8_t slotIndex) {
4729 return emitLoadArgumentSlot(resultId, slotIndex);
4732 bool WarpCacheIRTranspiler::emitLoadArgumentDynamicSlot(ValOperandId resultId,
4733 Int32OperandId argcId,
4734 uint8_t slotIndex) {
4735 #ifdef DEBUG
4736 MDefinition* argc = getOperand(argcId);
4737 MOZ_ASSERT(argc->toConstant()->toInt32() ==
4738 static_cast<int32_t>(callInfo_->argc()));
4739 #endif
4741 return emitLoadArgumentSlot(resultId, callInfo_->argc() + slotIndex);
4744 WrappedFunction* WarpCacheIRTranspiler::maybeWrappedFunction(
4745 MDefinition* callee, CallKind kind, uint16_t nargs, FunctionFlags flags) {
4746 MOZ_ASSERT(callee->isConstant() || callee->isNurseryObject());
4748 // If this is a native without a JitEntry, WrappedFunction needs to know the
4749 // target JSFunction.
4750 // TODO: support nursery-allocated natives with WrappedFunction, maybe by
4751 // storing the JSNative in the Baseline stub like flags/nargs.
4752 bool isNative = flags.isNativeWithoutJitEntry();
4753 if (isNative && !callee->isConstant()) {
4754 return nullptr;
4757 JSFunction* nativeTarget = nullptr;
4758 if (isNative) {
4759 nativeTarget = &callee->toConstant()->toObject().as<JSFunction>();
4762 WrappedFunction* wrappedTarget =
4763 new (alloc()) WrappedFunction(nativeTarget, nargs, flags);
4764 MOZ_ASSERT_IF(kind == CallKind::Native || kind == CallKind::DOM,
4765 wrappedTarget->isNativeWithoutJitEntry());
4766 MOZ_ASSERT_IF(kind == CallKind::Scripted, wrappedTarget->hasJitEntry());
4767 return wrappedTarget;
4770 WrappedFunction* WarpCacheIRTranspiler::maybeCallTarget(MDefinition* callee,
4771 CallKind kind) {
4772 // CacheIR emits the following for specialized calls:
4773 // GuardSpecificFunction <callee> <func> ..
4774 // Call(Native|Scripted)Function <callee> ..
4775 // or:
4776 // GuardClass <callee> ..
4777 // GuardFunctionScript <callee> <script> ..
4778 // CallScriptedFunction <callee> ..
4780 // We can use the <func> JSFunction or <script> BaseScript to specialize this
4781 // call.
4782 if (callee->isGuardSpecificFunction()) {
4783 auto* guard = callee->toGuardSpecificFunction();
4784 return maybeWrappedFunction(guard->expected(), kind, guard->nargs(),
4785 guard->flags());
4787 if (callee->isGuardFunctionScript()) {
4788 MOZ_ASSERT(kind == CallKind::Scripted);
4789 auto* guard = callee->toGuardFunctionScript();
4790 WrappedFunction* wrappedTarget = new (alloc()) WrappedFunction(
4791 /* nativeFun = */ nullptr, guard->nargs(), guard->flags());
4792 MOZ_ASSERT(wrappedTarget->hasJitEntry());
4793 return wrappedTarget;
4795 return nullptr;
4798 // If it is possible to use MCall for this call, update callInfo_ to use
4799 // the correct arguments. Otherwise, update the ArgFormat of callInfo_.
4800 bool WarpCacheIRTranspiler::updateCallInfo(MDefinition* callee,
4801 CallFlags flags) {
4802 // The transpilation will add various guards to the callee.
4803 // We replace the callee referenced by the CallInfo, so that
4804 // the resulting call instruction depends on these guards.
4805 callInfo_->setCallee(callee);
4807 // The transpilation may also add guards to other arguments.
4808 // We replace those arguments in the CallInfo here.
4809 updateArgumentsFromOperands();
4811 switch (flags.getArgFormat()) {
4812 case CallFlags::Standard:
4813 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
4814 break;
4815 case CallFlags::Spread:
4816 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Array);
4817 break;
4818 case CallFlags::FunCall:
4819 // Note: We already changed the callee to the target
4820 // function instead of the |call| function.
4821 MOZ_ASSERT(!callInfo_->constructing());
4822 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
4824 if (callInfo_->argc() == 0) {
4825 // Special case for fun.call() with no arguments.
4826 auto* undef = constant(UndefinedValue());
4827 callInfo_->setThis(undef);
4828 } else {
4829 // The first argument for |call| is the new this value.
4830 callInfo_->setThis(callInfo_->getArg(0));
4832 // Shift down all other arguments by removing the first.
4833 callInfo_->removeArg(0);
4835 break;
4836 case CallFlags::FunApplyArgsObj:
4837 MOZ_ASSERT(!callInfo_->constructing());
4838 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
4840 callInfo_->setArgFormat(CallInfo::ArgFormat::FunApplyArgsObj);
4841 break;
4842 case CallFlags::FunApplyArray: {
4843 MDefinition* argFunc = callInfo_->thisArg();
4844 MDefinition* argThis = callInfo_->getArg(0);
4845 callInfo_->setCallee(argFunc);
4846 callInfo_->setThis(argThis);
4847 callInfo_->setArgFormat(CallInfo::ArgFormat::Array);
4848 break;
4850 case CallFlags::FunApplyNullUndefined:
4851 // Note: We already changed the callee to the target
4852 // function instead of the |apply| function.
4853 MOZ_ASSERT(callInfo_->argc() == 2);
4854 MOZ_ASSERT(!callInfo_->constructing());
4855 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
4856 callInfo_->setThis(callInfo_->getArg(0));
4857 callInfo_->getArg(1)->setImplicitlyUsedUnchecked();
4858 callInfo_->removeArg(1);
4859 callInfo_->removeArg(0);
4860 break;
4861 default:
4862 MOZ_CRASH("Unsupported arg format");
4864 return true;
4867 // Returns true if we are generating a call to CreateThisFromIon and
4868 // must check its return value.
4869 bool WarpCacheIRTranspiler::maybeCreateThis(MDefinition* callee,
4870 CallFlags flags, CallKind kind) {
4871 MOZ_ASSERT(kind != CallKind::DOM, "DOM functions are not constructors");
4872 MDefinition* thisArg = callInfo_->thisArg();
4874 if (kind == CallKind::Native) {
4875 // Native functions keep the is-constructing MagicValue as |this|.
4876 // If one of the arguments uses spread syntax this can be a loop phi with
4877 // MIRType::Value.
4878 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
4879 thisArg->isPhi());
4880 return false;
4882 MOZ_ASSERT(kind == CallKind::Scripted);
4884 if (thisArg->isNewPlainObject()) {
4885 // We have already updated |this| based on MetaScriptedThisShape. We do
4886 // not need to generate a check.
4887 return false;
4889 if (flags.needsUninitializedThis()) {
4890 MConstant* uninit = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
4891 thisArg->setImplicitlyUsedUnchecked();
4892 callInfo_->setThis(uninit);
4893 return false;
4895 // See the Native case above.
4896 MOZ_ASSERT(thisArg->type() == MIRType::MagicIsConstructing ||
4897 thisArg->isPhi());
4899 MDefinition* newTarget = callInfo_->getNewTarget();
4900 auto* createThis = MCreateThis::New(alloc(), callee, newTarget);
4901 add(createThis);
4903 thisArg->setImplicitlyUsedUnchecked();
4904 callInfo_->setThis(createThis);
4906 return true;
4909 bool WarpCacheIRTranspiler::emitCallFunction(
4910 ObjOperandId calleeId, Int32OperandId argcId,
4911 mozilla::Maybe<ObjOperandId> thisObjId, CallFlags flags, CallKind kind) {
4912 MDefinition* callee = getOperand(calleeId);
4913 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
4914 // We are transpiling to generate the correct guards. We also
4915 // update the CallInfo to use the correct arguments. Code for the
4916 // inlined function itself will be generated in
4917 // WarpBuilder::buildInlinedCall.
4918 if (!updateCallInfo(callee, flags)) {
4919 return false;
4921 if (callInfo_->constructing()) {
4922 MOZ_ASSERT(flags.isConstructing());
4924 // We call maybeCreateThis to update |this|, but inlined constructors
4925 // never need a VM call. CallIRGenerator::getThisForScripted ensures that
4926 // we don't attach a specialized stub unless we have a template object or
4927 // know that the constructor needs uninitialized this.
4928 MOZ_ALWAYS_FALSE(maybeCreateThis(callee, flags, CallKind::Scripted));
4929 mozilla::DebugOnly<MDefinition*> thisArg = callInfo_->thisArg();
4930 MOZ_ASSERT(thisArg->isNewPlainObject() ||
4931 thisArg->type() == MIRType::MagicUninitializedLexical);
4934 if (flags.getArgFormat() == CallFlags::FunCall) {
4935 callInfo_->setInliningResumeMode(ResumeMode::InlinedFunCall);
4936 } else {
4937 MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
4938 callInfo_->setInliningResumeMode(ResumeMode::InlinedStandardCall);
4941 switch (callInfo_->argFormat()) {
4942 case CallInfo::ArgFormat::Standard:
4943 break;
4944 default:
4945 MOZ_CRASH("Unsupported arg format");
4947 return true;
4950 #ifdef DEBUG
4951 MDefinition* argc = getOperand(argcId);
4952 MOZ_ASSERT(argc->toConstant()->toInt32() ==
4953 static_cast<int32_t>(callInfo_->argc()));
4954 #endif
4956 if (!updateCallInfo(callee, flags)) {
4957 return false;
4960 if (kind == CallKind::DOM) {
4961 MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
4962 // For DOM calls |this| has a class guard.
4963 MDefinition* thisObj = getOperand(*thisObjId);
4964 callInfo_->setThis(thisObj);
4967 WrappedFunction* wrappedTarget = maybeCallTarget(callee, kind);
4969 bool needsThisCheck = false;
4970 if (callInfo_->constructing()) {
4971 MOZ_ASSERT(flags.isConstructing());
4972 needsThisCheck = maybeCreateThis(callee, flags, kind);
4973 if (needsThisCheck) {
4974 wrappedTarget = nullptr;
4978 switch (callInfo_->argFormat()) {
4979 case CallInfo::ArgFormat::Standard: {
4980 MCall* call = makeCall(*callInfo_, needsThisCheck, wrappedTarget,
4981 kind == CallKind::DOM);
4982 if (!call) {
4983 return false;
4986 if (flags.isSameRealm()) {
4987 call->setNotCrossRealm();
4990 if (call->isEffectful()) {
4991 addEffectful(call);
4992 pushResult(call);
4993 return resumeAfter(call);
4996 MOZ_ASSERT(kind == CallKind::DOM);
4997 add(call);
4998 pushResult(call);
4999 return true;
5001 case CallInfo::ArgFormat::Array: {
5002 MInstruction* call = makeSpreadCall(*callInfo_, needsThisCheck,
5003 flags.isSameRealm(), wrappedTarget);
5004 if (!call) {
5005 return false;
5007 addEffectful(call);
5008 pushResult(call);
5010 return resumeAfter(call);
5012 case CallInfo::ArgFormat::FunApplyArgsObj: {
5013 return emitFunApplyArgsObj(wrappedTarget, flags);
5016 MOZ_CRASH("unreachable");
5019 bool WarpCacheIRTranspiler::emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
5020 CallFlags flags) {
5021 MOZ_ASSERT(!callInfo_->constructing());
5023 MDefinition* callee = callInfo_->thisArg();
5024 MDefinition* thisArg = callInfo_->getArg(0);
5025 MDefinition* argsObj = callInfo_->getArg(1);
5027 MApplyArgsObj* apply =
5028 MApplyArgsObj::New(alloc(), wrappedTarget, callee, argsObj, thisArg);
5030 if (flags.isSameRealm()) {
5031 apply->setNotCrossRealm();
5033 if (callInfo_->ignoresReturnValue()) {
5034 apply->setIgnoresReturnValue();
5037 addEffectful(apply);
5038 pushResult(apply);
5040 return resumeAfter(apply);
5043 #ifndef JS_SIMULATOR
5044 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5045 Int32OperandId argcId,
5046 CallFlags flags,
5047 uint32_t argcFixed,
5048 bool ignoresReturnValue) {
5049 // Instead of ignoresReturnValue we use CallInfo::ignoresReturnValue.
5050 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5051 CallKind::Native);
5054 bool WarpCacheIRTranspiler::emitCallDOMFunction(ObjOperandId calleeId,
5055 Int32OperandId argcId,
5056 ObjOperandId thisObjId,
5057 CallFlags flags,
5058 uint32_t argcFixed) {
5059 return emitCallFunction(calleeId, argcId, mozilla::Some(thisObjId), flags,
5060 CallKind::DOM);
5062 #else
5063 bool WarpCacheIRTranspiler::emitCallNativeFunction(ObjOperandId calleeId,
5064 Int32OperandId argcId,
5065 CallFlags flags,
5066 uint32_t argcFixed,
5067 uint32_t targetOffset) {
5068 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5069 CallKind::Native);
5072 bool WarpCacheIRTranspiler::emitCallDOMFunction(
5073 ObjOperandId calleeId, Int32OperandId argcId, ObjOperandId thisObjId,
5074 CallFlags flags, uint32_t argcFixed, uint32_t targetOffset) {
5075 return emitCallFunction(calleeId, argcId, mozilla::Some(thisObjId), flags,
5076 CallKind::DOM);
5078 #endif
5080 bool WarpCacheIRTranspiler::emitCallScriptedFunction(ObjOperandId calleeId,
5081 Int32OperandId argcId,
5082 CallFlags flags,
5083 uint32_t argcFixed) {
5084 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5085 CallKind::Scripted);
5088 bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId,
5089 Int32OperandId argcId,
5090 uint32_t icScriptOffset,
5091 CallFlags flags,
5092 uint32_t argcFixed) {
5093 return emitCallFunction(calleeId, argcId, mozilla::Nothing(), flags,
5094 CallKind::Scripted);
5097 bool WarpCacheIRTranspiler::emitCallClassHook(ObjOperandId calleeId,
5098 Int32OperandId argcId,
5099 CallFlags flags,
5100 uint32_t argcFixed,
5101 uint32_t targetOffset) {
5102 MDefinition* callee = getOperand(calleeId);
5103 JSNative target = jsnativeStubField(targetOffset);
5105 #ifdef DEBUG
5106 MDefinition* argc = getOperand(argcId);
5107 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5108 static_cast<int32_t>(callInfo_->argc()));
5109 #endif
5111 if (!updateCallInfo(callee, flags)) {
5112 return false;
5115 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5116 MOZ_ASSERT(flags.getArgFormat() == CallFlags::ArgFormat::Standard);
5118 // Callees can be from any realm. If this changes, we should update
5119 // MCallClassHook::maybeCrossRealm.
5120 MOZ_ASSERT(!flags.isSameRealm());
5122 auto* call = MCallClassHook::New(alloc(), target, callInfo_->argc(),
5123 callInfo_->constructing());
5124 if (!call) {
5125 return false;
5128 if (callInfo_->ignoresReturnValue()) {
5129 call->setIgnoresReturnValue();
5132 call->initCallee(callInfo_->callee());
5133 call->addArg(0, callInfo_->thisArg());
5135 for (uint32_t i = 0; i < callInfo_->argc(); i++) {
5136 call->addArg(i + 1, callInfo_->getArg(i));
5139 if (callInfo_->constructing()) {
5140 call->addArg(1 + callInfo_->argc(), callInfo_->getNewTarget());
5143 addEffectful(call);
5144 pushResult(call);
5146 return resumeAfter(call);
5149 bool WarpCacheIRTranspiler::emitCallBoundScriptedFunction(
5150 ObjOperandId calleeId, ObjOperandId targetId, Int32OperandId argcId,
5151 CallFlags flags, uint32_t numBoundArgs) {
5152 MDefinition* callee = getOperand(calleeId);
5153 MDefinition* target = getOperand(targetId);
5155 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5156 MOZ_ASSERT(callInfo_->constructing() == flags.isConstructing());
5158 callInfo_->setCallee(target);
5159 updateArgumentsFromOperands();
5161 WrappedFunction* wrappedTarget = maybeCallTarget(target, CallKind::Scripted);
5163 bool needsThisCheck = false;
5164 if (callInfo_->constructing()) {
5165 callInfo_->setNewTarget(target);
5166 needsThisCheck = maybeCreateThis(target, flags, CallKind::Scripted);
5167 if (needsThisCheck) {
5168 wrappedTarget = nullptr;
5170 } else {
5171 auto* thisv = MLoadFixedSlot::New(alloc(), callee,
5172 BoundFunctionObject::boundThisSlot());
5173 add(thisv);
5174 callInfo_->thisArg()->setImplicitlyUsedUnchecked();
5175 callInfo_->setThis(thisv);
5178 bool usingInlineBoundArgs =
5179 numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs;
5181 MElements* elements = nullptr;
5182 if (!usingInlineBoundArgs) {
5183 auto* boundArgs = MLoadFixedSlot::New(
5184 alloc(), callee, BoundFunctionObject::firstInlineBoundArgSlot());
5185 add(boundArgs);
5186 elements = MElements::New(alloc(), boundArgs);
5187 add(elements);
5190 auto loadBoundArg = [&](size_t index) {
5191 MInstruction* arg;
5192 if (usingInlineBoundArgs) {
5193 size_t slot = BoundFunctionObject::firstInlineBoundArgSlot() + index;
5194 arg = MLoadFixedSlot::New(alloc(), callee, slot);
5195 } else {
5196 arg = MLoadElement::New(alloc(), elements, constant(Int32Value(index)));
5198 add(arg);
5199 return arg;
5201 if (!callInfo_->prependArgs(numBoundArgs, loadBoundArg)) {
5202 return false;
5205 MCall* call = makeCall(*callInfo_, needsThisCheck, wrappedTarget);
5206 if (!call) {
5207 return false;
5210 if (flags.isSameRealm()) {
5211 call->setNotCrossRealm();
5214 addEffectful(call);
5215 pushResult(call);
5216 return resumeAfter(call);
5219 bool WarpCacheIRTranspiler::emitBindFunctionResult(
5220 ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) {
5221 MDefinition* target = getOperand(targetId);
5222 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
5224 MOZ_ASSERT(callInfo_->argc() == argc);
5226 auto* bound = MBindFunction::New(alloc(), target, argc, templateObj);
5227 if (!bound) {
5228 return false;
5230 addEffectful(bound);
5232 for (uint32_t i = 0; i < argc; i++) {
5233 bound->initArg(i, callInfo_->getArg(i));
5236 pushResult(bound);
5237 return resumeAfter(bound);
5240 bool WarpCacheIRTranspiler::emitSpecializedBindFunctionResult(
5241 ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) {
5242 MDefinition* target = getOperand(targetId);
5243 JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
5245 MOZ_ASSERT(callInfo_->argc() == argc);
5247 auto* bound = MNewBoundFunction::New(alloc(), templateObj);
5248 add(bound);
5250 size_t numBoundArgs = argc > 0 ? argc - 1 : 0;
5251 MOZ_ASSERT(numBoundArgs <= BoundFunctionObject::MaxInlineBoundArgs);
5253 auto initSlot = [&](size_t slot, MDefinition* value) {
5254 #ifdef DEBUG
5255 // Assert we can elide the post write barrier. See also the comment in
5256 // WarpBuilder::buildNamedLambdaEnv.
5257 add(MAssertCanElidePostWriteBarrier::New(alloc(), bound, value));
5258 #endif
5259 addUnchecked(MStoreFixedSlot::NewUnbarriered(alloc(), bound, slot, value));
5262 initSlot(BoundFunctionObject::targetSlot(), target);
5263 if (argc > 0) {
5264 initSlot(BoundFunctionObject::boundThisSlot(), callInfo_->getArg(0));
5266 for (size_t i = 0; i < numBoundArgs; i++) {
5267 size_t slot = BoundFunctionObject::firstInlineBoundArgSlot() + i;
5268 initSlot(slot, callInfo_->getArg(1 + i));
5271 pushResult(bound);
5272 return true;
5275 bool WarpCacheIRTranspiler::emitCallWasmFunction(
5276 ObjOperandId calleeId, Int32OperandId argcId, CallFlags flags,
5277 uint32_t argcFixed, uint32_t funcExportOffset, uint32_t instanceOffset) {
5278 MDefinition* callee = getOperand(calleeId);
5279 #ifdef DEBUG
5280 MDefinition* argc = getOperand(argcId);
5281 MOZ_ASSERT(argc->toConstant()->toInt32() ==
5282 static_cast<int32_t>(callInfo_->argc()));
5283 #endif
5284 JSObject* instanceObject = tenuredObjectStubField(instanceOffset);
5285 auto* wasmInstanceObj = &instanceObject->as<WasmInstanceObject>();
5286 const wasm::FuncExport* funcExport = wasmFuncExportField(funcExportOffset);
5287 const wasm::FuncType& sig =
5288 wasmInstanceObj->instance().metadata().getFuncExportType(*funcExport);
5290 if (!updateCallInfo(callee, flags)) {
5291 return false;
5294 static_assert(wasm::MaxArgsForJitInlineCall <= MaxNumLInstructionOperands,
5295 "arguments must fit in LIR operands");
5296 MOZ_ASSERT(sig.args().length() <= wasm::MaxArgsForJitInlineCall);
5298 MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
5300 auto* call = MIonToWasmCall::New(alloc(), wasmInstanceObj, *funcExport);
5301 if (!call) {
5302 return false;
5305 mozilla::Maybe<MDefinition*> undefined;
5306 for (size_t i = 0; i < sig.args().length(); i++) {
5307 if (!alloc().ensureBallast()) {
5308 return false;
5311 MDefinition* arg;
5312 if (i < callInfo_->argc()) {
5313 arg = callInfo_->getArg(i);
5314 } else {
5315 if (!undefined) {
5316 undefined.emplace(constant(UndefinedValue()));
5318 arg = convertWasmArg(*undefined, sig.args()[i].kind());
5320 call->initArg(i, arg);
5323 addEffectful(call);
5325 // Add any post-function call conversions that are necessary.
5326 MInstruction* postConversion = call;
5327 const wasm::ValTypeVector& results = sig.results();
5328 MOZ_ASSERT(results.length() <= 1, "Multi-value returns not supported.");
5329 if (results.length() == 0) {
5330 // No results to convert.
5331 } else {
5332 switch (results[0].kind()) {
5333 case wasm::ValType::I64:
5334 // JS expects a BigInt from I64 types.
5335 postConversion = MInt64ToBigInt::New(alloc(), call);
5337 // Make non-movable so we can attach a resume point.
5338 postConversion->setNotMovable();
5340 add(postConversion);
5341 break;
5342 default:
5343 // No spectre.index_masking of i32 results required, as the generated
5344 // stub takes care of that.
5345 break;
5349 // The resume point has to be attached to the post-conversion instruction
5350 // (if present) instead of to the call. This way, if the call triggers an
5351 // invalidation bailout, we will have the BigInt value on the Baseline stack.
5352 // Potential alternative solution: attach the resume point to the call and
5353 // have bailouts turn the Int64 value into a BigInt, maybe with a recover
5354 // instruction.
5355 pushResult(postConversion);
5356 return resumeAfterUnchecked(postConversion);
5359 MDefinition* WarpCacheIRTranspiler::convertWasmArg(MDefinition* arg,
5360 wasm::ValType::Kind kind) {
5361 // An invariant in this code is that any type conversion operation that has
5362 // externally visible effects, such as invoking valueOf on an object argument,
5363 // must bailout so that we don't have to worry about replaying effects during
5364 // argument conversion.
5365 MInstruction* conversion = nullptr;
5366 switch (kind) {
5367 case wasm::ValType::I32:
5368 conversion = MTruncateToInt32::New(alloc(), arg);
5369 break;
5370 case wasm::ValType::I64:
5371 conversion = MToInt64::New(alloc(), arg);
5372 break;
5373 case wasm::ValType::F32:
5374 conversion = MToFloat32::New(alloc(), arg);
5375 break;
5376 case wasm::ValType::F64:
5377 conversion = MToDouble::New(alloc(), arg);
5378 break;
5379 case wasm::ValType::V128:
5380 MOZ_CRASH("Unexpected type for Wasm JitEntry");
5381 case wasm::ValType::Ref:
5382 // Transform the JS representation into an AnyRef representation.
5383 // The resulting type is MIRType::RefOrNull. These cases are all
5384 // effect-free.
5385 switch (arg->type()) {
5386 case MIRType::Object:
5387 conversion = MWasmAnyRefFromJSObject::New(alloc(), arg);
5388 break;
5389 case MIRType::Null:
5390 arg->setImplicitlyUsedUnchecked();
5391 conversion = MWasmNullConstant::New(alloc());
5392 break;
5393 default:
5394 conversion = MWasmBoxValue::New(alloc(), arg);
5395 break;
5397 break;
5400 add(conversion);
5401 return conversion;
5404 bool WarpCacheIRTranspiler::emitGuardWasmArg(ValOperandId argId,
5405 wasm::ValType::Kind kind) {
5406 MDefinition* arg = getOperand(argId);
5407 MDefinition* conversion = convertWasmArg(arg, kind);
5409 setOperand(argId, conversion);
5410 return true;
5413 bool WarpCacheIRTranspiler::emitCallGetterResult(CallKind kind,
5414 ValOperandId receiverId,
5415 uint32_t getterOffset,
5416 bool sameRealm,
5417 uint32_t nargsAndFlagsOffset) {
5418 MDefinition* receiver = getOperand(receiverId);
5419 MDefinition* getter = objectStubField(getterOffset);
5420 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
5421 // We are transpiling to generate the correct guards. We also update the
5422 // CallInfo to use the correct arguments. Code for the inlined getter
5423 // itself will be generated in WarpBuilder::buildInlinedCall.
5424 callInfo_->initForGetterCall(getter, receiver);
5425 callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
5427 // Make sure there's enough room to push the arguments on the stack.
5428 if (!current->ensureHasSlots(2)) {
5429 return false;
5432 return true;
5435 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
5436 uint16_t nargs = nargsAndFlags >> 16;
5437 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
5438 WrappedFunction* wrappedTarget =
5439 maybeWrappedFunction(getter, kind, nargs, flags);
5441 bool ignoresRval = loc_.resultIsPopped();
5442 CallInfo callInfo(alloc(), /* constructing = */ false, ignoresRval);
5443 callInfo.initForGetterCall(getter, receiver);
5445 MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, wrappedTarget);
5446 if (!call) {
5447 return false;
5450 if (sameRealm) {
5451 call->setNotCrossRealm();
5454 addEffectful(call);
5455 pushResult(call);
5457 return resumeAfter(call);
5460 bool WarpCacheIRTranspiler::emitCallScriptedGetterResult(
5461 ValOperandId receiverId, uint32_t getterOffset, bool sameRealm,
5462 uint32_t nargsAndFlagsOffset) {
5463 return emitCallGetterResult(CallKind::Scripted, receiverId, getterOffset,
5464 sameRealm, nargsAndFlagsOffset);
5467 bool WarpCacheIRTranspiler::emitCallInlinedGetterResult(
5468 ValOperandId receiverId, uint32_t getterOffset, uint32_t icScriptOffset,
5469 bool sameRealm, uint32_t nargsAndFlagsOffset) {
5470 return emitCallGetterResult(CallKind::Scripted, receiverId, getterOffset,
5471 sameRealm, nargsAndFlagsOffset);
5474 bool WarpCacheIRTranspiler::emitCallNativeGetterResult(
5475 ValOperandId receiverId, uint32_t getterOffset, bool sameRealm,
5476 uint32_t nargsAndFlagsOffset) {
5477 return emitCallGetterResult(CallKind::Native, receiverId, getterOffset,
5478 sameRealm, nargsAndFlagsOffset);
5481 bool WarpCacheIRTranspiler::emitCallSetter(CallKind kind,
5482 ObjOperandId receiverId,
5483 uint32_t setterOffset,
5484 ValOperandId rhsId, bool sameRealm,
5485 uint32_t nargsAndFlagsOffset) {
5486 MDefinition* receiver = getOperand(receiverId);
5487 MDefinition* setter = objectStubField(setterOffset);
5488 MDefinition* rhs = getOperand(rhsId);
5489 if (kind == CallKind::Scripted && callInfo_ && callInfo_->isInlined()) {
5490 // We are transpiling to generate the correct guards. We also update the
5491 // CallInfo to use the correct arguments. Code for the inlined setter
5492 // itself will be generated in WarpBuilder::buildInlinedCall.
5493 callInfo_->initForSetterCall(setter, receiver, rhs);
5494 callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
5496 // Make sure there's enough room to push the arguments on the stack.
5497 if (!current->ensureHasSlots(3)) {
5498 return false;
5501 return true;
5504 uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
5505 uint16_t nargs = nargsAndFlags >> 16;
5506 FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
5507 WrappedFunction* wrappedTarget =
5508 maybeWrappedFunction(setter, kind, nargs, flags);
5510 CallInfo callInfo(alloc(), /* constructing = */ false,
5511 /* ignoresReturnValue = */ true);
5512 callInfo.initForSetterCall(setter, receiver, rhs);
5514 MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, wrappedTarget);
5515 if (!call) {
5516 return false;
5519 if (sameRealm) {
5520 call->setNotCrossRealm();
5523 addEffectful(call);
5524 return resumeAfter(call);
5527 bool WarpCacheIRTranspiler::emitCallScriptedSetter(
5528 ObjOperandId receiverId, uint32_t setterOffset, ValOperandId rhsId,
5529 bool sameRealm, uint32_t nargsAndFlagsOffset) {
5530 return emitCallSetter(CallKind::Scripted, receiverId, setterOffset, rhsId,
5531 sameRealm, nargsAndFlagsOffset);
5534 bool WarpCacheIRTranspiler::emitCallInlinedSetter(
5535 ObjOperandId receiverId, uint32_t setterOffset, ValOperandId rhsId,
5536 uint32_t icScriptOffset, bool sameRealm, uint32_t nargsAndFlagsOffset) {
5537 return emitCallSetter(CallKind::Scripted, receiverId, setterOffset, rhsId,
5538 sameRealm, nargsAndFlagsOffset);
5541 bool WarpCacheIRTranspiler::emitCallNativeSetter(ObjOperandId receiverId,
5542 uint32_t setterOffset,
5543 ValOperandId rhsId,
5544 bool sameRealm,
5545 uint32_t nargsAndFlagsOffset) {
5546 return emitCallSetter(CallKind::Native, receiverId, setterOffset, rhsId,
5547 sameRealm, nargsAndFlagsOffset);
5550 bool WarpCacheIRTranspiler::emitMetaScriptedThisShape(
5551 uint32_t thisShapeOffset) {
5552 SharedShape* shape = &shapeStubField(thisShapeOffset)->asShared();
5553 MOZ_ASSERT(shape->getObjectClass() == &PlainObject::class_);
5555 MConstant* shapeConst = MConstant::NewShape(alloc(), shape);
5556 add(shapeConst);
5558 // TODO: support pre-tenuring.
5559 gc::Heap heap = gc::Heap::Default;
5561 uint32_t numFixedSlots = shape->numFixedSlots();
5562 uint32_t numDynamicSlots = NativeObject::calculateDynamicSlots(shape);
5563 gc::AllocKind kind = gc::GetGCObjectKind(numFixedSlots);
5564 MOZ_ASSERT(gc::CanChangeToBackgroundAllocKind(kind, &PlainObject::class_));
5565 kind = gc::ForegroundToBackgroundAllocKind(kind);
5567 auto* createThis = MNewPlainObject::New(alloc(), shapeConst, numFixedSlots,
5568 numDynamicSlots, kind, heap);
5569 add(createThis);
5571 callInfo_->thisArg()->setImplicitlyUsedUnchecked();
5572 callInfo_->setThis(createThis);
5573 return true;
5576 bool WarpCacheIRTranspiler::emitReturnFromIC() { return true; }
5578 bool WarpCacheIRTranspiler::emitBailout() {
5579 auto* bail = MBail::New(alloc());
5580 add(bail);
5582 return true;
5585 bool WarpCacheIRTranspiler::emitAssertPropertyLookup(ObjOperandId objId,
5586 uint32_t idOffset,
5587 uint32_t slotOffset) {
5588 // We currently only emit checks in baseline.
5589 return true;
5592 bool WarpCacheIRTranspiler::emitAssertRecoveredOnBailoutResult(
5593 ValOperandId valId, bool mustBeRecovered) {
5594 MDefinition* val = getOperand(valId);
5596 // Don't assert for recovered instructions when recovering is disabled.
5597 if (JitOptions.disableRecoverIns) {
5598 pushResult(constant(UndefinedValue()));
5599 return true;
5602 if (JitOptions.checkRangeAnalysis) {
5603 // If we are checking the range of all instructions, then the guards
5604 // inserted by Range Analysis prevent the use of recover instruction. Thus,
5605 // we just disable these checks.
5606 pushResult(constant(UndefinedValue()));
5607 return true;
5610 auto* assert = MAssertRecoveredOnBailout::New(alloc(), val, mustBeRecovered);
5611 addEffectfulUnsafe(assert);
5612 current->push(assert);
5614 // Create an instruction sequence which implies that the argument of the
5615 // assertRecoveredOnBailout function would be encoded at least in one
5616 // Snapshot.
5617 auto* nop = MNop::New(alloc());
5618 add(nop);
5620 auto* resumePoint = MResumePoint::New(
5621 alloc(), nop->block(), loc_.toRawBytecode(), ResumeMode::ResumeAfter);
5622 if (!resumePoint) {
5623 return false;
5625 nop->setResumePoint(resumePoint);
5627 auto* encode = MEncodeSnapshot::New(alloc());
5628 addEffectfulUnsafe(encode);
5630 current->pop();
5632 pushResult(constant(UndefinedValue()));
5633 return true;
5636 bool WarpCacheIRTranspiler::emitGuardNoAllocationMetadataBuilder(
5637 uint32_t builderAddrOffset) {
5638 // This is a no-op because we discard all JIT code when set an allocation
5639 // metadata callback.
5640 return true;
5643 bool WarpCacheIRTranspiler::emitNewPlainObjectResult(uint32_t numFixedSlots,
5644 uint32_t numDynamicSlots,
5645 gc::AllocKind allocKind,
5646 uint32_t shapeOffset,
5647 uint32_t siteOffset) {
5648 Shape* shape = shapeStubField(shapeOffset);
5649 gc::Heap heap = allocSiteInitialHeapField(siteOffset);
5651 auto* shapeConstant = MConstant::NewShape(alloc(), shape);
5652 add(shapeConstant);
5654 auto* obj = MNewPlainObject::New(alloc(), shapeConstant, numFixedSlots,
5655 numDynamicSlots, allocKind, heap);
5656 add(obj);
5658 pushResult(obj);
5659 return true;
5662 bool WarpCacheIRTranspiler::emitNewArrayObjectResult(uint32_t length,
5663 uint32_t shapeOffset,
5664 uint32_t siteOffset) {
5665 Shape* shape = shapeStubField(shapeOffset);
5666 gc::Heap heap = allocSiteInitialHeapField(siteOffset);
5668 auto* shapeConstant = MConstant::NewShape(alloc(), shape);
5669 add(shapeConstant);
5671 auto* obj = MNewArrayObject::New(alloc(), shapeConstant, length, heap);
5672 add(obj);
5674 pushResult(obj);
5675 return true;
5678 bool WarpCacheIRTranspiler::emitCloseIterScriptedResult(ObjOperandId iterId,
5679 ObjOperandId calleeId,
5680 CompletionKind kind,
5681 uint32_t calleeNargs) {
5682 MDefinition* iter = getOperand(iterId);
5683 MDefinition* callee = getOperand(calleeId);
5685 WrappedFunction* wrappedTarget = maybeCallTarget(callee, CallKind::Scripted);
5686 MOZ_ASSERT(wrappedTarget);
5687 MOZ_ASSERT(wrappedTarget->nargs() == calleeNargs);
5688 MOZ_ASSERT(wrappedTarget->hasJitEntry());
5690 bool constructing = false;
5691 bool ignoresRval = false;
5692 bool needsThisCheck = false;
5693 bool isDOMCall = false;
5694 CallInfo callInfo(alloc(), constructing, ignoresRval);
5695 callInfo.initForCloseIter(iter, callee);
5696 MCall* call = makeCall(callInfo, needsThisCheck, wrappedTarget, isDOMCall);
5697 if (!call) {
5698 return false;
5700 addEffectful(call);
5701 if (kind == CompletionKind::Throw) {
5702 return resumeAfter(call);
5705 // If we bail out here, after the call but before the CheckIsObj, we
5706 // can't simply resume in the baseline interpreter. If we resume
5707 // after the CloseIter, we won't check the return value. If we
5708 // resume at the CloseIter, we will call the |return| method twice.
5709 // Instead, we use a special resume mode that captures the
5710 // intermediate value, and then checks that it's an object while
5711 // bailing out.
5712 current->push(call);
5713 MResumePoint* resumePoint =
5714 MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
5715 ResumeMode::ResumeAfterCheckIsObject);
5716 if (!resumePoint) {
5717 return false;
5719 call->setResumePoint(resumePoint);
5720 current->pop();
5722 MCheckIsObj* check = MCheckIsObj::New(
5723 alloc(), call, uint8_t(CheckIsObjectKind::IteratorReturn));
5724 add(check);
5726 return true;
5729 bool WarpCacheIRTranspiler::emitGuardGlobalGeneration(
5730 uint32_t expectedOffset, uint32_t generationAddrOffset) {
5731 uint32_t expected = uint32StubField(expectedOffset);
5732 const void* generationAddr = rawPointerField(generationAddrOffset);
5734 auto guard = MGuardGlobalGeneration::New(alloc(), expected, generationAddr);
5735 add(guard);
5737 return true;
5740 #ifdef FUZZING_JS_FUZZILLI
5741 bool WarpCacheIRTranspiler::emitFuzzilliHashResult(ValOperandId valId) {
5742 MDefinition* input = getOperand(valId);
5744 auto* hash = MFuzzilliHash::New(alloc(), input);
5745 add(hash);
5747 auto* store = MFuzzilliHashStore::New(alloc(), hash);
5748 addEffectful(store);
5749 pushResult(constant(UndefinedValue()));
5751 return resumeAfter(store);
5753 #endif
5755 static void MaybeSetImplicitlyUsed(uint32_t numInstructionIdsBefore,
5756 MDefinition* input) {
5757 // When building MIR from bytecode, for each MDefinition that's an operand to
5758 // a bytecode instruction, we must either add an SSA use or set the
5759 // ImplicitlyUsed flag on that definition. The ImplicitlyUsed flag prevents
5760 // the backend from optimizing-out values that will be used by Baseline after
5761 // a bailout.
5763 // WarpBuilder uses WarpPoppedValueUseChecker to assert this invariant in
5764 // debug builds.
5766 // This function is responsible for setting the ImplicitlyUsed flag for an
5767 // input when using the transpiler. It looks at the input's most recent use
5768 // and if that's an instruction that was added while transpiling this JSOp
5769 // (based on the MIR instruction id) we don't set the ImplicitlyUsed flag.
5771 if (input->isImplicitlyUsed()) {
5772 // Nothing to do.
5773 return;
5776 // If the most recent use of 'input' is an instruction we just added, there is
5777 // nothing to do.
5778 MDefinition* inputUse = input->maybeMostRecentlyAddedDefUse();
5779 if (inputUse && inputUse->id() >= numInstructionIdsBefore) {
5780 return;
5783 // The transpiler didn't add a use for 'input'.
5784 input->setImplicitlyUsed();
5787 bool jit::TranspileCacheIRToMIR(WarpBuilder* builder, BytecodeLocation loc,
5788 const WarpCacheIR* cacheIRSnapshot,
5789 std::initializer_list<MDefinition*> inputs,
5790 CallInfo* maybeCallInfo) {
5791 uint32_t numInstructionIdsBefore =
5792 builder->mirGen().graph().getNumInstructionIds();
5794 WarpCacheIRTranspiler transpiler(builder, loc, maybeCallInfo,
5795 cacheIRSnapshot);
5796 if (!transpiler.transpile(inputs)) {
5797 return false;
5800 for (MDefinition* input : inputs) {
5801 MaybeSetImplicitlyUsed(numInstructionIdsBefore, input);
5804 if (maybeCallInfo) {
5805 auto maybeSetFlag = [numInstructionIdsBefore](MDefinition* def) {
5806 MaybeSetImplicitlyUsed(numInstructionIdsBefore, def);
5808 maybeCallInfo->forEachCallOperand(maybeSetFlag);
5811 return true;