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/. */
6
7 #include "jit/WarpBuilder.h"
8
9 #include "mozilla/DebugOnly.h"
10
11 #include "jit/BaselineFrame.h"
12 #include "jit/CacheIR.h"
13 #include "jit/CompileInfo.h"
14 #include "jit/InlineScriptTree.h"
15 #include "jit/MIR.h"
16 #include "jit/MIRGenerator.h"
17 #include "jit/MIRGraph.h"
18 #include "jit/WarpCacheIRTranspiler.h"
19 #include "jit/WarpSnapshot.h"
20 #include "js/friend/ErrorMessages.h" // JSMSG_BAD_CONST_ASSIGN
21 #include "vm/GeneratorObject.h"
22 #include "vm/Opcodes.h"
23
24 #include "gc/ObjectKind-inl.h"
25 #include "jit/JitScript-inl.h"
26 #include "vm/BytecodeIterator-inl.h"
27 #include "vm/BytecodeLocation-inl.h"
28
29 using namespace js;
30 using namespace js::jit;
31
32 // Used for building the outermost script.
WarpBuilder(WarpSnapshot & snapshot,MIRGenerator & mirGen,WarpCompilation * warpCompilation)33 WarpBuilder::WarpBuilder(WarpSnapshot& snapshot, MIRGenerator& mirGen,
34 WarpCompilation* warpCompilation)
35 : WarpBuilderShared(snapshot, mirGen, nullptr),
36 warpCompilation_(warpCompilation),
37 graph_(mirGen.graph()),
38 info_(mirGen.outerInfo()),
39 scriptSnapshot_(snapshot.rootScript()),
40 script_(snapshot.rootScript()->script()),
41 loopStack_(mirGen.alloc()) {
42 opSnapshotIter_ = scriptSnapshot_->opSnapshots().getFirst();
43 }
44
45 // Used for building inlined scripts.
WarpBuilder(WarpBuilder * caller,WarpScriptSnapshot * snapshot,CompileInfo & compileInfo,CallInfo * inlineCallInfo,MResumePoint * callerResumePoint)46 WarpBuilder::WarpBuilder(WarpBuilder* caller, WarpScriptSnapshot* snapshot,
47 CompileInfo& compileInfo, CallInfo* inlineCallInfo,
48 MResumePoint* callerResumePoint)
49 : WarpBuilderShared(caller->snapshot(), caller->mirGen(), nullptr),
50 warpCompilation_(caller->warpCompilation()),
51 graph_(caller->mirGen().graph()),
52 info_(compileInfo),
53 scriptSnapshot_(snapshot),
54 script_(snapshot->script()),
55 loopStack_(caller->mirGen().alloc()),
56 callerBuilder_(caller),
57 callerResumePoint_(callerResumePoint),
58 inlineCallInfo_(inlineCallInfo) {
59 opSnapshotIter_ = snapshot->opSnapshots().getFirst();
60 }
61
newBytecodeSite(BytecodeLocation loc)62 BytecodeSite* WarpBuilder::newBytecodeSite(BytecodeLocation loc) {
63 jsbytecode* pc = loc.toRawBytecode();
64 MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc));
65 return new (alloc()) BytecodeSite(info().inlineScriptTree(), pc);
66 }
67
getOpSnapshotImpl(BytecodeLocation loc,WarpOpSnapshot::Kind kind)68 const WarpOpSnapshot* WarpBuilder::getOpSnapshotImpl(
69 BytecodeLocation loc, WarpOpSnapshot::Kind kind) {
70 uint32_t offset = loc.bytecodeToOffset(script_);
71
72 // Skip snapshots until we get to a snapshot with offset >= offset. This is
73 // a loop because WarpBuilder can skip unreachable bytecode ops.
74 while (opSnapshotIter_ && opSnapshotIter_->offset() < offset) {
75 opSnapshotIter_ = opSnapshotIter_->getNext();
76 }
77
78 if (!opSnapshotIter_ || opSnapshotIter_->offset() != offset ||
79 opSnapshotIter_->kind() != kind) {
80 return nullptr;
81 }
82
83 return opSnapshotIter_;
84 }
85
initBlock(MBasicBlock * block)86 void WarpBuilder::initBlock(MBasicBlock* block) {
87 graph().addBlock(block);
88
89 block->setLoopDepth(loopDepth());
90
91 current = block;
92 }
93
startNewBlock(MBasicBlock * predecessor,BytecodeLocation loc,size_t numToPop)94 bool WarpBuilder::startNewBlock(MBasicBlock* predecessor, BytecodeLocation loc,
95 size_t numToPop) {
96 MBasicBlock* block =
97 MBasicBlock::NewPopN(graph(), info(), predecessor, newBytecodeSite(loc),
98 MBasicBlock::NORMAL, numToPop);
99 if (!block) {
100 return false;
101 }
102
103 initBlock(block);
104 return true;
105 }
106
startNewEntryBlock(size_t stackDepth,BytecodeLocation loc)107 bool WarpBuilder::startNewEntryBlock(size_t stackDepth, BytecodeLocation loc) {
108 MBasicBlock* block =
109 MBasicBlock::New(graph(), stackDepth, info(), /* maybePred = */ nullptr,
110 newBytecodeSite(loc), MBasicBlock::NORMAL);
111 if (!block) {
112 return false;
113 }
114
115 initBlock(block);
116 return true;
117 }
118
startNewLoopHeaderBlock(BytecodeLocation loopHead)119 bool WarpBuilder::startNewLoopHeaderBlock(BytecodeLocation loopHead) {
120 MBasicBlock* header = MBasicBlock::NewPendingLoopHeader(
121 graph(), info(), current, newBytecodeSite(loopHead));
122 if (!header) {
123 return false;
124 }
125
126 initBlock(header);
127 return loopStack_.emplaceBack(header);
128 }
129
startNewOsrPreHeaderBlock(BytecodeLocation loopHead)130 bool WarpBuilder::startNewOsrPreHeaderBlock(BytecodeLocation loopHead) {
131 MOZ_ASSERT(loopHead.is(JSOp::LoopHead));
132 MOZ_ASSERT(loopHead.toRawBytecode() == info().osrPc());
133
134 // Create two blocks:
135 // * The OSR entry block. This is always the graph's second block and has no
136 // predecessors. This is the entry point for OSR from the Baseline JIT.
137 // * The OSR preheader block. This has two predecessors: the OSR entry block
138 // and the current block.
139
140 MBasicBlock* pred = current;
141
142 // Create the OSR entry block.
143 if (!startNewEntryBlock(pred->stackDepth(), loopHead)) {
144 return false;
145 }
146
147 MBasicBlock* osrBlock = current;
148 graph().setOsrBlock(osrBlock);
149 graph().moveBlockAfter(*graph().begin(), osrBlock);
150
151 MOsrEntry* entry = MOsrEntry::New(alloc());
152 osrBlock->add(entry);
153
154 // Initialize environment chain.
155 {
156 uint32_t slot = info().environmentChainSlot();
157 MInstruction* envv;
158 if (usesEnvironmentChain()) {
159 envv = MOsrEnvironmentChain::New(alloc(), entry);
160 } else {
161 // Use an undefined value if the script does not need its environment
162 // chain, to match the main entry point.
163 envv = MConstant::New(alloc(), UndefinedValue());
164 }
165 osrBlock->add(envv);
166 osrBlock->initSlot(slot, envv);
167 }
168
169 // Initialize return value.
170 {
171 MInstruction* returnValue;
172 if (!script_->noScriptRval()) {
173 returnValue = MOsrReturnValue::New(alloc(), entry);
174 } else {
175 returnValue = MConstant::New(alloc(), UndefinedValue());
176 }
177 osrBlock->add(returnValue);
178 osrBlock->initSlot(info().returnValueSlot(), returnValue);
179 }
180
181 // Initialize arguments object.
182 MInstruction* argsObj = nullptr;
183 if (info().needsArgsObj()) {
184 argsObj = MOsrArgumentsObject::New(alloc(), entry);
185 osrBlock->add(argsObj);
186 osrBlock->initSlot(info().argsObjSlot(), argsObj);
187 }
188
189 if (info().funMaybeLazy()) {
190 // Initialize |this| parameter.
191 MParameter* thisv = MParameter::New(alloc(), MParameter::THIS_SLOT);
192 osrBlock->add(thisv);
193 osrBlock->initSlot(info().thisSlot(), thisv);
194
195 // Initialize arguments. There are three cases:
196 //
197 // 1) There's no ArgumentsObject or it doesn't alias formals. In this case
198 // we can just use the frame's argument slot.
199 // 2) The ArgumentsObject aliases formals and the argument is stored in the
200 // CallObject. Use |undefined| because we can't load from the arguments
201 // object and code will use the CallObject anyway.
202 // 3) The ArgumentsObject aliases formals and the argument isn't stored in
203 // the CallObject. We have to load it from the ArgumentsObject.
204 for (uint32_t i = 0; i < info().nargs(); i++) {
205 uint32_t slot = info().argSlotUnchecked(i);
206 MInstruction* osrv;
207 if (!info().argsObjAliasesFormals()) {
208 osrv = MParameter::New(alloc().fallible(), i);
209 } else if (script_->formalIsAliased(i)) {
210 osrv = MConstant::New(alloc().fallible(), UndefinedValue());
211 } else {
212 osrv = MGetArgumentsObjectArg::New(alloc().fallible(), argsObj, i);
213 }
214 if (!osrv) {
215 return false;
216 }
217 current->add(osrv);
218 current->initSlot(slot, osrv);
219 }
220 }
221
222 // Initialize locals.
223 uint32_t nlocals = info().nlocals();
224 for (uint32_t i = 0; i < nlocals; i++) {
225 uint32_t slot = info().localSlot(i);
226 ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(i);
227 MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
228 if (!osrv) {
229 return false;
230 }
231 current->add(osrv);
232 current->initSlot(slot, osrv);
233 }
234
235 // Initialize expression stack slots.
236 uint32_t numStackSlots = current->stackDepth() - info().firstStackSlot();
237 for (uint32_t i = 0; i < numStackSlots; i++) {
238 uint32_t slot = info().stackSlot(i);
239 ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(nlocals + i);
240 MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
241 if (!osrv) {
242 return false;
243 }
244 current->add(osrv);
245 current->initSlot(slot, osrv);
246 }
247
248 MStart* start = MStart::New(alloc());
249 current->add(start);
250
251 // Note: phi specialization can add type guard instructions to the OSR entry
252 // block if needed. See TypeAnalyzer::shouldSpecializeOsrPhis.
253
254 // Create the preheader block, with the predecessor block and OSR block as
255 // predecessors.
256 if (!startNewBlock(pred, loopHead)) {
257 return false;
258 }
259
260 pred->end(MGoto::New(alloc(), current));
261 osrBlock->end(MGoto::New(alloc(), current));
262
263 if (!current->addPredecessor(alloc(), osrBlock)) {
264 return false;
265 }
266
267 return true;
268 }
269
addPendingEdge(const PendingEdge & edge,BytecodeLocation target)270 bool WarpBuilder::addPendingEdge(const PendingEdge& edge,
271 BytecodeLocation target) {
272 jsbytecode* targetPC = target.toRawBytecode();
273 PendingEdgesMap::AddPtr p = pendingEdges_.lookupForAdd(targetPC);
274 if (p) {
275 return p->value().append(edge);
276 }
277
278 PendingEdges edges;
279 static_assert(PendingEdges::InlineLength >= 1,
280 "Appending one element should be infallible");
281 MOZ_ALWAYS_TRUE(edges.append(edge));
282
283 return pendingEdges_.add(p, targetPC, std::move(edges));
284 }
285
build()286 bool WarpBuilder::build() {
287 if (!buildPrologue()) {
288 return false;
289 }
290
291 if (!buildBody()) {
292 return false;
293 }
294
295 if (!MPhi::markIteratorPhis(*iterators())) {
296 return false;
297 }
298
299 MOZ_ASSERT_IF(info().osrPc(), graph().osrBlock());
300 MOZ_ASSERT(loopStack_.empty());
301 MOZ_ASSERT(loopDepth() == 0);
302
303 return true;
304 }
305
buildInline()306 bool WarpBuilder::buildInline() {
307 if (!buildInlinePrologue()) {
308 return false;
309 }
310
311 if (!buildBody()) {
312 return false;
313 }
314
315 MOZ_ASSERT(loopStack_.empty());
316 return true;
317 }
318
buildNamedLambdaEnv(MDefinition * callee,MDefinition * env,NamedLambdaObject * templateObj)319 MInstruction* WarpBuilder::buildNamedLambdaEnv(MDefinition* callee,
320 MDefinition* env,
321 NamedLambdaObject* templateObj) {
322 MOZ_ASSERT(!templateObj->hasDynamicSlots());
323
324 MInstruction* namedLambda = MNewNamedLambdaObject::New(alloc(), templateObj);
325 current->add(namedLambda);
326
327 // Initialize the object's reserved slots. No post barrier is needed here:
328 // the object will be allocated in the nursery if possible, and if the
329 // tenured heap is used instead, a minor collection will have been performed
330 // that moved env/callee to the tenured heap.
331 size_t enclosingSlot = NamedLambdaObject::enclosingEnvironmentSlot();
332 size_t lambdaSlot = NamedLambdaObject::lambdaSlot();
333 current->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda,
334 enclosingSlot, env));
335 current->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda, lambdaSlot,
336 callee));
337
338 return namedLambda;
339 }
340
buildCallObject(MDefinition * callee,MDefinition * env,CallObject * templateObj)341 MInstruction* WarpBuilder::buildCallObject(MDefinition* callee,
342 MDefinition* env,
343 CallObject* templateObj) {
344 MConstant* templateCst = constant(ObjectValue(*templateObj));
345
346 MNewCallObject* callObj = MNewCallObject::New(alloc(), templateCst);
347 current->add(callObj);
348
349 // Initialize the object's reserved slots. No post barrier is needed here,
350 // for the same reason as in buildNamedLambdaEnv.
351 size_t enclosingSlot = CallObject::enclosingEnvironmentSlot();
352 size_t calleeSlot = CallObject::calleeSlot();
353 current->add(
354 MStoreFixedSlot::NewUnbarriered(alloc(), callObj, enclosingSlot, env));
355 current->add(
356 MStoreFixedSlot::NewUnbarriered(alloc(), callObj, calleeSlot, callee));
357
358 // Copy closed-over argument slots if there aren't parameter expressions.
359 MSlots* slots = nullptr;
360 for (PositionalFormalParameterIter fi(script_); fi; fi++) {
361 if (!fi.closedOver()) {
362 continue;
363 }
364
365 if (!alloc().ensureBallast()) {
366 return nullptr;
367 }
368
369 uint32_t slot = fi.location().slot();
370 uint32_t formal = fi.argumentSlot();
371 uint32_t numFixedSlots = templateObj->numFixedSlots();
372 MDefinition* param;
373 if (script_->functionHasParameterExprs()) {
374 param = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
375 } else {
376 param = current->getSlot(info().argSlotUnchecked(formal));
377 }
378
379 if (slot >= numFixedSlots) {
380 if (!slots) {
381 slots = MSlots::New(alloc(), callObj);
382 current->add(slots);
383 }
384 uint32_t dynamicSlot = slot - numFixedSlots;
385 current->add(MStoreDynamicSlot::NewUnbarriered(alloc(), slots,
386 dynamicSlot, param));
387 } else {
388 current->add(
389 MStoreFixedSlot::NewUnbarriered(alloc(), callObj, slot, param));
390 }
391 }
392
393 return callObj;
394 }
395
buildEnvironmentChain()396 bool WarpBuilder::buildEnvironmentChain() {
397 const WarpEnvironment& env = scriptSnapshot()->environment();
398
399 if (env.is<NoEnvironment>()) {
400 return true;
401 }
402
403 MInstruction* envDef = env.match(
404 [](const NoEnvironment&) -> MInstruction* {
405 MOZ_CRASH("Already handled");
406 },
407 [this](JSObject* obj) -> MInstruction* {
408 return constant(ObjectValue(*obj));
409 },
410 [this](const FunctionEnvironment& env) -> MInstruction* {
411 MDefinition* callee = getCallee();
412 MInstruction* envDef = MFunctionEnvironment::New(alloc(), callee);
413 current->add(envDef);
414 if (NamedLambdaObject* obj = env.namedLambdaTemplate) {
415 envDef = buildNamedLambdaEnv(callee, envDef, obj);
416 }
417 if (CallObject* obj = env.callObjectTemplate) {
418 envDef = buildCallObject(callee, envDef, obj);
419 if (!envDef) {
420 return nullptr;
421 }
422 }
423 return envDef;
424 });
425 if (!envDef) {
426 return false;
427 }
428
429 // Update the environment slot from UndefinedValue only after the initial
430 // environment is created so that bailout doesn't see a partial environment.
431 // See: |BaselineStackBuilder::buildBaselineFrame|
432 current->setEnvironmentChain(envDef);
433 return true;
434 }
435
buildPrologue()436 bool WarpBuilder::buildPrologue() {
437 BytecodeLocation startLoc(script_, script_->code());
438 if (!startNewEntryBlock(info().firstStackSlot(), startLoc)) {
439 return false;
440 }
441
442 if (info().funMaybeLazy()) {
443 // Initialize |this|.
444 MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT);
445 current->add(param);
446 current->initSlot(info().thisSlot(), param);
447
448 // Initialize arguments.
449 for (uint32_t i = 0; i < info().nargs(); i++) {
450 MParameter* param = MParameter::New(alloc().fallible(), i);
451 if (!param) {
452 return false;
453 }
454 current->add(param);
455 current->initSlot(info().argSlotUnchecked(i), param);
456 }
457 }
458
459 MConstant* undef = constant(UndefinedValue());
460
461 // Initialize local slots.
462 for (uint32_t i = 0; i < info().nlocals(); i++) {
463 current->initSlot(info().localSlot(i), undef);
464 }
465
466 // Initialize the environment chain, return value, and arguments object slots.
467 current->initSlot(info().environmentChainSlot(), undef);
468 current->initSlot(info().returnValueSlot(), undef);
469 if (info().needsArgsObj()) {
470 current->initSlot(info().argsObjSlot(), undef);
471 }
472
473 current->add(MStart::New(alloc()));
474
475 // Guard against over-recursion.
476 MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
477 current->add(check);
478
479 if (!buildEnvironmentChain()) {
480 return false;
481 }
482
483 #ifdef JS_CACHEIR_SPEW
484 if (snapshot().needsFinalWarmUpCount()) {
485 MIncrementWarmUpCounter* ins =
486 MIncrementWarmUpCounter::New(alloc(), script_);
487 current->add(ins);
488 }
489 #endif
490
491 return true;
492 }
493
buildInlinePrologue()494 bool WarpBuilder::buildInlinePrologue() {
495 // Generate entry block.
496 BytecodeLocation startLoc(script_, script_->code());
497 if (!startNewEntryBlock(info().firstStackSlot(), startLoc)) {
498 return false;
499 }
500 current->setCallerResumePoint(callerResumePoint());
501
502 // Connect the entry block to the last block in the caller's graph.
503 MBasicBlock* pred = callerBuilder()->current;
504 MOZ_ASSERT(pred == callerResumePoint()->block());
505
506 pred->end(MGoto::New(alloc(), current));
507 if (!current->addPredecessorWithoutPhis(pred)) {
508 return false;
509 }
510
511 MConstant* undef = constant(UndefinedValue());
512
513 // Initialize env chain slot to Undefined. It's set later by
514 // |buildEnvironmentChain|.
515 current->initSlot(info().environmentChainSlot(), undef);
516
517 // Initialize |return value| slot.
518 current->initSlot(info().returnValueSlot(), undef);
519
520 // Initialize |arguments| slot if needed.
521 if (info().needsArgsObj()) {
522 current->initSlot(info().argsObjSlot(), undef);
523 }
524
525 // Initialize |this| slot.
526 current->initSlot(info().thisSlot(), inlineCallInfo()->thisArg());
527
528 uint32_t callerArgs = inlineCallInfo()->argc();
529 uint32_t actualArgs = info().nargs();
530 uint32_t passedArgs = std::min<uint32_t>(callerArgs, actualArgs);
531
532 // Initialize actually set arguments.
533 for (uint32_t i = 0; i < passedArgs; i++) {
534 MDefinition* arg = inlineCallInfo()->getArg(i);
535 current->initSlot(info().argSlotUnchecked(i), arg);
536 }
537
538 // Pass undefined for missing arguments.
539 for (uint32_t i = passedArgs; i < actualArgs; i++) {
540 current->initSlot(info().argSlotUnchecked(i), undef);
541 }
542
543 // Initialize local slots.
544 for (uint32_t i = 0; i < info().nlocals(); i++) {
545 current->initSlot(info().localSlot(i), undef);
546 }
547
548 MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
549
550 if (!buildEnvironmentChain()) {
551 return false;
552 }
553
554 return true;
555 }
556
557 #ifdef DEBUG
558 // In debug builds, after compiling a bytecode op, this class is used to check
559 // that all values popped by this opcode either:
560 //
561 // (1) Have the ImplicitlyUsed flag set on them.
562 // (2) Have more uses than before compiling this op (the value is
563 // used as operand of a new MIR instruction).
564 //
565 // This is used to catch problems where WarpBuilder pops a value without
566 // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
567 class MOZ_RAII WarpPoppedValueUseChecker {
568 Vector<MDefinition*, 4, SystemAllocPolicy> popped_;
569 Vector<size_t, 4, SystemAllocPolicy> poppedUses_;
570 MBasicBlock* current_;
571 BytecodeLocation loc_;
572
573 public:
WarpPoppedValueUseChecker(MBasicBlock * current,BytecodeLocation loc)574 WarpPoppedValueUseChecker(MBasicBlock* current, BytecodeLocation loc)
575 : current_(current), loc_(loc) {}
576
init()577 [[nodiscard]] bool init() {
578 // Don't require SSA uses for values popped by these ops.
579 switch (loc_.getOp()) {
580 case JSOp::Pop:
581 case JSOp::PopN:
582 case JSOp::DupAt:
583 case JSOp::Dup:
584 case JSOp::Dup2:
585 case JSOp::Pick:
586 case JSOp::Unpick:
587 case JSOp::Swap:
588 case JSOp::SetArg:
589 case JSOp::SetLocal:
590 case JSOp::InitLexical:
591 case JSOp::SetRval:
592 case JSOp::Void:
593 // Basic stack/local/argument management opcodes.
594 return true;
595
596 case JSOp::Case:
597 case JSOp::Default:
598 // These ops have to pop the switch value when branching but don't
599 // actually use it.
600 return true;
601
602 default:
603 break;
604 }
605
606 unsigned nuses = loc_.useCount();
607
608 for (unsigned i = 0; i < nuses; i++) {
609 MDefinition* def = current_->peek(-int32_t(i + 1));
610 if (!popped_.append(def) || !poppedUses_.append(def->defUseCount())) {
611 return false;
612 }
613 }
614
615 return true;
616 }
617
checkAfterOp()618 void checkAfterOp() {
619 for (size_t i = 0; i < popped_.length(); i++) {
620 // First value popped by JSOp::EndIter is not used at all, it's similar
621 // to JSOp::Pop above.
622 if (loc_.is(JSOp::EndIter) && i == 0) {
623 continue;
624 }
625 MOZ_ASSERT(popped_[i]->isImplicitlyUsed() ||
626 popped_[i]->defUseCount() > poppedUses_[i]);
627 }
628 }
629 };
630 #endif
631
buildBody()632 bool WarpBuilder::buildBody() {
633 for (BytecodeLocation loc : AllBytecodesIterable(script_)) {
634 if (mirGen().shouldCancel("WarpBuilder (opcode loop)")) {
635 return false;
636 }
637
638 // Skip unreachable ops (for example code after a 'return' or 'throw') until
639 // we get to the next jump target.
640 if (hasTerminatedBlock()) {
641 // Finish any "broken" loops with an unreachable backedge. For example:
642 //
643 // do {
644 // ...
645 // return;
646 // ...
647 // } while (x);
648 //
649 // This loop never actually loops.
650 if (loc.isBackedge() && !loopStack_.empty()) {
651 BytecodeLocation loopHead(script_, loopStack_.back().header()->pc());
652 if (loc.isBackedgeForLoophead(loopHead)) {
653 decLoopDepth();
654 loopStack_.popBack();
655 }
656 }
657 if (!loc.isJumpTarget()) {
658 continue;
659 }
660 }
661
662 if (!alloc().ensureBallast()) {
663 return false;
664 }
665
666 #ifdef DEBUG
667 WarpPoppedValueUseChecker useChecker(current, loc);
668 if (!useChecker.init()) {
669 return false;
670 }
671 #endif
672
673 JSOp op = loc.getOp();
674
675 #define BUILD_OP(OP, ...) \
676 case JSOp::OP: \
677 if (MOZ_UNLIKELY(!this->build_##OP(loc))) { \
678 return false; \
679 } \
680 break;
681 switch (op) { FOR_EACH_OPCODE(BUILD_OP) }
682 #undef BUILD_OP
683
684 #ifdef DEBUG
685 useChecker.checkAfterOp();
686 #endif
687 }
688
689 return true;
690 }
691
692 #define DEF_OP(OP) \
693 bool WarpBuilder::build_##OP(BytecodeLocation) { \
694 MOZ_CRASH("Unsupported op"); \
695 }
WARP_UNSUPPORTED_OPCODE_LIST(DEF_OP)696 WARP_UNSUPPORTED_OPCODE_LIST(DEF_OP)
697 #undef DEF_OP
698
699 bool WarpBuilder::build_Nop(BytecodeLocation) { return true; }
700
build_NopDestructuring(BytecodeLocation)701 bool WarpBuilder::build_NopDestructuring(BytecodeLocation) { return true; }
702
build_TryDestructuring(BytecodeLocation)703 bool WarpBuilder::build_TryDestructuring(BytecodeLocation) {
704 // Set the hasTryBlock flag to turn off optimizations that eliminate dead
705 // resume points operands because the exception handler code for
706 // TryNoteKind::Destructuring is effectively a (specialized) catch-block.
707 graph().setHasTryBlock();
708 return true;
709 }
710
build_Lineno(BytecodeLocation)711 bool WarpBuilder::build_Lineno(BytecodeLocation) { return true; }
712
build_DebugLeaveLexicalEnv(BytecodeLocation)713 bool WarpBuilder::build_DebugLeaveLexicalEnv(BytecodeLocation) { return true; }
714
build_Undefined(BytecodeLocation)715 bool WarpBuilder::build_Undefined(BytecodeLocation) {
716 pushConstant(UndefinedValue());
717 return true;
718 }
719
build_Void(BytecodeLocation)720 bool WarpBuilder::build_Void(BytecodeLocation) {
721 current->pop();
722 pushConstant(UndefinedValue());
723 return true;
724 }
725
build_Null(BytecodeLocation)726 bool WarpBuilder::build_Null(BytecodeLocation) {
727 pushConstant(NullValue());
728 return true;
729 }
730
build_Hole(BytecodeLocation)731 bool WarpBuilder::build_Hole(BytecodeLocation) {
732 pushConstant(MagicValue(JS_ELEMENTS_HOLE));
733 return true;
734 }
735
build_Uninitialized(BytecodeLocation)736 bool WarpBuilder::build_Uninitialized(BytecodeLocation) {
737 pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL));
738 return true;
739 }
740
build_IsConstructing(BytecodeLocation)741 bool WarpBuilder::build_IsConstructing(BytecodeLocation) {
742 pushConstant(MagicValue(JS_IS_CONSTRUCTING));
743 return true;
744 }
745
build_False(BytecodeLocation)746 bool WarpBuilder::build_False(BytecodeLocation) {
747 pushConstant(BooleanValue(false));
748 return true;
749 }
750
build_True(BytecodeLocation)751 bool WarpBuilder::build_True(BytecodeLocation) {
752 pushConstant(BooleanValue(true));
753 return true;
754 }
755
build_Pop(BytecodeLocation)756 bool WarpBuilder::build_Pop(BytecodeLocation) {
757 current->pop();
758 return true;
759 }
760
build_PopN(BytecodeLocation loc)761 bool WarpBuilder::build_PopN(BytecodeLocation loc) {
762 for (uint32_t i = 0, n = loc.getPopCount(); i < n; i++) {
763 current->pop();
764 }
765 return true;
766 }
767
build_Dup(BytecodeLocation)768 bool WarpBuilder::build_Dup(BytecodeLocation) {
769 current->pushSlot(current->stackDepth() - 1);
770 return true;
771 }
772
build_Dup2(BytecodeLocation)773 bool WarpBuilder::build_Dup2(BytecodeLocation) {
774 uint32_t lhsSlot = current->stackDepth() - 2;
775 uint32_t rhsSlot = current->stackDepth() - 1;
776 current->pushSlot(lhsSlot);
777 current->pushSlot(rhsSlot);
778 return true;
779 }
780
build_DupAt(BytecodeLocation loc)781 bool WarpBuilder::build_DupAt(BytecodeLocation loc) {
782 current->pushSlot(current->stackDepth() - 1 - loc.getDupAtIndex());
783 return true;
784 }
785
build_Swap(BytecodeLocation)786 bool WarpBuilder::build_Swap(BytecodeLocation) {
787 current->swapAt(-1);
788 return true;
789 }
790
build_Pick(BytecodeLocation loc)791 bool WarpBuilder::build_Pick(BytecodeLocation loc) {
792 int32_t depth = -int32_t(loc.getPickDepth());
793 current->pick(depth);
794 return true;
795 }
796
build_Unpick(BytecodeLocation loc)797 bool WarpBuilder::build_Unpick(BytecodeLocation loc) {
798 int32_t depth = -int32_t(loc.getUnpickDepth());
799 current->unpick(depth);
800 return true;
801 }
802
build_Zero(BytecodeLocation)803 bool WarpBuilder::build_Zero(BytecodeLocation) {
804 pushConstant(Int32Value(0));
805 return true;
806 }
807
build_One(BytecodeLocation)808 bool WarpBuilder::build_One(BytecodeLocation) {
809 pushConstant(Int32Value(1));
810 return true;
811 }
812
build_Int8(BytecodeLocation loc)813 bool WarpBuilder::build_Int8(BytecodeLocation loc) {
814 pushConstant(Int32Value(loc.getInt8()));
815 return true;
816 }
817
build_Uint16(BytecodeLocation loc)818 bool WarpBuilder::build_Uint16(BytecodeLocation loc) {
819 pushConstant(Int32Value(loc.getUint16()));
820 return true;
821 }
822
build_Uint24(BytecodeLocation loc)823 bool WarpBuilder::build_Uint24(BytecodeLocation loc) {
824 pushConstant(Int32Value(loc.getUint24()));
825 return true;
826 }
827
build_Int32(BytecodeLocation loc)828 bool WarpBuilder::build_Int32(BytecodeLocation loc) {
829 pushConstant(Int32Value(loc.getInt32()));
830 return true;
831 }
832
build_Double(BytecodeLocation loc)833 bool WarpBuilder::build_Double(BytecodeLocation loc) {
834 pushConstant(loc.getInlineValue());
835 return true;
836 }
837
build_ResumeIndex(BytecodeLocation loc)838 bool WarpBuilder::build_ResumeIndex(BytecodeLocation loc) {
839 pushConstant(Int32Value(loc.getResumeIndex()));
840 return true;
841 }
842
build_BigInt(BytecodeLocation loc)843 bool WarpBuilder::build_BigInt(BytecodeLocation loc) {
844 BigInt* bi = loc.getBigInt(script_);
845 pushConstant(BigIntValue(bi));
846 return true;
847 }
848
build_String(BytecodeLocation loc)849 bool WarpBuilder::build_String(BytecodeLocation loc) {
850 JSAtom* atom = loc.getAtom(script_);
851 pushConstant(StringValue(atom));
852 return true;
853 }
854
build_Symbol(BytecodeLocation loc)855 bool WarpBuilder::build_Symbol(BytecodeLocation loc) {
856 uint32_t which = loc.getSymbolIndex();
857 JS::Symbol* sym = mirGen().runtime->wellKnownSymbols().get(which);
858 pushConstant(SymbolValue(sym));
859 return true;
860 }
861
build_RegExp(BytecodeLocation loc)862 bool WarpBuilder::build_RegExp(BytecodeLocation loc) {
863 RegExpObject* reObj = loc.getRegExp(script_);
864
865 auto* snapshot = getOpSnapshot<WarpRegExp>(loc);
866
867 MRegExp* regexp = MRegExp::New(alloc(), reObj, snapshot->hasShared());
868 current->add(regexp);
869 current->push(regexp);
870
871 return true;
872 }
873
build_Return(BytecodeLocation)874 bool WarpBuilder::build_Return(BytecodeLocation) {
875 MDefinition* def = current->pop();
876
877 MReturn* ret = MReturn::New(alloc(), def);
878 current->end(ret);
879
880 if (!graph().addReturn(current)) {
881 return false;
882 }
883
884 setTerminatedBlock();
885 return true;
886 }
887
build_RetRval(BytecodeLocation)888 bool WarpBuilder::build_RetRval(BytecodeLocation) {
889 MDefinition* rval;
890 if (script_->noScriptRval()) {
891 rval = constant(UndefinedValue());
892 } else {
893 rval = current->getSlot(info().returnValueSlot());
894 }
895
896 MReturn* ret = MReturn::New(alloc(), rval);
897 current->end(ret);
898
899 if (!graph().addReturn(current)) {
900 return false;
901 }
902
903 setTerminatedBlock();
904 return true;
905 }
906
build_SetRval(BytecodeLocation)907 bool WarpBuilder::build_SetRval(BytecodeLocation) {
908 MOZ_ASSERT(!script_->noScriptRval());
909
910 MDefinition* rval = current->pop();
911 current->setSlot(info().returnValueSlot(), rval);
912 return true;
913 }
914
build_GetRval(BytecodeLocation)915 bool WarpBuilder::build_GetRval(BytecodeLocation) {
916 MOZ_ASSERT(!script_->noScriptRval());
917 MDefinition* rval = current->getSlot(info().returnValueSlot());
918 current->push(rval);
919 return true;
920 }
921
build_GetLocal(BytecodeLocation loc)922 bool WarpBuilder::build_GetLocal(BytecodeLocation loc) {
923 current->pushLocal(loc.local());
924 return true;
925 }
926
build_SetLocal(BytecodeLocation loc)927 bool WarpBuilder::build_SetLocal(BytecodeLocation loc) {
928 current->setLocal(loc.local());
929 return true;
930 }
931
build_InitLexical(BytecodeLocation loc)932 bool WarpBuilder::build_InitLexical(BytecodeLocation loc) {
933 current->setLocal(loc.local());
934 return true;
935 }
936
build_GetArg(BytecodeLocation loc)937 bool WarpBuilder::build_GetArg(BytecodeLocation loc) {
938 uint32_t arg = loc.arg();
939 if (info().argsObjAliasesFormals()) {
940 MDefinition* argsObj = current->argumentsObject();
941 auto* getArg = MGetArgumentsObjectArg::New(alloc(), argsObj, arg);
942 current->add(getArg);
943 current->push(getArg);
944 } else {
945 current->pushArg(arg);
946 }
947 return true;
948 }
949
build_SetArg(BytecodeLocation loc)950 bool WarpBuilder::build_SetArg(BytecodeLocation loc) {
951 MOZ_ASSERT(script_->jitScript()->modifiesArguments());
952
953 uint32_t arg = loc.arg();
954 MDefinition* val = current->peek(-1);
955
956 if (!info().argsObjAliasesFormals()) {
957 // Either |arguments| is never referenced within this function, or
958 // it doesn't map to the actual arguments values. Either way, we
959 // don't need to worry about synchronizing the argument values
960 // when writing to them.
961 current->setArg(arg);
962 return true;
963 }
964
965 // If an arguments object is in use, and it aliases formals, then all SetArgs
966 // must go through the arguments object.
967 MDefinition* argsObj = current->argumentsObject();
968 current->add(MPostWriteBarrier::New(alloc(), argsObj, val));
969 auto* ins = MSetArgumentsObjectArg::New(alloc(), argsObj, val, arg);
970 current->add(ins);
971 return resumeAfter(ins, loc);
972 }
973
build_ToNumeric(BytecodeLocation loc)974 bool WarpBuilder::build_ToNumeric(BytecodeLocation loc) {
975 return buildUnaryOp(loc);
976 }
977
buildUnaryOp(BytecodeLocation loc)978 bool WarpBuilder::buildUnaryOp(BytecodeLocation loc) {
979 MDefinition* value = current->pop();
980 return buildIC(loc, CacheKind::UnaryArith, {value});
981 }
982
build_Inc(BytecodeLocation loc)983 bool WarpBuilder::build_Inc(BytecodeLocation loc) { return buildUnaryOp(loc); }
984
build_Dec(BytecodeLocation loc)985 bool WarpBuilder::build_Dec(BytecodeLocation loc) { return buildUnaryOp(loc); }
986
build_Pos(BytecodeLocation loc)987 bool WarpBuilder::build_Pos(BytecodeLocation loc) { return buildUnaryOp(loc); }
988
build_Neg(BytecodeLocation loc)989 bool WarpBuilder::build_Neg(BytecodeLocation loc) { return buildUnaryOp(loc); }
990
build_BitNot(BytecodeLocation loc)991 bool WarpBuilder::build_BitNot(BytecodeLocation loc) {
992 return buildUnaryOp(loc);
993 }
994
buildBinaryOp(BytecodeLocation loc)995 bool WarpBuilder::buildBinaryOp(BytecodeLocation loc) {
996 MDefinition* right = current->pop();
997 MDefinition* left = current->pop();
998 return buildIC(loc, CacheKind::BinaryArith, {left, right});
999 }
1000
build_Add(BytecodeLocation loc)1001 bool WarpBuilder::build_Add(BytecodeLocation loc) { return buildBinaryOp(loc); }
1002
build_Sub(BytecodeLocation loc)1003 bool WarpBuilder::build_Sub(BytecodeLocation loc) { return buildBinaryOp(loc); }
1004
build_Mul(BytecodeLocation loc)1005 bool WarpBuilder::build_Mul(BytecodeLocation loc) { return buildBinaryOp(loc); }
1006
build_Div(BytecodeLocation loc)1007 bool WarpBuilder::build_Div(BytecodeLocation loc) { return buildBinaryOp(loc); }
1008
build_Mod(BytecodeLocation loc)1009 bool WarpBuilder::build_Mod(BytecodeLocation loc) { return buildBinaryOp(loc); }
1010
build_Pow(BytecodeLocation loc)1011 bool WarpBuilder::build_Pow(BytecodeLocation loc) { return buildBinaryOp(loc); }
1012
build_BitAnd(BytecodeLocation loc)1013 bool WarpBuilder::build_BitAnd(BytecodeLocation loc) {
1014 return buildBinaryOp(loc);
1015 }
1016
build_BitOr(BytecodeLocation loc)1017 bool WarpBuilder::build_BitOr(BytecodeLocation loc) {
1018 return buildBinaryOp(loc);
1019 }
1020
build_BitXor(BytecodeLocation loc)1021 bool WarpBuilder::build_BitXor(BytecodeLocation loc) {
1022 return buildBinaryOp(loc);
1023 }
1024
build_Lsh(BytecodeLocation loc)1025 bool WarpBuilder::build_Lsh(BytecodeLocation loc) { return buildBinaryOp(loc); }
1026
build_Rsh(BytecodeLocation loc)1027 bool WarpBuilder::build_Rsh(BytecodeLocation loc) { return buildBinaryOp(loc); }
1028
build_Ursh(BytecodeLocation loc)1029 bool WarpBuilder::build_Ursh(BytecodeLocation loc) {
1030 return buildBinaryOp(loc);
1031 }
1032
buildCompareOp(BytecodeLocation loc)1033 bool WarpBuilder::buildCompareOp(BytecodeLocation loc) {
1034 MDefinition* right = current->pop();
1035 MDefinition* left = current->pop();
1036 return buildIC(loc, CacheKind::Compare, {left, right});
1037 }
1038
build_Eq(BytecodeLocation loc)1039 bool WarpBuilder::build_Eq(BytecodeLocation loc) { return buildCompareOp(loc); }
1040
build_Ne(BytecodeLocation loc)1041 bool WarpBuilder::build_Ne(BytecodeLocation loc) { return buildCompareOp(loc); }
1042
build_Lt(BytecodeLocation loc)1043 bool WarpBuilder::build_Lt(BytecodeLocation loc) { return buildCompareOp(loc); }
1044
build_Le(BytecodeLocation loc)1045 bool WarpBuilder::build_Le(BytecodeLocation loc) { return buildCompareOp(loc); }
1046
build_Gt(BytecodeLocation loc)1047 bool WarpBuilder::build_Gt(BytecodeLocation loc) { return buildCompareOp(loc); }
1048
build_Ge(BytecodeLocation loc)1049 bool WarpBuilder::build_Ge(BytecodeLocation loc) { return buildCompareOp(loc); }
1050
build_StrictEq(BytecodeLocation loc)1051 bool WarpBuilder::build_StrictEq(BytecodeLocation loc) {
1052 return buildCompareOp(loc);
1053 }
1054
build_StrictNe(BytecodeLocation loc)1055 bool WarpBuilder::build_StrictNe(BytecodeLocation loc) {
1056 return buildCompareOp(loc);
1057 }
1058
1059 // Returns true iff the MTest added for |op| has a true-target corresponding
1060 // with the join point in the bytecode.
TestTrueTargetIsJoinPoint(JSOp op)1061 static bool TestTrueTargetIsJoinPoint(JSOp op) {
1062 switch (op) {
1063 case JSOp::JumpIfTrue:
1064 case JSOp::Or:
1065 case JSOp::Case:
1066 return true;
1067
1068 case JSOp::JumpIfFalse:
1069 case JSOp::And:
1070 case JSOp::Coalesce:
1071 return false;
1072
1073 default:
1074 MOZ_CRASH("Unexpected op");
1075 }
1076 }
1077
build_JumpTarget(BytecodeLocation loc)1078 bool WarpBuilder::build_JumpTarget(BytecodeLocation loc) {
1079 PendingEdgesMap::Ptr p = pendingEdges_.lookup(loc.toRawBytecode());
1080 if (!p) {
1081 // No (reachable) jumps so this is just a no-op.
1082 return true;
1083 }
1084
1085 PendingEdges edges(std::move(p->value()));
1086 pendingEdges_.remove(p);
1087
1088 MOZ_ASSERT(!edges.empty());
1089
1090 MBasicBlock* joinBlock = nullptr;
1091
1092 // Create join block if there's fall-through from the previous bytecode op.
1093 if (!hasTerminatedBlock()) {
1094 MBasicBlock* pred = current;
1095 if (!startNewBlock(pred, loc)) {
1096 return false;
1097 }
1098 pred->end(MGoto::New(alloc(), current));
1099 joinBlock = current;
1100 setTerminatedBlock();
1101 }
1102
1103 auto addEdge = [&](MBasicBlock* pred, size_t numToPop) -> bool {
1104 if (joinBlock) {
1105 MOZ_ASSERT(pred->stackDepth() - numToPop == joinBlock->stackDepth());
1106 return joinBlock->addPredecessorPopN(alloc(), pred, numToPop);
1107 }
1108 if (!startNewBlock(pred, loc, numToPop)) {
1109 return false;
1110 }
1111 joinBlock = current;
1112 setTerminatedBlock();
1113 return true;
1114 };
1115
1116 // When a block is terminated with an MTest instruction we can end up with the
1117 // following triangle structure:
1118 //
1119 // testBlock
1120 // / |
1121 // block |
1122 // \ |
1123 // joinBlock
1124 //
1125 // Although this is fine for correctness, the FoldTests pass is unable to
1126 // optimize this pattern. This matters for short-circuit operations
1127 // (JSOp::And, JSOp::Coalesce, etc).
1128 //
1129 // To fix these issues, we create an empty block to get a diamond structure:
1130 //
1131 // testBlock
1132 // / |
1133 // block emptyBlock
1134 // \ |
1135 // joinBlock
1136 //
1137 // TODO(post-Warp): re-evaluate this. It would probably be better to fix
1138 // FoldTests to support the triangle pattern so that we can remove this.
1139 // IonBuilder had other concerns that don't apply to WarpBuilder.
1140 auto createEmptyBlockForTest = [&](MBasicBlock* pred, size_t successor,
1141 size_t numToPop) -> MBasicBlock* {
1142 MOZ_ASSERT(joinBlock);
1143
1144 if (!startNewBlock(pred, loc, numToPop)) {
1145 return nullptr;
1146 }
1147
1148 MBasicBlock* emptyBlock = current;
1149 MOZ_ASSERT(emptyBlock->stackDepth() == joinBlock->stackDepth());
1150
1151 MTest* test = pred->lastIns()->toTest();
1152 test->initSuccessor(successor, emptyBlock);
1153
1154 emptyBlock->end(MGoto::New(alloc(), joinBlock));
1155 setTerminatedBlock();
1156
1157 return emptyBlock;
1158 };
1159
1160 for (const PendingEdge& edge : edges) {
1161 MBasicBlock* source = edge.block();
1162 MControlInstruction* lastIns = source->lastIns();
1163 switch (edge.kind()) {
1164 case PendingEdge::Kind::TestTrue: {
1165 // JSOp::Case must pop the value when branching to the true-target.
1166 // If we create an empty block, we have to pop the value there instead
1167 // of as part of the emptyBlock -> joinBlock edge so stack depths match
1168 // the current depth.
1169 const size_t numToPop = (edge.testOp() == JSOp::Case) ? 1 : 0;
1170
1171 const size_t successor = 0; // true-branch
1172 if (joinBlock && TestTrueTargetIsJoinPoint(edge.testOp())) {
1173 MBasicBlock* pred =
1174 createEmptyBlockForTest(source, successor, numToPop);
1175 if (!pred || !addEdge(pred, /* numToPop = */ 0)) {
1176 return false;
1177 }
1178 } else {
1179 if (!addEdge(source, numToPop)) {
1180 return false;
1181 }
1182 lastIns->toTest()->initSuccessor(successor, joinBlock);
1183 }
1184 continue;
1185 }
1186
1187 case PendingEdge::Kind::TestFalse: {
1188 const size_t numToPop = 0;
1189 const size_t successor = 1; // false-branch
1190 if (joinBlock && !TestTrueTargetIsJoinPoint(edge.testOp())) {
1191 MBasicBlock* pred =
1192 createEmptyBlockForTest(source, successor, numToPop);
1193 if (!pred || !addEdge(pred, /* numToPop = */ 0)) {
1194 return false;
1195 }
1196 } else {
1197 if (!addEdge(source, numToPop)) {
1198 return false;
1199 }
1200 lastIns->toTest()->initSuccessor(successor, joinBlock);
1201 }
1202 continue;
1203 }
1204
1205 case PendingEdge::Kind::Goto:
1206 if (!addEdge(source, /* numToPop = */ 0)) {
1207 return false;
1208 }
1209 lastIns->toGoto()->initSuccessor(0, joinBlock);
1210 continue;
1211 }
1212 MOZ_CRASH("Invalid kind");
1213 }
1214
1215 // Start traversing the join block. Make sure it comes after predecessor
1216 // blocks created by createEmptyBlockForTest.
1217 MOZ_ASSERT(hasTerminatedBlock());
1218 MOZ_ASSERT(joinBlock);
1219 graph().moveBlockToEnd(joinBlock);
1220 current = joinBlock;
1221
1222 return true;
1223 }
1224
addIteratorLoopPhis(BytecodeLocation loopHead)1225 bool WarpBuilder::addIteratorLoopPhis(BytecodeLocation loopHead) {
1226 // When unwinding the stack for a thrown exception, the exception handler must
1227 // close live iterators. For ForIn and Destructuring loops, the exception
1228 // handler needs access to values on the stack. To prevent them from being
1229 // optimized away (and replaced with the JS_OPTIMIZED_OUT MagicValue), we need
1230 // to mark the phis (and phis they flow into) as having implicit uses.
1231 // See ProcessTryNotes in vm/Interpreter.cpp and CloseLiveIteratorIon in
1232 // jit/JitFrames.cpp
1233
1234 MOZ_ASSERT(current->stackDepth() >= info().firstStackSlot());
1235
1236 bool emptyStack = current->stackDepth() == info().firstStackSlot();
1237 if (emptyStack) {
1238 return true;
1239 }
1240
1241 jsbytecode* loopHeadPC = loopHead.toRawBytecode();
1242
1243 for (TryNoteIterAllNoGC tni(script_, loopHeadPC); !tni.done(); ++tni) {
1244 const TryNote& tn = **tni;
1245
1246 // Stop if we reach an outer loop because outer loops were already
1247 // processed when we visited their loop headers.
1248 if (tn.isLoop()) {
1249 BytecodeLocation tnStart = script_->offsetToLocation(tn.start);
1250 if (tnStart != loopHead) {
1251 MOZ_ASSERT(tnStart.is(JSOp::LoopHead));
1252 MOZ_ASSERT(tnStart < loopHead);
1253 return true;
1254 }
1255 }
1256
1257 switch (tn.kind()) {
1258 case TryNoteKind::Destructuring:
1259 case TryNoteKind::ForIn: {
1260 // For for-in loops we add the iterator object to iterators(). For
1261 // destructuring loops we add the "done" value that's on top of the
1262 // stack and used in the exception handler.
1263 MOZ_ASSERT(tn.stackDepth >= 1);
1264 uint32_t slot = info().stackSlot(tn.stackDepth - 1);
1265 MPhi* phi = current->getSlot(slot)->toPhi();
1266 if (!iterators()->append(phi)) {
1267 return false;
1268 }
1269 break;
1270 }
1271 case TryNoteKind::Loop:
1272 case TryNoteKind::ForOf:
1273 // Regular loops do not have iterators to close. ForOf loops handle
1274 // unwinding using catch blocks.
1275 break;
1276 default:
1277 break;
1278 }
1279 }
1280
1281 return true;
1282 }
1283
build_LoopHead(BytecodeLocation loc)1284 bool WarpBuilder::build_LoopHead(BytecodeLocation loc) {
1285 // All loops have the following bytecode structure:
1286 //
1287 // LoopHead
1288 // ...
1289 // JumpIfTrue/Goto to LoopHead
1290
1291 if (hasTerminatedBlock()) {
1292 // The whole loop is unreachable.
1293 return true;
1294 }
1295
1296 // Handle OSR from Baseline JIT code.
1297 if (loc.toRawBytecode() == info().osrPc()) {
1298 if (!startNewOsrPreHeaderBlock(loc)) {
1299 return false;
1300 }
1301 }
1302
1303 incLoopDepth();
1304
1305 MBasicBlock* pred = current;
1306 if (!startNewLoopHeaderBlock(loc)) {
1307 return false;
1308 }
1309
1310 pred->end(MGoto::New(alloc(), current));
1311
1312 if (!addIteratorLoopPhis(loc)) {
1313 return false;
1314 }
1315
1316 MInterruptCheck* check = MInterruptCheck::New(alloc());
1317 current->add(check);
1318
1319 #ifdef JS_CACHEIR_SPEW
1320 if (snapshot().needsFinalWarmUpCount()) {
1321 MIncrementWarmUpCounter* ins =
1322 MIncrementWarmUpCounter::New(alloc(), script_);
1323 current->add(ins);
1324 }
1325 #endif
1326
1327 return true;
1328 }
1329
buildTestOp(BytecodeLocation loc)1330 bool WarpBuilder::buildTestOp(BytecodeLocation loc) {
1331 MDefinition* originalValue = current->peek(-1);
1332
1333 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) {
1334 // If we have CacheIR, we can use it to refine the input. Note that
1335 // the transpiler doesn't generate any control instructions. Instead,
1336 // we fall through and generate them below.
1337 MDefinition* value = current->pop();
1338 if (!TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {value})) {
1339 return false;
1340 }
1341 }
1342
1343 if (loc.isBackedge()) {
1344 return buildTestBackedge(loc);
1345 }
1346
1347 JSOp op = loc.getOp();
1348 BytecodeLocation target1 = loc.next();
1349 BytecodeLocation target2 = loc.getJumpTarget();
1350
1351 if (TestTrueTargetIsJoinPoint(op)) {
1352 std::swap(target1, target2);
1353 }
1354
1355 MDefinition* value = current->pop();
1356
1357 // JSOp::And and JSOp::Or leave the top stack value unchanged. The
1358 // top stack value may have been converted to bool by a transpiled
1359 // ToBool IC, so we push the original value. Also note that
1360 // JSOp::Case must pop a second value on the true-branch (the input
1361 // to the switch-statement). This conditional pop happens in
1362 // build_JumpTarget.
1363 bool mustKeepCondition = (op == JSOp::And || op == JSOp::Or);
1364 if (mustKeepCondition) {
1365 current->push(originalValue);
1366 }
1367
1368 // If this op always branches to the same location we treat this as a
1369 // JSOp::Goto.
1370 if (target1 == target2) {
1371 value->setImplicitlyUsedUnchecked();
1372 return buildForwardGoto(target1);
1373 }
1374
1375 MTest* test = MTest::New(alloc(), value, /* ifTrue = */ nullptr,
1376 /* ifFalse = */ nullptr);
1377 current->end(test);
1378
1379 if (!addPendingEdge(PendingEdge::NewTestTrue(current, op), target1)) {
1380 return false;
1381 }
1382 if (!addPendingEdge(PendingEdge::NewTestFalse(current, op), target2)) {
1383 return false;
1384 }
1385
1386 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) {
1387 test->setObservedTypes(typesSnapshot->list());
1388 }
1389
1390 setTerminatedBlock();
1391 return true;
1392 }
1393
buildTestBackedge(BytecodeLocation loc)1394 bool WarpBuilder::buildTestBackedge(BytecodeLocation loc) {
1395 JSOp op = loc.getOp();
1396 MOZ_ASSERT(op == JSOp::JumpIfTrue);
1397 MOZ_ASSERT(loopDepth() > 0);
1398
1399 MDefinition* value = current->pop();
1400
1401 BytecodeLocation loopHead = loc.getJumpTarget();
1402 MOZ_ASSERT(loopHead.is(JSOp::LoopHead));
1403
1404 BytecodeLocation successor = loc.next();
1405
1406 // We can finish the loop now. Use the loophead pc instead of the current pc
1407 // because the stack depth at the start of that op matches the current stack
1408 // depth (after popping our operand).
1409 MBasicBlock* pred = current;
1410 if (!startNewBlock(current, loopHead)) {
1411 return false;
1412 }
1413
1414 MTest* test = MTest::New(alloc(), value, /* ifTrue = */ current,
1415 /* ifFalse = */ nullptr);
1416 pred->end(test);
1417
1418 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) {
1419 test->setObservedTypes(typesSnapshot->list());
1420 }
1421
1422 if (!addPendingEdge(PendingEdge::NewTestFalse(pred, op), successor)) {
1423 return false;
1424 }
1425
1426 return buildBackedge();
1427 }
1428
build_JumpIfFalse(BytecodeLocation loc)1429 bool WarpBuilder::build_JumpIfFalse(BytecodeLocation loc) {
1430 return buildTestOp(loc);
1431 }
1432
build_JumpIfTrue(BytecodeLocation loc)1433 bool WarpBuilder::build_JumpIfTrue(BytecodeLocation loc) {
1434 return buildTestOp(loc);
1435 }
1436
build_And(BytecodeLocation loc)1437 bool WarpBuilder::build_And(BytecodeLocation loc) { return buildTestOp(loc); }
1438
build_Or(BytecodeLocation loc)1439 bool WarpBuilder::build_Or(BytecodeLocation loc) { return buildTestOp(loc); }
1440
build_Case(BytecodeLocation loc)1441 bool WarpBuilder::build_Case(BytecodeLocation loc) { return buildTestOp(loc); }
1442
build_Default(BytecodeLocation loc)1443 bool WarpBuilder::build_Default(BytecodeLocation loc) {
1444 current->pop();
1445 return buildForwardGoto(loc.getJumpTarget());
1446 }
1447
build_Coalesce(BytecodeLocation loc)1448 bool WarpBuilder::build_Coalesce(BytecodeLocation loc) {
1449 BytecodeLocation target1 = loc.next();
1450 BytecodeLocation target2 = loc.getJumpTarget();
1451 MOZ_ASSERT(target2 > target1);
1452
1453 MDefinition* value = current->peek(-1);
1454
1455 MInstruction* isNullOrUndefined = MIsNullOrUndefined::New(alloc(), value);
1456 current->add(isNullOrUndefined);
1457
1458 current->end(MTest::New(alloc(), isNullOrUndefined, /* ifTrue = */ nullptr,
1459 /* ifFalse = */ nullptr));
1460
1461 if (!addPendingEdge(PendingEdge::NewTestTrue(current, JSOp::Coalesce),
1462 target1)) {
1463 return false;
1464 }
1465 if (!addPendingEdge(PendingEdge::NewTestFalse(current, JSOp::Coalesce),
1466 target2)) {
1467 return false;
1468 }
1469
1470 setTerminatedBlock();
1471 return true;
1472 }
1473
buildBackedge()1474 bool WarpBuilder::buildBackedge() {
1475 decLoopDepth();
1476
1477 MBasicBlock* header = loopStack_.popCopy().header();
1478 current->end(MGoto::New(alloc(), header));
1479
1480 if (!header->setBackedge(current)) {
1481 return false;
1482 }
1483
1484 setTerminatedBlock();
1485 return true;
1486 }
1487
buildForwardGoto(BytecodeLocation target)1488 bool WarpBuilder::buildForwardGoto(BytecodeLocation target) {
1489 current->end(MGoto::New(alloc(), nullptr));
1490
1491 if (!addPendingEdge(PendingEdge::NewGoto(current), target)) {
1492 return false;
1493 }
1494
1495 setTerminatedBlock();
1496 return true;
1497 }
1498
build_Goto(BytecodeLocation loc)1499 bool WarpBuilder::build_Goto(BytecodeLocation loc) {
1500 if (loc.isBackedge()) {
1501 return buildBackedge();
1502 }
1503
1504 return buildForwardGoto(loc.getJumpTarget());
1505 }
1506
build_DebugCheckSelfHosted(BytecodeLocation loc)1507 bool WarpBuilder::build_DebugCheckSelfHosted(BytecodeLocation loc) {
1508 #ifdef DEBUG
1509 MDefinition* val = current->pop();
1510 MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), val);
1511 current->add(check);
1512 current->push(check);
1513 if (!resumeAfter(check, loc)) {
1514 return false;
1515 }
1516 #endif
1517 return true;
1518 }
1519
build_DynamicImport(BytecodeLocation loc)1520 bool WarpBuilder::build_DynamicImport(BytecodeLocation loc) {
1521 MDefinition* specifier = current->pop();
1522 MDynamicImport* ins = MDynamicImport::New(alloc(), specifier);
1523 current->add(ins);
1524 current->push(ins);
1525 return resumeAfter(ins, loc);
1526 }
1527
build_Not(BytecodeLocation loc)1528 bool WarpBuilder::build_Not(BytecodeLocation loc) {
1529 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) {
1530 // If we have CacheIR, we can use it to refine the input before
1531 // emitting the MNot.
1532 MDefinition* value = current->pop();
1533 if (!TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {value})) {
1534 return false;
1535 }
1536 }
1537
1538 MDefinition* value = current->pop();
1539 MNot* ins = MNot::New(alloc(), value);
1540 current->add(ins);
1541 current->push(ins);
1542
1543 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) {
1544 ins->setObservedTypes(typesSnapshot->list());
1545 }
1546
1547 return true;
1548 }
1549
build_ToString(BytecodeLocation loc)1550 bool WarpBuilder::build_ToString(BytecodeLocation loc) {
1551 MDefinition* value = current->pop();
1552
1553 if (value->type() == MIRType::String) {
1554 value->setImplicitlyUsedUnchecked();
1555 current->push(value);
1556 return true;
1557 }
1558
1559 MToString* ins =
1560 MToString::New(alloc(), value, MToString::SideEffectHandling::Supported);
1561 current->add(ins);
1562 current->push(ins);
1563 if (ins->isEffectful()) {
1564 return resumeAfter(ins, loc);
1565 }
1566 return true;
1567 }
1568
usesEnvironmentChain() const1569 bool WarpBuilder::usesEnvironmentChain() const {
1570 return script_->jitScript()->usesEnvironmentChain();
1571 }
1572
build_GlobalOrEvalDeclInstantiation(BytecodeLocation loc)1573 bool WarpBuilder::build_GlobalOrEvalDeclInstantiation(BytecodeLocation loc) {
1574 MOZ_ASSERT(!script_->isForEval(), "Eval scripts not supported");
1575 auto* redeclCheck = MGlobalDeclInstantiation::New(alloc());
1576 current->add(redeclCheck);
1577 return resumeAfter(redeclCheck, loc);
1578 }
1579
build_BindVar(BytecodeLocation)1580 bool WarpBuilder::build_BindVar(BytecodeLocation) {
1581 MOZ_ASSERT(usesEnvironmentChain());
1582
1583 MDefinition* env = current->environmentChain();
1584 MCallBindVar* ins = MCallBindVar::New(alloc(), env);
1585 current->add(ins);
1586 current->push(ins);
1587 return true;
1588 }
1589
build_MutateProto(BytecodeLocation loc)1590 bool WarpBuilder::build_MutateProto(BytecodeLocation loc) {
1591 MDefinition* value = current->pop();
1592 MDefinition* obj = current->peek(-1);
1593 MMutateProto* mutate = MMutateProto::New(alloc(), obj, value);
1594 current->add(mutate);
1595 return resumeAfter(mutate, loc);
1596 }
1597
getCallee()1598 MDefinition* WarpBuilder::getCallee() {
1599 if (inlineCallInfo()) {
1600 return inlineCallInfo()->callee();
1601 }
1602
1603 MInstruction* callee = MCallee::New(alloc());
1604 current->add(callee);
1605 return callee;
1606 }
1607
build_Callee(BytecodeLocation)1608 bool WarpBuilder::build_Callee(BytecodeLocation) {
1609 MDefinition* callee = getCallee();
1610 current->push(callee);
1611 return true;
1612 }
1613
build_ToAsyncIter(BytecodeLocation loc)1614 bool WarpBuilder::build_ToAsyncIter(BytecodeLocation loc) {
1615 MDefinition* nextMethod = current->pop();
1616 MDefinition* iterator = current->pop();
1617 MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod);
1618 current->add(ins);
1619 current->push(ins);
1620 return resumeAfter(ins, loc);
1621 }
1622
build_ToPropertyKey(BytecodeLocation loc)1623 bool WarpBuilder::build_ToPropertyKey(BytecodeLocation loc) {
1624 MDefinition* value = current->pop();
1625 return buildIC(loc, CacheKind::ToPropertyKey, {value});
1626 }
1627
build_Typeof(BytecodeLocation loc)1628 bool WarpBuilder::build_Typeof(BytecodeLocation loc) {
1629 MDefinition* input = current->pop();
1630
1631 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) {
1632 auto* ins = MTypeOf::New(alloc(), input);
1633 ins->setObservedTypes(typesSnapshot->list());
1634 current->add(ins);
1635 current->push(ins);
1636 return true;
1637 }
1638
1639 return buildIC(loc, CacheKind::TypeOf, {input});
1640 }
1641
build_TypeofExpr(BytecodeLocation loc)1642 bool WarpBuilder::build_TypeofExpr(BytecodeLocation loc) {
1643 return build_Typeof(loc);
1644 }
1645
build_Arguments(BytecodeLocation loc)1646 bool WarpBuilder::build_Arguments(BytecodeLocation loc) {
1647 auto* snapshot = getOpSnapshot<WarpArguments>(loc);
1648 MOZ_ASSERT(info().needsArgsObj());
1649 MOZ_ASSERT(snapshot);
1650
1651 ArgumentsObject* templateObj = snapshot->templateObj();
1652 MDefinition* env = current->environmentChain();
1653
1654 MInstruction* argsObj;
1655 if (inlineCallInfo()) {
1656 argsObj = MCreateInlinedArgumentsObject::New(alloc(), env, getCallee(),
1657 inlineCallInfo()->argv());
1658 } else {
1659 argsObj = MCreateArgumentsObject::New(alloc(), env, templateObj);
1660 }
1661 current->add(argsObj);
1662 current->setArgumentsObject(argsObj);
1663 current->push(argsObj);
1664
1665 return true;
1666 }
1667
build_ObjWithProto(BytecodeLocation loc)1668 bool WarpBuilder::build_ObjWithProto(BytecodeLocation loc) {
1669 MDefinition* proto = current->pop();
1670 MInstruction* ins = MObjectWithProto::New(alloc(), proto);
1671 current->add(ins);
1672 current->push(ins);
1673 return resumeAfter(ins, loc);
1674 }
1675
walkEnvironmentChain(uint32_t numHops)1676 MDefinition* WarpBuilder::walkEnvironmentChain(uint32_t numHops) {
1677 MDefinition* env = current->environmentChain();
1678
1679 for (uint32_t i = 0; i < numHops; i++) {
1680 if (!alloc().ensureBallast()) {
1681 return nullptr;
1682 }
1683
1684 MInstruction* ins = MEnclosingEnvironment::New(alloc(), env);
1685 current->add(ins);
1686 env = ins;
1687 }
1688
1689 return env;
1690 }
1691
build_GetAliasedVar(BytecodeLocation loc)1692 bool WarpBuilder::build_GetAliasedVar(BytecodeLocation loc) {
1693 EnvironmentCoordinate ec = loc.getEnvironmentCoordinate();
1694 MDefinition* obj = walkEnvironmentChain(ec.hops());
1695 if (!obj) {
1696 return false;
1697 }
1698
1699 MInstruction* load;
1700 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) {
1701 load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
1702 } else {
1703 MInstruction* slots = MSlots::New(alloc(), obj);
1704 current->add(slots);
1705
1706 uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec);
1707 load = MLoadDynamicSlot::New(alloc(), slots, slot);
1708 }
1709
1710 current->add(load);
1711 current->push(load);
1712 return true;
1713 }
1714
build_SetAliasedVar(BytecodeLocation loc)1715 bool WarpBuilder::build_SetAliasedVar(BytecodeLocation loc) {
1716 EnvironmentCoordinate ec = loc.getEnvironmentCoordinate();
1717 MDefinition* val = current->peek(-1);
1718 MDefinition* obj = walkEnvironmentChain(ec.hops());
1719 if (!obj) {
1720 return false;
1721 }
1722
1723 current->add(MPostWriteBarrier::New(alloc(), obj, val));
1724
1725 MInstruction* store;
1726 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) {
1727 store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), val);
1728 } else {
1729 MInstruction* slots = MSlots::New(alloc(), obj);
1730 current->add(slots);
1731
1732 uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec);
1733 store = MStoreDynamicSlot::NewBarriered(alloc(), slots, slot, val);
1734 }
1735
1736 current->add(store);
1737 return resumeAfter(store, loc);
1738 }
1739
build_InitAliasedLexical(BytecodeLocation loc)1740 bool WarpBuilder::build_InitAliasedLexical(BytecodeLocation loc) {
1741 return build_SetAliasedVar(loc);
1742 }
1743
build_EnvCallee(BytecodeLocation loc)1744 bool WarpBuilder::build_EnvCallee(BytecodeLocation loc) {
1745 uint32_t numHops = loc.getEnvCalleeNumHops();
1746 MDefinition* env = walkEnvironmentChain(numHops);
1747 if (!env) {
1748 return false;
1749 }
1750
1751 auto* callee = MLoadFixedSlot::New(alloc(), env, CallObject::calleeSlot());
1752 current->add(callee);
1753 current->push(callee);
1754 return true;
1755 }
1756
build_Iter(BytecodeLocation loc)1757 bool WarpBuilder::build_Iter(BytecodeLocation loc) {
1758 MDefinition* obj = current->pop();
1759 return buildIC(loc, CacheKind::GetIterator, {obj});
1760 }
1761
build_MoreIter(BytecodeLocation loc)1762 bool WarpBuilder::build_MoreIter(BytecodeLocation loc) {
1763 MDefinition* iter = current->peek(-1);
1764 MInstruction* ins = MIteratorMore::New(alloc(), iter);
1765 current->add(ins);
1766 current->push(ins);
1767 return resumeAfter(ins, loc);
1768 }
1769
build_EndIter(BytecodeLocation loc)1770 bool WarpBuilder::build_EndIter(BytecodeLocation loc) {
1771 current->pop(); // Iterator value is not used.
1772 MDefinition* iter = current->pop();
1773 MInstruction* ins = MIteratorEnd::New(alloc(), iter);
1774 current->add(ins);
1775 return resumeAfter(ins, loc);
1776 }
1777
build_IsNoIter(BytecodeLocation)1778 bool WarpBuilder::build_IsNoIter(BytecodeLocation) {
1779 MDefinition* def = current->peek(-1);
1780 MOZ_ASSERT(def->isIteratorMore());
1781 MInstruction* ins = MIsNoIter::New(alloc(), def);
1782 current->add(ins);
1783 current->push(ins);
1784 return true;
1785 }
1786
transpileCall(BytecodeLocation loc,const WarpCacheIR * cacheIRSnapshot,CallInfo * callInfo)1787 bool WarpBuilder::transpileCall(BytecodeLocation loc,
1788 const WarpCacheIR* cacheIRSnapshot,
1789 CallInfo* callInfo) {
1790 // Synthesize the constant number of arguments for this call op.
1791 auto* argc = MConstant::New(alloc(), Int32Value(callInfo->argc()));
1792 current->add(argc);
1793
1794 return TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {argc}, callInfo);
1795 }
1796
buildCallOp(BytecodeLocation loc)1797 bool WarpBuilder::buildCallOp(BytecodeLocation loc) {
1798 uint32_t argc = loc.getCallArgc();
1799 JSOp op = loc.getOp();
1800 bool constructing = IsConstructOp(op);
1801 bool ignoresReturnValue = (op == JSOp::CallIgnoresRv || loc.resultIsPopped());
1802
1803 CallInfo callInfo(alloc(), constructing, ignoresReturnValue);
1804 if (!callInfo.init(current, argc)) {
1805 return false;
1806 }
1807
1808 if (const auto* inliningSnapshot = getOpSnapshot<WarpInlinedCall>(loc)) {
1809 // Transpile the CacheIR to generate the correct guards before
1810 // inlining. In this case, CacheOp::CallInlinedFunction updates
1811 // the CallInfo, but does not generate a call.
1812 callInfo.markAsInlined();
1813 if (!transpileCall(loc, inliningSnapshot->cacheIRSnapshot(), &callInfo)) {
1814 return false;
1815 }
1816
1817 // Generate the body of the inlined function.
1818 return buildInlinedCall(loc, inliningSnapshot, callInfo);
1819 }
1820
1821 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) {
1822 return transpileCall(loc, cacheIRSnapshot, &callInfo);
1823 }
1824
1825 if (getOpSnapshot<WarpBailout>(loc)) {
1826 callInfo.setImplicitlyUsedUnchecked();
1827 return buildBailoutForColdIC(loc, CacheKind::Call);
1828 }
1829
1830 bool needsThisCheck = false;
1831 if (callInfo.constructing()) {
1832 // Inline the this-object allocation on the caller-side.
1833 MDefinition* callee = callInfo.callee();
1834 MDefinition* newTarget = callInfo.getNewTarget();
1835 MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
1836 current->add(createThis);
1837 callInfo.thisArg()->setImplicitlyUsedUnchecked();
1838 callInfo.setThis(createThis);
1839 needsThisCheck = true;
1840 }
1841
1842 MCall* call = makeCall(callInfo, needsThisCheck);
1843 if (!call) {
1844 return false;
1845 }
1846
1847 current->add(call);
1848 current->push(call);
1849 return resumeAfter(call, loc);
1850 }
1851
build_Call(BytecodeLocation loc)1852 bool WarpBuilder::build_Call(BytecodeLocation loc) { return buildCallOp(loc); }
1853
build_CallIgnoresRv(BytecodeLocation loc)1854 bool WarpBuilder::build_CallIgnoresRv(BytecodeLocation loc) {
1855 return buildCallOp(loc);
1856 }
1857
build_CallIter(BytecodeLocation loc)1858 bool WarpBuilder::build_CallIter(BytecodeLocation loc) {
1859 return buildCallOp(loc);
1860 }
1861
build_FunCall(BytecodeLocation loc)1862 bool WarpBuilder::build_FunCall(BytecodeLocation loc) {
1863 return buildCallOp(loc);
1864 }
1865
build_FunApply(BytecodeLocation loc)1866 bool WarpBuilder::build_FunApply(BytecodeLocation loc) {
1867 return buildCallOp(loc);
1868 }
1869
build_New(BytecodeLocation loc)1870 bool WarpBuilder::build_New(BytecodeLocation loc) { return buildCallOp(loc); }
1871
build_SuperCall(BytecodeLocation loc)1872 bool WarpBuilder::build_SuperCall(BytecodeLocation loc) {
1873 return buildCallOp(loc);
1874 }
1875
build_FunctionThis(BytecodeLocation loc)1876 bool WarpBuilder::build_FunctionThis(BytecodeLocation loc) {
1877 MOZ_ASSERT(info().funMaybeLazy());
1878
1879 if (script_->strict()) {
1880 // No need to wrap primitive |this| in strict mode.
1881 current->pushSlot(info().thisSlot());
1882 return true;
1883 }
1884
1885 MOZ_ASSERT(!script_->hasNonSyntacticScope(),
1886 "WarpOracle should have aborted compilation");
1887
1888 MDefinition* def = current->getSlot(info().thisSlot());
1889 JSObject* globalThis = snapshot().globalLexicalEnvThis();
1890
1891 auto* thisObj = MBoxNonStrictThis::New(alloc(), def, globalThis);
1892 current->add(thisObj);
1893 current->push(thisObj);
1894
1895 return true;
1896 }
1897
build_GlobalThis(BytecodeLocation loc)1898 bool WarpBuilder::build_GlobalThis(BytecodeLocation loc) {
1899 MOZ_ASSERT(!script_->hasNonSyntacticScope(),
1900 "WarpOracle should have aborted compilation");
1901 JSObject* obj = snapshot().globalLexicalEnvThis();
1902 pushConstant(ObjectValue(*obj));
1903 return true;
1904 }
1905
globalLexicalEnvConstant()1906 MConstant* WarpBuilder::globalLexicalEnvConstant() {
1907 JSObject* globalLexical = snapshot().globalLexicalEnv();
1908 return constant(ObjectValue(*globalLexical));
1909 }
1910
build_GetName(BytecodeLocation loc)1911 bool WarpBuilder::build_GetName(BytecodeLocation loc) {
1912 MDefinition* env = current->environmentChain();
1913 return buildIC(loc, CacheKind::GetName, {env});
1914 }
1915
build_GetGName(BytecodeLocation loc)1916 bool WarpBuilder::build_GetGName(BytecodeLocation loc) {
1917 if (script_->hasNonSyntacticScope()) {
1918 return build_GetName(loc);
1919 }
1920
1921 // Try to optimize undefined/NaN/Infinity.
1922 PropertyName* name = loc.getPropertyName(script_);
1923 const JSAtomState& names = mirGen().runtime->names();
1924
1925 if (name == names.undefined) {
1926 pushConstant(UndefinedValue());
1927 return true;
1928 }
1929 if (name == names.NaN) {
1930 pushConstant(JS::NaNValue());
1931 return true;
1932 }
1933 if (name == names.Infinity) {
1934 pushConstant(JS::InfinityValue());
1935 return true;
1936 }
1937
1938 MDefinition* env = globalLexicalEnvConstant();
1939 return buildIC(loc, CacheKind::GetName, {env});
1940 }
1941
build_BindName(BytecodeLocation loc)1942 bool WarpBuilder::build_BindName(BytecodeLocation loc) {
1943 MDefinition* env = current->environmentChain();
1944 return buildIC(loc, CacheKind::BindName, {env});
1945 }
1946
build_BindGName(BytecodeLocation loc)1947 bool WarpBuilder::build_BindGName(BytecodeLocation loc) {
1948 if (const auto* snapshot = getOpSnapshot<WarpBindGName>(loc)) {
1949 MOZ_ASSERT(!script_->hasNonSyntacticScope());
1950 JSObject* globalEnv = snapshot->globalEnv();
1951 pushConstant(ObjectValue(*globalEnv));
1952 return true;
1953 }
1954
1955 if (script_->hasNonSyntacticScope()) {
1956 return build_BindName(loc);
1957 }
1958
1959 MDefinition* env = globalLexicalEnvConstant();
1960 return buildIC(loc, CacheKind::BindName, {env});
1961 }
1962
build_GetProp(BytecodeLocation loc)1963 bool WarpBuilder::build_GetProp(BytecodeLocation loc) {
1964 MDefinition* val = current->pop();
1965 return buildIC(loc, CacheKind::GetProp, {val});
1966 }
1967
build_GetElem(BytecodeLocation loc)1968 bool WarpBuilder::build_GetElem(BytecodeLocation loc) {
1969 MDefinition* id = current->pop();
1970 MDefinition* val = current->pop();
1971 return buildIC(loc, CacheKind::GetElem, {val, id});
1972 }
1973
build_SetProp(BytecodeLocation loc)1974 bool WarpBuilder::build_SetProp(BytecodeLocation loc) {
1975 MDefinition* val = current->pop();
1976 MDefinition* obj = current->pop();
1977 current->push(val);
1978 return buildIC(loc, CacheKind::SetProp, {obj, val});
1979 }
1980
build_StrictSetProp(BytecodeLocation loc)1981 bool WarpBuilder::build_StrictSetProp(BytecodeLocation loc) {
1982 return build_SetProp(loc);
1983 }
1984
build_SetName(BytecodeLocation loc)1985 bool WarpBuilder::build_SetName(BytecodeLocation loc) {
1986 return build_SetProp(loc);
1987 }
1988
build_StrictSetName(BytecodeLocation loc)1989 bool WarpBuilder::build_StrictSetName(BytecodeLocation loc) {
1990 return build_SetProp(loc);
1991 }
1992
build_SetGName(BytecodeLocation loc)1993 bool WarpBuilder::build_SetGName(BytecodeLocation loc) {
1994 return build_SetProp(loc);
1995 }
1996
build_StrictSetGName(BytecodeLocation loc)1997 bool WarpBuilder::build_StrictSetGName(BytecodeLocation loc) {
1998 return build_SetProp(loc);
1999 }
2000
build_InitGLexical(BytecodeLocation loc)2001 bool WarpBuilder::build_InitGLexical(BytecodeLocation loc) {
2002 MOZ_ASSERT(!script_->hasNonSyntacticScope());
2003
2004 MDefinition* globalLexical = globalLexicalEnvConstant();
2005 MDefinition* val = current->peek(-1);
2006
2007 return buildIC(loc, CacheKind::SetProp, {globalLexical, val});
2008 }
2009
build_SetElem(BytecodeLocation loc)2010 bool WarpBuilder::build_SetElem(BytecodeLocation loc) {
2011 MDefinition* val = current->pop();
2012 MDefinition* id = current->pop();
2013 MDefinition* obj = current->pop();
2014 current->push(val);
2015 return buildIC(loc, CacheKind::SetElem, {obj, id, val});
2016 }
2017
build_StrictSetElem(BytecodeLocation loc)2018 bool WarpBuilder::build_StrictSetElem(BytecodeLocation loc) {
2019 return build_SetElem(loc);
2020 }
2021
build_DelProp(BytecodeLocation loc)2022 bool WarpBuilder::build_DelProp(BytecodeLocation loc) {
2023 PropertyName* name = loc.getPropertyName(script_);
2024 MDefinition* obj = current->pop();
2025 bool strict = loc.getOp() == JSOp::StrictDelProp;
2026
2027 MInstruction* ins = MDeleteProperty::New(alloc(), obj, name, strict);
2028 current->add(ins);
2029 current->push(ins);
2030 return resumeAfter(ins, loc);
2031 }
2032
build_StrictDelProp(BytecodeLocation loc)2033 bool WarpBuilder::build_StrictDelProp(BytecodeLocation loc) {
2034 return build_DelProp(loc);
2035 }
2036
build_DelElem(BytecodeLocation loc)2037 bool WarpBuilder::build_DelElem(BytecodeLocation loc) {
2038 MDefinition* id = current->pop();
2039 MDefinition* obj = current->pop();
2040 bool strict = loc.getOp() == JSOp::StrictDelElem;
2041
2042 MInstruction* ins = MDeleteElement::New(alloc(), obj, id, strict);
2043 current->add(ins);
2044 current->push(ins);
2045 return resumeAfter(ins, loc);
2046 }
2047
build_StrictDelElem(BytecodeLocation loc)2048 bool WarpBuilder::build_StrictDelElem(BytecodeLocation loc) {
2049 return build_DelElem(loc);
2050 }
2051
build_SetFunName(BytecodeLocation loc)2052 bool WarpBuilder::build_SetFunName(BytecodeLocation loc) {
2053 FunctionPrefixKind prefixKind = loc.getFunctionPrefixKind();
2054 MDefinition* name = current->pop();
2055 MDefinition* fun = current->pop();
2056
2057 MSetFunName* ins = MSetFunName::New(alloc(), fun, name, uint8_t(prefixKind));
2058 current->add(ins);
2059 current->push(fun);
2060 return resumeAfter(ins, loc);
2061 }
2062
build_PushLexicalEnv(BytecodeLocation loc)2063 bool WarpBuilder::build_PushLexicalEnv(BytecodeLocation loc) {
2064 MOZ_ASSERT(usesEnvironmentChain());
2065
2066 LexicalScope* scope = &loc.getScope(script_)->as<LexicalScope>();
2067 MDefinition* env = current->environmentChain();
2068
2069 auto* ins = MNewLexicalEnvironmentObject::New(alloc(), env, scope);
2070 current->add(ins);
2071 current->setEnvironmentChain(ins);
2072 return true;
2073 }
2074
build_PushClassBodyEnv(BytecodeLocation loc)2075 bool WarpBuilder::build_PushClassBodyEnv(BytecodeLocation loc) {
2076 MOZ_ASSERT(usesEnvironmentChain());
2077
2078 ClassBodyScope* scope = &loc.getScope(script_)->as<ClassBodyScope>();
2079 MDefinition* env = current->environmentChain();
2080
2081 auto* ins = MNewClassBodyEnvironmentObject::New(alloc(), env, scope);
2082 current->add(ins);
2083 current->setEnvironmentChain(ins);
2084 return true;
2085 }
2086
build_PopLexicalEnv(BytecodeLocation)2087 bool WarpBuilder::build_PopLexicalEnv(BytecodeLocation) {
2088 MDefinition* enclosingEnv = walkEnvironmentChain(1);
2089 if (!enclosingEnv) {
2090 return false;
2091 }
2092 current->setEnvironmentChain(enclosingEnv);
2093 return true;
2094 }
2095
buildCopyLexicalEnvOp(bool copySlots)2096 void WarpBuilder::buildCopyLexicalEnvOp(bool copySlots) {
2097 MOZ_ASSERT(usesEnvironmentChain());
2098
2099 MDefinition* env = current->environmentChain();
2100 auto* ins = MCopyLexicalEnvironmentObject::New(alloc(), env, copySlots);
2101 current->add(ins);
2102 current->setEnvironmentChain(ins);
2103 }
2104
build_FreshenLexicalEnv(BytecodeLocation)2105 bool WarpBuilder::build_FreshenLexicalEnv(BytecodeLocation) {
2106 buildCopyLexicalEnvOp(/* copySlots = */ true);
2107 return true;
2108 }
2109
build_RecreateLexicalEnv(BytecodeLocation)2110 bool WarpBuilder::build_RecreateLexicalEnv(BytecodeLocation) {
2111 buildCopyLexicalEnvOp(/* copySlots = */ false);
2112 return true;
2113 }
2114
build_ImplicitThis(BytecodeLocation loc)2115 bool WarpBuilder::build_ImplicitThis(BytecodeLocation loc) {
2116 MOZ_ASSERT(usesEnvironmentChain());
2117
2118 PropertyName* name = loc.getPropertyName(script_);
2119 MDefinition* env = current->environmentChain();
2120
2121 auto* ins = MImplicitThis::New(alloc(), env, name);
2122 current->add(ins);
2123 current->push(ins);
2124 return resumeAfter(ins, loc);
2125 }
2126
build_GImplicitThis(BytecodeLocation loc)2127 bool WarpBuilder::build_GImplicitThis(BytecodeLocation loc) {
2128 if (script_->hasNonSyntacticScope()) {
2129 return build_ImplicitThis(loc);
2130 }
2131 return build_Undefined(loc);
2132 }
2133
build_CheckClassHeritage(BytecodeLocation loc)2134 bool WarpBuilder::build_CheckClassHeritage(BytecodeLocation loc) {
2135 MDefinition* def = current->pop();
2136 auto* ins = MCheckClassHeritage::New(alloc(), def);
2137 current->add(ins);
2138 current->push(ins);
2139 return resumeAfter(ins, loc);
2140 }
2141
build_CheckThis(BytecodeLocation)2142 bool WarpBuilder::build_CheckThis(BytecodeLocation) {
2143 MDefinition* def = current->pop();
2144 auto* ins = MCheckThis::New(alloc(), def);
2145 current->add(ins);
2146 current->push(ins);
2147 return true;
2148 }
2149
build_CheckThisReinit(BytecodeLocation)2150 bool WarpBuilder::build_CheckThisReinit(BytecodeLocation) {
2151 MDefinition* def = current->pop();
2152 auto* ins = MCheckThisReinit::New(alloc(), def);
2153 current->add(ins);
2154 current->push(ins);
2155 return true;
2156 }
2157
build_Generator(BytecodeLocation loc)2158 bool WarpBuilder::build_Generator(BytecodeLocation loc) {
2159 MDefinition* callee = getCallee();
2160 MDefinition* environmentChain = current->environmentChain();
2161 MDefinition* argsObj = info().needsArgsObj() ? current->argumentsObject()
2162 : constant(Int32Value(0));
2163
2164 MGenerator* generator =
2165 MGenerator::New(alloc(), callee, environmentChain, argsObj);
2166
2167 current->add(generator);
2168 current->push(generator);
2169 return resumeAfter(generator, loc);
2170 }
2171
build_AfterYield(BytecodeLocation loc)2172 bool WarpBuilder::build_AfterYield(BytecodeLocation loc) {
2173 // Unreachable blocks don't need to generate a bail.
2174 if (hasTerminatedBlock()) {
2175 return true;
2176 }
2177
2178 // This comes after a yield, which we generate as a return,
2179 // so we know this should be unreachable code.
2180 //
2181 // We emit an unreachable bail for this, which will assert if we
2182 // ever execute this.
2183 //
2184 // An Unreachable bail, instead of MUnreachable, because MUnreachable
2185 // is a control instruction, and injecting it in the middle of a block
2186 // causes various graph state assertions to fail.
2187 MBail* bail = MBail::New(alloc(), BailoutKind::Unreachable);
2188 current->add(bail);
2189
2190 return true;
2191 }
2192
build_FinalYieldRval(BytecodeLocation loc)2193 bool WarpBuilder::build_FinalYieldRval(BytecodeLocation loc) {
2194 MDefinition* gen = current->pop();
2195
2196 auto setSlotNull = [this, gen](size_t slot) {
2197 auto* ins = MStoreFixedSlot::NewBarriered(alloc(), gen, slot,
2198 constant(NullValue()));
2199 current->add(ins);
2200 };
2201
2202 // Close the generator
2203 setSlotNull(AbstractGeneratorObject::calleeSlot());
2204 setSlotNull(AbstractGeneratorObject::envChainSlot());
2205 setSlotNull(AbstractGeneratorObject::argsObjectSlot());
2206 setSlotNull(AbstractGeneratorObject::stackStorageSlot());
2207 setSlotNull(AbstractGeneratorObject::resumeIndexSlot());
2208
2209 // Return
2210 return build_RetRval(loc);
2211 }
2212
build_AsyncResolve(BytecodeLocation loc)2213 bool WarpBuilder::build_AsyncResolve(BytecodeLocation loc) {
2214 MDefinition* generator = current->pop();
2215 MDefinition* valueOrReason = current->pop();
2216 auto resolveKind = loc.getAsyncFunctionResolveKind();
2217
2218 MAsyncResolve* resolve =
2219 MAsyncResolve::New(alloc(), generator, valueOrReason, resolveKind);
2220 current->add(resolve);
2221 current->push(resolve);
2222 return resumeAfter(resolve, loc);
2223 }
2224
build_ResumeKind(BytecodeLocation loc)2225 bool WarpBuilder::build_ResumeKind(BytecodeLocation loc) {
2226 GeneratorResumeKind resumeKind = loc.resumeKind();
2227
2228 current->push(constant(Int32Value(static_cast<int32_t>(resumeKind))));
2229 return true;
2230 }
2231
build_CheckResumeKind(BytecodeLocation loc)2232 bool WarpBuilder::build_CheckResumeKind(BytecodeLocation loc) {
2233 // Outside of `yield*`, this is normally unreachable code in Warp,
2234 // so we just manipulate the stack appropriately to ensure correct
2235 // MIR generation.
2236 //
2237 // However, `yield*` emits a forced generator return which can be
2238 // warp compiled, so in order to correctly handle these semantics
2239 // we also generate a bailout, so that the forced generator return
2240 // runs in baseline.
2241 MDefinition* resumeKind = current->pop();
2242 MDefinition* gen = current->pop();
2243 MDefinition* rval = current->peek(-1);
2244
2245 // Mark operands as implicitly used.
2246 resumeKind->setImplicitlyUsedUnchecked();
2247 gen->setImplicitlyUsedUnchecked();
2248 rval->setImplicitlyUsedUnchecked();
2249
2250 // Bail out if we encounter CheckResumeKind.
2251 MBail* bail = MBail::New(alloc(), BailoutKind::Inevitable);
2252 current->add(bail);
2253 current->setAlwaysBails();
2254
2255 return true;
2256 }
2257
build_CanSkipAwait(BytecodeLocation loc)2258 bool WarpBuilder::build_CanSkipAwait(BytecodeLocation loc) {
2259 MDefinition* val = current->pop();
2260
2261 MCanSkipAwait* canSkip = MCanSkipAwait::New(alloc(), val);
2262 current->add(canSkip);
2263
2264 current->push(val);
2265 current->push(canSkip);
2266
2267 return resumeAfter(canSkip, loc);
2268 }
2269
build_MaybeExtractAwaitValue(BytecodeLocation loc)2270 bool WarpBuilder::build_MaybeExtractAwaitValue(BytecodeLocation loc) {
2271 MDefinition* canSkip = current->pop();
2272 MDefinition* value = current->pop();
2273
2274 MMaybeExtractAwaitValue* extracted =
2275 MMaybeExtractAwaitValue::New(alloc(), value, canSkip);
2276 current->add(extracted);
2277
2278 current->push(extracted);
2279 current->push(canSkip);
2280
2281 return resumeAfter(extracted, loc);
2282 }
2283
build_InitialYield(BytecodeLocation loc)2284 bool WarpBuilder::build_InitialYield(BytecodeLocation loc) {
2285 MDefinition* gen = current->pop();
2286 return buildSuspend(loc, gen, gen);
2287 }
2288
build_Await(BytecodeLocation loc)2289 bool WarpBuilder::build_Await(BytecodeLocation loc) {
2290 MDefinition* gen = current->pop();
2291 MDefinition* promiseOrGenerator = current->pop();
2292
2293 return buildSuspend(loc, gen, promiseOrGenerator);
2294 }
build_Yield(BytecodeLocation loc)2295 bool WarpBuilder::build_Yield(BytecodeLocation loc) { return build_Await(loc); }
2296
buildSuspend(BytecodeLocation loc,MDefinition * gen,MDefinition * retVal)2297 bool WarpBuilder::buildSuspend(BytecodeLocation loc, MDefinition* gen,
2298 MDefinition* retVal) {
2299 // If required, unbox the generator object explicitly and infallibly.
2300 //
2301 // This is done to avoid fuzz-bugs where ApplyTypeInformation does the
2302 // unboxing, and generates fallible unboxes which can lead to torn object
2303 // state due to `bailAfter`.
2304 MDefinition* genObj = gen;
2305 if (genObj->type() != MIRType::Object) {
2306 auto* unbox =
2307 MUnbox::New(alloc(), gen, MIRType::Object, MUnbox::Mode::Infallible);
2308 current->add(unbox);
2309
2310 genObj = unbox;
2311 }
2312
2313 int32_t slotsToCopy = current->stackDepth() - info().firstLocalSlot();
2314 MOZ_ASSERT(slotsToCopy >= 0);
2315 if (slotsToCopy > 0) {
2316 auto* arraySlot = MLoadFixedSlot::New(
2317 alloc(), genObj, AbstractGeneratorObject::stackStorageSlot());
2318 current->add(arraySlot);
2319
2320 auto* arrayObj = MUnbox::New(alloc(), arraySlot, MIRType::Object,
2321 MUnbox::Mode::Infallible);
2322 current->add(arrayObj);
2323
2324 auto* stackStorage = MElements::New(alloc(), arrayObj);
2325 current->add(stackStorage);
2326
2327 for (int32_t i = 0; i < slotsToCopy; i++) {
2328 if (!alloc().ensureBallast()) {
2329 return false;
2330 }
2331 // Use peekUnchecked because we're also writing out the argument slots
2332 int32_t peek = -slotsToCopy + i;
2333 MDefinition* stackElem = current->peekUnchecked(peek);
2334 auto* store = MStoreElement::New(alloc(), stackStorage,
2335 constant(Int32Value(i)), stackElem,
2336 /* needsHoleCheck = */ false);
2337
2338 current->add(store);
2339 current->add(MPostWriteBarrier::New(alloc(), arrayObj, stackElem));
2340 }
2341
2342 auto* len = constant(Int32Value(slotsToCopy - 1));
2343
2344 auto* setInitLength =
2345 MSetInitializedLength::New(alloc(), stackStorage, len);
2346 current->add(setInitLength);
2347
2348 auto* setLength = MSetArrayLength::New(alloc(), stackStorage, len);
2349 current->add(setLength);
2350 }
2351
2352 // Update Generator Object state
2353 uint32_t resumeIndex = loc.getResumeIndex();
2354
2355 // This store is unbarriered, as it's only ever storing an integer, and as
2356 // such doesn't partake of object tracing.
2357 current->add(MStoreFixedSlot::NewUnbarriered(
2358 alloc(), genObj, AbstractGeneratorObject::resumeIndexSlot(),
2359 constant(Int32Value(resumeIndex))));
2360
2361 // This store is barriered because it stores an object value.
2362 current->add(MStoreFixedSlot::NewBarriered(
2363 alloc(), genObj, AbstractGeneratorObject::envChainSlot(),
2364 current->environmentChain()));
2365
2366 current->add(
2367 MPostWriteBarrier::New(alloc(), genObj, current->environmentChain()));
2368
2369 // GeneratorReturn will return from the method, however to support MIR
2370 // generation isn't treated like the end of a block
2371 MGeneratorReturn* ret = MGeneratorReturn::New(alloc(), retVal);
2372 current->add(ret);
2373
2374 // To ensure the rest of the MIR generation looks correct, fill the stack with
2375 // the appropriately typed MUnreachable's for the stack pushes from this
2376 // opcode.
2377 auto* unreachableResumeKind =
2378 MUnreachableResult::New(alloc(), MIRType::Int32);
2379 current->add(unreachableResumeKind);
2380 current->push(unreachableResumeKind);
2381
2382 auto* unreachableGenerator =
2383 MUnreachableResult::New(alloc(), MIRType::Object);
2384 current->add(unreachableGenerator);
2385 current->push(unreachableGenerator);
2386
2387 auto* unreachableRval = MUnreachableResult::New(alloc(), MIRType::Value);
2388 current->add(unreachableRval);
2389 current->push(unreachableRval);
2390
2391 return true;
2392 }
2393
build_AsyncAwait(BytecodeLocation loc)2394 bool WarpBuilder::build_AsyncAwait(BytecodeLocation loc) {
2395 MDefinition* gen = current->pop();
2396 MDefinition* value = current->pop();
2397
2398 MAsyncAwait* asyncAwait = MAsyncAwait::New(alloc(), value, gen);
2399 current->add(asyncAwait);
2400 current->push(asyncAwait);
2401 return resumeAfter(asyncAwait, loc);
2402 }
2403
build_CheckReturn(BytecodeLocation)2404 bool WarpBuilder::build_CheckReturn(BytecodeLocation) {
2405 MOZ_ASSERT(!script_->noScriptRval());
2406
2407 MDefinition* returnValue = current->getSlot(info().returnValueSlot());
2408 MDefinition* thisValue = current->pop();
2409
2410 auto* ins = MCheckReturn::New(alloc(), returnValue, thisValue);
2411 current->add(ins);
2412 current->setSlot(info().returnValueSlot(), ins);
2413 return true;
2414 }
2415
buildCheckLexicalOp(BytecodeLocation loc)2416 void WarpBuilder::buildCheckLexicalOp(BytecodeLocation loc) {
2417 JSOp op = loc.getOp();
2418 MOZ_ASSERT(op == JSOp::CheckLexical || op == JSOp::CheckAliasedLexical);
2419
2420 MDefinition* input = current->pop();
2421 MInstruction* lexicalCheck = MLexicalCheck::New(alloc(), input);
2422 current->add(lexicalCheck);
2423 current->push(lexicalCheck);
2424
2425 if (snapshot().bailoutInfo().failedLexicalCheck()) {
2426 lexicalCheck->setNotMovable();
2427 }
2428
2429 if (op == JSOp::CheckLexical) {
2430 // Set the local slot so that a subsequent GetLocal without a CheckLexical
2431 // (the frontend can elide lexical checks) doesn't let a definition with
2432 // MIRType::MagicUninitializedLexical escape to arbitrary MIR instructions.
2433 // Note that in this case the GetLocal would be unreachable because we throw
2434 // an exception here, but we still generate MIR instructions for it.
2435 uint32_t slot = info().localSlot(loc.local());
2436 current->setSlot(slot, lexicalCheck);
2437 }
2438 }
2439
build_CheckLexical(BytecodeLocation loc)2440 bool WarpBuilder::build_CheckLexical(BytecodeLocation loc) {
2441 buildCheckLexicalOp(loc);
2442 return true;
2443 }
2444
build_CheckAliasedLexical(BytecodeLocation loc)2445 bool WarpBuilder::build_CheckAliasedLexical(BytecodeLocation loc) {
2446 buildCheckLexicalOp(loc);
2447 return true;
2448 }
2449
build_InitHomeObject(BytecodeLocation loc)2450 bool WarpBuilder::build_InitHomeObject(BytecodeLocation loc) {
2451 MDefinition* homeObject = current->pop();
2452 MDefinition* function = current->pop();
2453
2454 current->add(MPostWriteBarrier::New(alloc(), function, homeObject));
2455
2456 auto* ins = MInitHomeObject::New(alloc(), function, homeObject);
2457 current->add(ins);
2458 current->push(ins);
2459 return true;
2460 }
2461
build_SuperBase(BytecodeLocation)2462 bool WarpBuilder::build_SuperBase(BytecodeLocation) {
2463 MDefinition* callee = current->pop();
2464
2465 auto* homeObject = MHomeObject::New(alloc(), callee);
2466 current->add(homeObject);
2467
2468 auto* superBase = MHomeObjectSuperBase::New(alloc(), homeObject);
2469 current->add(superBase);
2470 current->push(superBase);
2471 return true;
2472 }
2473
build_SuperFun(BytecodeLocation)2474 bool WarpBuilder::build_SuperFun(BytecodeLocation) {
2475 MDefinition* callee = current->pop();
2476 auto* ins = MSuperFunction::New(alloc(), callee);
2477 current->add(ins);
2478 current->push(ins);
2479 return true;
2480 }
2481
build_BuiltinObject(BytecodeLocation loc)2482 bool WarpBuilder::build_BuiltinObject(BytecodeLocation loc) {
2483 if (auto* snapshot = getOpSnapshot<WarpBuiltinObject>(loc)) {
2484 JSObject* builtin = snapshot->builtin();
2485 pushConstant(ObjectValue(*builtin));
2486 return true;
2487 }
2488
2489 auto kind = loc.getBuiltinObjectKind();
2490 auto* ins = MBuiltinObject::New(alloc(), kind);
2491 current->add(ins);
2492 current->push(ins);
2493 return resumeAfter(ins, loc);
2494 }
2495
build_GetIntrinsic(BytecodeLocation loc)2496 bool WarpBuilder::build_GetIntrinsic(BytecodeLocation loc) {
2497 if (auto* snapshot = getOpSnapshot<WarpGetIntrinsic>(loc)) {
2498 Value intrinsic = snapshot->intrinsic();
2499 pushConstant(intrinsic);
2500 return true;
2501 }
2502
2503 PropertyName* name = loc.getPropertyName(script_);
2504 MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name);
2505 current->add(ins);
2506 current->push(ins);
2507 return true;
2508 }
2509
build_ImportMeta(BytecodeLocation loc)2510 bool WarpBuilder::build_ImportMeta(BytecodeLocation loc) {
2511 ModuleObject* moduleObj = scriptSnapshot()->moduleObject();
2512 MOZ_ASSERT(moduleObj);
2513
2514 MModuleMetadata* ins = MModuleMetadata::New(alloc(), moduleObj);
2515 current->add(ins);
2516 current->push(ins);
2517 return resumeAfter(ins, loc);
2518 }
2519
build_CallSiteObj(BytecodeLocation loc)2520 bool WarpBuilder::build_CallSiteObj(BytecodeLocation loc) {
2521 // WarpOracle already called ProcessCallSiteObjOperation to prepare the
2522 // object.
2523 JSObject* obj = loc.getObject(script_);
2524 pushConstant(ObjectValue(*obj));
2525 return true;
2526 }
2527
build_NewArray(BytecodeLocation loc)2528 bool WarpBuilder::build_NewArray(BytecodeLocation loc) {
2529 return buildIC(loc, CacheKind::NewArray, {});
2530 }
2531
build_NewObject(BytecodeLocation loc)2532 bool WarpBuilder::build_NewObject(BytecodeLocation loc) {
2533 return buildIC(loc, CacheKind::NewObject, {});
2534 }
2535
build_NewInit(BytecodeLocation loc)2536 bool WarpBuilder::build_NewInit(BytecodeLocation loc) {
2537 return build_NewObject(loc);
2538 }
2539
build_Object(BytecodeLocation loc)2540 bool WarpBuilder::build_Object(BytecodeLocation loc) {
2541 JSObject* obj = loc.getObject(script_);
2542 MConstant* objConst = constant(ObjectValue(*obj));
2543
2544 current->push(objConst);
2545 return true;
2546 }
2547
buildInitPropGetterSetterOp(BytecodeLocation loc)2548 bool WarpBuilder::buildInitPropGetterSetterOp(BytecodeLocation loc) {
2549 PropertyName* name = loc.getPropertyName(script_);
2550 MDefinition* value = current->pop();
2551 MDefinition* obj = current->peek(-1);
2552
2553 auto* ins = MInitPropGetterSetter::New(alloc(), obj, value, name);
2554 current->add(ins);
2555 return resumeAfter(ins, loc);
2556 }
2557
build_InitPropGetter(BytecodeLocation loc)2558 bool WarpBuilder::build_InitPropGetter(BytecodeLocation loc) {
2559 return buildInitPropGetterSetterOp(loc);
2560 }
2561
build_InitPropSetter(BytecodeLocation loc)2562 bool WarpBuilder::build_InitPropSetter(BytecodeLocation loc) {
2563 return buildInitPropGetterSetterOp(loc);
2564 }
2565
build_InitHiddenPropGetter(BytecodeLocation loc)2566 bool WarpBuilder::build_InitHiddenPropGetter(BytecodeLocation loc) {
2567 return buildInitPropGetterSetterOp(loc);
2568 }
2569
build_InitHiddenPropSetter(BytecodeLocation loc)2570 bool WarpBuilder::build_InitHiddenPropSetter(BytecodeLocation loc) {
2571 return buildInitPropGetterSetterOp(loc);
2572 }
2573
buildInitElemGetterSetterOp(BytecodeLocation loc)2574 bool WarpBuilder::buildInitElemGetterSetterOp(BytecodeLocation loc) {
2575 MDefinition* value = current->pop();
2576 MDefinition* id = current->pop();
2577 MDefinition* obj = current->peek(-1);
2578
2579 auto* ins = MInitElemGetterSetter::New(alloc(), obj, id, value);
2580 current->add(ins);
2581 return resumeAfter(ins, loc);
2582 }
2583
build_InitElemGetter(BytecodeLocation loc)2584 bool WarpBuilder::build_InitElemGetter(BytecodeLocation loc) {
2585 return buildInitElemGetterSetterOp(loc);
2586 }
2587
build_InitElemSetter(BytecodeLocation loc)2588 bool WarpBuilder::build_InitElemSetter(BytecodeLocation loc) {
2589 return buildInitElemGetterSetterOp(loc);
2590 }
2591
build_InitHiddenElemGetter(BytecodeLocation loc)2592 bool WarpBuilder::build_InitHiddenElemGetter(BytecodeLocation loc) {
2593 return buildInitElemGetterSetterOp(loc);
2594 }
2595
build_InitHiddenElemSetter(BytecodeLocation loc)2596 bool WarpBuilder::build_InitHiddenElemSetter(BytecodeLocation loc) {
2597 return buildInitElemGetterSetterOp(loc);
2598 }
2599
build_In(BytecodeLocation loc)2600 bool WarpBuilder::build_In(BytecodeLocation loc) {
2601 MDefinition* obj = current->pop();
2602 MDefinition* id = current->pop();
2603 return buildIC(loc, CacheKind::In, {id, obj});
2604 }
2605
build_HasOwn(BytecodeLocation loc)2606 bool WarpBuilder::build_HasOwn(BytecodeLocation loc) {
2607 MDefinition* obj = current->pop();
2608 MDefinition* id = current->pop();
2609 return buildIC(loc, CacheKind::HasOwn, {id, obj});
2610 }
2611
build_CheckPrivateField(BytecodeLocation loc)2612 bool WarpBuilder::build_CheckPrivateField(BytecodeLocation loc) {
2613 MDefinition* id = current->peek(-1);
2614 MDefinition* obj = current->peek(-2);
2615 return buildIC(loc, CacheKind::CheckPrivateField, {obj, id});
2616 }
2617
build_Instanceof(BytecodeLocation loc)2618 bool WarpBuilder::build_Instanceof(BytecodeLocation loc) {
2619 MDefinition* rhs = current->pop();
2620 MDefinition* obj = current->pop();
2621 return buildIC(loc, CacheKind::InstanceOf, {obj, rhs});
2622 }
2623
build_NewTarget(BytecodeLocation loc)2624 bool WarpBuilder::build_NewTarget(BytecodeLocation loc) {
2625 MOZ_ASSERT(script_->isFunction());
2626 MOZ_ASSERT(info().funMaybeLazy());
2627
2628 if (scriptSnapshot()->isArrowFunction()) {
2629 MDefinition* callee = getCallee();
2630 MArrowNewTarget* ins = MArrowNewTarget::New(alloc(), callee);
2631 current->add(ins);
2632 current->push(ins);
2633 return true;
2634 }
2635
2636 if (inlineCallInfo()) {
2637 if (inlineCallInfo()->constructing()) {
2638 current->push(inlineCallInfo()->getNewTarget());
2639 } else {
2640 pushConstant(UndefinedValue());
2641 }
2642 return true;
2643 }
2644
2645 MNewTarget* ins = MNewTarget::New(alloc());
2646 current->add(ins);
2647 current->push(ins);
2648 return true;
2649 }
2650
build_CheckIsObj(BytecodeLocation loc)2651 bool WarpBuilder::build_CheckIsObj(BytecodeLocation loc) {
2652 CheckIsObjectKind kind = loc.getCheckIsObjectKind();
2653
2654 MDefinition* toCheck = current->peek(-1);
2655 if (toCheck->type() == MIRType::Object) {
2656 toCheck->setImplicitlyUsedUnchecked();
2657 return true;
2658 }
2659
2660 MDefinition* val = current->pop();
2661 MCheckIsObj* ins = MCheckIsObj::New(alloc(), val, uint8_t(kind));
2662 current->add(ins);
2663 current->push(ins);
2664 return true;
2665 }
2666
build_CheckObjCoercible(BytecodeLocation loc)2667 bool WarpBuilder::build_CheckObjCoercible(BytecodeLocation loc) {
2668 MDefinition* val = current->pop();
2669 MCheckObjCoercible* ins = MCheckObjCoercible::New(alloc(), val);
2670 current->add(ins);
2671 current->push(ins);
2672 return resumeAfter(ins, loc);
2673 }
2674
buildLoadSlot(MDefinition * obj,uint32_t numFixedSlots,uint32_t slot)2675 MInstruction* WarpBuilder::buildLoadSlot(MDefinition* obj,
2676 uint32_t numFixedSlots,
2677 uint32_t slot) {
2678 if (slot < numFixedSlots) {
2679 MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot);
2680 current->add(load);
2681 return load;
2682 }
2683
2684 MSlots* slots = MSlots::New(alloc(), obj);
2685 current->add(slots);
2686
2687 MLoadDynamicSlot* load =
2688 MLoadDynamicSlot::New(alloc(), slots, slot - numFixedSlots);
2689 current->add(load);
2690 return load;
2691 }
2692
build_GetImport(BytecodeLocation loc)2693 bool WarpBuilder::build_GetImport(BytecodeLocation loc) {
2694 auto* snapshot = getOpSnapshot<WarpGetImport>(loc);
2695
2696 ModuleEnvironmentObject* targetEnv = snapshot->targetEnv();
2697
2698 // Load the target environment slot.
2699 MConstant* obj = constant(ObjectValue(*targetEnv));
2700 auto* load = buildLoadSlot(obj, snapshot->numFixedSlots(), snapshot->slot());
2701
2702 if (snapshot->needsLexicalCheck()) {
2703 // TODO: IonBuilder has code to mark non-movable. See buildCheckLexicalOp.
2704 MInstruction* lexicalCheck = MLexicalCheck::New(alloc(), load);
2705 current->add(lexicalCheck);
2706 current->push(lexicalCheck);
2707 } else {
2708 current->push(load);
2709 }
2710
2711 return true;
2712 }
2713
build_GetPropSuper(BytecodeLocation loc)2714 bool WarpBuilder::build_GetPropSuper(BytecodeLocation loc) {
2715 MDefinition* obj = current->pop();
2716 MDefinition* receiver = current->pop();
2717 return buildIC(loc, CacheKind::GetPropSuper, {obj, receiver});
2718 }
2719
build_GetElemSuper(BytecodeLocation loc)2720 bool WarpBuilder::build_GetElemSuper(BytecodeLocation loc) {
2721 MDefinition* obj = current->pop();
2722 MDefinition* id = current->pop();
2723 MDefinition* receiver = current->pop();
2724 return buildIC(loc, CacheKind::GetElemSuper, {obj, id, receiver});
2725 }
2726
build_InitProp(BytecodeLocation loc)2727 bool WarpBuilder::build_InitProp(BytecodeLocation loc) {
2728 MDefinition* val = current->pop();
2729 MDefinition* obj = current->peek(-1);
2730 return buildIC(loc, CacheKind::SetProp, {obj, val});
2731 }
2732
build_InitLockedProp(BytecodeLocation loc)2733 bool WarpBuilder::build_InitLockedProp(BytecodeLocation loc) {
2734 return build_InitProp(loc);
2735 }
2736
build_InitHiddenProp(BytecodeLocation loc)2737 bool WarpBuilder::build_InitHiddenProp(BytecodeLocation loc) {
2738 return build_InitProp(loc);
2739 }
2740
build_InitElem(BytecodeLocation loc)2741 bool WarpBuilder::build_InitElem(BytecodeLocation loc) {
2742 MDefinition* val = current->pop();
2743 MDefinition* id = current->pop();
2744 MDefinition* obj = current->peek(-1);
2745 return buildIC(loc, CacheKind::SetElem, {obj, id, val});
2746 }
2747
build_InitHiddenElem(BytecodeLocation loc)2748 bool WarpBuilder::build_InitHiddenElem(BytecodeLocation loc) {
2749 return build_InitElem(loc);
2750 }
2751
build_InitElemArray(BytecodeLocation loc)2752 bool WarpBuilder::build_InitElemArray(BytecodeLocation loc) {
2753 MDefinition* val = current->pop();
2754 MDefinition* obj = current->peek(-1);
2755
2756 // Note: getInitElemArrayIndex asserts the index fits in int32_t.
2757 uint32_t index = loc.getInitElemArrayIndex();
2758 MConstant* indexConst = constant(Int32Value(index));
2759
2760 // Note: InitArrayElemOperation asserts the index does not exceed the array's
2761 // dense element capacity.
2762
2763 auto* elements = MElements::New(alloc(), obj);
2764 current->add(elements);
2765
2766 if (val->type() == MIRType::MagicHole) {
2767 val->setImplicitlyUsedUnchecked();
2768 auto* store = MStoreHoleValueElement::New(alloc(), elements, indexConst);
2769 current->add(store);
2770 } else {
2771 current->add(MPostWriteBarrier::New(alloc(), obj, val));
2772 auto* store = MStoreElement::New(alloc(), elements, indexConst, val,
2773 /* needsHoleCheck = */ false);
2774 current->add(store);
2775 }
2776
2777 auto* setLength = MSetInitializedLength::New(alloc(), elements, indexConst);
2778 current->add(setLength);
2779
2780 return resumeAfter(setLength, loc);
2781 }
2782
build_InitElemInc(BytecodeLocation loc)2783 bool WarpBuilder::build_InitElemInc(BytecodeLocation loc) {
2784 MDefinition* val = current->pop();
2785 MDefinition* index = current->pop();
2786 MDefinition* obj = current->peek(-1);
2787
2788 // Push index + 1.
2789 MConstant* constOne = constant(Int32Value(1));
2790 MAdd* nextIndex = MAdd::New(alloc(), index, constOne, TruncateKind::Truncate);
2791 current->add(nextIndex);
2792 current->push(nextIndex);
2793
2794 return buildIC(loc, CacheKind::SetElem, {obj, index, val});
2795 }
2796
LambdaInfoFromSnapshot(JSFunction * fun,const WarpLambda * snapshot)2797 static LambdaFunctionInfo LambdaInfoFromSnapshot(JSFunction* fun,
2798 const WarpLambda* snapshot) {
2799 return LambdaFunctionInfo(fun, snapshot->baseScript(), snapshot->flags(),
2800 snapshot->nargs());
2801 }
2802
build_Lambda(BytecodeLocation loc)2803 bool WarpBuilder::build_Lambda(BytecodeLocation loc) {
2804 MOZ_ASSERT(usesEnvironmentChain());
2805
2806 auto* snapshot = getOpSnapshot<WarpLambda>(loc);
2807
2808 MDefinition* env = current->environmentChain();
2809
2810 JSFunction* fun = loc.getFunction(script_);
2811 MConstant* funConst = constant(ObjectValue(*fun));
2812
2813 auto* ins = MLambda::New(alloc(), env, funConst,
2814 LambdaInfoFromSnapshot(fun, snapshot));
2815 current->add(ins);
2816 current->push(ins);
2817 return resumeAfter(ins, loc);
2818 }
2819
build_LambdaArrow(BytecodeLocation loc)2820 bool WarpBuilder::build_LambdaArrow(BytecodeLocation loc) {
2821 MOZ_ASSERT(usesEnvironmentChain());
2822
2823 auto* snapshot = getOpSnapshot<WarpLambda>(loc);
2824
2825 MDefinition* env = current->environmentChain();
2826 MDefinition* newTarget = current->pop();
2827
2828 JSFunction* fun = loc.getFunction(script_);
2829 MConstant* funConst = constant(ObjectValue(*fun));
2830
2831 auto* ins = MLambdaArrow::New(alloc(), env, newTarget, funConst,
2832 LambdaInfoFromSnapshot(fun, snapshot));
2833 current->add(ins);
2834 current->push(ins);
2835 return resumeAfter(ins, loc);
2836 }
2837
build_FunWithProto(BytecodeLocation loc)2838 bool WarpBuilder::build_FunWithProto(BytecodeLocation loc) {
2839 MOZ_ASSERT(usesEnvironmentChain());
2840
2841 MDefinition* proto = current->pop();
2842 MDefinition* env = current->environmentChain();
2843
2844 JSFunction* fun = loc.getFunction(script_);
2845 MConstant* funConst = constant(ObjectValue(*fun));
2846
2847 auto* ins = MFunctionWithProto::New(alloc(), env, proto, funConst);
2848 current->add(ins);
2849 current->push(ins);
2850 return resumeAfter(ins, loc);
2851 }
2852
build_SpreadCall(BytecodeLocation loc)2853 bool WarpBuilder::build_SpreadCall(BytecodeLocation loc) {
2854 bool constructing = false;
2855 CallInfo callInfo(alloc(), constructing, loc.resultIsPopped());
2856 callInfo.initForSpreadCall(current);
2857
2858 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) {
2859 return transpileCall(loc, cacheIRSnapshot, &callInfo);
2860 }
2861
2862 MInstruction* call = makeSpreadCall(callInfo);
2863 if (!call) {
2864 return false;
2865 }
2866 call->setBailoutKind(BailoutKind::TooManyArguments);
2867 current->add(call);
2868 current->push(call);
2869 return resumeAfter(call, loc);
2870 }
2871
build_SpreadNew(BytecodeLocation loc)2872 bool WarpBuilder::build_SpreadNew(BytecodeLocation loc) {
2873 MDefinition* newTarget = current->pop();
2874 MDefinition* argArr = current->pop();
2875 MDefinition* thisValue = current->pop();
2876 MDefinition* callee = current->pop();
2877
2878 // Inline the constructor on the caller-side.
2879 MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
2880 current->add(createThis);
2881 thisValue->setImplicitlyUsedUnchecked();
2882
2883 // Load dense elements of the argument array. Note that the bytecode ensures
2884 // this is an array.
2885 MElements* elements = MElements::New(alloc(), argArr);
2886 current->add(elements);
2887
2888 WrappedFunction* wrappedTarget = nullptr;
2889 auto* apply = MConstructArray::New(alloc(), wrappedTarget, callee, elements,
2890 createThis, newTarget);
2891 apply->setBailoutKind(BailoutKind::TooManyArguments);
2892 current->add(apply);
2893 current->push(apply);
2894 return resumeAfter(apply, loc);
2895 }
2896
build_SpreadSuperCall(BytecodeLocation loc)2897 bool WarpBuilder::build_SpreadSuperCall(BytecodeLocation loc) {
2898 return build_SpreadNew(loc);
2899 }
2900
build_OptimizeSpreadCall(BytecodeLocation loc)2901 bool WarpBuilder::build_OptimizeSpreadCall(BytecodeLocation loc) {
2902 MDefinition* value = current->peek(-1);
2903 return buildIC(loc, CacheKind::OptimizeSpreadCall, {value});
2904 }
2905
build_Debugger(BytecodeLocation loc)2906 bool WarpBuilder::build_Debugger(BytecodeLocation loc) {
2907 // The |debugger;| statement will bail out to Baseline if the realm is a
2908 // debuggee realm with an onDebuggerStatement hook.
2909 MDebugger* debugger = MDebugger::New(alloc());
2910 current->add(debugger);
2911 return resumeAfter(debugger, loc);
2912 }
2913
build_TableSwitch(BytecodeLocation loc)2914 bool WarpBuilder::build_TableSwitch(BytecodeLocation loc) {
2915 int32_t low = loc.getTableSwitchLow();
2916 int32_t high = loc.getTableSwitchHigh();
2917 size_t numCases = high - low + 1;
2918
2919 MDefinition* input = current->pop();
2920 MTableSwitch* tableswitch = MTableSwitch::New(alloc(), input, low, high);
2921 current->end(tableswitch);
2922
2923 MBasicBlock* switchBlock = current;
2924
2925 // Create |default| block.
2926 {
2927 BytecodeLocation defaultLoc = loc.getTableSwitchDefaultTarget();
2928 if (!startNewBlock(switchBlock, defaultLoc)) {
2929 return false;
2930 }
2931
2932 size_t index;
2933 if (!tableswitch->addDefault(current, &index)) {
2934 return false;
2935 }
2936 MOZ_ASSERT(index == 0);
2937
2938 if (!buildForwardGoto(defaultLoc)) {
2939 return false;
2940 }
2941 }
2942
2943 // Create blocks for all cases.
2944 for (size_t i = 0; i < numCases; i++) {
2945 BytecodeLocation caseLoc = loc.getTableSwitchCaseTarget(script_, i);
2946 if (!startNewBlock(switchBlock, caseLoc)) {
2947 return false;
2948 }
2949
2950 size_t index;
2951 if (!tableswitch->addSuccessor(current, &index)) {
2952 return false;
2953 }
2954 if (!tableswitch->addCase(index)) {
2955 return false;
2956 }
2957
2958 // TODO: IonBuilder has an optimization where it replaces the switch input
2959 // with the case value. This probably matters less for Warp. Re-evaluate.
2960
2961 if (!buildForwardGoto(caseLoc)) {
2962 return false;
2963 }
2964 }
2965
2966 MOZ_ASSERT(hasTerminatedBlock());
2967 return true;
2968 }
2969
build_Rest(BytecodeLocation loc)2970 bool WarpBuilder::build_Rest(BytecodeLocation loc) {
2971 auto* snapshot = getOpSnapshot<WarpRest>(loc);
2972 Shape* shape = snapshot ? snapshot->shape() : nullptr;
2973
2974 if (inlineCallInfo()) {
2975 // If we are inlining, we know the actual arguments.
2976 unsigned numActuals = inlineCallInfo()->argc();
2977 unsigned numFormals = info().nargs() - 1;
2978 unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
2979
2980 // TODO: support pre-tenuring.
2981 gc::InitialHeap heap = gc::DefaultHeap;
2982
2983 // Allocate an array of the correct size.
2984 MInstruction* newArray;
2985 if (shape && gc::CanUseFixedElementsForArray(numRest)) {
2986 auto* shapeConstant = MConstant::NewShape(alloc(), shape);
2987 current->add(shapeConstant);
2988 newArray = MNewArrayObject::New(alloc(), shapeConstant, numRest, heap);
2989 } else {
2990 MConstant* templateConst = constant(NullValue());
2991 newArray = MNewArray::NewVM(alloc(), numRest, templateConst, heap);
2992 }
2993 current->add(newArray);
2994 current->push(newArray);
2995
2996 if (numRest == 0) {
2997 // No more updating to do.
2998 return true;
2999 }
3000
3001 MElements* elements = MElements::New(alloc(), newArray);
3002 current->add(elements);
3003
3004 // Unroll the argument copy loop. We don't need to do any bounds or hole
3005 // checking here.
3006 MConstant* index = nullptr;
3007 for (uint32_t i = numFormals; i < numActuals; i++) {
3008 if (!alloc().ensureBallast()) {
3009 return false;
3010 }
3011
3012 index = MConstant::New(alloc(), Int32Value(i - numFormals));
3013 current->add(index);
3014
3015 MDefinition* arg = inlineCallInfo()->argv()[i];
3016 MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
3017 /* needsHoleCheck = */ false);
3018 current->add(store);
3019 current->add(MPostWriteBarrier::New(alloc(), newArray, arg));
3020 }
3021
3022 // Update the initialized length for all the (necessarily non-hole)
3023 // elements added.
3024 MSetInitializedLength* initLength =
3025 MSetInitializedLength::New(alloc(), elements, index);
3026 current->add(initLength);
3027
3028 return true;
3029 }
3030
3031 MArgumentsLength* numActuals = MArgumentsLength::New(alloc());
3032 current->add(numActuals);
3033
3034 // Pass in the number of actual arguments, the number of formals (not
3035 // including the rest parameter slot itself), and the shape.
3036 unsigned numFormals = info().nargs() - 1;
3037 MRest* rest = MRest::New(alloc(), numActuals, numFormals, shape);
3038 current->add(rest);
3039 current->push(rest);
3040 return true;
3041 }
3042
build_Try(BytecodeLocation loc)3043 bool WarpBuilder::build_Try(BytecodeLocation loc) {
3044 // Note: WarpOracle aborts compilation for try-statements with a 'finally'
3045 // block.
3046
3047 graph().setHasTryBlock();
3048
3049 MBasicBlock* pred = current;
3050 if (!startNewBlock(pred, loc.next())) {
3051 return false;
3052 }
3053
3054 pred->end(MGoto::New(alloc(), current));
3055 return true;
3056 }
3057
build_Exception(BytecodeLocation)3058 bool WarpBuilder::build_Exception(BytecodeLocation) {
3059 MOZ_CRASH("Unreachable because we skip catch-blocks");
3060 }
3061
build_Throw(BytecodeLocation loc)3062 bool WarpBuilder::build_Throw(BytecodeLocation loc) {
3063 MDefinition* def = current->pop();
3064
3065 MThrow* ins = MThrow::New(alloc(), def);
3066 current->add(ins);
3067 if (!resumeAfter(ins, loc)) {
3068 return false;
3069 }
3070
3071 // Terminate the block.
3072 current->end(MUnreachable::New(alloc()));
3073 setTerminatedBlock();
3074 return true;
3075 }
3076
build_ThrowSetConst(BytecodeLocation loc)3077 bool WarpBuilder::build_ThrowSetConst(BytecodeLocation loc) {
3078 auto* ins = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN);
3079 current->add(ins);
3080 if (!resumeAfter(ins, loc)) {
3081 return false;
3082 }
3083
3084 // Terminate the block.
3085 current->end(MUnreachable::New(alloc()));
3086 setTerminatedBlock();
3087 return true;
3088 }
3089
build_ThrowMsg(BytecodeLocation loc)3090 bool WarpBuilder::build_ThrowMsg(BytecodeLocation loc) {
3091 auto* ins = MThrowMsg::New(alloc(), loc.throwMsgKind());
3092 current->add(ins);
3093 if (!resumeAfter(ins, loc)) {
3094 return false;
3095 }
3096
3097 // Terminate the block.
3098 current->end(MUnreachable::New(alloc()));
3099 setTerminatedBlock();
3100 return true;
3101 }
3102
buildIC(BytecodeLocation loc,CacheKind kind,std::initializer_list<MDefinition * > inputs)3103 bool WarpBuilder::buildIC(BytecodeLocation loc, CacheKind kind,
3104 std::initializer_list<MDefinition*> inputs) {
3105 MOZ_ASSERT(loc.opHasIC());
3106
3107 mozilla::DebugOnly<size_t> numInputs = inputs.size();
3108 MOZ_ASSERT(numInputs == NumInputsForCacheKind(kind));
3109
3110 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) {
3111 return TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, inputs);
3112 }
3113
3114 if (getOpSnapshot<WarpBailout>(loc)) {
3115 for (MDefinition* input : inputs) {
3116 input->setImplicitlyUsedUnchecked();
3117 }
3118 return buildBailoutForColdIC(loc, kind);
3119 }
3120
3121 if (const auto* inliningSnapshot = getOpSnapshot<WarpInlinedCall>(loc)) {
3122 // The CallInfo will be initialized by the transpiler.
3123 bool ignoresRval = BytecodeIsPopped(loc.toRawBytecode());
3124 CallInfo callInfo(alloc(), /*constructing =*/false, ignoresRval);
3125 callInfo.markAsInlined();
3126
3127 if (!TranspileCacheIRToMIR(this, loc, inliningSnapshot->cacheIRSnapshot(),
3128 inputs, &callInfo)) {
3129 return false;
3130 }
3131 return buildInlinedCall(loc, inliningSnapshot, callInfo);
3132 }
3133
3134 // Work around std::initializer_list not defining operator[].
3135 auto getInput = [&](size_t index) -> MDefinition* {
3136 MOZ_ASSERT(index < numInputs);
3137 return inputs.begin()[index];
3138 };
3139
3140 switch (kind) {
3141 case CacheKind::UnaryArith: {
3142 MOZ_ASSERT(numInputs == 1);
3143 auto* ins = MUnaryCache::New(alloc(), getInput(0));
3144 current->add(ins);
3145 current->push(ins);
3146 return resumeAfter(ins, loc);
3147 }
3148 case CacheKind::ToPropertyKey: {
3149 MOZ_ASSERT(numInputs == 1);
3150 auto* ins = MToPropertyKeyCache::New(alloc(), getInput(0));
3151 current->add(ins);
3152 current->push(ins);
3153 return resumeAfter(ins, loc);
3154 }
3155 case CacheKind::BinaryArith: {
3156 MOZ_ASSERT(numInputs == 2);
3157 auto* ins =
3158 MBinaryCache::New(alloc(), getInput(0), getInput(1), MIRType::Value);
3159 current->add(ins);
3160 current->push(ins);
3161 return resumeAfter(ins, loc);
3162 }
3163 case CacheKind::Compare: {
3164 MOZ_ASSERT(numInputs == 2);
3165 auto* ins = MBinaryCache::New(alloc(), getInput(0), getInput(1),
3166 MIRType::Boolean);
3167 current->add(ins);
3168 current->push(ins);
3169 return resumeAfter(ins, loc);
3170 }
3171 case CacheKind::In: {
3172 MOZ_ASSERT(numInputs == 2);
3173 auto* ins = MInCache::New(alloc(), getInput(0), getInput(1));
3174 current->add(ins);
3175 current->push(ins);
3176 return resumeAfter(ins, loc);
3177 }
3178 case CacheKind::HasOwn: {
3179 MOZ_ASSERT(numInputs == 2);
3180 // Note: the MHasOwnCache constructor takes obj/id instead of id/obj.
3181 auto* ins = MHasOwnCache::New(alloc(), getInput(1), getInput(0));
3182 current->add(ins);
3183 current->push(ins);
3184 return resumeAfter(ins, loc);
3185 }
3186 case CacheKind::CheckPrivateField: {
3187 MOZ_ASSERT(numInputs == 2);
3188 auto* ins =
3189 MCheckPrivateFieldCache::New(alloc(), getInput(0), getInput(1));
3190 current->add(ins);
3191 current->push(ins);
3192 return resumeAfter(ins, loc);
3193 }
3194 case CacheKind::InstanceOf: {
3195 MOZ_ASSERT(numInputs == 2);
3196 auto* ins = MInstanceOfCache::New(alloc(), getInput(0), getInput(1));
3197 current->add(ins);
3198 current->push(ins);
3199 return resumeAfter(ins, loc);
3200 }
3201 case CacheKind::BindName: {
3202 MOZ_ASSERT(numInputs == 1);
3203 auto* ins = MBindNameCache::New(alloc(), getInput(0));
3204 current->add(ins);
3205 current->push(ins);
3206 return resumeAfter(ins, loc);
3207 }
3208 case CacheKind::GetIterator: {
3209 MOZ_ASSERT(numInputs == 1);
3210 auto* ins = MGetIteratorCache::New(alloc(), getInput(0));
3211 current->add(ins);
3212 current->push(ins);
3213 return resumeAfter(ins, loc);
3214 }
3215 case CacheKind::GetName: {
3216 MOZ_ASSERT(numInputs == 1);
3217 auto* ins = MGetNameCache::New(alloc(), getInput(0));
3218 current->add(ins);
3219 current->push(ins);
3220 return resumeAfter(ins, loc);
3221 }
3222 case CacheKind::GetProp: {
3223 MOZ_ASSERT(numInputs == 1);
3224 PropertyName* name = loc.getPropertyName(script_);
3225 MConstant* id = constant(StringValue(name));
3226 MDefinition* val = getInput(0);
3227 auto* ins = MGetPropertyCache::New(alloc(), val, id);
3228 current->add(ins);
3229 current->push(ins);
3230 return resumeAfter(ins, loc);
3231 }
3232 case CacheKind::GetElem: {
3233 MOZ_ASSERT(numInputs == 2);
3234 MDefinition* val = getInput(0);
3235 auto* ins = MGetPropertyCache::New(alloc(), val, getInput(1));
3236 current->add(ins);
3237 current->push(ins);
3238 return resumeAfter(ins, loc);
3239 }
3240 case CacheKind::SetProp: {
3241 MOZ_ASSERT(numInputs == 2);
3242 PropertyName* name = loc.getPropertyName(script_);
3243 MConstant* id = constant(StringValue(name));
3244 bool strict = loc.isStrictSetOp();
3245 auto* ins =
3246 MSetPropertyCache::New(alloc(), getInput(0), id, getInput(1), strict);
3247 current->add(ins);
3248 return resumeAfter(ins, loc);
3249 }
3250 case CacheKind::SetElem: {
3251 MOZ_ASSERT(numInputs == 3);
3252 bool strict = loc.isStrictSetOp();
3253 auto* ins = MSetPropertyCache::New(alloc(), getInput(0), getInput(1),
3254 getInput(2), strict);
3255 current->add(ins);
3256 return resumeAfter(ins, loc);
3257 }
3258 case CacheKind::GetPropSuper: {
3259 MOZ_ASSERT(numInputs == 2);
3260 PropertyName* name = loc.getPropertyName(script_);
3261 MConstant* id = constant(StringValue(name));
3262 auto* ins =
3263 MGetPropSuperCache::New(alloc(), getInput(0), getInput(1), id);
3264 current->add(ins);
3265 current->push(ins);
3266 return resumeAfter(ins, loc);
3267 }
3268 case CacheKind::GetElemSuper: {
3269 MOZ_ASSERT(numInputs == 3);
3270 // Note: CacheIR expects obj/id/receiver but MGetPropSuperCache takes
3271 // obj/receiver/id so swap the last two inputs.
3272 auto* ins = MGetPropSuperCache::New(alloc(), getInput(0), getInput(2),
3273 getInput(1));
3274 current->add(ins);
3275 current->push(ins);
3276 return resumeAfter(ins, loc);
3277 }
3278 case CacheKind::OptimizeSpreadCall: {
3279 MOZ_ASSERT(numInputs == 1);
3280 auto* ins = MOptimizeSpreadCallCache::New(alloc(), getInput(0));
3281 current->add(ins);
3282 current->push(ins);
3283 return resumeAfter(ins, loc);
3284 }
3285 case CacheKind::TypeOf: {
3286 // Note: Warp does not have a TypeOf IC, it just inlines the operation.
3287 MOZ_ASSERT(numInputs == 1);
3288 auto* ins = MTypeOf::New(alloc(), getInput(0));
3289 current->add(ins);
3290 current->push(ins);
3291 return true;
3292 }
3293 case CacheKind::NewObject: {
3294 auto* templateConst = constant(NullValue());
3295 MNewObject* ins = MNewObject::NewVM(
3296 alloc(), templateConst, gc::DefaultHeap, MNewObject::ObjectLiteral);
3297 current->add(ins);
3298 current->push(ins);
3299 return resumeAfter(ins, loc);
3300 }
3301 case CacheKind::NewArray: {
3302 uint32_t length = loc.getNewArrayLength();
3303 MConstant* templateConst = constant(NullValue());
3304 MNewArray* ins =
3305 MNewArray::NewVM(alloc(), length, templateConst, gc::DefaultHeap);
3306 current->add(ins);
3307 current->push(ins);
3308 return true;
3309 }
3310 case CacheKind::GetIntrinsic:
3311 case CacheKind::ToBool:
3312 case CacheKind::Call:
3313 // We're currently not using an IC or transpiling CacheIR for these kinds.
3314 MOZ_CRASH("Unexpected kind");
3315 }
3316
3317 return true;
3318 }
3319
buildBailoutForColdIC(BytecodeLocation loc,CacheKind kind)3320 bool WarpBuilder::buildBailoutForColdIC(BytecodeLocation loc, CacheKind kind) {
3321 MOZ_ASSERT(loc.opHasIC());
3322
3323 MBail* bail = MBail::New(alloc(), BailoutKind::FirstExecution);
3324 current->add(bail);
3325 current->setAlwaysBails();
3326
3327 MIRType resultType;
3328 switch (kind) {
3329 case CacheKind::UnaryArith:
3330 case CacheKind::BinaryArith:
3331 case CacheKind::GetName:
3332 case CacheKind::GetProp:
3333 case CacheKind::GetElem:
3334 case CacheKind::GetPropSuper:
3335 case CacheKind::GetElemSuper:
3336 case CacheKind::GetIntrinsic:
3337 case CacheKind::Call:
3338 case CacheKind::ToPropertyKey:
3339 resultType = MIRType::Value;
3340 break;
3341 case CacheKind::BindName:
3342 case CacheKind::GetIterator:
3343 case CacheKind::NewArray:
3344 case CacheKind::NewObject:
3345 resultType = MIRType::Object;
3346 break;
3347 case CacheKind::TypeOf:
3348 resultType = MIRType::String;
3349 break;
3350 case CacheKind::ToBool:
3351 case CacheKind::Compare:
3352 case CacheKind::In:
3353 case CacheKind::HasOwn:
3354 case CacheKind::CheckPrivateField:
3355 case CacheKind::InstanceOf:
3356 case CacheKind::OptimizeSpreadCall:
3357 resultType = MIRType::Boolean;
3358 break;
3359 case CacheKind::SetProp:
3360 case CacheKind::SetElem:
3361 return true; // No result.
3362 }
3363
3364 auto* ins = MUnreachableResult::New(alloc(), resultType);
3365 current->add(ins);
3366 current->push(ins);
3367
3368 return true;
3369 }
3370
3371 class MOZ_RAII AutoAccumulateReturns {
3372 MIRGraph& graph_;
3373 MIRGraphReturns* prev_;
3374
3375 public:
AutoAccumulateReturns(MIRGraph & graph,MIRGraphReturns & returns)3376 AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns)
3377 : graph_(graph) {
3378 prev_ = graph_.returnAccumulator();
3379 graph_.setReturnAccumulator(&returns);
3380 }
~AutoAccumulateReturns()3381 ~AutoAccumulateReturns() { graph_.setReturnAccumulator(prev_); }
3382 };
3383
buildInlinedCall(BytecodeLocation loc,const WarpInlinedCall * inlineSnapshot,CallInfo & callInfo)3384 bool WarpBuilder::buildInlinedCall(BytecodeLocation loc,
3385 const WarpInlinedCall* inlineSnapshot,
3386 CallInfo& callInfo) {
3387 jsbytecode* pc = loc.toRawBytecode();
3388
3389 if (callInfo.isSetter()) {
3390 // build_SetProp pushes the rhs argument onto the stack. Remove it
3391 // in preparation for pushCallStack.
3392 current->pop();
3393 }
3394
3395 callInfo.setImplicitlyUsedUnchecked();
3396
3397 // Capture formals in the outer resume point.
3398 if (!callInfo.pushCallStack(current)) {
3399 return false;
3400 }
3401 MResumePoint* outerResumePoint =
3402 MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
3403 if (!outerResumePoint) {
3404 return false;
3405 }
3406 current->setOuterResumePoint(outerResumePoint);
3407
3408 // Pop formals again, except leave |callee| on stack for duration of call.
3409 callInfo.popCallStack(current);
3410 current->push(callInfo.callee());
3411
3412 // Build the graph.
3413 CompileInfo* calleeCompileInfo = inlineSnapshot->info();
3414 MIRGraphReturns returns(alloc());
3415 AutoAccumulateReturns aar(graph(), returns);
3416 WarpBuilder inlineBuilder(this, inlineSnapshot->scriptSnapshot(),
3417 *calleeCompileInfo, &callInfo, outerResumePoint);
3418 if (!inlineBuilder.buildInline()) {
3419 // Note: Inlining only aborts on OOM. If inlining would fail for
3420 // any other reason, we detect it in advance and don't inline.
3421 return false;
3422 }
3423
3424 // We mark scripts as uninlineable in BytecodeAnalysis if we cannot
3425 // reach a return statement (without going through a catch/finally).
3426 MOZ_ASSERT(!returns.empty());
3427
3428 // Create return block
3429 BytecodeLocation postCall = loc.next();
3430 MBasicBlock* prev = current;
3431 if (!startNewEntryBlock(prev->stackDepth(), postCall)) {
3432 return false;
3433 }
3434 // Restore previous value of callerResumePoint.
3435 current->setCallerResumePoint(callerResumePoint());
3436 current->inheritSlots(prev);
3437
3438 // Pop |callee|.
3439 current->pop();
3440
3441 // Accumulate return values.
3442 MDefinition* returnValue =
3443 patchInlinedReturns(calleeCompileInfo, callInfo, returns, current);
3444 if (!returnValue) {
3445 return false;
3446 }
3447 current->push(returnValue);
3448
3449 // Initialize entry slots
3450 if (!current->initEntrySlots(alloc())) {
3451 return false;
3452 }
3453
3454 return true;
3455 }
3456
patchInlinedReturns(CompileInfo * calleeCompileInfo,CallInfo & callInfo,MIRGraphReturns & exits,MBasicBlock * returnBlock)3457 MDefinition* WarpBuilder::patchInlinedReturns(CompileInfo* calleeCompileInfo,
3458 CallInfo& callInfo,
3459 MIRGraphReturns& exits,
3460 MBasicBlock* returnBlock) {
3461 if (exits.length() == 1) {
3462 return patchInlinedReturn(calleeCompileInfo, callInfo, exits[0],
3463 returnBlock);
3464 }
3465
3466 // Accumulate multiple returns with a phi.
3467 MPhi* phi = MPhi::New(alloc());
3468 if (!phi->reserveLength(exits.length())) {
3469 return nullptr;
3470 }
3471
3472 for (auto* exit : exits) {
3473 MDefinition* rdef =
3474 patchInlinedReturn(calleeCompileInfo, callInfo, exit, returnBlock);
3475 if (!rdef) {
3476 return nullptr;
3477 }
3478 phi->addInput(rdef);
3479 }
3480 returnBlock->addPhi(phi);
3481 return phi;
3482 }
3483
patchInlinedReturn(CompileInfo * calleeCompileInfo,CallInfo & callInfo,MBasicBlock * exit,MBasicBlock * returnBlock)3484 MDefinition* WarpBuilder::patchInlinedReturn(CompileInfo* calleeCompileInfo,
3485 CallInfo& callInfo,
3486 MBasicBlock* exit,
3487 MBasicBlock* returnBlock) {
3488 // Replace the MReturn in the exit block with an MGoto branching to
3489 // the return block.
3490 MDefinition* rdef = exit->lastIns()->toReturn()->input();
3491 exit->discardLastIns();
3492
3493 // Constructors must be patched by the caller to always return an object.
3494 // Derived class constructors contain extra bytecode to ensure an object
3495 // is always returned, so no additional patching is needed.
3496 if (callInfo.constructing() &&
3497 !calleeCompileInfo->isDerivedClassConstructor()) {
3498 auto* filter = MReturnFromCtor::New(alloc(), rdef, callInfo.thisArg());
3499 exit->add(filter);
3500 rdef = filter;
3501 } else if (callInfo.isSetter()) {
3502 // Setters return the rhs argument, not whatever value is returned.
3503 rdef = callInfo.getArg(0);
3504 }
3505
3506 exit->end(MGoto::New(alloc(), returnBlock));
3507 if (!returnBlock->addPredecessorWithoutPhis(exit)) {
3508 return nullptr;
3509 }
3510
3511 return rdef;
3512 }
3513