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/WarpBuilder.h"
9 #include "mozilla/DebugOnly.h"
11 #include "jit/BaselineFrame.h"
12 #include "jit/CacheIR.h"
13 #include "jit/CompileInfo.h"
14 #include "jit/InlineScriptTree.h"
15 #include "jit/MIR-wasm.h"
17 #include "jit/MIRGenerator.h"
18 #include "jit/MIRGraph.h"
19 #include "jit/WarpCacheIRTranspiler.h"
20 #include "jit/WarpSnapshot.h"
21 #include "js/friend/ErrorMessages.h" // JSMSG_BAD_CONST_ASSIGN
22 #include "vm/GeneratorObject.h"
23 #include "vm/Interpreter.h"
24 #include "vm/Opcodes.h"
25 #include "vm/TypeofEqOperand.h" // TypeofEqOperand
27 #include "gc/ObjectKind-inl.h"
28 #include "vm/BytecodeIterator-inl.h"
29 #include "vm/BytecodeLocation-inl.h"
30 #include "vm/JSObject-inl.h"
33 using namespace js::jit
;
35 // Used for building the outermost script.
36 WarpBuilder::WarpBuilder(WarpSnapshot
& snapshot
, MIRGenerator
& mirGen
,
37 WarpCompilation
* warpCompilation
)
38 : WarpBuilderShared(snapshot
, mirGen
, nullptr),
39 warpCompilation_(warpCompilation
),
40 graph_(mirGen
.graph()),
41 info_(mirGen
.outerInfo()),
42 scriptSnapshot_(snapshot
.rootScript()),
43 script_(snapshot
.rootScript()->script()),
44 loopStack_(mirGen
.alloc()) {
45 opSnapshotIter_
= scriptSnapshot_
->opSnapshots().getFirst();
48 // Used for building inlined scripts.
49 WarpBuilder::WarpBuilder(WarpBuilder
* caller
, WarpScriptSnapshot
* snapshot
,
50 CompileInfo
& compileInfo
, CallInfo
* inlineCallInfo
,
51 MResumePoint
* callerResumePoint
)
52 : WarpBuilderShared(caller
->snapshot(), caller
->mirGen(), nullptr),
53 warpCompilation_(caller
->warpCompilation()),
54 graph_(caller
->mirGen().graph()),
56 scriptSnapshot_(snapshot
),
57 script_(snapshot
->script()),
58 loopStack_(caller
->mirGen().alloc()),
59 callerBuilder_(caller
),
60 callerResumePoint_(callerResumePoint
),
61 inlineCallInfo_(inlineCallInfo
) {
62 opSnapshotIter_
= snapshot
->opSnapshots().getFirst();
65 BytecodeSite
* WarpBuilder::newBytecodeSite(BytecodeLocation loc
) {
66 jsbytecode
* pc
= loc
.toRawBytecode();
67 MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc
));
68 return new (alloc()) BytecodeSite(info().inlineScriptTree(), pc
);
71 const WarpOpSnapshot
* WarpBuilder::getOpSnapshotImpl(
72 BytecodeLocation loc
, WarpOpSnapshot::Kind kind
) {
73 uint32_t offset
= loc
.bytecodeToOffset(script_
);
75 // Skip snapshots until we get to a snapshot with offset >= offset. This is
76 // a loop because WarpBuilder can skip unreachable bytecode ops.
77 while (opSnapshotIter_
&& opSnapshotIter_
->offset() < offset
) {
78 opSnapshotIter_
= opSnapshotIter_
->getNext();
81 if (!opSnapshotIter_
|| opSnapshotIter_
->offset() != offset
||
82 opSnapshotIter_
->kind() != kind
) {
86 return opSnapshotIter_
;
89 void WarpBuilder::initBlock(MBasicBlock
* block
) {
90 graph().addBlock(block
);
92 block
->setLoopDepth(loopDepth());
97 bool WarpBuilder::startNewBlock(MBasicBlock
* predecessor
, BytecodeLocation loc
,
100 MBasicBlock::NewPopN(graph(), info(), predecessor
, newBytecodeSite(loc
),
101 MBasicBlock::NORMAL
, numToPop
);
110 bool WarpBuilder::startNewEntryBlock(size_t stackDepth
, BytecodeLocation loc
) {
112 MBasicBlock::New(graph(), stackDepth
, info(), /* maybePred = */ nullptr,
113 newBytecodeSite(loc
), MBasicBlock::NORMAL
);
122 bool WarpBuilder::startNewLoopHeaderBlock(BytecodeLocation loopHead
) {
123 MBasicBlock
* header
= MBasicBlock::NewPendingLoopHeader(
124 graph(), info(), current
, newBytecodeSite(loopHead
));
130 return loopStack_
.emplaceBack(header
);
133 bool WarpBuilder::startNewOsrPreHeaderBlock(BytecodeLocation loopHead
) {
134 MOZ_ASSERT(loopHead
.is(JSOp::LoopHead
));
135 MOZ_ASSERT(loopHead
.toRawBytecode() == info().osrPc());
137 // Create two blocks:
138 // * The OSR entry block. This is always the graph's second block and has no
139 // predecessors. This is the entry point for OSR from the Baseline JIT.
140 // * The OSR preheader block. This has two predecessors: the OSR entry block
141 // and the current block.
143 MBasicBlock
* pred
= current
;
145 // Create the OSR entry block.
146 if (!startNewEntryBlock(pred
->stackDepth(), loopHead
)) {
150 MBasicBlock
* osrBlock
= current
;
151 graph().setOsrBlock(osrBlock
);
152 graph().moveBlockAfter(*graph().begin(), osrBlock
);
154 MOsrEntry
* entry
= MOsrEntry::New(alloc());
155 osrBlock
->add(entry
);
157 // Initialize environment chain.
159 uint32_t slot
= info().environmentChainSlot();
161 if (usesEnvironmentChain()) {
162 envv
= MOsrEnvironmentChain::New(alloc(), entry
);
164 // Use an undefined value if the script does not need its environment
165 // chain, to match the main entry point.
166 envv
= MConstant::New(alloc(), UndefinedValue());
169 osrBlock
->initSlot(slot
, envv
);
172 // Initialize return value.
174 MInstruction
* returnValue
;
175 if (!script_
->noScriptRval()) {
176 returnValue
= MOsrReturnValue::New(alloc(), entry
);
178 returnValue
= MConstant::New(alloc(), UndefinedValue());
180 osrBlock
->add(returnValue
);
181 osrBlock
->initSlot(info().returnValueSlot(), returnValue
);
184 // Initialize arguments object.
185 MInstruction
* argsObj
= nullptr;
186 if (info().needsArgsObj()) {
187 argsObj
= MOsrArgumentsObject::New(alloc(), entry
);
188 osrBlock
->add(argsObj
);
189 osrBlock
->initSlot(info().argsObjSlot(), argsObj
);
192 if (info().hasFunMaybeLazy()) {
193 // Initialize |this| parameter.
194 MParameter
* thisv
= MParameter::New(alloc(), MParameter::THIS_SLOT
);
195 osrBlock
->add(thisv
);
196 osrBlock
->initSlot(info().thisSlot(), thisv
);
198 // Initialize arguments. There are three cases:
200 // 1) There's no ArgumentsObject or it doesn't alias formals. In this case
201 // we can just use the frame's argument slot.
202 // 2) The ArgumentsObject aliases formals and the argument is stored in the
203 // CallObject. Use |undefined| because we can't load from the arguments
204 // object and code will use the CallObject anyway.
205 // 3) The ArgumentsObject aliases formals and the argument isn't stored in
206 // the CallObject. We have to load it from the ArgumentsObject.
207 for (uint32_t i
= 0; i
< info().nargs(); i
++) {
208 uint32_t slot
= info().argSlotUnchecked(i
);
210 if (!info().argsObjAliasesFormals()) {
211 osrv
= MParameter::New(alloc().fallible(), i
);
212 } else if (script_
->formalIsAliased(i
)) {
213 osrv
= MConstant::New(alloc().fallible(), UndefinedValue());
215 osrv
= MGetArgumentsObjectArg::New(alloc().fallible(), argsObj
, i
);
221 current
->initSlot(slot
, osrv
);
225 // Initialize locals.
226 uint32_t nlocals
= info().nlocals();
227 for (uint32_t i
= 0; i
< nlocals
; i
++) {
228 uint32_t slot
= info().localSlot(i
);
229 ptrdiff_t offset
= BaselineFrame::reverseOffsetOfLocal(i
);
230 MOsrValue
* osrv
= MOsrValue::New(alloc().fallible(), entry
, offset
);
235 current
->initSlot(slot
, osrv
);
238 // Initialize expression stack slots.
239 uint32_t numStackSlots
= current
->stackDepth() - info().firstStackSlot();
240 for (uint32_t i
= 0; i
< numStackSlots
; i
++) {
241 uint32_t slot
= info().stackSlot(i
);
242 ptrdiff_t offset
= BaselineFrame::reverseOffsetOfLocal(nlocals
+ i
);
243 MOsrValue
* osrv
= MOsrValue::New(alloc().fallible(), entry
, offset
);
248 current
->initSlot(slot
, osrv
);
251 MStart
* start
= MStart::New(alloc());
254 // Note: phi specialization can add type guard instructions to the OSR entry
255 // block if needed. See TypeAnalyzer::shouldSpecializeOsrPhis.
257 // Create the preheader block, with the predecessor block and OSR block as
259 if (!startNewBlock(pred
, loopHead
)) {
263 pred
->end(MGoto::New(alloc(), current
));
264 osrBlock
->end(MGoto::New(alloc(), current
));
266 if (!current
->addPredecessor(alloc(), osrBlock
)) {
273 bool WarpBuilder::addPendingEdge(BytecodeLocation target
, MBasicBlock
* block
,
274 uint32_t successor
, uint32_t numToPop
) {
275 MOZ_ASSERT(successor
< block
->lastIns()->numSuccessors());
276 MOZ_ASSERT(numToPop
<= block
->stackDepth());
278 jsbytecode
* targetPC
= target
.toRawBytecode();
279 PendingEdgesMap::AddPtr p
= pendingEdges_
.lookupForAdd(targetPC
);
281 return p
->value().emplaceBack(block
, successor
, numToPop
);
285 static_assert(PendingEdges::InlineLength
>= 1,
286 "Appending one element should be infallible");
287 MOZ_ALWAYS_TRUE(edges
.emplaceBack(block
, successor
, numToPop
));
289 return pendingEdges_
.add(p
, targetPC
, std::move(edges
));
292 bool WarpBuilder::build() {
293 if (!buildPrologue()) {
301 if (!MPhi::markIteratorPhis(*iterators())) {
305 MOZ_ASSERT_IF(info().osrPc(), graph().osrBlock());
306 MOZ_ASSERT(loopStack_
.empty());
307 MOZ_ASSERT(loopDepth() == 0);
312 bool WarpBuilder::buildInline() {
313 if (!buildInlinePrologue()) {
321 MOZ_ASSERT(loopStack_
.empty());
325 MInstruction
* WarpBuilder::buildNamedLambdaEnv(MDefinition
* callee
,
327 NamedLambdaObject
* templateObj
) {
328 MOZ_ASSERT(templateObj
->numDynamicSlots() == 0);
330 MInstruction
* namedLambda
= MNewNamedLambdaObject::New(alloc(), templateObj
);
331 current
->add(namedLambda
);
334 // Assert in debug mode we can elide the post write barriers.
335 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), namedLambda
, env
));
337 MAssertCanElidePostWriteBarrier::New(alloc(), namedLambda
, callee
));
340 // Initialize the object's reserved slots. No post barrier is needed here:
341 // the object will be allocated in the nursery if possible, and if the
342 // tenured heap is used instead, a minor collection will have been performed
343 // that moved env/callee to the tenured heap.
344 size_t enclosingSlot
= NamedLambdaObject::enclosingEnvironmentSlot();
345 size_t lambdaSlot
= NamedLambdaObject::lambdaSlot();
346 current
->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda
,
347 enclosingSlot
, env
));
348 current
->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda
, lambdaSlot
,
354 MInstruction
* WarpBuilder::buildCallObject(MDefinition
* callee
,
356 CallObject
* templateObj
) {
357 MConstant
* templateCst
= constant(ObjectValue(*templateObj
));
359 MNewCallObject
* callObj
= MNewCallObject::New(alloc(), templateCst
);
360 current
->add(callObj
);
363 // Assert in debug mode we can elide the post write barriers.
364 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), callObj
, env
));
365 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), callObj
, callee
));
368 // Initialize the object's reserved slots. No post barrier is needed here,
369 // for the same reason as in buildNamedLambdaEnv.
370 size_t enclosingSlot
= CallObject::enclosingEnvironmentSlot();
371 size_t calleeSlot
= CallObject::calleeSlot();
373 MStoreFixedSlot::NewUnbarriered(alloc(), callObj
, enclosingSlot
, env
));
375 MStoreFixedSlot::NewUnbarriered(alloc(), callObj
, calleeSlot
, callee
));
380 bool WarpBuilder::buildEnvironmentChain() {
381 const WarpEnvironment
& env
= scriptSnapshot()->environment();
383 if (env
.is
<NoEnvironment
>()) {
387 MInstruction
* envDef
= env
.match(
388 [](const NoEnvironment
&) -> MInstruction
* {
389 MOZ_CRASH("Already handled");
391 [this](JSObject
* obj
) -> MInstruction
* {
392 return constant(ObjectValue(*obj
));
394 [this](const FunctionEnvironment
& env
) -> MInstruction
* {
395 MDefinition
* callee
= getCallee();
396 MInstruction
* envDef
= MFunctionEnvironment::New(alloc(), callee
);
397 current
->add(envDef
);
398 if (NamedLambdaObject
* obj
= env
.namedLambdaTemplate
) {
399 envDef
= buildNamedLambdaEnv(callee
, envDef
, obj
);
401 if (CallObject
* obj
= env
.callObjectTemplate
) {
402 envDef
= buildCallObject(callee
, envDef
, obj
);
413 // Update the environment slot from UndefinedValue only after the initial
414 // environment is created so that bailout doesn't see a partial environment.
415 // See: |BaselineStackBuilder::buildBaselineFrame|
416 current
->setEnvironmentChain(envDef
);
420 bool WarpBuilder::buildPrologue() {
421 BytecodeLocation
startLoc(script_
, script_
->code());
422 if (!startNewEntryBlock(info().firstStackSlot(), startLoc
)) {
426 if (info().hasFunMaybeLazy()) {
427 // Initialize |this|.
428 MParameter
* param
= MParameter::New(alloc(), MParameter::THIS_SLOT
);
430 current
->initSlot(info().thisSlot(), param
);
432 // Initialize arguments.
433 for (uint32_t i
= 0; i
< info().nargs(); i
++) {
434 MParameter
* param
= MParameter::New(alloc().fallible(), i
);
439 current
->initSlot(info().argSlotUnchecked(i
), param
);
443 MConstant
* undef
= constant(UndefinedValue());
445 // Initialize local slots.
446 for (uint32_t i
= 0; i
< info().nlocals(); i
++) {
447 current
->initSlot(info().localSlot(i
), undef
);
450 // Initialize the environment chain, return value, and arguments object slots.
451 current
->initSlot(info().environmentChainSlot(), undef
);
452 current
->initSlot(info().returnValueSlot(), undef
);
453 if (info().needsArgsObj()) {
454 current
->initSlot(info().argsObjSlot(), undef
);
457 current
->add(MStart::New(alloc()));
459 // Guard against over-recursion.
460 MCheckOverRecursed
* check
= MCheckOverRecursed::New(alloc());
463 if (!buildEnvironmentChain()) {
467 #ifdef JS_CACHEIR_SPEW
468 if (snapshot().needsFinalWarmUpCount()) {
469 MIncrementWarmUpCounter
* ins
=
470 MIncrementWarmUpCounter::New(alloc(), script_
);
478 bool WarpBuilder::buildInlinePrologue() {
479 // Generate entry block.
480 BytecodeLocation
startLoc(script_
, script_
->code());
481 if (!startNewEntryBlock(info().firstStackSlot(), startLoc
)) {
484 current
->setCallerResumePoint(callerResumePoint());
486 // Connect the entry block to the last block in the caller's graph.
487 MBasicBlock
* pred
= callerBuilder()->current
;
488 MOZ_ASSERT(pred
== callerResumePoint()->block());
490 pred
->end(MGoto::New(alloc(), current
));
491 if (!current
->addPredecessorWithoutPhis(pred
)) {
495 MConstant
* undef
= constant(UndefinedValue());
497 // Initialize env chain slot to Undefined. It's set later by
498 // |buildEnvironmentChain|.
499 current
->initSlot(info().environmentChainSlot(), undef
);
501 // Initialize |return value| slot.
502 current
->initSlot(info().returnValueSlot(), undef
);
504 // Initialize |arguments| slot if needed.
505 if (info().needsArgsObj()) {
506 current
->initSlot(info().argsObjSlot(), undef
);
509 // Initialize |this| slot.
510 current
->initSlot(info().thisSlot(), inlineCallInfo()->thisArg());
512 uint32_t callerArgs
= inlineCallInfo()->argc();
513 uint32_t actualArgs
= info().nargs();
514 uint32_t passedArgs
= std::min
<uint32_t>(callerArgs
, actualArgs
);
516 // Initialize actually set arguments.
517 for (uint32_t i
= 0; i
< passedArgs
; i
++) {
518 MDefinition
* arg
= inlineCallInfo()->getArg(i
);
519 current
->initSlot(info().argSlotUnchecked(i
), arg
);
522 // Pass undefined for missing arguments.
523 for (uint32_t i
= passedArgs
; i
< actualArgs
; i
++) {
524 current
->initSlot(info().argSlotUnchecked(i
), undef
);
527 // Initialize local slots.
528 for (uint32_t i
= 0; i
< info().nlocals(); i
++) {
529 current
->initSlot(info().localSlot(i
), undef
);
532 MOZ_ASSERT(current
->entryResumePoint()->stackDepth() == info().totalSlots());
534 if (!buildEnvironmentChain()) {
542 // In debug builds, after compiling a bytecode op, this class is used to check
543 // that all values popped by this opcode either:
545 // (1) Have the ImplicitlyUsed flag set on them.
546 // (2) Have more uses than before compiling this op (the value is
547 // used as operand of a new MIR instruction).
549 // This is used to catch problems where WarpBuilder pops a value without
550 // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
551 class MOZ_RAII WarpPoppedValueUseChecker
{
552 Vector
<MDefinition
*, 4, SystemAllocPolicy
> popped_
;
553 Vector
<size_t, 4, SystemAllocPolicy
> poppedUses_
;
554 MBasicBlock
* current_
;
555 BytecodeLocation loc_
;
558 WarpPoppedValueUseChecker(MBasicBlock
* current
, BytecodeLocation loc
)
559 : current_(current
), loc_(loc
) {}
561 [[nodiscard
]] bool init() {
562 // Don't require SSA uses for values popped by these ops.
563 switch (loc_
.getOp()) {
574 case JSOp::InitLexical
:
577 // Basic stack/local/argument management opcodes.
582 // These ops have to pop the switch value when branching but don't
590 unsigned nuses
= loc_
.useCount();
592 for (unsigned i
= 0; i
< nuses
; i
++) {
593 MDefinition
* def
= current_
->peek(-int32_t(i
+ 1));
594 if (!popped_
.append(def
) || !poppedUses_
.append(def
->defUseCount())) {
602 void checkAfterOp() {
603 for (size_t i
= 0; i
< popped_
.length(); i
++) {
604 // First value popped by JSOp::EndIter is not used at all, it's similar
605 // to JSOp::Pop above.
606 if (loc_
.is(JSOp::EndIter
) && i
== 0) {
609 MOZ_ASSERT(popped_
[i
]->isImplicitlyUsed() ||
610 popped_
[i
]->defUseCount() > poppedUses_
[i
]);
616 bool WarpBuilder::buildBody() {
617 for (BytecodeLocation loc
: AllBytecodesIterable(script_
)) {
618 if (mirGen().shouldCancel("WarpBuilder (opcode loop)")) {
622 // Skip unreachable ops (for example code after a 'return' or 'throw') until
623 // we get to the next jump target.
624 if (hasTerminatedBlock()) {
625 // Finish any "broken" loops with an unreachable backedge. For example:
633 // This loop never actually loops.
634 if (loc
.isBackedge() && !loopStack_
.empty()) {
635 BytecodeLocation
loopHead(script_
,
636 loopStack_
.back().header()->entryPC());
637 if (loc
.isBackedgeForLoophead(loopHead
)) {
639 loopStack_
.popBack();
642 if (!loc
.isJumpTarget()) {
647 if (!alloc().ensureBallast()) {
652 WarpPoppedValueUseChecker
useChecker(current
, loc
);
653 if (!useChecker
.init()) {
657 bool wantPreciseLineNumbers
= js::jit::PerfEnabled();
658 if (wantPreciseLineNumbers
&& !hasTerminatedBlock()) {
659 current
->updateTrackedSite(newBytecodeSite(loc
));
662 JSOp op
= loc
.getOp();
664 #define BUILD_OP(OP, ...) \
666 if (MOZ_UNLIKELY(!this->build_##OP(loc))) { \
670 switch (op
) { FOR_EACH_OPCODE(BUILD_OP
) }
674 useChecker
.checkAfterOp();
682 bool WarpBuilder::build_##OP(BytecodeLocation) { \
683 MOZ_CRASH("Unsupported op"); \
685 WARP_UNSUPPORTED_OPCODE_LIST(DEF_OP
)
688 bool WarpBuilder::build_Nop(BytecodeLocation
) { return true; }
690 bool WarpBuilder::build_NopDestructuring(BytecodeLocation
) { return true; }
692 bool WarpBuilder::build_NopIsAssignOp(BytecodeLocation
) { return true; }
694 bool WarpBuilder::build_TryDestructuring(BytecodeLocation
) {
695 // Set the hasTryBlock flag to turn off optimizations that eliminate dead
696 // resume points operands because the exception handler code for
697 // TryNoteKind::Destructuring is effectively a (specialized) catch-block.
698 graph().setHasTryBlock();
702 bool WarpBuilder::build_Lineno(BytecodeLocation
) { return true; }
704 bool WarpBuilder::build_DebugLeaveLexicalEnv(BytecodeLocation
) { return true; }
706 bool WarpBuilder::build_Undefined(BytecodeLocation
) {
707 pushConstant(UndefinedValue());
711 bool WarpBuilder::build_Void(BytecodeLocation
) {
713 pushConstant(UndefinedValue());
717 bool WarpBuilder::build_Null(BytecodeLocation
) {
718 pushConstant(NullValue());
722 bool WarpBuilder::build_Hole(BytecodeLocation
) {
723 pushConstant(MagicValue(JS_ELEMENTS_HOLE
));
727 bool WarpBuilder::build_Uninitialized(BytecodeLocation
) {
728 pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL
));
732 bool WarpBuilder::build_IsConstructing(BytecodeLocation
) {
733 pushConstant(MagicValue(JS_IS_CONSTRUCTING
));
737 bool WarpBuilder::build_False(BytecodeLocation
) {
738 pushConstant(BooleanValue(false));
742 bool WarpBuilder::build_True(BytecodeLocation
) {
743 pushConstant(BooleanValue(true));
747 bool WarpBuilder::build_Pop(BytecodeLocation
) {
752 bool WarpBuilder::build_PopN(BytecodeLocation loc
) {
753 for (uint32_t i
= 0, n
= loc
.getPopCount(); i
< n
; i
++) {
759 bool WarpBuilder::build_Dup(BytecodeLocation
) {
760 current
->pushSlot(current
->stackDepth() - 1);
764 bool WarpBuilder::build_Dup2(BytecodeLocation
) {
765 uint32_t lhsSlot
= current
->stackDepth() - 2;
766 uint32_t rhsSlot
= current
->stackDepth() - 1;
767 current
->pushSlot(lhsSlot
);
768 current
->pushSlot(rhsSlot
);
772 bool WarpBuilder::build_DupAt(BytecodeLocation loc
) {
773 current
->pushSlot(current
->stackDepth() - 1 - loc
.getDupAtIndex());
777 bool WarpBuilder::build_Swap(BytecodeLocation
) {
782 bool WarpBuilder::build_Pick(BytecodeLocation loc
) {
783 int32_t depth
= -int32_t(loc
.getPickDepth());
784 current
->pick(depth
);
788 bool WarpBuilder::build_Unpick(BytecodeLocation loc
) {
789 int32_t depth
= -int32_t(loc
.getUnpickDepth());
790 current
->unpick(depth
);
794 bool WarpBuilder::build_Zero(BytecodeLocation
) {
795 pushConstant(Int32Value(0));
799 bool WarpBuilder::build_One(BytecodeLocation
) {
800 pushConstant(Int32Value(1));
804 bool WarpBuilder::build_Int8(BytecodeLocation loc
) {
805 pushConstant(Int32Value(loc
.getInt8()));
809 bool WarpBuilder::build_Uint16(BytecodeLocation loc
) {
810 pushConstant(Int32Value(loc
.getUint16()));
814 bool WarpBuilder::build_Uint24(BytecodeLocation loc
) {
815 pushConstant(Int32Value(loc
.getUint24()));
819 bool WarpBuilder::build_Int32(BytecodeLocation loc
) {
820 pushConstant(Int32Value(loc
.getInt32()));
824 bool WarpBuilder::build_Double(BytecodeLocation loc
) {
825 pushConstant(loc
.getInlineValue());
829 bool WarpBuilder::build_BigInt(BytecodeLocation loc
) {
830 BigInt
* bi
= loc
.getBigInt(script_
);
831 pushConstant(BigIntValue(bi
));
835 bool WarpBuilder::build_String(BytecodeLocation loc
) {
836 JSString
* str
= loc
.getString(script_
);
837 pushConstant(StringValue(str
));
841 bool WarpBuilder::build_Symbol(BytecodeLocation loc
) {
842 uint32_t which
= loc
.getSymbolIndex();
843 JS::Symbol
* sym
= mirGen().runtime
->wellKnownSymbols().get(which
);
844 pushConstant(SymbolValue(sym
));
848 bool WarpBuilder::build_RegExp(BytecodeLocation loc
) {
849 RegExpObject
* reObj
= loc
.getRegExp(script_
);
851 auto* snapshot
= getOpSnapshot
<WarpRegExp
>(loc
);
853 MRegExp
* regexp
= MRegExp::New(alloc(), reObj
, snapshot
->hasShared());
854 current
->add(regexp
);
855 current
->push(regexp
);
860 bool WarpBuilder::build_Return(BytecodeLocation
) {
861 MDefinition
* def
= current
->pop();
863 MReturn
* ret
= MReturn::New(alloc(), def
);
866 if (!graph().addReturn(current
)) {
870 setTerminatedBlock();
874 bool WarpBuilder::build_RetRval(BytecodeLocation
) {
876 if (script_
->noScriptRval()) {
877 rval
= constant(UndefinedValue());
879 rval
= current
->getSlot(info().returnValueSlot());
882 MReturn
* ret
= MReturn::New(alloc(), rval
);
885 if (!graph().addReturn(current
)) {
889 setTerminatedBlock();
893 bool WarpBuilder::build_SetRval(BytecodeLocation
) {
894 MOZ_ASSERT(!script_
->noScriptRval());
895 MDefinition
* rval
= current
->pop();
896 current
->setSlot(info().returnValueSlot(), rval
);
900 bool WarpBuilder::build_GetRval(BytecodeLocation
) {
901 MOZ_ASSERT(!script_
->noScriptRval());
902 MDefinition
* rval
= current
->getSlot(info().returnValueSlot());
907 bool WarpBuilder::build_GetLocal(BytecodeLocation loc
) {
908 current
->pushLocal(loc
.local());
912 bool WarpBuilder::build_SetLocal(BytecodeLocation loc
) {
913 current
->setLocal(loc
.local());
917 bool WarpBuilder::build_InitLexical(BytecodeLocation loc
) {
918 current
->setLocal(loc
.local());
922 bool WarpBuilder::build_GetArg(BytecodeLocation loc
) {
923 uint32_t arg
= loc
.arg();
924 if (info().argsObjAliasesFormals()) {
925 MDefinition
* argsObj
= current
->argumentsObject();
926 auto* getArg
= MGetArgumentsObjectArg::New(alloc(), argsObj
, arg
);
927 current
->add(getArg
);
928 current
->push(getArg
);
930 current
->pushArg(arg
);
935 bool WarpBuilder::build_GetFrameArg(BytecodeLocation loc
) {
936 current
->pushArgUnchecked(loc
.arg());
940 bool WarpBuilder::build_SetArg(BytecodeLocation loc
) {
941 uint32_t arg
= loc
.arg();
942 MDefinition
* val
= current
->peek(-1);
944 if (!info().argsObjAliasesFormals()) {
945 // Either |arguments| is never referenced within this function, or
946 // it doesn't map to the actual arguments values. Either way, we
947 // don't need to worry about synchronizing the argument values
948 // when writing to them.
949 current
->setArg(arg
);
953 // If an arguments object is in use, and it aliases formals, then all SetArgs
954 // must go through the arguments object.
955 MDefinition
* argsObj
= current
->argumentsObject();
956 current
->add(MPostWriteBarrier::New(alloc(), argsObj
, val
));
957 auto* ins
= MSetArgumentsObjectArg::New(alloc(), argsObj
, val
, arg
);
959 return resumeAfter(ins
, loc
);
962 bool WarpBuilder::build_ArgumentsLength(BytecodeLocation
) {
963 if (inlineCallInfo()) {
964 pushConstant(Int32Value(inlineCallInfo()->argc()));
966 auto* argsLength
= MArgumentsLength::New(alloc());
967 current
->add(argsLength
);
968 current
->push(argsLength
);
973 bool WarpBuilder::build_GetActualArg(BytecodeLocation
) {
974 MDefinition
* index
= current
->pop();
976 if (inlineCallInfo()) {
977 arg
= MGetInlinedArgument::New(alloc(), index
, *inlineCallInfo());
982 arg
= MGetFrameArgument::New(alloc(), index
);
989 bool WarpBuilder::build_ToNumeric(BytecodeLocation loc
) {
990 return buildUnaryOp(loc
);
993 bool WarpBuilder::buildUnaryOp(BytecodeLocation loc
) {
994 MDefinition
* value
= current
->pop();
995 return buildIC(loc
, CacheKind::UnaryArith
, {value
});
998 bool WarpBuilder::build_Inc(BytecodeLocation loc
) { return buildUnaryOp(loc
); }
1000 bool WarpBuilder::build_Dec(BytecodeLocation loc
) { return buildUnaryOp(loc
); }
1002 bool WarpBuilder::build_Pos(BytecodeLocation loc
) { return buildUnaryOp(loc
); }
1004 bool WarpBuilder::build_Neg(BytecodeLocation loc
) { return buildUnaryOp(loc
); }
1006 bool WarpBuilder::build_BitNot(BytecodeLocation loc
) {
1007 return buildUnaryOp(loc
);
1010 bool WarpBuilder::buildBinaryOp(BytecodeLocation loc
) {
1011 MDefinition
* right
= current
->pop();
1012 MDefinition
* left
= current
->pop();
1013 return buildIC(loc
, CacheKind::BinaryArith
, {left
, right
});
1016 bool WarpBuilder::build_Add(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1018 bool WarpBuilder::build_Sub(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1020 bool WarpBuilder::build_Mul(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1022 bool WarpBuilder::build_Div(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1024 bool WarpBuilder::build_Mod(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1026 bool WarpBuilder::build_Pow(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1028 bool WarpBuilder::build_BitAnd(BytecodeLocation loc
) {
1029 return buildBinaryOp(loc
);
1032 bool WarpBuilder::build_BitOr(BytecodeLocation loc
) {
1033 return buildBinaryOp(loc
);
1036 bool WarpBuilder::build_BitXor(BytecodeLocation loc
) {
1037 return buildBinaryOp(loc
);
1040 bool WarpBuilder::build_Lsh(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1042 bool WarpBuilder::build_Rsh(BytecodeLocation loc
) { return buildBinaryOp(loc
); }
1044 bool WarpBuilder::build_Ursh(BytecodeLocation loc
) {
1045 return buildBinaryOp(loc
);
1048 bool WarpBuilder::buildCompareOp(BytecodeLocation loc
) {
1049 MDefinition
* right
= current
->pop();
1050 MDefinition
* left
= current
->pop();
1051 return buildIC(loc
, CacheKind::Compare
, {left
, right
});
1054 bool WarpBuilder::build_Eq(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1056 bool WarpBuilder::build_Ne(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1058 bool WarpBuilder::build_Lt(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1060 bool WarpBuilder::build_Le(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1062 bool WarpBuilder::build_Gt(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1064 bool WarpBuilder::build_Ge(BytecodeLocation loc
) { return buildCompareOp(loc
); }
1066 bool WarpBuilder::build_StrictEq(BytecodeLocation loc
) {
1067 return buildCompareOp(loc
);
1070 bool WarpBuilder::build_StrictNe(BytecodeLocation loc
) {
1071 return buildCompareOp(loc
);
1074 // Returns true iff the MTest added for |op| has a true-target corresponding
1075 // with the join point in the bytecode.
1076 static bool TestTrueTargetIsJoinPoint(JSOp op
) {
1078 case JSOp::JumpIfTrue
:
1083 case JSOp::JumpIfFalse
:
1085 case JSOp::Coalesce
:
1089 MOZ_CRASH("Unexpected op");
1093 bool WarpBuilder::build_JumpTarget(BytecodeLocation loc
) {
1094 PendingEdgesMap::Ptr p
= pendingEdges_
.lookup(loc
.toRawBytecode());
1096 // No (reachable) jumps so this is just a no-op.
1100 PendingEdges
edges(std::move(p
->value()));
1101 pendingEdges_
.remove(p
);
1103 MOZ_ASSERT(!edges
.empty());
1105 // Create join block if there's fall-through from the previous bytecode op.
1106 if (!hasTerminatedBlock()) {
1107 MBasicBlock
* pred
= current
;
1108 if (!startNewBlock(pred
, loc
)) {
1111 pred
->end(MGoto::New(alloc(), current
));
1114 for (const PendingEdge
& edge
: edges
) {
1115 MBasicBlock
* source
= edge
.block();
1116 uint32_t numToPop
= edge
.numToPop();
1118 if (hasTerminatedBlock()) {
1119 if (!startNewBlock(source
, loc
, numToPop
)) {
1123 MOZ_ASSERT(source
->stackDepth() - numToPop
== current
->stackDepth());
1124 if (!current
->addPredecessorPopN(alloc(), source
, numToPop
)) {
1129 MOZ_ASSERT(source
->lastIns()->isTest() || source
->lastIns()->isGoto() ||
1130 source
->lastIns()->isTableSwitch());
1131 source
->lastIns()->initSuccessor(edge
.successor(), current
);
1134 MOZ_ASSERT(!hasTerminatedBlock());
1138 bool WarpBuilder::addIteratorLoopPhis(BytecodeLocation loopHead
) {
1139 // When unwinding the stack for a thrown exception, the exception handler must
1140 // close live iterators. For ForIn and Destructuring loops, the exception
1141 // handler needs access to values on the stack. To prevent them from being
1142 // optimized away (and replaced with the JS_OPTIMIZED_OUT MagicValue), we need
1143 // to mark the phis (and phis they flow into) as having implicit uses.
1144 // See ProcessTryNotes in vm/Interpreter.cpp and CloseLiveIteratorIon in
1145 // jit/JitFrames.cpp
1147 MOZ_ASSERT(current
->stackDepth() >= info().firstStackSlot());
1149 bool emptyStack
= current
->stackDepth() == info().firstStackSlot();
1154 jsbytecode
* loopHeadPC
= loopHead
.toRawBytecode();
1156 for (TryNoteIterAllNoGC
tni(script_
, loopHeadPC
); !tni
.done(); ++tni
) {
1157 const TryNote
& tn
= **tni
;
1159 // Stop if we reach an outer loop because outer loops were already
1160 // processed when we visited their loop headers.
1162 BytecodeLocation tnStart
= script_
->offsetToLocation(tn
.start
);
1163 if (tnStart
!= loopHead
) {
1164 MOZ_ASSERT(tnStart
.is(JSOp::LoopHead
));
1165 MOZ_ASSERT(tnStart
< loopHead
);
1170 switch (tn
.kind()) {
1171 case TryNoteKind::Destructuring
:
1172 case TryNoteKind::ForIn
: {
1173 // For for-in loops we add the iterator object to iterators(). For
1174 // destructuring loops we add the "done" value that's on top of the
1175 // stack and used in the exception handler.
1176 MOZ_ASSERT(tn
.stackDepth
>= 1);
1177 uint32_t slot
= info().stackSlot(tn
.stackDepth
- 1);
1178 MPhi
* phi
= current
->getSlot(slot
)->toPhi();
1179 if (!iterators()->append(phi
)) {
1184 case TryNoteKind::Loop
:
1185 case TryNoteKind::ForOf
:
1186 // Regular loops do not have iterators to close. ForOf loops handle
1187 // unwinding using catch blocks.
1197 bool WarpBuilder::build_LoopHead(BytecodeLocation loc
) {
1198 // All loops have the following bytecode structure:
1202 // JumpIfTrue/Goto to LoopHead
1204 if (hasTerminatedBlock()) {
1205 // The whole loop is unreachable.
1209 // Handle OSR from Baseline JIT code.
1210 if (loc
.toRawBytecode() == info().osrPc()) {
1211 if (!startNewOsrPreHeaderBlock(loc
)) {
1218 MBasicBlock
* pred
= current
;
1219 if (!startNewLoopHeaderBlock(loc
)) {
1223 pred
->end(MGoto::New(alloc(), current
));
1225 if (!addIteratorLoopPhis(loc
)) {
1229 MInterruptCheck
* check
= MInterruptCheck::New(alloc());
1230 current
->add(check
);
1232 #ifdef JS_CACHEIR_SPEW
1233 if (snapshot().needsFinalWarmUpCount()) {
1234 MIncrementWarmUpCounter
* ins
=
1235 MIncrementWarmUpCounter::New(alloc(), script_
);
1243 bool WarpBuilder::buildTestOp(BytecodeLocation loc
) {
1244 MDefinition
* originalValue
= current
->peek(-1);
1246 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
1247 // If we have CacheIR, we can use it to refine the input. Note that
1248 // the transpiler doesn't generate any control instructions. Instead,
1249 // we fall through and generate them below.
1250 MDefinition
* value
= current
->pop();
1251 if (!TranspileCacheIRToMIR(this, loc
, cacheIRSnapshot
, {value
})) {
1256 if (loc
.isBackedge()) {
1257 return buildTestBackedge(loc
);
1260 JSOp op
= loc
.getOp();
1261 BytecodeLocation target1
= loc
.next();
1262 BytecodeLocation target2
= loc
.getJumpTarget();
1264 if (TestTrueTargetIsJoinPoint(op
)) {
1265 std::swap(target1
, target2
);
1268 MDefinition
* value
= current
->pop();
1270 // JSOp::And and JSOp::Or leave the top stack value unchanged. The
1271 // top stack value may have been converted to bool by a transpiled
1272 // ToBool IC, so we push the original value.
1273 bool mustKeepCondition
= (op
== JSOp::And
|| op
== JSOp::Or
);
1274 if (mustKeepCondition
) {
1275 current
->push(originalValue
);
1278 // If this op always branches to the same location we treat this as a
1280 if (target1
== target2
) {
1281 value
->setImplicitlyUsedUnchecked();
1282 return buildForwardGoto(target1
);
1285 MTest
* test
= MTest::New(alloc(), value
, /* ifTrue = */ nullptr,
1286 /* ifFalse = */ nullptr);
1289 // JSOp::Case must pop a second value on the true-branch (the input to the
1290 // switch-statement).
1291 uint32_t numToPop
= (loc
.getOp() == JSOp::Case
) ? 1 : 0;
1293 if (!addPendingEdge(target1
, current
, MTest::TrueBranchIndex
, numToPop
)) {
1296 if (!addPendingEdge(target2
, current
, MTest::FalseBranchIndex
)) {
1300 if (const auto* typesSnapshot
= getOpSnapshot
<WarpPolymorphicTypes
>(loc
)) {
1301 test
->setObservedTypes(typesSnapshot
->list());
1304 setTerminatedBlock();
1308 bool WarpBuilder::buildTestBackedge(BytecodeLocation loc
) {
1309 MOZ_ASSERT(loc
.is(JSOp::JumpIfTrue
));
1310 MOZ_ASSERT(loopDepth() > 0);
1312 MDefinition
* value
= current
->pop();
1314 BytecodeLocation loopHead
= loc
.getJumpTarget();
1315 MOZ_ASSERT(loopHead
.is(JSOp::LoopHead
));
1317 BytecodeLocation successor
= loc
.next();
1319 // We can finish the loop now. Use the loophead pc instead of the current pc
1320 // because the stack depth at the start of that op matches the current stack
1321 // depth (after popping our operand).
1322 MBasicBlock
* pred
= current
;
1323 if (!startNewBlock(current
, loopHead
)) {
1327 MTest
* test
= MTest::New(alloc(), value
, /* ifTrue = */ current
,
1328 /* ifFalse = */ nullptr);
1331 if (const auto* typesSnapshot
= getOpSnapshot
<WarpPolymorphicTypes
>(loc
)) {
1332 test
->setObservedTypes(typesSnapshot
->list());
1335 if (!addPendingEdge(successor
, pred
, MTest::FalseBranchIndex
)) {
1339 return buildBackedge();
1342 bool WarpBuilder::build_JumpIfFalse(BytecodeLocation loc
) {
1343 return buildTestOp(loc
);
1346 bool WarpBuilder::build_JumpIfTrue(BytecodeLocation loc
) {
1347 return buildTestOp(loc
);
1350 bool WarpBuilder::build_And(BytecodeLocation loc
) { return buildTestOp(loc
); }
1352 bool WarpBuilder::build_Or(BytecodeLocation loc
) { return buildTestOp(loc
); }
1354 bool WarpBuilder::build_Case(BytecodeLocation loc
) { return buildTestOp(loc
); }
1356 bool WarpBuilder::build_Default(BytecodeLocation loc
) {
1358 return buildForwardGoto(loc
.getJumpTarget());
1361 bool WarpBuilder::build_Coalesce(BytecodeLocation loc
) {
1362 BytecodeLocation target1
= loc
.next();
1363 BytecodeLocation target2
= loc
.getJumpTarget();
1364 MOZ_ASSERT(target2
> target1
);
1366 MDefinition
* value
= current
->peek(-1);
1368 MInstruction
* isNullOrUndefined
= MIsNullOrUndefined::New(alloc(), value
);
1369 current
->add(isNullOrUndefined
);
1371 current
->end(MTest::New(alloc(), isNullOrUndefined
, /* ifTrue = */ nullptr,
1372 /* ifFalse = */ nullptr));
1374 if (!addPendingEdge(target1
, current
, MTest::TrueBranchIndex
)) {
1377 if (!addPendingEdge(target2
, current
, MTest::FalseBranchIndex
)) {
1381 setTerminatedBlock();
1385 bool WarpBuilder::buildBackedge() {
1388 MBasicBlock
* header
= loopStack_
.popCopy().header();
1389 current
->end(MGoto::New(alloc(), header
));
1391 if (!header
->setBackedge(current
)) {
1395 setTerminatedBlock();
1399 bool WarpBuilder::buildForwardGoto(BytecodeLocation target
) {
1400 current
->end(MGoto::New(alloc(), nullptr));
1402 if (!addPendingEdge(target
, current
, MGoto::TargetIndex
)) {
1406 setTerminatedBlock();
1410 bool WarpBuilder::build_Goto(BytecodeLocation loc
) {
1411 if (loc
.isBackedge()) {
1412 return buildBackedge();
1415 return buildForwardGoto(loc
.getJumpTarget());
1418 bool WarpBuilder::build_IsNullOrUndefined(BytecodeLocation loc
) {
1419 MDefinition
* value
= current
->peek(-1);
1420 auto* isNullOrUndef
= MIsNullOrUndefined::New(alloc(), value
);
1421 current
->add(isNullOrUndef
);
1422 current
->push(isNullOrUndef
);
1426 bool WarpBuilder::build_DebugCheckSelfHosted(BytecodeLocation loc
) {
1428 MDefinition
* val
= current
->pop();
1429 MDebugCheckSelfHosted
* check
= MDebugCheckSelfHosted::New(alloc(), val
);
1430 current
->add(check
);
1431 current
->push(check
);
1432 if (!resumeAfter(check
, loc
)) {
1439 bool WarpBuilder::build_DynamicImport(BytecodeLocation loc
) {
1440 MDefinition
* options
= current
->pop();
1441 MDefinition
* specifier
= current
->pop();
1442 MDynamicImport
* ins
= MDynamicImport::New(alloc(), specifier
, options
);
1445 return resumeAfter(ins
, loc
);
1448 bool WarpBuilder::build_Not(BytecodeLocation loc
) {
1449 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
1450 // If we have CacheIR, we can use it to refine the input before
1451 // emitting the MNot.
1452 MDefinition
* value
= current
->pop();
1453 if (!TranspileCacheIRToMIR(this, loc
, cacheIRSnapshot
, {value
})) {
1458 MDefinition
* value
= current
->pop();
1459 MNot
* ins
= MNot::New(alloc(), value
);
1463 if (const auto* typesSnapshot
= getOpSnapshot
<WarpPolymorphicTypes
>(loc
)) {
1464 ins
->setObservedTypes(typesSnapshot
->list());
1470 bool WarpBuilder::build_ToString(BytecodeLocation loc
) {
1471 MDefinition
* value
= current
->pop();
1473 if (value
->type() == MIRType::String
) {
1474 value
->setImplicitlyUsedUnchecked();
1475 current
->push(value
);
1480 MToString::New(alloc(), value
, MToString::SideEffectHandling::Supported
);
1483 if (ins
->isEffectful()) {
1484 return resumeAfter(ins
, loc
);
1489 bool WarpBuilder::usesEnvironmentChain() const {
1490 return script_
->jitScript()->usesEnvironmentChain();
1493 bool WarpBuilder::build_GlobalOrEvalDeclInstantiation(BytecodeLocation loc
) {
1494 MOZ_ASSERT(!script_
->isForEval(), "Eval scripts not supported");
1495 auto* redeclCheck
= MGlobalDeclInstantiation::New(alloc());
1496 current
->add(redeclCheck
);
1497 return resumeAfter(redeclCheck
, loc
);
1500 bool WarpBuilder::build_BindVar(BytecodeLocation
) {
1501 MOZ_ASSERT(usesEnvironmentChain());
1503 MDefinition
* env
= current
->environmentChain();
1504 MCallBindVar
* ins
= MCallBindVar::New(alloc(), env
);
1510 bool WarpBuilder::build_MutateProto(BytecodeLocation loc
) {
1511 MDefinition
* value
= current
->pop();
1512 MDefinition
* obj
= current
->peek(-1);
1513 MMutateProto
* mutate
= MMutateProto::New(alloc(), obj
, value
);
1514 current
->add(mutate
);
1515 return resumeAfter(mutate
, loc
);
1518 MDefinition
* WarpBuilder::getCallee() {
1519 if (inlineCallInfo()) {
1520 return inlineCallInfo()->callee();
1523 MInstruction
* callee
= MCallee::New(alloc());
1524 current
->add(callee
);
1528 bool WarpBuilder::build_Callee(BytecodeLocation
) {
1529 MDefinition
* callee
= getCallee();
1530 current
->push(callee
);
1534 bool WarpBuilder::build_ToAsyncIter(BytecodeLocation loc
) {
1535 MDefinition
* nextMethod
= current
->pop();
1536 MDefinition
* iterator
= current
->pop();
1537 MToAsyncIter
* ins
= MToAsyncIter::New(alloc(), iterator
, nextMethod
);
1540 return resumeAfter(ins
, loc
);
1543 bool WarpBuilder::build_ToPropertyKey(BytecodeLocation loc
) {
1544 MDefinition
* value
= current
->pop();
1545 return buildIC(loc
, CacheKind::ToPropertyKey
, {value
});
1548 bool WarpBuilder::build_Typeof(BytecodeLocation loc
) {
1549 MDefinition
* input
= current
->pop();
1551 if (const auto* typesSnapshot
= getOpSnapshot
<WarpPolymorphicTypes
>(loc
)) {
1552 auto* typeOf
= MTypeOf::New(alloc(), input
);
1553 typeOf
->setObservedTypes(typesSnapshot
->list());
1554 current
->add(typeOf
);
1556 auto* ins
= MTypeOfName::New(alloc(), typeOf
);
1562 return buildIC(loc
, CacheKind::TypeOf
, {input
});
1565 bool WarpBuilder::build_TypeofExpr(BytecodeLocation loc
) {
1566 return build_Typeof(loc
);
1569 bool WarpBuilder::build_TypeofEq(BytecodeLocation loc
) {
1570 auto operand
= loc
.getTypeofEqOperand();
1571 JSType type
= operand
.type();
1572 JSOp compareOp
= operand
.compareOp();
1573 MDefinition
* input
= current
->pop();
1575 if (const auto* typesSnapshot
= getOpSnapshot
<WarpPolymorphicTypes
>(loc
)) {
1576 auto* typeOf
= MTypeOf::New(alloc(), input
);
1577 typeOf
->setObservedTypes(typesSnapshot
->list());
1578 current
->add(typeOf
);
1580 auto* typeInt
= MConstant::New(alloc(), Int32Value(type
));
1581 current
->add(typeInt
);
1583 auto* ins
= MCompare::New(alloc(), typeOf
, typeInt
, compareOp
,
1584 MCompare::Compare_Int32
);
1590 return buildIC(loc
, CacheKind::TypeOfEq
, {input
});
1593 bool WarpBuilder::build_Arguments(BytecodeLocation loc
) {
1594 auto* snapshot
= getOpSnapshot
<WarpArguments
>(loc
);
1595 MOZ_ASSERT(info().needsArgsObj());
1596 MOZ_ASSERT(snapshot
);
1597 MOZ_ASSERT(usesEnvironmentChain());
1599 ArgumentsObject
* templateObj
= snapshot
->templateObj();
1600 MDefinition
* env
= current
->environmentChain();
1602 MInstruction
* argsObj
;
1603 if (inlineCallInfo()) {
1604 argsObj
= MCreateInlinedArgumentsObject::New(
1605 alloc(), env
, getCallee(), inlineCallInfo()->argv(), templateObj
);
1610 argsObj
= MCreateArgumentsObject::New(alloc(), env
, templateObj
);
1612 current
->add(argsObj
);
1613 current
->setArgumentsObject(argsObj
);
1614 current
->push(argsObj
);
1619 bool WarpBuilder::build_ObjWithProto(BytecodeLocation loc
) {
1620 MDefinition
* proto
= current
->pop();
1621 MInstruction
* ins
= MObjectWithProto::New(alloc(), proto
);
1624 return resumeAfter(ins
, loc
);
1627 MDefinition
* WarpBuilder::walkEnvironmentChain(uint32_t numHops
) {
1628 MDefinition
* env
= current
->environmentChain();
1630 for (uint32_t i
= 0; i
< numHops
; i
++) {
1631 if (!alloc().ensureBallast()) {
1635 MInstruction
* ins
= MEnclosingEnvironment::New(alloc(), env
);
1643 bool WarpBuilder::build_GetAliasedVar(BytecodeLocation loc
) {
1644 EnvironmentCoordinate ec
= loc
.getEnvironmentCoordinate();
1645 MDefinition
* obj
= walkEnvironmentChain(ec
.hops());
1651 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec
)) {
1652 load
= MLoadFixedSlot::New(alloc(), obj
, ec
.slot());
1654 MInstruction
* slots
= MSlots::New(alloc(), obj
);
1655 current
->add(slots
);
1657 uint32_t slot
= EnvironmentObject::nonExtensibleDynamicSlotIndex(ec
);
1658 load
= MLoadDynamicSlot::New(alloc(), slots
, slot
);
1662 current
->push(load
);
1666 bool WarpBuilder::build_SetAliasedVar(BytecodeLocation loc
) {
1667 EnvironmentCoordinate ec
= loc
.getEnvironmentCoordinate();
1668 MDefinition
* val
= current
->peek(-1);
1669 MDefinition
* obj
= walkEnvironmentChain(ec
.hops());
1674 current
->add(MPostWriteBarrier::New(alloc(), obj
, val
));
1676 MInstruction
* store
;
1677 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec
)) {
1678 store
= MStoreFixedSlot::NewBarriered(alloc(), obj
, ec
.slot(), val
);
1680 MInstruction
* slots
= MSlots::New(alloc(), obj
);
1681 current
->add(slots
);
1683 uint32_t slot
= EnvironmentObject::nonExtensibleDynamicSlotIndex(ec
);
1684 store
= MStoreDynamicSlot::NewBarriered(alloc(), slots
, slot
, val
);
1687 current
->add(store
);
1688 return resumeAfter(store
, loc
);
1691 bool WarpBuilder::build_InitAliasedLexical(BytecodeLocation loc
) {
1692 return build_SetAliasedVar(loc
);
1695 bool WarpBuilder::build_EnvCallee(BytecodeLocation loc
) {
1696 uint32_t numHops
= loc
.getEnvCalleeNumHops();
1697 MDefinition
* env
= walkEnvironmentChain(numHops
);
1702 auto* callee
= MLoadFixedSlot::New(alloc(), env
, CallObject::calleeSlot());
1703 current
->add(callee
);
1704 current
->push(callee
);
1708 bool WarpBuilder::build_Iter(BytecodeLocation loc
) {
1709 MDefinition
* obj
= current
->pop();
1710 return buildIC(loc
, CacheKind::GetIterator
, {obj
});
1713 bool WarpBuilder::build_MoreIter(BytecodeLocation loc
) {
1714 MDefinition
* iter
= current
->peek(-1);
1715 MInstruction
* ins
= MIteratorMore::New(alloc(), iter
);
1718 return resumeAfter(ins
, loc
);
1721 bool WarpBuilder::build_EndIter(BytecodeLocation loc
) {
1722 current
->pop(); // Iterator value is not used.
1723 MDefinition
* iter
= current
->pop();
1724 MInstruction
* ins
= MIteratorEnd::New(alloc(), iter
);
1726 return resumeAfter(ins
, loc
);
1729 bool WarpBuilder::build_CloseIter(BytecodeLocation loc
) {
1730 MDefinition
* iter
= current
->pop();
1731 iter
= unboxObjectInfallible(iter
, IsMovable::Yes
);
1732 return buildIC(loc
, CacheKind::CloseIter
, {iter
});
1735 bool WarpBuilder::build_IsNoIter(BytecodeLocation
) {
1736 MDefinition
* def
= current
->peek(-1);
1737 MOZ_ASSERT(def
->isIteratorMore());
1738 MInstruction
* ins
= MIsNoIter::New(alloc(), def
);
1744 bool WarpBuilder::build_OptimizeGetIterator(BytecodeLocation loc
) {
1745 MDefinition
* value
= current
->pop();
1746 return buildIC(loc
, CacheKind::OptimizeGetIterator
, {value
});
1749 bool WarpBuilder::transpileCall(BytecodeLocation loc
,
1750 const WarpCacheIR
* cacheIRSnapshot
,
1751 CallInfo
* callInfo
) {
1752 // Synthesize the constant number of arguments for this call op.
1753 auto* argc
= MConstant::New(alloc(), Int32Value(callInfo
->argc()));
1756 return TranspileCacheIRToMIR(this, loc
, cacheIRSnapshot
, {argc
}, callInfo
);
1759 void WarpBuilder::buildCreateThis(CallInfo
& callInfo
) {
1760 MOZ_ASSERT(callInfo
.constructing());
1762 // Inline the this-object allocation on the caller-side.
1763 MDefinition
* callee
= callInfo
.callee();
1764 MDefinition
* newTarget
= callInfo
.getNewTarget();
1765 auto* createThis
= MCreateThis::New(alloc(), callee
, newTarget
);
1766 current
->add(createThis
);
1767 callInfo
.thisArg()->setImplicitlyUsedUnchecked();
1768 callInfo
.setThis(createThis
);
1771 bool WarpBuilder::buildCallOp(BytecodeLocation loc
) {
1772 uint32_t argc
= loc
.getCallArgc();
1773 JSOp op
= loc
.getOp();
1774 bool constructing
= IsConstructOp(op
);
1775 bool ignoresReturnValue
= (op
== JSOp::CallIgnoresRv
|| loc
.resultIsPopped());
1777 CallInfo
callInfo(alloc(), constructing
, ignoresReturnValue
);
1778 if (!callInfo
.init(current
, argc
)) {
1782 if (const auto* inliningSnapshot
= getOpSnapshot
<WarpInlinedCall
>(loc
)) {
1783 // Transpile the CacheIR to generate the correct guards before
1784 // inlining. In this case, CacheOp::CallInlinedFunction updates
1785 // the CallInfo, but does not generate a call.
1786 callInfo
.markAsInlined();
1787 if (!transpileCall(loc
, inliningSnapshot
->cacheIRSnapshot(), &callInfo
)) {
1791 // Generate the body of the inlined function.
1792 return buildInlinedCall(loc
, inliningSnapshot
, callInfo
);
1795 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
1796 return transpileCall(loc
, cacheIRSnapshot
, &callInfo
);
1799 if (getOpSnapshot
<WarpBailout
>(loc
)) {
1800 callInfo
.setImplicitlyUsedUnchecked();
1801 return buildBailoutForColdIC(loc
, CacheKind::Call
);
1804 bool needsThisCheck
= false;
1805 if (callInfo
.constructing()) {
1806 buildCreateThis(callInfo
);
1807 needsThisCheck
= true;
1810 MCall
* call
= makeCall(callInfo
, needsThisCheck
);
1816 current
->push(call
);
1817 return resumeAfter(call
, loc
);
1820 bool WarpBuilder::build_Call(BytecodeLocation loc
) { return buildCallOp(loc
); }
1822 bool WarpBuilder::build_CallContent(BytecodeLocation loc
) {
1823 return buildCallOp(loc
);
1826 bool WarpBuilder::build_CallIgnoresRv(BytecodeLocation loc
) {
1827 return buildCallOp(loc
);
1830 bool WarpBuilder::build_CallIter(BytecodeLocation loc
) {
1831 return buildCallOp(loc
);
1834 bool WarpBuilder::build_CallContentIter(BytecodeLocation loc
) {
1835 return buildCallOp(loc
);
1838 bool WarpBuilder::build_New(BytecodeLocation loc
) { return buildCallOp(loc
); }
1840 bool WarpBuilder::build_NewContent(BytecodeLocation loc
) {
1841 return buildCallOp(loc
);
1844 bool WarpBuilder::build_SuperCall(BytecodeLocation loc
) {
1845 return buildCallOp(loc
);
1848 bool WarpBuilder::build_FunctionThis(BytecodeLocation loc
) {
1849 MOZ_ASSERT(info().hasFunMaybeLazy());
1851 if (script_
->strict()) {
1852 // No need to wrap primitive |this| in strict mode.
1853 current
->pushSlot(info().thisSlot());
1857 MOZ_ASSERT(!script_
->hasNonSyntacticScope(),
1858 "WarpOracle should have aborted compilation");
1860 MDefinition
* def
= current
->getSlot(info().thisSlot());
1861 JSObject
* globalThis
= snapshot().globalLexicalEnvThis();
1863 auto* thisObj
= MBoxNonStrictThis::New(alloc(), def
, globalThis
);
1864 current
->add(thisObj
);
1865 current
->push(thisObj
);
1870 bool WarpBuilder::build_GlobalThis(BytecodeLocation loc
) {
1871 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
1872 JSObject
* obj
= snapshot().globalLexicalEnvThis();
1873 pushConstant(ObjectValue(*obj
));
1877 MConstant
* WarpBuilder::globalLexicalEnvConstant() {
1878 JSObject
* globalLexical
= snapshot().globalLexicalEnv();
1879 return constant(ObjectValue(*globalLexical
));
1882 bool WarpBuilder::build_GetName(BytecodeLocation loc
) {
1883 MOZ_ASSERT(usesEnvironmentChain());
1885 MDefinition
* env
= current
->environmentChain();
1886 env
= unboxObjectInfallible(env
, IsMovable::Yes
);
1887 return buildIC(loc
, CacheKind::GetName
, {env
});
1890 bool WarpBuilder::build_GetGName(BytecodeLocation loc
) {
1891 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
1893 MDefinition
* env
= globalLexicalEnvConstant();
1894 return buildIC(loc
, CacheKind::GetName
, {env
});
1897 bool WarpBuilder::build_BindName(BytecodeLocation loc
) {
1898 MOZ_ASSERT(usesEnvironmentChain());
1900 MDefinition
* env
= current
->environmentChain();
1901 env
= unboxObjectInfallible(env
, IsMovable::Yes
);
1902 return buildIC(loc
, CacheKind::BindName
, {env
});
1905 bool WarpBuilder::build_BindGName(BytecodeLocation loc
) {
1906 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
1908 if (const auto* snapshot
= getOpSnapshot
<WarpBindGName
>(loc
)) {
1909 JSObject
* globalEnv
= snapshot
->globalEnv();
1910 pushConstant(ObjectValue(*globalEnv
));
1914 MDefinition
* env
= globalLexicalEnvConstant();
1915 return buildIC(loc
, CacheKind::BindName
, {env
});
1918 bool WarpBuilder::build_GetProp(BytecodeLocation loc
) {
1919 MDefinition
* val
= current
->pop();
1920 return buildIC(loc
, CacheKind::GetProp
, {val
});
1923 bool WarpBuilder::build_GetElem(BytecodeLocation loc
) {
1924 MDefinition
* id
= current
->pop();
1925 MDefinition
* val
= current
->pop();
1926 return buildIC(loc
, CacheKind::GetElem
, {val
, id
});
1929 bool WarpBuilder::build_SetProp(BytecodeLocation loc
) {
1930 MDefinition
* val
= current
->pop();
1931 MDefinition
* obj
= current
->pop();
1933 return buildIC(loc
, CacheKind::SetProp
, {obj
, val
});
1936 bool WarpBuilder::build_StrictSetProp(BytecodeLocation loc
) {
1937 return build_SetProp(loc
);
1940 bool WarpBuilder::build_SetName(BytecodeLocation loc
) {
1941 return build_SetProp(loc
);
1944 bool WarpBuilder::build_StrictSetName(BytecodeLocation loc
) {
1945 return build_SetProp(loc
);
1948 bool WarpBuilder::build_SetGName(BytecodeLocation loc
) {
1949 return build_SetProp(loc
);
1952 bool WarpBuilder::build_StrictSetGName(BytecodeLocation loc
) {
1953 return build_SetProp(loc
);
1956 bool WarpBuilder::build_InitGLexical(BytecodeLocation loc
) {
1957 MOZ_ASSERT(!script_
->hasNonSyntacticScope());
1959 MDefinition
* globalLexical
= globalLexicalEnvConstant();
1960 MDefinition
* val
= current
->peek(-1);
1962 return buildIC(loc
, CacheKind::SetProp
, {globalLexical
, val
});
1965 bool WarpBuilder::build_SetElem(BytecodeLocation loc
) {
1966 MDefinition
* val
= current
->pop();
1967 MDefinition
* id
= current
->pop();
1968 MDefinition
* obj
= current
->pop();
1970 return buildIC(loc
, CacheKind::SetElem
, {obj
, id
, val
});
1973 bool WarpBuilder::build_StrictSetElem(BytecodeLocation loc
) {
1974 return build_SetElem(loc
);
1977 bool WarpBuilder::build_DelProp(BytecodeLocation loc
) {
1978 PropertyName
* name
= loc
.getPropertyName(script_
);
1979 MDefinition
* obj
= current
->pop();
1980 bool strict
= loc
.getOp() == JSOp::StrictDelProp
;
1982 MInstruction
* ins
= MDeleteProperty::New(alloc(), obj
, name
, strict
);
1985 return resumeAfter(ins
, loc
);
1988 bool WarpBuilder::build_StrictDelProp(BytecodeLocation loc
) {
1989 return build_DelProp(loc
);
1992 bool WarpBuilder::build_DelElem(BytecodeLocation loc
) {
1993 MDefinition
* id
= current
->pop();
1994 MDefinition
* obj
= current
->pop();
1995 bool strict
= loc
.getOp() == JSOp::StrictDelElem
;
1997 MInstruction
* ins
= MDeleteElement::New(alloc(), obj
, id
, strict
);
2000 return resumeAfter(ins
, loc
);
2003 bool WarpBuilder::build_StrictDelElem(BytecodeLocation loc
) {
2004 return build_DelElem(loc
);
2007 bool WarpBuilder::build_SetFunName(BytecodeLocation loc
) {
2008 FunctionPrefixKind prefixKind
= loc
.getFunctionPrefixKind();
2009 MDefinition
* name
= current
->pop();
2010 MDefinition
* fun
= current
->pop();
2012 MSetFunName
* ins
= MSetFunName::New(alloc(), fun
, name
, uint8_t(prefixKind
));
2015 return resumeAfter(ins
, loc
);
2018 bool WarpBuilder::build_PushLexicalEnv(BytecodeLocation loc
) {
2019 MOZ_ASSERT(usesEnvironmentChain());
2021 const auto* snapshot
= getOpSnapshot
<WarpLexicalEnvironment
>(loc
);
2022 MOZ_ASSERT(snapshot
);
2024 MDefinition
* env
= current
->environmentChain();
2025 MConstant
* templateCst
= constant(ObjectValue(*snapshot
->templateObj()));
2027 auto* ins
= MNewLexicalEnvironmentObject::New(alloc(), templateCst
);
2031 // Assert in debug mode we can elide the post write barrier.
2032 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins
, env
));
2035 // Initialize the object's reserved slots. No post barrier is needed here,
2036 // for the same reason as in buildNamedLambdaEnv.
2037 current
->add(MStoreFixedSlot::NewUnbarriered(
2038 alloc(), ins
, EnvironmentObject::enclosingEnvironmentSlot(), env
));
2040 current
->setEnvironmentChain(ins
);
2044 bool WarpBuilder::build_PushClassBodyEnv(BytecodeLocation loc
) {
2045 MOZ_ASSERT(usesEnvironmentChain());
2047 const auto* snapshot
= getOpSnapshot
<WarpClassBodyEnvironment
>(loc
);
2048 MOZ_ASSERT(snapshot
);
2050 MDefinition
* env
= current
->environmentChain();
2051 MConstant
* templateCst
= constant(ObjectValue(*snapshot
->templateObj()));
2053 auto* ins
= MNewClassBodyEnvironmentObject::New(alloc(), templateCst
);
2057 // Assert in debug mode we can elide the post write barrier.
2058 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins
, env
));
2061 // Initialize the object's reserved slots. No post barrier is needed here,
2062 // for the same reason as in buildNamedLambdaEnv.
2063 current
->add(MStoreFixedSlot::NewUnbarriered(
2064 alloc(), ins
, EnvironmentObject::enclosingEnvironmentSlot(), env
));
2066 current
->setEnvironmentChain(ins
);
2070 bool WarpBuilder::build_PopLexicalEnv(BytecodeLocation
) {
2071 MDefinition
* enclosingEnv
= walkEnvironmentChain(1);
2072 if (!enclosingEnv
) {
2075 current
->setEnvironmentChain(enclosingEnv
);
2079 bool WarpBuilder::build_FreshenLexicalEnv(BytecodeLocation loc
) {
2080 MOZ_ASSERT(usesEnvironmentChain());
2082 const auto* snapshot
= getOpSnapshot
<WarpLexicalEnvironment
>(loc
);
2083 MOZ_ASSERT(snapshot
);
2085 MDefinition
* enclosingEnv
= walkEnvironmentChain(1);
2086 if (!enclosingEnv
) {
2090 MDefinition
* env
= current
->environmentChain();
2091 MConstant
* templateCst
= constant(ObjectValue(*snapshot
->templateObj()));
2093 auto* templateObj
= snapshot
->templateObj();
2094 auto* scope
= &templateObj
->scope();
2095 MOZ_ASSERT(scope
->hasEnvironment());
2097 auto* ins
= MNewLexicalEnvironmentObject::New(alloc(), templateCst
);
2101 // Assert in debug mode we can elide the post write barrier.
2103 MAssertCanElidePostWriteBarrier::New(alloc(), ins
, enclosingEnv
));
2106 // Initialize the object's reserved slots. No post barrier is needed here,
2107 // for the same reason as in buildNamedLambdaEnv.
2108 current
->add(MStoreFixedSlot::NewUnbarriered(
2109 alloc(), ins
, EnvironmentObject::enclosingEnvironmentSlot(),
2112 // Copy environment slots.
2113 MSlots
* envSlots
= nullptr;
2114 MSlots
* slots
= nullptr;
2115 for (BindingIter
iter(scope
); iter
; iter
++) {
2116 auto loc
= iter
.location();
2117 if (loc
.kind() != BindingLocation::Kind::Environment
) {
2118 MOZ_ASSERT(loc
.kind() == BindingLocation::Kind::Frame
);
2122 if (!alloc().ensureBallast()) {
2126 uint32_t slot
= loc
.slot();
2127 uint32_t numFixedSlots
= templateObj
->numFixedSlots();
2128 if (slot
>= numFixedSlots
) {
2130 envSlots
= MSlots::New(alloc(), env
);
2131 current
->add(envSlots
);
2134 slots
= MSlots::New(alloc(), ins
);
2135 current
->add(slots
);
2138 uint32_t dynamicSlot
= slot
- numFixedSlots
;
2140 auto* load
= MLoadDynamicSlot::New(alloc(), envSlots
, dynamicSlot
);
2144 // Assert in debug mode we can elide the post write barrier.
2145 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins
, load
));
2149 MStoreDynamicSlot::NewUnbarriered(alloc(), slots
, dynamicSlot
, load
));
2151 auto* load
= MLoadFixedSlot::New(alloc(), env
, slot
);
2155 // Assert in debug mode we can elide the post write barrier.
2156 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins
, load
));
2159 current
->add(MStoreFixedSlot::NewUnbarriered(alloc(), ins
, slot
, load
));
2163 current
->setEnvironmentChain(ins
);
2167 bool WarpBuilder::build_RecreateLexicalEnv(BytecodeLocation loc
) {
2168 MOZ_ASSERT(usesEnvironmentChain());
2170 const auto* snapshot
= getOpSnapshot
<WarpLexicalEnvironment
>(loc
);
2171 MOZ_ASSERT(snapshot
);
2173 MDefinition
* enclosingEnv
= walkEnvironmentChain(1);
2174 if (!enclosingEnv
) {
2178 MConstant
* templateCst
= constant(ObjectValue(*snapshot
->templateObj()));
2180 auto* ins
= MNewLexicalEnvironmentObject::New(alloc(), templateCst
);
2184 // Assert in debug mode we can elide the post write barrier.
2186 MAssertCanElidePostWriteBarrier::New(alloc(), ins
, enclosingEnv
));
2189 // Initialize the object's reserved slots. No post barrier is needed here,
2190 // for the same reason as in buildNamedLambdaEnv.
2191 current
->add(MStoreFixedSlot::NewUnbarriered(
2192 alloc(), ins
, EnvironmentObject::enclosingEnvironmentSlot(),
2195 current
->setEnvironmentChain(ins
);
2199 bool WarpBuilder::build_PushVarEnv(BytecodeLocation loc
) {
2200 MOZ_ASSERT(usesEnvironmentChain());
2202 const auto* snapshot
= getOpSnapshot
<WarpVarEnvironment
>(loc
);
2203 MOZ_ASSERT(snapshot
);
2205 MDefinition
* env
= current
->environmentChain();
2206 MConstant
* templateCst
= constant(ObjectValue(*snapshot
->templateObj()));
2208 auto* ins
= MNewVarEnvironmentObject::New(alloc(), templateCst
);
2212 // Assert in debug mode we can elide the post write barrier.
2213 current
->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins
, env
));
2216 // Initialize the object's reserved slots. No post barrier is needed here,
2217 // for the same reason as in buildNamedLambdaEnv.
2218 current
->add(MStoreFixedSlot::NewUnbarriered(
2219 alloc(), ins
, EnvironmentObject::enclosingEnvironmentSlot(), env
));
2221 current
->setEnvironmentChain(ins
);
2225 bool WarpBuilder::build_ImplicitThis(BytecodeLocation loc
) {
2226 MOZ_ASSERT(usesEnvironmentChain());
2228 PropertyName
* name
= loc
.getPropertyName(script_
);
2229 MDefinition
* env
= current
->environmentChain();
2231 auto* ins
= MImplicitThis::New(alloc(), env
, name
);
2234 return resumeAfter(ins
, loc
);
2237 bool WarpBuilder::build_CheckClassHeritage(BytecodeLocation loc
) {
2238 MDefinition
* def
= current
->pop();
2239 auto* ins
= MCheckClassHeritage::New(alloc(), def
);
2242 return resumeAfter(ins
, loc
);
2245 bool WarpBuilder::build_CheckThis(BytecodeLocation loc
) {
2246 MDefinition
* def
= current
->pop();
2247 auto* ins
= MCheckThis::New(alloc(), def
);
2250 return resumeAfter(ins
, loc
);
2253 bool WarpBuilder::build_CheckThisReinit(BytecodeLocation loc
) {
2254 MDefinition
* def
= current
->pop();
2255 auto* ins
= MCheckThisReinit::New(alloc(), def
);
2258 return resumeAfter(ins
, loc
);
2261 bool WarpBuilder::build_Generator(BytecodeLocation loc
) {
2262 MOZ_ASSERT(usesEnvironmentChain());
2264 MDefinition
* callee
= getCallee();
2265 MDefinition
* environmentChain
= current
->environmentChain();
2266 MDefinition
* argsObj
= info().needsArgsObj() ? current
->argumentsObject()
2267 : constant(Int32Value(0));
2269 MGenerator
* generator
=
2270 MGenerator::New(alloc(), callee
, environmentChain
, argsObj
);
2272 current
->add(generator
);
2273 current
->push(generator
);
2274 return resumeAfter(generator
, loc
);
2277 bool WarpBuilder::build_AfterYield(BytecodeLocation loc
) {
2278 // Unreachable blocks don't need to generate a bail.
2279 if (hasTerminatedBlock()) {
2283 // This comes after a yield, which we generate as a return,
2284 // so we know this should be unreachable code.
2286 // We emit an unreachable bail for this, which will assert if we
2287 // ever execute this.
2289 // An Unreachable bail, instead of MUnreachable, because MUnreachable
2290 // is a control instruction, and injecting it in the middle of a block
2291 // causes various graph state assertions to fail.
2292 MBail
* bail
= MBail::New(alloc(), BailoutKind::Unreachable
);
2298 bool WarpBuilder::build_FinalYieldRval(BytecodeLocation loc
) {
2299 MDefinition
* gen
= current
->pop();
2301 auto setSlotNull
= [this, gen
](size_t slot
) {
2302 auto* ins
= MStoreFixedSlot::NewBarriered(alloc(), gen
, slot
,
2303 constant(NullValue()));
2307 // Close the generator
2308 setSlotNull(AbstractGeneratorObject::calleeSlot());
2309 setSlotNull(AbstractGeneratorObject::envChainSlot());
2310 setSlotNull(AbstractGeneratorObject::argsObjectSlot());
2311 setSlotNull(AbstractGeneratorObject::stackStorageSlot());
2312 setSlotNull(AbstractGeneratorObject::resumeIndexSlot());
2315 return build_RetRval(loc
);
2318 bool WarpBuilder::build_AsyncResolve(BytecodeLocation loc
) {
2319 MDefinition
* generator
= current
->pop();
2320 MDefinition
* value
= current
->pop();
2322 auto* resolve
= MAsyncResolve::New(alloc(), generator
, value
);
2323 current
->add(resolve
);
2324 current
->push(resolve
);
2325 return resumeAfter(resolve
, loc
);
2328 bool WarpBuilder::build_AsyncReject(BytecodeLocation loc
) {
2329 MDefinition
* generator
= current
->pop();
2330 MDefinition
* stack
= current
->pop();
2331 MDefinition
* reason
= current
->pop();
2333 auto* reject
= MAsyncReject::New(alloc(), generator
, reason
, stack
);
2334 current
->add(reject
);
2335 current
->push(reject
);
2336 return resumeAfter(reject
, loc
);
2339 bool WarpBuilder::build_ResumeKind(BytecodeLocation loc
) {
2340 GeneratorResumeKind resumeKind
= loc
.resumeKind();
2342 current
->push(constant(Int32Value(static_cast<int32_t>(resumeKind
))));
2346 bool WarpBuilder::build_CheckResumeKind(BytecodeLocation loc
) {
2347 // Outside of `yield*`, this is normally unreachable code in Warp,
2348 // so we just manipulate the stack appropriately to ensure correct
2351 // However, `yield*` emits a forced generator return which can be
2352 // warp compiled, so in order to correctly handle these semantics
2353 // we also generate a bailout, so that the forced generator return
2354 // runs in baseline.
2355 MDefinition
* resumeKind
= current
->pop();
2356 MDefinition
* gen
= current
->pop();
2357 MDefinition
* rval
= current
->peek(-1);
2359 // Mark operands as implicitly used.
2360 resumeKind
->setImplicitlyUsedUnchecked();
2361 gen
->setImplicitlyUsedUnchecked();
2362 rval
->setImplicitlyUsedUnchecked();
2364 // Bail out if we encounter CheckResumeKind.
2365 MBail
* bail
= MBail::New(alloc(), BailoutKind::Inevitable
);
2367 current
->setAlwaysBails();
2372 bool WarpBuilder::build_CanSkipAwait(BytecodeLocation loc
) {
2373 MDefinition
* val
= current
->pop();
2375 MCanSkipAwait
* canSkip
= MCanSkipAwait::New(alloc(), val
);
2376 current
->add(canSkip
);
2379 current
->push(canSkip
);
2381 return resumeAfter(canSkip
, loc
);
2384 bool WarpBuilder::build_MaybeExtractAwaitValue(BytecodeLocation loc
) {
2385 MDefinition
* canSkip
= current
->pop();
2386 MDefinition
* value
= current
->pop();
2388 MMaybeExtractAwaitValue
* extracted
=
2389 MMaybeExtractAwaitValue::New(alloc(), value
, canSkip
);
2390 current
->add(extracted
);
2392 current
->push(extracted
);
2393 current
->push(canSkip
);
2395 return resumeAfter(extracted
, loc
);
2398 bool WarpBuilder::build_InitialYield(BytecodeLocation loc
) {
2399 MDefinition
* gen
= current
->pop();
2400 return buildSuspend(loc
, gen
, gen
);
2403 bool WarpBuilder::build_Await(BytecodeLocation loc
) {
2404 MDefinition
* gen
= current
->pop();
2405 MDefinition
* promiseOrGenerator
= current
->pop();
2407 return buildSuspend(loc
, gen
, promiseOrGenerator
);
2409 bool WarpBuilder::build_Yield(BytecodeLocation loc
) { return build_Await(loc
); }
2411 bool WarpBuilder::buildSuspend(BytecodeLocation loc
, MDefinition
* gen
,
2412 MDefinition
* retVal
) {
2413 // If required, unbox the generator object explicitly and infallibly.
2415 // This is done to avoid fuzz-bugs where ApplyTypeInformation does the
2416 // unboxing, and generates fallible unboxes which can lead to torn object
2417 // state due to `bailAfter`.
2418 MDefinition
* genObj
= gen
;
2419 if (genObj
->type() != MIRType::Object
) {
2421 MUnbox::New(alloc(), gen
, MIRType::Object
, MUnbox::Mode::Infallible
);
2422 current
->add(unbox
);
2427 int32_t slotsToCopy
= current
->stackDepth() - info().firstLocalSlot();
2428 MOZ_ASSERT(slotsToCopy
>= 0);
2429 if (slotsToCopy
> 0) {
2430 auto* arrayObj
= MLoadFixedSlotAndUnbox::New(
2431 alloc(), genObj
, AbstractGeneratorObject::stackStorageSlot(),
2432 MUnbox::Mode::Infallible
, MIRType::Object
);
2433 current
->add(arrayObj
);
2435 auto* stackStorage
= MElements::New(alloc(), arrayObj
);
2436 current
->add(stackStorage
);
2438 for (int32_t i
= 0; i
< slotsToCopy
; i
++) {
2439 if (!alloc().ensureBallast()) {
2442 // Use peekUnchecked because we're also writing out the argument slots
2443 int32_t peek
= -slotsToCopy
+ i
;
2444 MDefinition
* stackElem
= current
->peekUnchecked(peek
);
2445 auto* store
= MStoreElement::NewUnbarriered(
2446 alloc(), stackStorage
, constant(Int32Value(i
)), stackElem
,
2447 /* needsHoleCheck = */ false);
2449 current
->add(store
);
2450 current
->add(MPostWriteBarrier::New(alloc(), arrayObj
, stackElem
));
2453 auto* len
= constant(Int32Value(slotsToCopy
- 1));
2455 auto* setInitLength
=
2456 MSetInitializedLength::New(alloc(), stackStorage
, len
);
2457 current
->add(setInitLength
);
2459 auto* setLength
= MSetArrayLength::New(alloc(), stackStorage
, len
);
2460 current
->add(setLength
);
2463 // Update Generator Object state
2464 uint32_t resumeIndex
= loc
.getResumeIndex();
2466 // This store is unbarriered, as it's only ever storing an integer, and as
2467 // such doesn't partake of object tracing.
2468 current
->add(MStoreFixedSlot::NewUnbarriered(
2469 alloc(), genObj
, AbstractGeneratorObject::resumeIndexSlot(),
2470 constant(Int32Value(resumeIndex
))));
2472 // This store is barriered because it stores an object value.
2473 current
->add(MStoreFixedSlot::NewBarriered(
2474 alloc(), genObj
, AbstractGeneratorObject::envChainSlot(),
2475 current
->environmentChain()));
2478 MPostWriteBarrier::New(alloc(), genObj
, current
->environmentChain()));
2480 // GeneratorReturn will return from the method, however to support MIR
2481 // generation isn't treated like the end of a block
2482 MGeneratorReturn
* ret
= MGeneratorReturn::New(alloc(), retVal
);
2485 // To ensure the rest of the MIR generation looks correct, fill the stack with
2486 // the appropriately typed MUnreachable's for the stack pushes from this
2488 auto* unreachableResumeKind
=
2489 MUnreachableResult::New(alloc(), MIRType::Int32
);
2490 current
->add(unreachableResumeKind
);
2491 current
->push(unreachableResumeKind
);
2493 auto* unreachableGenerator
=
2494 MUnreachableResult::New(alloc(), MIRType::Object
);
2495 current
->add(unreachableGenerator
);
2496 current
->push(unreachableGenerator
);
2498 auto* unreachableRval
= MUnreachableResult::New(alloc(), MIRType::Value
);
2499 current
->add(unreachableRval
);
2500 current
->push(unreachableRval
);
2505 bool WarpBuilder::build_AsyncAwait(BytecodeLocation loc
) {
2506 MDefinition
* gen
= current
->pop();
2507 MDefinition
* value
= current
->pop();
2509 MAsyncAwait
* asyncAwait
= MAsyncAwait::New(alloc(), value
, gen
);
2510 current
->add(asyncAwait
);
2511 current
->push(asyncAwait
);
2512 return resumeAfter(asyncAwait
, loc
);
2515 bool WarpBuilder::build_CheckReturn(BytecodeLocation loc
) {
2516 MOZ_ASSERT(!script_
->noScriptRval());
2518 MDefinition
* returnValue
= current
->getSlot(info().returnValueSlot());
2519 MDefinition
* thisValue
= current
->pop();
2521 auto* ins
= MCheckReturn::New(alloc(), returnValue
, thisValue
);
2524 return resumeAfter(ins
, loc
);
2527 void WarpBuilder::buildCheckLexicalOp(BytecodeLocation loc
) {
2528 JSOp op
= loc
.getOp();
2529 MOZ_ASSERT(op
== JSOp::CheckLexical
|| op
== JSOp::CheckAliasedLexical
);
2531 MDefinition
* input
= current
->pop();
2532 MInstruction
* lexicalCheck
= MLexicalCheck::New(alloc(), input
);
2533 current
->add(lexicalCheck
);
2534 current
->push(lexicalCheck
);
2536 if (snapshot().bailoutInfo().failedLexicalCheck()) {
2537 // If we have previously had a failed lexical check in Ion, we want to avoid
2538 // hoisting any lexical checks, which can cause spurious failures. In this
2539 // case, we also have to be careful not to hoist any loads of this lexical
2540 // past the check. For unaliased lexical variables, we can set the local
2541 // slot to create a dependency (see below). For aliased lexicals, that
2542 // doesn't work, so we disable LICM instead.
2543 lexicalCheck
->setNotMovable();
2544 if (op
== JSOp::CheckAliasedLexical
) {
2545 mirGen().disableLICM();
2549 if (op
== JSOp::CheckLexical
) {
2550 // Set the local slot so that a subsequent GetLocal without a CheckLexical
2551 // (the frontend can elide lexical checks) doesn't let a definition with
2552 // MIRType::MagicUninitializedLexical escape to arbitrary MIR instructions.
2553 // Note that in this case the GetLocal would be unreachable because we throw
2554 // an exception here, but we still generate MIR instructions for it.
2555 uint32_t slot
= info().localSlot(loc
.local());
2556 current
->setSlot(slot
, lexicalCheck
);
2560 bool WarpBuilder::build_CheckLexical(BytecodeLocation loc
) {
2561 buildCheckLexicalOp(loc
);
2565 bool WarpBuilder::build_CheckAliasedLexical(BytecodeLocation loc
) {
2566 buildCheckLexicalOp(loc
);
2570 bool WarpBuilder::build_InitHomeObject(BytecodeLocation loc
) {
2571 MDefinition
* homeObject
= current
->pop();
2572 MDefinition
* function
= current
->pop();
2574 current
->add(MPostWriteBarrier::New(alloc(), function
, homeObject
));
2576 auto* ins
= MInitHomeObject::New(alloc(), function
, homeObject
);
2582 bool WarpBuilder::build_SuperBase(BytecodeLocation
) {
2583 MDefinition
* callee
= current
->pop();
2585 auto* homeObject
= MHomeObject::New(alloc(), callee
);
2586 current
->add(homeObject
);
2588 auto* superBase
= MHomeObjectSuperBase::New(alloc(), homeObject
);
2589 current
->add(superBase
);
2590 current
->push(superBase
);
2594 bool WarpBuilder::build_SuperFun(BytecodeLocation
) {
2595 MDefinition
* callee
= current
->pop();
2596 auto* ins
= MSuperFunction::New(alloc(), callee
);
2602 bool WarpBuilder::build_BuiltinObject(BytecodeLocation loc
) {
2603 if (auto* snapshot
= getOpSnapshot
<WarpBuiltinObject
>(loc
)) {
2604 JSObject
* builtin
= snapshot
->builtin();
2605 pushConstant(ObjectValue(*builtin
));
2609 auto kind
= loc
.getBuiltinObjectKind();
2610 auto* ins
= MBuiltinObject::New(alloc(), kind
);
2613 return resumeAfter(ins
, loc
);
2616 bool WarpBuilder::build_GetIntrinsic(BytecodeLocation loc
) {
2617 if (auto* snapshot
= getOpSnapshot
<WarpGetIntrinsic
>(loc
)) {
2618 Value intrinsic
= snapshot
->intrinsic();
2619 pushConstant(intrinsic
);
2623 PropertyName
* name
= loc
.getPropertyName(script_
);
2624 MCallGetIntrinsicValue
* ins
= MCallGetIntrinsicValue::New(alloc(), name
);
2627 return resumeAfter(ins
, loc
);
2630 bool WarpBuilder::build_ImportMeta(BytecodeLocation loc
) {
2631 ModuleObject
* moduleObj
= scriptSnapshot()->moduleObject();
2632 MOZ_ASSERT(moduleObj
);
2634 MModuleMetadata
* ins
= MModuleMetadata::New(alloc(), moduleObj
);
2637 return resumeAfter(ins
, loc
);
2640 bool WarpBuilder::build_CallSiteObj(BytecodeLocation loc
) {
2641 return build_Object(loc
);
2644 bool WarpBuilder::build_NewArray(BytecodeLocation loc
) {
2645 return buildIC(loc
, CacheKind::NewArray
, {});
2648 bool WarpBuilder::build_NewObject(BytecodeLocation loc
) {
2649 return buildIC(loc
, CacheKind::NewObject
, {});
2652 bool WarpBuilder::build_NewInit(BytecodeLocation loc
) {
2653 return build_NewObject(loc
);
2656 bool WarpBuilder::build_Object(BytecodeLocation loc
) {
2657 JSObject
* obj
= loc
.getObject(script_
);
2658 MConstant
* objConst
= constant(ObjectValue(*obj
));
2660 current
->push(objConst
);
2664 bool WarpBuilder::buildInitPropGetterSetterOp(BytecodeLocation loc
) {
2665 PropertyName
* name
= loc
.getPropertyName(script_
);
2666 MDefinition
* value
= current
->pop();
2667 MDefinition
* obj
= current
->peek(-1);
2669 auto* ins
= MInitPropGetterSetter::New(alloc(), obj
, value
, name
);
2671 return resumeAfter(ins
, loc
);
2674 bool WarpBuilder::build_InitPropGetter(BytecodeLocation loc
) {
2675 return buildInitPropGetterSetterOp(loc
);
2678 bool WarpBuilder::build_InitPropSetter(BytecodeLocation loc
) {
2679 return buildInitPropGetterSetterOp(loc
);
2682 bool WarpBuilder::build_InitHiddenPropGetter(BytecodeLocation loc
) {
2683 return buildInitPropGetterSetterOp(loc
);
2686 bool WarpBuilder::build_InitHiddenPropSetter(BytecodeLocation loc
) {
2687 return buildInitPropGetterSetterOp(loc
);
2690 bool WarpBuilder::buildInitElemGetterSetterOp(BytecodeLocation loc
) {
2691 MDefinition
* value
= current
->pop();
2692 MDefinition
* id
= current
->pop();
2693 MDefinition
* obj
= current
->peek(-1);
2695 auto* ins
= MInitElemGetterSetter::New(alloc(), obj
, id
, value
);
2697 return resumeAfter(ins
, loc
);
2700 bool WarpBuilder::build_InitElemGetter(BytecodeLocation loc
) {
2701 return buildInitElemGetterSetterOp(loc
);
2704 bool WarpBuilder::build_InitElemSetter(BytecodeLocation loc
) {
2705 return buildInitElemGetterSetterOp(loc
);
2708 bool WarpBuilder::build_InitHiddenElemGetter(BytecodeLocation loc
) {
2709 return buildInitElemGetterSetterOp(loc
);
2712 bool WarpBuilder::build_InitHiddenElemSetter(BytecodeLocation loc
) {
2713 return buildInitElemGetterSetterOp(loc
);
2716 bool WarpBuilder::build_In(BytecodeLocation loc
) {
2717 MDefinition
* obj
= current
->pop();
2718 MDefinition
* id
= current
->pop();
2719 return buildIC(loc
, CacheKind::In
, {id
, obj
});
2722 bool WarpBuilder::build_HasOwn(BytecodeLocation loc
) {
2723 MDefinition
* obj
= current
->pop();
2724 MDefinition
* id
= current
->pop();
2725 return buildIC(loc
, CacheKind::HasOwn
, {id
, obj
});
2728 bool WarpBuilder::build_CheckPrivateField(BytecodeLocation loc
) {
2729 MDefinition
* id
= current
->peek(-1);
2730 MDefinition
* obj
= current
->peek(-2);
2731 return buildIC(loc
, CacheKind::CheckPrivateField
, {obj
, id
});
2734 bool WarpBuilder::build_NewPrivateName(BytecodeLocation loc
) {
2735 JSAtom
* name
= loc
.getAtom(script_
);
2737 auto* ins
= MNewPrivateName::New(alloc(), name
);
2740 return resumeAfter(ins
, loc
);
2743 bool WarpBuilder::build_Instanceof(BytecodeLocation loc
) {
2744 MDefinition
* rhs
= current
->pop();
2745 MDefinition
* obj
= current
->pop();
2746 return buildIC(loc
, CacheKind::InstanceOf
, {obj
, rhs
});
2749 bool WarpBuilder::build_NewTarget(BytecodeLocation loc
) {
2750 MOZ_ASSERT(script_
->isFunction());
2751 MOZ_ASSERT(info().hasFunMaybeLazy());
2752 MOZ_ASSERT(!scriptSnapshot()->isArrowFunction());
2754 if (inlineCallInfo()) {
2755 if (inlineCallInfo()->constructing()) {
2756 current
->push(inlineCallInfo()->getNewTarget());
2758 pushConstant(UndefinedValue());
2763 MNewTarget
* ins
= MNewTarget::New(alloc());
2769 bool WarpBuilder::build_CheckIsObj(BytecodeLocation loc
) {
2770 CheckIsObjectKind kind
= loc
.getCheckIsObjectKind();
2772 MDefinition
* toCheck
= current
->peek(-1);
2773 if (toCheck
->type() == MIRType::Object
) {
2774 toCheck
->setImplicitlyUsedUnchecked();
2778 MDefinition
* val
= current
->pop();
2779 MCheckIsObj
* ins
= MCheckIsObj::New(alloc(), val
, uint8_t(kind
));
2782 return resumeAfter(ins
, loc
);
2785 bool WarpBuilder::build_CheckObjCoercible(BytecodeLocation loc
) {
2786 MDefinition
* val
= current
->pop();
2787 MCheckObjCoercible
* ins
= MCheckObjCoercible::New(alloc(), val
);
2790 return resumeAfter(ins
, loc
);
2793 MInstruction
* WarpBuilder::buildLoadSlot(MDefinition
* obj
,
2794 uint32_t numFixedSlots
,
2796 if (slot
< numFixedSlots
) {
2797 MLoadFixedSlot
* load
= MLoadFixedSlot::New(alloc(), obj
, slot
);
2802 MSlots
* slots
= MSlots::New(alloc(), obj
);
2803 current
->add(slots
);
2805 MLoadDynamicSlot
* load
=
2806 MLoadDynamicSlot::New(alloc(), slots
, slot
- numFixedSlots
);
2811 bool WarpBuilder::build_GetImport(BytecodeLocation loc
) {
2812 auto* snapshot
= getOpSnapshot
<WarpGetImport
>(loc
);
2814 ModuleEnvironmentObject
* targetEnv
= snapshot
->targetEnv();
2816 // Load the target environment slot.
2817 MConstant
* obj
= constant(ObjectValue(*targetEnv
));
2818 auto* load
= buildLoadSlot(obj
, snapshot
->numFixedSlots(), snapshot
->slot());
2820 if (snapshot
->needsLexicalCheck()) {
2821 // TODO: IonBuilder has code to mark non-movable. See buildCheckLexicalOp.
2822 MInstruction
* lexicalCheck
= MLexicalCheck::New(alloc(), load
);
2823 current
->add(lexicalCheck
);
2824 current
->push(lexicalCheck
);
2826 current
->push(load
);
2832 bool WarpBuilder::build_GetPropSuper(BytecodeLocation loc
) {
2833 MDefinition
* obj
= current
->pop();
2834 MDefinition
* receiver
= current
->pop();
2835 return buildIC(loc
, CacheKind::GetPropSuper
, {obj
, receiver
});
2838 bool WarpBuilder::build_GetElemSuper(BytecodeLocation loc
) {
2839 MDefinition
* obj
= current
->pop();
2840 MDefinition
* id
= current
->pop();
2841 MDefinition
* receiver
= current
->pop();
2842 return buildIC(loc
, CacheKind::GetElemSuper
, {obj
, id
, receiver
});
2845 bool WarpBuilder::build_InitProp(BytecodeLocation loc
) {
2846 MDefinition
* val
= current
->pop();
2847 MDefinition
* obj
= current
->peek(-1);
2848 return buildIC(loc
, CacheKind::SetProp
, {obj
, val
});
2851 bool WarpBuilder::build_InitLockedProp(BytecodeLocation loc
) {
2852 return build_InitProp(loc
);
2855 bool WarpBuilder::build_InitHiddenProp(BytecodeLocation loc
) {
2856 return build_InitProp(loc
);
2859 bool WarpBuilder::build_InitElem(BytecodeLocation loc
) {
2860 MDefinition
* val
= current
->pop();
2861 MDefinition
* id
= current
->pop();
2862 MDefinition
* obj
= current
->peek(-1);
2863 return buildIC(loc
, CacheKind::SetElem
, {obj
, id
, val
});
2866 bool WarpBuilder::build_InitLockedElem(BytecodeLocation loc
) {
2867 return build_InitElem(loc
);
2870 bool WarpBuilder::build_InitHiddenElem(BytecodeLocation loc
) {
2871 return build_InitElem(loc
);
2874 bool WarpBuilder::build_InitElemArray(BytecodeLocation loc
) {
2875 MDefinition
* val
= current
->pop();
2876 MDefinition
* obj
= current
->peek(-1);
2878 // Note: getInitElemArrayIndex asserts the index fits in int32_t.
2879 uint32_t index
= loc
.getInitElemArrayIndex();
2880 MConstant
* indexConst
= constant(Int32Value(index
));
2882 // Note: InitArrayElemOperation asserts the index does not exceed the array's
2883 // dense element capacity.
2885 auto* elements
= MElements::New(alloc(), obj
);
2886 current
->add(elements
);
2888 if (val
->type() == MIRType::MagicHole
) {
2889 val
->setImplicitlyUsedUnchecked();
2890 auto* store
= MStoreHoleValueElement::New(alloc(), elements
, indexConst
);
2891 current
->add(store
);
2893 current
->add(MPostWriteBarrier::New(alloc(), obj
, val
));
2895 MStoreElement::NewUnbarriered(alloc(), elements
, indexConst
, val
,
2896 /* needsHoleCheck = */ false);
2897 current
->add(store
);
2900 auto* setLength
= MSetInitializedLength::New(alloc(), elements
, indexConst
);
2901 current
->add(setLength
);
2903 return resumeAfter(setLength
, loc
);
2906 bool WarpBuilder::build_InitElemInc(BytecodeLocation loc
) {
2907 MDefinition
* val
= current
->pop();
2908 MDefinition
* index
= current
->pop();
2909 MDefinition
* obj
= current
->peek(-1);
2912 MConstant
* constOne
= constant(Int32Value(1));
2913 MAdd
* nextIndex
= MAdd::New(alloc(), index
, constOne
, TruncateKind::Truncate
);
2914 current
->add(nextIndex
);
2915 current
->push(nextIndex
);
2917 return buildIC(loc
, CacheKind::SetElem
, {obj
, index
, val
});
2920 bool WarpBuilder::build_Lambda(BytecodeLocation loc
) {
2921 MOZ_ASSERT(usesEnvironmentChain());
2923 MDefinition
* env
= current
->environmentChain();
2925 JSFunction
* fun
= loc
.getFunction(script_
);
2926 MConstant
* funConst
= constant(ObjectValue(*fun
));
2928 auto* ins
= MLambda::New(alloc(), env
, funConst
);
2931 return resumeAfter(ins
, loc
);
2934 bool WarpBuilder::build_FunWithProto(BytecodeLocation loc
) {
2935 MOZ_ASSERT(usesEnvironmentChain());
2937 MDefinition
* proto
= current
->pop();
2938 MDefinition
* env
= current
->environmentChain();
2940 JSFunction
* fun
= loc
.getFunction(script_
);
2941 MConstant
* funConst
= constant(ObjectValue(*fun
));
2943 auto* ins
= MFunctionWithProto::New(alloc(), env
, proto
, funConst
);
2946 return resumeAfter(ins
, loc
);
2949 bool WarpBuilder::build_SpreadCall(BytecodeLocation loc
) {
2950 bool constructing
= false;
2951 CallInfo
callInfo(alloc(), constructing
, loc
.resultIsPopped());
2952 callInfo
.initForSpreadCall(current
);
2954 // The argument must be an array object. Add an infallible MUnbox if needed,
2955 // but ensure it's not loop hoisted before the branch in the bytecode guarding
2956 // that it's not undefined.
2957 MOZ_ASSERT(callInfo
.argc() == 1);
2958 callInfo
.setArg(0, unboxObjectInfallible(callInfo
.getArg(0), IsMovable::No
));
2960 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
2961 return transpileCall(loc
, cacheIRSnapshot
, &callInfo
);
2964 bool needsThisCheck
= false;
2965 MInstruction
* call
= makeSpreadCall(callInfo
, needsThisCheck
);
2969 call
->setBailoutKind(BailoutKind::TooManyArguments
);
2971 current
->push(call
);
2972 return resumeAfter(call
, loc
);
2975 bool WarpBuilder::build_SpreadNew(BytecodeLocation loc
) {
2976 bool constructing
= true;
2977 CallInfo
callInfo(alloc(), constructing
, loc
.resultIsPopped());
2978 callInfo
.initForSpreadCall(current
);
2980 // See build_SpreadCall.
2981 MOZ_ASSERT(callInfo
.argc() == 1);
2982 callInfo
.setArg(0, unboxObjectInfallible(callInfo
.getArg(0), IsMovable::No
));
2984 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
2985 return transpileCall(loc
, cacheIRSnapshot
, &callInfo
);
2988 buildCreateThis(callInfo
);
2990 bool needsThisCheck
= true;
2991 MInstruction
* call
= makeSpreadCall(callInfo
, needsThisCheck
);
2995 call
->setBailoutKind(BailoutKind::TooManyArguments
);
2997 current
->push(call
);
2998 return resumeAfter(call
, loc
);
3001 bool WarpBuilder::build_SpreadSuperCall(BytecodeLocation loc
) {
3002 return build_SpreadNew(loc
);
3005 bool WarpBuilder::build_OptimizeSpreadCall(BytecodeLocation loc
) {
3006 MDefinition
* value
= current
->pop();
3007 return buildIC(loc
, CacheKind::OptimizeSpreadCall
, {value
});
3010 bool WarpBuilder::build_Debugger(BytecodeLocation loc
) {
3011 // The |debugger;| statement will bail out to Baseline if the realm is a
3012 // debuggee realm with an onDebuggerStatement hook.
3013 MDebugger
* debugger
= MDebugger::New(alloc());
3014 current
->add(debugger
);
3015 return resumeAfter(debugger
, loc
);
3018 bool WarpBuilder::build_TableSwitch(BytecodeLocation loc
) {
3019 int32_t low
= loc
.getTableSwitchLow();
3020 int32_t high
= loc
.getTableSwitchHigh();
3021 size_t numCases
= high
- low
+ 1;
3023 MDefinition
* input
= current
->pop();
3024 MTableSwitch
* tableswitch
= MTableSwitch::New(alloc(), input
, low
, high
);
3025 current
->end(tableswitch
);
3027 // Table mapping from target bytecode offset to MTableSwitch successor index.
3028 // This prevents adding multiple predecessor/successor edges to the same
3029 // target block, which isn't valid in MIR.
3030 using TargetToSuccessorMap
=
3031 InlineMap
<uint32_t, uint32_t, 8, DefaultHasher
<uint32_t>,
3033 TargetToSuccessorMap targetToSuccessor
;
3035 // Create |default| edge.
3037 BytecodeLocation defaultLoc
= loc
.getTableSwitchDefaultTarget();
3038 uint32_t defaultOffset
= defaultLoc
.bytecodeToOffset(script_
);
3041 if (!tableswitch
->addDefault(nullptr, &index
)) {
3044 if (!addPendingEdge(defaultLoc
, current
, index
)) {
3047 if (!targetToSuccessor
.put(defaultOffset
, index
)) {
3053 for (size_t i
= 0; i
< numCases
; i
++) {
3054 BytecodeLocation caseLoc
= loc
.getTableSwitchCaseTarget(script_
, i
);
3055 uint32_t caseOffset
= caseLoc
.bytecodeToOffset(script_
);
3058 if (auto p
= targetToSuccessor
.lookupForAdd(caseOffset
)) {
3061 if (!tableswitch
->addSuccessor(nullptr, &index
)) {
3064 if (!addPendingEdge(caseLoc
, current
, index
)) {
3067 if (!targetToSuccessor
.add(p
, caseOffset
, index
)) {
3071 if (!tableswitch
->addCase(index
)) {
3076 setTerminatedBlock();
3080 bool WarpBuilder::build_Rest(BytecodeLocation loc
) {
3081 auto* snapshot
= getOpSnapshot
<WarpRest
>(loc
);
3082 Shape
* shape
= snapshot
? snapshot
->shape() : nullptr;
3084 // NOTE: Keep this code in sync with |ArgumentsReplacer|.
3086 if (inlineCallInfo()) {
3087 // If we are inlining, we know the actual arguments.
3088 unsigned numActuals
= inlineCallInfo()->argc();
3089 unsigned numFormals
= info().nargs() - 1;
3090 unsigned numRest
= numActuals
> numFormals
? numActuals
- numFormals
: 0;
3092 // TODO: support pre-tenuring.
3093 gc::Heap heap
= gc::Heap::Default
;
3095 // Allocate an array of the correct size.
3096 MInstruction
* newArray
;
3097 if (shape
&& gc::CanUseFixedElementsForArray(numRest
)) {
3098 auto* shapeConstant
= MConstant::NewShape(alloc(), shape
);
3099 current
->add(shapeConstant
);
3100 newArray
= MNewArrayObject::New(alloc(), shapeConstant
, numRest
, heap
);
3102 MConstant
* templateConst
= constant(NullValue());
3103 newArray
= MNewArray::NewVM(alloc(), numRest
, templateConst
, heap
);
3105 current
->add(newArray
);
3106 current
->push(newArray
);
3109 // No more updating to do.
3113 MElements
* elements
= MElements::New(alloc(), newArray
);
3114 current
->add(elements
);
3116 // Unroll the argument copy loop. We don't need to do any bounds or hole
3118 MConstant
* index
= nullptr;
3119 for (uint32_t i
= numFormals
; i
< numActuals
; i
++) {
3120 if (!alloc().ensureBallast()) {
3124 index
= MConstant::New(alloc(), Int32Value(i
- numFormals
));
3125 current
->add(index
);
3127 MDefinition
* arg
= inlineCallInfo()->argv()[i
];
3128 MStoreElement
* store
=
3129 MStoreElement::NewUnbarriered(alloc(), elements
, index
, arg
,
3130 /* needsHoleCheck = */ false);
3131 current
->add(store
);
3132 current
->add(MPostWriteBarrier::New(alloc(), newArray
, arg
));
3135 // Update the initialized length for all the (necessarily non-hole)
3137 MSetInitializedLength
* initLength
=
3138 MSetInitializedLength::New(alloc(), elements
, index
);
3139 current
->add(initLength
);
3144 MArgumentsLength
* numActuals
= MArgumentsLength::New(alloc());
3145 current
->add(numActuals
);
3147 // Pass in the number of actual arguments, the number of formals (not
3148 // including the rest parameter slot itself), and the shape.
3149 unsigned numFormals
= info().nargs() - 1;
3150 MRest
* rest
= MRest::New(alloc(), numActuals
, numFormals
, shape
);
3152 current
->push(rest
);
3156 bool WarpBuilder::build_Try(BytecodeLocation loc
) {
3157 graph().setHasTryBlock();
3159 MBasicBlock
* pred
= current
;
3160 if (!startNewBlock(pred
, loc
.next())) {
3164 pred
->end(MGoto::New(alloc(), current
));
3168 bool WarpBuilder::build_Finally(BytecodeLocation loc
) {
3169 MOZ_ASSERT(graph().hasTryBlock());
3173 bool WarpBuilder::build_Exception(BytecodeLocation
) {
3174 MOZ_CRASH("Unreachable because we skip catch-blocks");
3177 bool WarpBuilder::build_ExceptionAndStack(BytecodeLocation
) {
3178 MOZ_CRASH("Unreachable because we skip catch-blocks");
3181 bool WarpBuilder::build_Throw(BytecodeLocation loc
) {
3182 MDefinition
* def
= current
->pop();
3184 MThrow
* ins
= MThrow::New(alloc(), def
);
3186 if (!resumeAfter(ins
, loc
)) {
3190 // Terminate the block.
3191 current
->end(MUnreachable::New(alloc()));
3192 setTerminatedBlock();
3196 bool WarpBuilder::build_ThrowWithStack(BytecodeLocation loc
) {
3197 MDefinition
* stack
= current
->pop();
3198 MDefinition
* value
= current
->pop();
3200 auto* ins
= MThrowWithStack::New(alloc(), value
, stack
);
3202 if (!resumeAfter(ins
, loc
)) {
3206 // Terminate the block.
3207 current
->end(MUnreachable::New(alloc()));
3208 setTerminatedBlock();
3212 bool WarpBuilder::build_ThrowSetConst(BytecodeLocation loc
) {
3213 auto* ins
= MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN
);
3215 if (!resumeAfter(ins
, loc
)) {
3219 // Terminate the block.
3220 current
->end(MUnreachable::New(alloc()));
3221 setTerminatedBlock();
3225 bool WarpBuilder::build_ThrowMsg(BytecodeLocation loc
) {
3226 auto* ins
= MThrowMsg::New(alloc(), loc
.throwMsgKind());
3228 if (!resumeAfter(ins
, loc
)) {
3232 // Terminate the block.
3233 current
->end(MUnreachable::New(alloc()));
3234 setTerminatedBlock();
3238 bool WarpBuilder::buildIC(BytecodeLocation loc
, CacheKind kind
,
3239 std::initializer_list
<MDefinition
*> inputs
) {
3240 MOZ_ASSERT(loc
.opHasIC());
3242 mozilla::DebugOnly
<size_t> numInputs
= inputs
.size();
3243 MOZ_ASSERT(numInputs
== NumInputsForCacheKind(kind
));
3245 if (auto* cacheIRSnapshot
= getOpSnapshot
<WarpCacheIR
>(loc
)) {
3246 return TranspileCacheIRToMIR(this, loc
, cacheIRSnapshot
, inputs
);
3249 if (getOpSnapshot
<WarpBailout
>(loc
)) {
3250 for (MDefinition
* input
: inputs
) {
3251 input
->setImplicitlyUsedUnchecked();
3253 return buildBailoutForColdIC(loc
, kind
);
3256 if (const auto* inliningSnapshot
= getOpSnapshot
<WarpInlinedCall
>(loc
)) {
3257 // The CallInfo will be initialized by the transpiler.
3258 bool ignoresRval
= BytecodeIsPopped(loc
.toRawBytecode());
3259 CallInfo
callInfo(alloc(), /*constructing =*/false, ignoresRval
);
3260 callInfo
.markAsInlined();
3262 if (!TranspileCacheIRToMIR(this, loc
, inliningSnapshot
->cacheIRSnapshot(),
3263 inputs
, &callInfo
)) {
3266 return buildInlinedCall(loc
, inliningSnapshot
, callInfo
);
3269 // Work around std::initializer_list not defining operator[].
3270 auto getInput
= [&](size_t index
) -> MDefinition
* {
3271 MOZ_ASSERT(index
< numInputs
);
3272 return inputs
.begin()[index
];
3276 case CacheKind::UnaryArith
: {
3277 MOZ_ASSERT(numInputs
== 1);
3278 auto* ins
= MUnaryCache::New(alloc(), getInput(0));
3281 return resumeAfter(ins
, loc
);
3283 case CacheKind::ToPropertyKey
: {
3284 MOZ_ASSERT(numInputs
== 1);
3285 auto* ins
= MToPropertyKeyCache::New(alloc(), getInput(0));
3288 return resumeAfter(ins
, loc
);
3290 case CacheKind::BinaryArith
: {
3291 MOZ_ASSERT(numInputs
== 2);
3293 MBinaryCache::New(alloc(), getInput(0), getInput(1), MIRType::Value
);
3296 return resumeAfter(ins
, loc
);
3298 case CacheKind::Compare
: {
3299 MOZ_ASSERT(numInputs
== 2);
3300 auto* ins
= MBinaryCache::New(alloc(), getInput(0), getInput(1),
3304 return resumeAfter(ins
, loc
);
3306 case CacheKind::In
: {
3307 MOZ_ASSERT(numInputs
== 2);
3308 auto* ins
= MInCache::New(alloc(), getInput(0), getInput(1));
3311 return resumeAfter(ins
, loc
);
3313 case CacheKind::HasOwn
: {
3314 MOZ_ASSERT(numInputs
== 2);
3315 // Note: the MHasOwnCache constructor takes obj/id instead of id/obj.
3316 auto* ins
= MHasOwnCache::New(alloc(), getInput(1), getInput(0));
3319 return resumeAfter(ins
, loc
);
3321 case CacheKind::CheckPrivateField
: {
3322 MOZ_ASSERT(numInputs
== 2);
3324 MCheckPrivateFieldCache::New(alloc(), getInput(0), getInput(1));
3327 return resumeAfter(ins
, loc
);
3329 case CacheKind::InstanceOf
: {
3330 MOZ_ASSERT(numInputs
== 2);
3331 auto* ins
= MInstanceOfCache::New(alloc(), getInput(0), getInput(1));
3334 return resumeAfter(ins
, loc
);
3336 case CacheKind::BindName
: {
3337 MOZ_ASSERT(numInputs
== 1);
3338 auto* ins
= MBindNameCache::New(alloc(), getInput(0));
3341 return resumeAfter(ins
, loc
);
3343 case CacheKind::GetIterator
: {
3344 MOZ_ASSERT(numInputs
== 1);
3345 auto* ins
= MGetIteratorCache::New(alloc(), getInput(0));
3348 return resumeAfter(ins
, loc
);
3350 case CacheKind::GetName
: {
3351 MOZ_ASSERT(numInputs
== 1);
3352 auto* ins
= MGetNameCache::New(alloc(), getInput(0));
3355 return resumeAfter(ins
, loc
);
3357 case CacheKind::GetProp
: {
3358 MOZ_ASSERT(numInputs
== 1);
3359 PropertyName
* name
= loc
.getPropertyName(script_
);
3360 MConstant
* id
= constant(StringValue(name
));
3361 MDefinition
* val
= getInput(0);
3362 auto* ins
= MGetPropertyCache::New(alloc(), val
, id
);
3365 return resumeAfter(ins
, loc
);
3367 case CacheKind::GetElem
: {
3368 MOZ_ASSERT(numInputs
== 2);
3369 MDefinition
* val
= getInput(0);
3370 auto* ins
= MGetPropertyCache::New(alloc(), val
, getInput(1));
3373 return resumeAfter(ins
, loc
);
3375 case CacheKind::SetProp
: {
3376 MOZ_ASSERT(numInputs
== 2);
3377 PropertyName
* name
= loc
.getPropertyName(script_
);
3378 MConstant
* id
= constant(StringValue(name
));
3379 bool strict
= loc
.isStrictSetOp();
3381 MSetPropertyCache::New(alloc(), getInput(0), id
, getInput(1), strict
);
3383 return resumeAfter(ins
, loc
);
3385 case CacheKind::SetElem
: {
3386 MOZ_ASSERT(numInputs
== 3);
3387 bool strict
= loc
.isStrictSetOp();
3388 auto* ins
= MSetPropertyCache::New(alloc(), getInput(0), getInput(1),
3389 getInput(2), strict
);
3391 return resumeAfter(ins
, loc
);
3393 case CacheKind::GetPropSuper
: {
3394 MOZ_ASSERT(numInputs
== 2);
3395 PropertyName
* name
= loc
.getPropertyName(script_
);
3396 MConstant
* id
= constant(StringValue(name
));
3398 MGetPropSuperCache::New(alloc(), getInput(0), getInput(1), id
);
3401 return resumeAfter(ins
, loc
);
3403 case CacheKind::GetElemSuper
: {
3404 MOZ_ASSERT(numInputs
== 3);
3405 // Note: CacheIR expects obj/id/receiver but MGetPropSuperCache takes
3406 // obj/receiver/id so swap the last two inputs.
3407 auto* ins
= MGetPropSuperCache::New(alloc(), getInput(0), getInput(2),
3411 return resumeAfter(ins
, loc
);
3413 case CacheKind::OptimizeSpreadCall
: {
3414 MOZ_ASSERT(numInputs
== 1);
3415 auto* ins
= MOptimizeSpreadCallCache::New(alloc(), getInput(0));
3418 return resumeAfter(ins
, loc
);
3420 case CacheKind::TypeOf
: {
3421 // Note: Warp does not have a TypeOf IC, it just inlines the operation.
3422 MOZ_ASSERT(numInputs
== 1);
3423 auto* typeOf
= MTypeOf::New(alloc(), getInput(0));
3424 current
->add(typeOf
);
3426 auto* ins
= MTypeOfName::New(alloc(), typeOf
);
3431 case CacheKind::TypeOfEq
: {
3432 MOZ_ASSERT(numInputs
== 1);
3433 auto operand
= loc
.getTypeofEqOperand();
3434 JSType type
= operand
.type();
3435 JSOp compareOp
= operand
.compareOp();
3436 auto* typeOf
= MTypeOf::New(alloc(), getInput(0));
3437 current
->add(typeOf
);
3439 auto* typeInt
= MConstant::New(alloc(), Int32Value(type
));
3440 current
->add(typeInt
);
3442 auto* ins
= MCompare::New(alloc(), typeOf
, typeInt
, compareOp
,
3443 MCompare::Compare_Int32
);
3448 case CacheKind::NewObject
: {
3449 auto* templateConst
= constant(NullValue());
3450 MNewObject
* ins
= MNewObject::NewVM(
3451 alloc(), templateConst
, gc::Heap::Default
, MNewObject::ObjectLiteral
);
3454 return resumeAfter(ins
, loc
);
3456 case CacheKind::NewArray
: {
3457 uint32_t length
= loc
.getNewArrayLength();
3458 MConstant
* templateConst
= constant(NullValue());
3460 MNewArray::NewVM(alloc(), length
, templateConst
, gc::Heap::Default
);
3465 case CacheKind::CloseIter
: {
3466 MOZ_ASSERT(numInputs
== 1);
3467 static_assert(sizeof(CompletionKind
) == sizeof(uint8_t));
3468 CompletionKind kind
= loc
.getCompletionKind();
3469 auto* ins
= MCloseIterCache::New(alloc(), getInput(0), uint8_t(kind
));
3471 return resumeAfter(ins
, loc
);
3473 case CacheKind::OptimizeGetIterator
: {
3474 MOZ_ASSERT(numInputs
== 1);
3475 auto* ins
= MOptimizeGetIteratorCache::New(alloc(), getInput(0));
3478 return resumeAfter(ins
, loc
);
3480 case CacheKind::GetIntrinsic
:
3481 case CacheKind::ToBool
:
3482 case CacheKind::Call
:
3483 // We're currently not using an IC or transpiling CacheIR for these kinds.
3484 MOZ_CRASH("Unexpected kind");
3490 bool WarpBuilder::buildBailoutForColdIC(BytecodeLocation loc
, CacheKind kind
) {
3491 MOZ_ASSERT(loc
.opHasIC());
3493 MBail
* bail
= MBail::New(alloc(), BailoutKind::FirstExecution
);
3495 current
->setAlwaysBails();
3499 case CacheKind::UnaryArith
:
3500 case CacheKind::BinaryArith
:
3501 case CacheKind::GetName
:
3502 case CacheKind::GetProp
:
3503 case CacheKind::GetElem
:
3504 case CacheKind::GetPropSuper
:
3505 case CacheKind::GetElemSuper
:
3506 case CacheKind::GetIntrinsic
:
3507 case CacheKind::Call
:
3508 case CacheKind::ToPropertyKey
:
3509 case CacheKind::OptimizeSpreadCall
:
3510 resultType
= MIRType::Value
;
3512 case CacheKind::BindName
:
3513 case CacheKind::GetIterator
:
3514 case CacheKind::NewArray
:
3515 case CacheKind::NewObject
:
3516 resultType
= MIRType::Object
;
3518 case CacheKind::TypeOf
:
3519 resultType
= MIRType::String
;
3521 case CacheKind::ToBool
:
3522 case CacheKind::Compare
:
3524 case CacheKind::HasOwn
:
3525 case CacheKind::CheckPrivateField
:
3526 case CacheKind::InstanceOf
:
3527 case CacheKind::OptimizeGetIterator
:
3528 case CacheKind::TypeOfEq
:
3529 resultType
= MIRType::Boolean
;
3531 case CacheKind::SetProp
:
3532 case CacheKind::SetElem
:
3533 case CacheKind::CloseIter
:
3534 return true; // No result.
3537 auto* ins
= MUnreachableResult::New(alloc(), resultType
);
3544 class MOZ_RAII AutoAccumulateReturns
{
3546 MIRGraphReturns
* prev_
;
3549 AutoAccumulateReturns(MIRGraph
& graph
, MIRGraphReturns
& returns
)
3551 prev_
= graph_
.returnAccumulator();
3552 graph_
.setReturnAccumulator(&returns
);
3554 ~AutoAccumulateReturns() { graph_
.setReturnAccumulator(prev_
); }
3557 bool WarpBuilder::buildInlinedCall(BytecodeLocation loc
,
3558 const WarpInlinedCall
* inlineSnapshot
,
3559 CallInfo
& callInfo
) {
3560 jsbytecode
* pc
= loc
.toRawBytecode();
3562 if (callInfo
.isSetter()) {
3563 // build_SetProp pushes the rhs argument onto the stack. Remove it
3564 // in preparation for pushCallStack.
3568 callInfo
.setImplicitlyUsedUnchecked();
3570 // Capture formals in the outer resume point.
3571 if (!callInfo
.pushCallStack(current
)) {
3574 MResumePoint
* outerResumePoint
=
3575 MResumePoint::New(alloc(), current
, pc
, callInfo
.inliningResumeMode());
3576 if (!outerResumePoint
) {
3579 current
->setOuterResumePoint(outerResumePoint
);
3581 // Pop formals again, except leave |callee| on stack for duration of call.
3582 callInfo
.popCallStack(current
);
3583 current
->push(callInfo
.callee());
3586 CompileInfo
* calleeCompileInfo
= inlineSnapshot
->info();
3587 MIRGraphReturns
returns(alloc());
3588 AutoAccumulateReturns
aar(graph(), returns
);
3589 WarpBuilder
inlineBuilder(this, inlineSnapshot
->scriptSnapshot(),
3590 *calleeCompileInfo
, &callInfo
, outerResumePoint
);
3591 if (!inlineBuilder
.buildInline()) {
3592 // Note: Inlining only aborts on OOM. If inlining would fail for
3593 // any other reason, we detect it in advance and don't inline.
3597 // We mark scripts as uninlineable in BytecodeAnalysis if we cannot
3598 // reach a return statement (without going through a catch/finally).
3599 MOZ_ASSERT(!returns
.empty());
3601 // Create return block
3602 BytecodeLocation postCall
= loc
.next();
3603 MBasicBlock
* prev
= current
;
3604 if (!startNewEntryBlock(prev
->stackDepth(), postCall
)) {
3607 // Restore previous value of callerResumePoint.
3608 current
->setCallerResumePoint(callerResumePoint());
3609 current
->inheritSlots(prev
);
3614 // Accumulate return values.
3615 MDefinition
* returnValue
=
3616 patchInlinedReturns(calleeCompileInfo
, callInfo
, returns
, current
);
3620 current
->push(returnValue
);
3622 // Initialize entry slots
3623 if (!current
->initEntrySlots(alloc())) {
3630 MDefinition
* WarpBuilder::patchInlinedReturns(CompileInfo
* calleeCompileInfo
,
3632 MIRGraphReturns
& exits
,
3633 MBasicBlock
* returnBlock
) {
3634 if (exits
.length() == 1) {
3635 return patchInlinedReturn(calleeCompileInfo
, callInfo
, exits
[0],
3639 // Accumulate multiple returns with a phi.
3640 MPhi
* phi
= MPhi::New(alloc());
3641 if (!phi
->reserveLength(exits
.length())) {
3645 for (auto* exit
: exits
) {
3647 patchInlinedReturn(calleeCompileInfo
, callInfo
, exit
, returnBlock
);
3651 phi
->addInput(rdef
);
3653 returnBlock
->addPhi(phi
);
3657 MDefinition
* WarpBuilder::patchInlinedReturn(CompileInfo
* calleeCompileInfo
,
3660 MBasicBlock
* returnBlock
) {
3661 // Replace the MReturn in the exit block with an MGoto branching to
3662 // the return block.
3663 MDefinition
* rdef
= exit
->lastIns()->toReturn()->input();
3664 exit
->discardLastIns();
3666 // Constructors must be patched by the caller to always return an object.
3667 // Derived class constructors contain extra bytecode to ensure an object
3668 // is always returned, so no additional patching is needed.
3669 if (callInfo
.constructing() &&
3670 !calleeCompileInfo
->isDerivedClassConstructor()) {
3671 auto* filter
= MReturnFromCtor::New(alloc(), rdef
, callInfo
.thisArg());
3674 } else if (callInfo
.isSetter()) {
3675 // Setters return the rhs argument, not whatever value is returned.
3676 rdef
= callInfo
.getArg(0);
3679 exit
->end(MGoto::New(alloc(), returnBlock
));
3680 if (!returnBlock
->addPredecessorWithoutPhis(exit
)) {