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