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/BaselineIC.h"
8 
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/Sprintf.h"
11 
12 #include "jstypes.h"
13 
14 #include "builtin/Eval.h"
15 #include "jit/BaselineCacheIRCompiler.h"
16 #include "jit/CacheIRGenerator.h"
17 #include "jit/CacheIRHealth.h"
18 #include "jit/JitFrames.h"
19 #include "jit/JitRuntime.h"
20 #include "jit/JitSpewer.h"
21 #include "jit/Linker.h"
22 #ifdef JS_ION_PERF
23 #  include "jit/PerfSpewer.h"
24 #endif
25 #include "jit/SharedICHelpers.h"
26 #include "jit/SharedICRegisters.h"
27 #include "jit/VMFunctions.h"
28 #include "js/Conversions.h"
29 #include "js/friend/ErrorMessages.h"  // JSMSG_*
30 #include "vm/BytecodeIterator.h"
31 #include "vm/BytecodeLocation.h"
32 #include "vm/BytecodeUtil.h"
33 #include "vm/EqualityOperations.h"
34 #include "vm/JSFunction.h"
35 #include "vm/JSScript.h"
36 #include "vm/Opcodes.h"
37 #ifdef MOZ_VTUNE
38 #  include "vtune/VTuneWrapper.h"
39 #endif
40 
41 #include "jit/MacroAssembler-inl.h"
42 #include "jit/SharedICHelpers-inl.h"
43 #include "jit/VMFunctionList-inl.h"
44 #include "vm/BytecodeIterator-inl.h"
45 #include "vm/BytecodeLocation-inl.h"
46 #include "vm/EnvironmentObject-inl.h"
47 #include "vm/Interpreter-inl.h"
48 #include "vm/JSScript-inl.h"
49 
50 using mozilla::DebugOnly;
51 
52 namespace js {
53 namespace jit {
54 
55 // Class used to emit all Baseline IC fallback code when initializing the
56 // JitRuntime.
57 class MOZ_RAII FallbackICCodeCompiler final {
58   BaselineICFallbackCode& code;
59   MacroAssembler& masm;
60 
61   JSContext* cx;
62   bool inStubFrame_ = false;
63 
64 #ifdef DEBUG
65   bool entersStubFrame_ = false;
66   uint32_t framePushedAtEnterStubFrame_ = 0;
67 #endif
68 
69   [[nodiscard]] bool emitCall(bool isSpread, bool isConstructing);
70   [[nodiscard]] bool emitGetElem(bool hasReceiver);
71   [[nodiscard]] bool emitGetProp(bool hasReceiver);
72 
73  public:
FallbackICCodeCompiler(JSContext * cx,BaselineICFallbackCode & code,MacroAssembler & masm)74   FallbackICCodeCompiler(JSContext* cx, BaselineICFallbackCode& code,
75                          MacroAssembler& masm)
76       : code(code), masm(masm), cx(cx) {}
77 
78 #define DEF_METHOD(kind) [[nodiscard]] bool emit_##kind();
79   IC_BASELINE_FALLBACK_CODE_KIND_LIST(DEF_METHOD)
80 #undef DEF_METHOD
81 
82   void pushCallArguments(MacroAssembler& masm,
83                          AllocatableGeneralRegisterSet regs, Register argcReg,
84                          bool isConstructing);
85 
86   // Push a payload specialized per compiler needed to execute stubs.
87   void PushStubPayload(MacroAssembler& masm, Register scratch);
88   void pushStubPayload(MacroAssembler& masm, Register scratch);
89 
90   // Emits a tail call to a VMFunction wrapper.
91   [[nodiscard]] bool tailCallVMInternal(MacroAssembler& masm,
92                                         TailCallVMFunctionId id);
93 
94   template <typename Fn, Fn fn>
95   [[nodiscard]] bool tailCallVM(MacroAssembler& masm);
96 
97   // Emits a normal (non-tail) call to a VMFunction wrapper.
98   [[nodiscard]] bool callVMInternal(MacroAssembler& masm, VMFunctionId id);
99 
100   template <typename Fn, Fn fn>
101   [[nodiscard]] bool callVM(MacroAssembler& masm);
102 
103   // A stub frame is used when a stub wants to call into the VM without
104   // performing a tail call. This is required for the return address
105   // to pc mapping to work.
106   void enterStubFrame(MacroAssembler& masm, Register scratch);
107   void assumeStubFrame();
108   void leaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false);
109 };
110 
BaselineICAvailableGeneralRegs(size_t numInputs)111 AllocatableGeneralRegisterSet BaselineICAvailableGeneralRegs(size_t numInputs) {
112   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
113 #if defined(JS_CODEGEN_ARM)
114   MOZ_ASSERT(!regs.has(BaselineStackReg));
115   MOZ_ASSERT(!regs.has(ICTailCallReg));
116   regs.take(BaselineSecondScratchReg);
117 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
118   MOZ_ASSERT(!regs.has(BaselineStackReg));
119   MOZ_ASSERT(!regs.has(ICTailCallReg));
120   MOZ_ASSERT(!regs.has(BaselineSecondScratchReg));
121 #elif defined(JS_CODEGEN_ARM64)
122   MOZ_ASSERT(!regs.has(PseudoStackPointer));
123   MOZ_ASSERT(!regs.has(RealStackPointer));
124   MOZ_ASSERT(!regs.has(ICTailCallReg));
125 #else
126   MOZ_ASSERT(!regs.has(BaselineStackReg));
127 #endif
128   regs.take(BaselineFrameReg);
129   regs.take(ICStubReg);
130 #ifdef JS_CODEGEN_X64
131   regs.take(ExtractTemp0);
132   regs.take(ExtractTemp1);
133 #endif
134 
135   switch (numInputs) {
136     case 0:
137       break;
138     case 1:
139       regs.take(R0);
140       break;
141     case 2:
142       regs.take(R0);
143       regs.take(R1);
144       break;
145     default:
146       MOZ_CRASH("Invalid numInputs");
147   }
148 
149   return regs;
150 }
151 
StubOffsetToPc(const ICFallbackStub * stub,const JSScript * script)152 static jsbytecode* StubOffsetToPc(const ICFallbackStub* stub,
153                                   const JSScript* script) {
154   return script->offsetToPC(stub->pcOffset());
155 }
156 
157 #ifdef JS_JITSPEW
FallbackICSpew(JSContext * cx,ICFallbackStub * stub,const char * fmt,...)158 void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...) {
159   if (JitSpewEnabled(JitSpew_BaselineICFallback)) {
160     RootedScript script(cx, GetTopJitJSScript(cx));
161     jsbytecode* pc = StubOffsetToPc(stub, script);
162 
163     char fmtbuf[100];
164     va_list args;
165     va_start(args, fmt);
166     (void)VsprintfLiteral(fmtbuf, fmt, args);
167     va_end(args);
168 
169     JitSpew(
170         JitSpew_BaselineICFallback,
171         "Fallback hit for (%s:%u:%u) (pc=%zu,line=%u,uses=%u,stubs=%zu): %s",
172         script->filename(), script->lineno(), script->column(),
173         script->pcToOffset(pc), PCToLineNumber(script, pc),
174         script->getWarmUpCount(), stub->numOptimizedStubs(), fmtbuf);
175   }
176 }
177 #endif  // JS_JITSPEW
178 
trace(JSTracer * trc)179 void ICEntry::trace(JSTracer* trc) {
180   ICStub* stub = firstStub();
181 
182   // Trace CacheIR stubs.
183   while (!stub->isFallback()) {
184     stub->toCacheIRStub()->trace(trc);
185     stub = stub->toCacheIRStub()->next();
186   }
187 
188   // Fallback stubs use runtime-wide trampoline code we don't need to trace.
189   MOZ_ASSERT(stub->usesTrampolineCode());
190 }
191 
192 // constexpr table mapping JSOp to BaselineICFallbackKind. Each value in the
193 // table is either a fallback kind or a sentinel value (NoICValue) indicating
194 // the JSOp is not a JOF_IC op.
195 class MOZ_STATIC_CLASS OpToFallbackKindTable {
196   static_assert(sizeof(BaselineICFallbackKind) == sizeof(uint8_t));
197   uint8_t table_[JSOP_LIMIT] = {};
198 
setKind(JSOp op,BaselineICFallbackKind kind)199   constexpr void setKind(JSOp op, BaselineICFallbackKind kind) {
200     MOZ_ASSERT(uint8_t(kind) != NoICValue);
201     table_[size_t(op)] = uint8_t(kind);
202   }
203 
204  public:
205   static constexpr uint8_t NoICValue = uint8_t(BaselineICFallbackKind::Count);
206 
lookup(JSOp op) const207   uint8_t lookup(JSOp op) const { return table_[size_t(op)]; }
208 
OpToFallbackKindTable()209   constexpr OpToFallbackKindTable() {
210     for (size_t i = 0; i < JSOP_LIMIT; i++) {
211       table_[i] = NoICValue;
212     }
213 
214     setKind(JSOp::Not, BaselineICFallbackKind::ToBool);
215     setKind(JSOp::And, BaselineICFallbackKind::ToBool);
216     setKind(JSOp::Or, BaselineICFallbackKind::ToBool);
217     setKind(JSOp::JumpIfTrue, BaselineICFallbackKind::ToBool);
218     setKind(JSOp::JumpIfFalse, BaselineICFallbackKind::ToBool);
219 
220     setKind(JSOp::BitNot, BaselineICFallbackKind::UnaryArith);
221     setKind(JSOp::Pos, BaselineICFallbackKind::UnaryArith);
222     setKind(JSOp::Neg, BaselineICFallbackKind::UnaryArith);
223     setKind(JSOp::Inc, BaselineICFallbackKind::UnaryArith);
224     setKind(JSOp::Dec, BaselineICFallbackKind::UnaryArith);
225     setKind(JSOp::ToNumeric, BaselineICFallbackKind::UnaryArith);
226 
227     setKind(JSOp::BitOr, BaselineICFallbackKind::BinaryArith);
228     setKind(JSOp::BitXor, BaselineICFallbackKind::BinaryArith);
229     setKind(JSOp::BitAnd, BaselineICFallbackKind::BinaryArith);
230     setKind(JSOp::Lsh, BaselineICFallbackKind::BinaryArith);
231     setKind(JSOp::Rsh, BaselineICFallbackKind::BinaryArith);
232     setKind(JSOp::Ursh, BaselineICFallbackKind::BinaryArith);
233     setKind(JSOp::Add, BaselineICFallbackKind::BinaryArith);
234     setKind(JSOp::Sub, BaselineICFallbackKind::BinaryArith);
235     setKind(JSOp::Mul, BaselineICFallbackKind::BinaryArith);
236     setKind(JSOp::Div, BaselineICFallbackKind::BinaryArith);
237     setKind(JSOp::Mod, BaselineICFallbackKind::BinaryArith);
238     setKind(JSOp::Pow, BaselineICFallbackKind::BinaryArith);
239 
240     setKind(JSOp::Eq, BaselineICFallbackKind::Compare);
241     setKind(JSOp::Ne, BaselineICFallbackKind::Compare);
242     setKind(JSOp::Lt, BaselineICFallbackKind::Compare);
243     setKind(JSOp::Le, BaselineICFallbackKind::Compare);
244     setKind(JSOp::Gt, BaselineICFallbackKind::Compare);
245     setKind(JSOp::Ge, BaselineICFallbackKind::Compare);
246     setKind(JSOp::StrictEq, BaselineICFallbackKind::Compare);
247     setKind(JSOp::StrictNe, BaselineICFallbackKind::Compare);
248 
249     setKind(JSOp::NewArray, BaselineICFallbackKind::NewArray);
250 
251     setKind(JSOp::NewObject, BaselineICFallbackKind::NewObject);
252     setKind(JSOp::NewInit, BaselineICFallbackKind::NewObject);
253 
254     setKind(JSOp::InitElem, BaselineICFallbackKind::SetElem);
255     setKind(JSOp::InitHiddenElem, BaselineICFallbackKind::SetElem);
256     setKind(JSOp::InitLockedElem, BaselineICFallbackKind::SetElem);
257     setKind(JSOp::InitElemInc, BaselineICFallbackKind::SetElem);
258     setKind(JSOp::SetElem, BaselineICFallbackKind::SetElem);
259     setKind(JSOp::StrictSetElem, BaselineICFallbackKind::SetElem);
260 
261     setKind(JSOp::InitProp, BaselineICFallbackKind::SetProp);
262     setKind(JSOp::InitLockedProp, BaselineICFallbackKind::SetProp);
263     setKind(JSOp::InitHiddenProp, BaselineICFallbackKind::SetProp);
264     setKind(JSOp::InitGLexical, BaselineICFallbackKind::SetProp);
265     setKind(JSOp::SetProp, BaselineICFallbackKind::SetProp);
266     setKind(JSOp::StrictSetProp, BaselineICFallbackKind::SetProp);
267     setKind(JSOp::SetName, BaselineICFallbackKind::SetProp);
268     setKind(JSOp::StrictSetName, BaselineICFallbackKind::SetProp);
269     setKind(JSOp::SetGName, BaselineICFallbackKind::SetProp);
270     setKind(JSOp::StrictSetGName, BaselineICFallbackKind::SetProp);
271 
272     setKind(JSOp::GetProp, BaselineICFallbackKind::GetProp);
273     setKind(JSOp::GetBoundName, BaselineICFallbackKind::GetProp);
274 
275     setKind(JSOp::GetPropSuper, BaselineICFallbackKind::GetPropSuper);
276 
277     setKind(JSOp::GetElem, BaselineICFallbackKind::GetElem);
278 
279     setKind(JSOp::GetElemSuper, BaselineICFallbackKind::GetElemSuper);
280 
281     setKind(JSOp::In, BaselineICFallbackKind::In);
282 
283     setKind(JSOp::HasOwn, BaselineICFallbackKind::HasOwn);
284 
285     setKind(JSOp::CheckPrivateField, BaselineICFallbackKind::CheckPrivateField);
286 
287     setKind(JSOp::GetName, BaselineICFallbackKind::GetName);
288     setKind(JSOp::GetGName, BaselineICFallbackKind::GetName);
289 
290     setKind(JSOp::BindName, BaselineICFallbackKind::BindName);
291     setKind(JSOp::BindGName, BaselineICFallbackKind::BindName);
292 
293     setKind(JSOp::GetIntrinsic, BaselineICFallbackKind::GetIntrinsic);
294 
295     setKind(JSOp::Call, BaselineICFallbackKind::Call);
296     setKind(JSOp::CallIgnoresRv, BaselineICFallbackKind::Call);
297     setKind(JSOp::CallIter, BaselineICFallbackKind::Call);
298     setKind(JSOp::FunCall, BaselineICFallbackKind::Call);
299     setKind(JSOp::FunApply, BaselineICFallbackKind::Call);
300     setKind(JSOp::Eval, BaselineICFallbackKind::Call);
301     setKind(JSOp::StrictEval, BaselineICFallbackKind::Call);
302 
303     setKind(JSOp::SuperCall, BaselineICFallbackKind::CallConstructing);
304     setKind(JSOp::New, BaselineICFallbackKind::CallConstructing);
305 
306     setKind(JSOp::SpreadCall, BaselineICFallbackKind::SpreadCall);
307     setKind(JSOp::SpreadEval, BaselineICFallbackKind::SpreadCall);
308     setKind(JSOp::StrictSpreadEval, BaselineICFallbackKind::SpreadCall);
309 
310     setKind(JSOp::SpreadSuperCall,
311             BaselineICFallbackKind::SpreadCallConstructing);
312     setKind(JSOp::SpreadNew, BaselineICFallbackKind::SpreadCallConstructing);
313 
314     setKind(JSOp::Instanceof, BaselineICFallbackKind::InstanceOf);
315 
316     setKind(JSOp::Typeof, BaselineICFallbackKind::TypeOf);
317     setKind(JSOp::TypeofExpr, BaselineICFallbackKind::TypeOf);
318 
319     setKind(JSOp::ToPropertyKey, BaselineICFallbackKind::ToPropertyKey);
320 
321     setKind(JSOp::Iter, BaselineICFallbackKind::GetIterator);
322 
323     setKind(JSOp::OptimizeSpreadCall,
324             BaselineICFallbackKind::OptimizeSpreadCall);
325 
326     setKind(JSOp::Rest, BaselineICFallbackKind::Rest);
327   }
328 };
329 
330 static constexpr OpToFallbackKindTable FallbackKindTable;
331 
initICEntries(JSContext * cx,JSScript * script)332 void ICScript::initICEntries(JSContext* cx, JSScript* script) {
333   MOZ_ASSERT(cx->realm()->jitRealm());
334   MOZ_ASSERT(jit::IsBaselineInterpreterEnabled());
335 
336   MOZ_ASSERT(numICEntries() == script->numICEntries());
337 
338   // Index of the next ICEntry to initialize.
339   uint32_t icEntryIndex = 0;
340 
341   const BaselineICFallbackCode& fallbackCode =
342       cx->runtime()->jitRuntime()->baselineICFallbackCode();
343 
344   // For JOF_IC ops: initialize ICEntries and fallback stubs.
345   for (BytecodeLocation loc : js::AllBytecodesIterable(script)) {
346     JSOp op = loc.getOp();
347 
348     // Assert the frontend stored the correct IC index in jump target ops.
349     MOZ_ASSERT_IF(BytecodeIsJumpTarget(op), loc.icIndex() == icEntryIndex);
350 
351     uint8_t tableValue = FallbackKindTable.lookup(op);
352 
353     if (tableValue == OpToFallbackKindTable::NoICValue) {
354       MOZ_ASSERT(!BytecodeOpHasIC(op),
355                  "Missing entry in OpToFallbackKindTable for JOF_IC op");
356       continue;
357     }
358 
359     MOZ_ASSERT(BytecodeOpHasIC(op),
360                "Unexpected fallback kind for non-JOF_IC op");
361 
362     BaselineICFallbackKind kind = BaselineICFallbackKind(tableValue);
363     TrampolinePtr stubCode = fallbackCode.addr(kind);
364 
365     // Initialize the ICEntry and ICFallbackStub.
366     uint32_t offset = loc.bytecodeToOffset(script);
367     ICEntry& entryRef = this->icEntry(icEntryIndex);
368     ICFallbackStub* stub = fallbackStub(icEntryIndex);
369     icEntryIndex++;
370     new (&entryRef) ICEntry(stub);
371     new (stub) ICFallbackStub(offset, stubCode);
372   }
373 
374   // Assert all ICEntries have been initialized.
375   MOZ_ASSERT(icEntryIndex == numICEntries());
376 }
377 
ICSupportsPolymorphicTypeData(JSOp op)378 bool ICSupportsPolymorphicTypeData(JSOp op) {
379   MOZ_ASSERT(BytecodeOpHasIC(op));
380   BaselineICFallbackKind kind =
381       BaselineICFallbackKind(FallbackKindTable.lookup(op));
382   switch (kind) {
383     case BaselineICFallbackKind::ToBool:
384     case BaselineICFallbackKind::TypeOf:
385       return true;
386     default:
387       return false;
388   }
389 }
390 
makesGCCalls() const391 bool ICCacheIRStub::makesGCCalls() const { return stubInfo()->makesGCCalls(); }
392 
trackNotAttached()393 void ICFallbackStub::trackNotAttached() { state().trackNotAttached(); }
394 
395 // When we enter a baseline fallback stub, if a Warp compilation
396 // exists that transpiled that IC, we notify that compilation. This
397 // helps the bailout code tell whether a bailing instruction hoisted
398 // by LICM would have been executed anyway.
MaybeNotifyWarp(JSScript * script,ICFallbackStub * stub)399 static void MaybeNotifyWarp(JSScript* script, ICFallbackStub* stub) {
400   if (stub->state().usedByTranspiler() && script->hasIonScript()) {
401     script->ionScript()->noteBaselineFallback();
402   }
403 }
404 
trace(JSTracer * trc)405 void ICCacheIRStub::trace(JSTracer* trc) {
406   JitCode* stubJitCode = jitCode();
407   TraceManuallyBarrieredEdge(trc, &stubJitCode, "baseline-ic-stub-code");
408 
409   TraceCacheIRStub(trc, this, stubInfo());
410 }
411 
MaybeTransition(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub)412 static void MaybeTransition(JSContext* cx, BaselineFrame* frame,
413                             ICFallbackStub* stub) {
414   if (stub->state().maybeTransition()) {
415     ICEntry* icEntry = frame->icScript()->icEntryForStub(stub);
416 #ifdef JS_CACHEIR_SPEW
417     if (cx->spewer().enabled(cx, frame->script(),
418                              SpewChannel::CacheIRHealthReport)) {
419       CacheIRHealth cih;
420       RootedScript script(cx, frame->script());
421       cih.healthReportForIC(cx, icEntry, stub, script, SpewContext::Transition);
422     }
423 #endif
424     stub->discardStubs(cx, icEntry);
425   }
426 }
427 
428 // This helper handles ICState updates/transitions while attaching CacheIR
429 // stubs.
430 template <typename IRGenerator, typename... Args>
TryAttachStub(const char * name,JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,Args &&...args)431 static void TryAttachStub(const char* name, JSContext* cx, BaselineFrame* frame,
432                           ICFallbackStub* stub, Args&&... args) {
433   MaybeTransition(cx, frame, stub);
434 
435   if (stub->state().canAttachStub()) {
436     RootedScript script(cx, frame->script());
437     ICScript* icScript = frame->icScript();
438     jsbytecode* pc = StubOffsetToPc(stub, script);
439     bool attached = false;
440     IRGenerator gen(cx, script, pc, stub->state(), std::forward<Args>(args)...);
441     switch (gen.tryAttachStub()) {
442       case AttachDecision::Attach: {
443         ICAttachResult result = AttachBaselineCacheIRStub(
444             cx, gen.writerRef(), gen.cacheKind(), script, icScript, stub);
445         if (result == ICAttachResult::Attached) {
446           attached = true;
447           JitSpew(JitSpew_BaselineIC, "  Attached %s CacheIR stub", name);
448         }
449       } break;
450       case AttachDecision::NoAction:
451         break;
452       case AttachDecision::TemporarilyUnoptimizable:
453       case AttachDecision::Deferred:
454         MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachStub");
455         break;
456     }
457     if (!attached) {
458       stub->trackNotAttached();
459     }
460   }
461 }
462 
unlinkStub(Zone * zone,ICEntry * icEntry,ICCacheIRStub * prev,ICCacheIRStub * stub)463 void ICFallbackStub::unlinkStub(Zone* zone, ICEntry* icEntry,
464                                 ICCacheIRStub* prev, ICCacheIRStub* stub) {
465   if (prev) {
466     MOZ_ASSERT(prev->next() == stub);
467     prev->setNext(stub->next());
468   } else {
469     MOZ_ASSERT(icEntry->firstStub() == stub);
470     icEntry->setFirstStub(stub->next());
471   }
472 
473   state_.trackUnlinkedStub();
474 
475   // We are removing edges from ICStub to gcthings. Perform a barrier to let the
476   // GC know about those edges.
477   PreWriteBarrier(zone, stub);
478 
479 #ifdef DEBUG
480   // Poison stub code to ensure we don't call this stub again. However, if
481   // this stub can make calls, a pointer to it may be stored in a stub frame
482   // on the stack, so we can't touch the stubCode_ or GC will crash when
483   // tracing this pointer.
484   if (!stub->makesGCCalls()) {
485     stub->stubCode_ = (uint8_t*)0xbad;
486   }
487 #endif
488 }
489 
discardStubs(JSContext * cx,ICEntry * icEntry)490 void ICFallbackStub::discardStubs(JSContext* cx, ICEntry* icEntry) {
491   ICStub* stub = icEntry->firstStub();
492   while (stub != this) {
493     unlinkStub(cx->zone(), icEntry, /* prev = */ nullptr,
494                stub->toCacheIRStub());
495     stub = stub->toCacheIRStub()->next();
496   }
497 }
498 
InitMacroAssemblerForICStub(StackMacroAssembler & masm)499 static void InitMacroAssemblerForICStub(StackMacroAssembler& masm) {
500 #ifndef JS_USE_LINK_REGISTER
501   // The first value contains the return addres,
502   // which we pull into ICTailCallReg for tail calls.
503   masm.adjustFrame(sizeof(intptr_t));
504 #endif
505 #ifdef JS_CODEGEN_ARM
506   masm.setSecondScratchReg(BaselineSecondScratchReg);
507 #endif
508 }
509 
tailCallVMInternal(MacroAssembler & masm,TailCallVMFunctionId id)510 bool FallbackICCodeCompiler::tailCallVMInternal(MacroAssembler& masm,
511                                                 TailCallVMFunctionId id) {
512   TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
513   const VMFunctionData& fun = GetVMFunction(id);
514   MOZ_ASSERT(fun.expectTailCall == TailCall);
515   uint32_t argSize = fun.explicitStackSlots() * sizeof(void*);
516   EmitBaselineTailCallVM(code, masm, argSize);
517   return true;
518 }
519 
callVMInternal(MacroAssembler & masm,VMFunctionId id)520 bool FallbackICCodeCompiler::callVMInternal(MacroAssembler& masm,
521                                             VMFunctionId id) {
522   MOZ_ASSERT(inStubFrame_);
523 
524   TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(id);
525   MOZ_ASSERT(GetVMFunction(id).expectTailCall == NonTailCall);
526 
527   EmitBaselineCallVM(code, masm);
528   return true;
529 }
530 
531 template <typename Fn, Fn fn>
callVM(MacroAssembler & masm)532 bool FallbackICCodeCompiler::callVM(MacroAssembler& masm) {
533   VMFunctionId id = VMFunctionToId<Fn, fn>::id;
534   return callVMInternal(masm, id);
535 }
536 
537 template <typename Fn, Fn fn>
tailCallVM(MacroAssembler & masm)538 bool FallbackICCodeCompiler::tailCallVM(MacroAssembler& masm) {
539   TailCallVMFunctionId id = TailCallVMFunctionToId<Fn, fn>::id;
540   return tailCallVMInternal(masm, id);
541 }
542 
enterStubFrame(MacroAssembler & masm,Register scratch)543 void FallbackICCodeCompiler::enterStubFrame(MacroAssembler& masm,
544                                             Register scratch) {
545   EmitBaselineEnterStubFrame(masm, scratch);
546 #ifdef DEBUG
547   framePushedAtEnterStubFrame_ = masm.framePushed();
548 #endif
549 
550   MOZ_ASSERT(!inStubFrame_);
551   inStubFrame_ = true;
552 
553 #ifdef DEBUG
554   entersStubFrame_ = true;
555 #endif
556 }
557 
assumeStubFrame()558 void FallbackICCodeCompiler::assumeStubFrame() {
559   MOZ_ASSERT(!inStubFrame_);
560   inStubFrame_ = true;
561 
562 #ifdef DEBUG
563   entersStubFrame_ = true;
564 
565   // |framePushed| isn't tracked precisely in ICStubs, so simply assume it to
566   // be STUB_FRAME_SIZE so that assertions don't fail in leaveStubFrame.
567   framePushedAtEnterStubFrame_ = STUB_FRAME_SIZE;
568 #endif
569 }
570 
leaveStubFrame(MacroAssembler & masm,bool calledIntoIon)571 void FallbackICCodeCompiler::leaveStubFrame(MacroAssembler& masm,
572                                             bool calledIntoIon) {
573   MOZ_ASSERT(entersStubFrame_ && inStubFrame_);
574   inStubFrame_ = false;
575 
576 #ifdef DEBUG
577   masm.setFramePushed(framePushedAtEnterStubFrame_);
578   if (calledIntoIon) {
579     masm.adjustFrame(sizeof(intptr_t));  // Calls into ion have this extra.
580   }
581 #endif
582   EmitBaselineLeaveStubFrame(masm, calledIntoIon);
583 }
584 
pushStubPayload(MacroAssembler & masm,Register scratch)585 void FallbackICCodeCompiler::pushStubPayload(MacroAssembler& masm,
586                                              Register scratch) {
587   if (inStubFrame_) {
588     masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
589     masm.pushBaselineFramePtr(scratch, scratch);
590   } else {
591     masm.pushBaselineFramePtr(BaselineFrameReg, scratch);
592   }
593 }
594 
PushStubPayload(MacroAssembler & masm,Register scratch)595 void FallbackICCodeCompiler::PushStubPayload(MacroAssembler& masm,
596                                              Register scratch) {
597   pushStubPayload(masm, scratch);
598   masm.adjustFrame(sizeof(intptr_t));
599 }
600 
601 //
602 // ToBool_Fallback
603 //
604 
DoToBoolFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue arg,MutableHandleValue ret)605 bool DoToBoolFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
606                       HandleValue arg, MutableHandleValue ret) {
607   stub->incrementEnteredCount();
608   MaybeNotifyWarp(frame->outerScript(), stub);
609   FallbackICSpew(cx, stub, "ToBool");
610 
611   TryAttachStub<ToBoolIRGenerator>("ToBool", cx, frame, stub, arg);
612 
613   bool cond = ToBoolean(arg);
614   ret.setBoolean(cond);
615 
616   return true;
617 }
618 
emit_ToBool()619 bool FallbackICCodeCompiler::emit_ToBool() {
620   static_assert(R0 == JSReturnOperand);
621 
622   // Restore the tail call register.
623   EmitRestoreTailCallReg(masm);
624 
625   // Push arguments.
626   masm.pushValue(R0);
627   masm.push(ICStubReg);
628   pushStubPayload(masm, R0.scratchReg());
629 
630   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
631                       MutableHandleValue);
632   return tailCallVM<Fn, DoToBoolFallback>(masm);
633 }
634 
635 //
636 // GetElem_Fallback
637 //
638 
DoGetElemFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue lhs,HandleValue rhs,MutableHandleValue res)639 bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame,
640                        ICFallbackStub* stub, HandleValue lhs, HandleValue rhs,
641                        MutableHandleValue res) {
642   stub->incrementEnteredCount();
643   MaybeNotifyWarp(frame->outerScript(), stub);
644   FallbackICSpew(cx, stub, "GetElem");
645 
646 #ifdef DEBUG
647   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
648   MOZ_ASSERT(JSOp(*pc) == JSOp::GetElem);
649 #endif
650 
651   TryAttachStub<GetPropIRGenerator>("GetElem", cx, frame, stub,
652                                     CacheKind::GetElem, lhs, rhs);
653 
654   if (!GetElementOperation(cx, lhs, rhs, res)) {
655     return false;
656   }
657 
658   return true;
659 }
660 
DoGetElemSuperFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue lhs,HandleValue rhs,HandleValue receiver,MutableHandleValue res)661 bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame,
662                             ICFallbackStub* stub, HandleValue lhs,
663                             HandleValue rhs, HandleValue receiver,
664                             MutableHandleValue res) {
665   stub->incrementEnteredCount();
666   MaybeNotifyWarp(frame->outerScript(), stub);
667 
668   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
669 
670   JSOp op = JSOp(*pc);
671   FallbackICSpew(cx, stub, "GetElemSuper(%s)", CodeName(op));
672 
673   MOZ_ASSERT(op == JSOp::GetElemSuper);
674 
675   // |lhs| is [[HomeObject]].[[Prototype]] which must be an Object or null.
676   MOZ_ASSERT(lhs.isObjectOrNull());
677 
678   int lhsIndex = -1;
679   RootedObject lhsObj(
680       cx, ToObjectFromStackForPropertyAccess(cx, lhs, lhsIndex, rhs));
681   if (!lhsObj) {
682     return false;
683   }
684 
685   TryAttachStub<GetPropIRGenerator>("GetElemSuper", cx, frame, stub,
686                                     CacheKind::GetElemSuper, lhs, rhs);
687 
688   return GetObjectElementOperation(cx, op, lhsObj, receiver, rhs, res);
689 }
690 
emitGetElem(bool hasReceiver)691 bool FallbackICCodeCompiler::emitGetElem(bool hasReceiver) {
692   static_assert(R0 == JSReturnOperand);
693 
694   // Restore the tail call register.
695   EmitRestoreTailCallReg(masm);
696 
697   // Super property getters use a |this| that differs from base object
698   if (hasReceiver) {
699     // State: receiver in R0, index in R1, obj on the stack
700 
701     // Ensure stack is fully synced for the expression decompiler.
702     // We need: receiver, index, obj
703     masm.pushValue(R0);
704     masm.pushValue(R1);
705     masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 2));
706 
707     // Push arguments.
708     masm.pushValue(R0);  // Receiver
709     masm.pushValue(R1);  // Index
710     masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 5));  // Obj
711     masm.push(ICStubReg);
712     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
713 
714     using Fn =
715         bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
716                  HandleValue, HandleValue, MutableHandleValue);
717     if (!tailCallVM<Fn, DoGetElemSuperFallback>(masm)) {
718       return false;
719     }
720   } else {
721     // Ensure stack is fully synced for the expression decompiler.
722     masm.pushValue(R0);
723     masm.pushValue(R1);
724 
725     // Push arguments.
726     masm.pushValue(R1);
727     masm.pushValue(R0);
728     masm.push(ICStubReg);
729     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
730 
731     using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*,
732                         HandleValue, HandleValue, MutableHandleValue);
733     if (!tailCallVM<Fn, DoGetElemFallback>(masm)) {
734       return false;
735     }
736   }
737 
738   // This is the resume point used when bailout rewrites call stack to undo
739   // Ion inlined frames. The return address pushed onto reconstructed stack
740   // will point here.
741   assumeStubFrame();
742   if (hasReceiver) {
743     code.initBailoutReturnOffset(BailoutReturnKind::GetElemSuper,
744                                  masm.currentOffset());
745   } else {
746     code.initBailoutReturnOffset(BailoutReturnKind::GetElem,
747                                  masm.currentOffset());
748   }
749 
750   leaveStubFrame(masm, true);
751 
752   EmitReturnFromIC(masm);
753   return true;
754 }
755 
emit_GetElem()756 bool FallbackICCodeCompiler::emit_GetElem() {
757   return emitGetElem(/* hasReceiver = */ false);
758 }
759 
emit_GetElemSuper()760 bool FallbackICCodeCompiler::emit_GetElemSuper() {
761   return emitGetElem(/* hasReceiver = */ true);
762 }
763 
DoSetElemFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,Value * stack,HandleValue objv,HandleValue index,HandleValue rhs)764 bool DoSetElemFallback(JSContext* cx, BaselineFrame* frame,
765                        ICFallbackStub* stub, Value* stack, HandleValue objv,
766                        HandleValue index, HandleValue rhs) {
767   using DeferType = SetPropIRGenerator::DeferType;
768 
769   stub->incrementEnteredCount();
770   MaybeNotifyWarp(frame->outerScript(), stub);
771 
772   RootedScript script(cx, frame->script());
773   RootedScript outerScript(cx, script);
774   jsbytecode* pc = StubOffsetToPc(stub, script);
775   JSOp op = JSOp(*pc);
776   FallbackICSpew(cx, stub, "SetElem(%s)", CodeName(JSOp(*pc)));
777 
778   MOZ_ASSERT(op == JSOp::SetElem || op == JSOp::StrictSetElem ||
779              op == JSOp::InitElem || op == JSOp::InitHiddenElem ||
780              op == JSOp::InitLockedElem || op == JSOp::InitElemInc);
781 
782   int objvIndex = -3;
783   RootedObject obj(
784       cx, ToObjectFromStackForPropertyAccess(cx, objv, objvIndex, index));
785   if (!obj) {
786     return false;
787   }
788 
789   RootedShape oldShape(cx, obj->shape());
790 
791   DeferType deferType = DeferType::None;
792   bool attached = false;
793 
794   MaybeTransition(cx, frame, stub);
795 
796   if (stub->state().canAttachStub()) {
797     ICScript* icScript = frame->icScript();
798     SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state(),
799                            objv, index, rhs);
800     switch (gen.tryAttachStub()) {
801       case AttachDecision::Attach: {
802         ICAttachResult result =
803             AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
804                                       frame->script(), icScript, stub);
805         if (result == ICAttachResult::Attached) {
806           attached = true;
807           JitSpew(JitSpew_BaselineIC, "  Attached SetElem CacheIR stub");
808         }
809       } break;
810       case AttachDecision::NoAction:
811         break;
812       case AttachDecision::TemporarilyUnoptimizable:
813         attached = true;
814         break;
815       case AttachDecision::Deferred:
816         deferType = gen.deferType();
817         MOZ_ASSERT(deferType != DeferType::None);
818         break;
819     }
820   }
821 
822   if (op == JSOp::InitElem || op == JSOp::InitHiddenElem ||
823       op == JSOp::InitLockedElem) {
824     if (!InitElemOperation(cx, pc, obj, index, rhs)) {
825       return false;
826     }
827   } else if (op == JSOp::InitElemInc) {
828     if (!InitElemIncOperation(cx, obj.as<ArrayObject>(), index.toInt32(),
829                               rhs)) {
830       return false;
831     }
832   } else {
833     if (!SetObjectElementWithReceiver(cx, obj, index, rhs, objv,
834                                       JSOp(*pc) == JSOp::StrictSetElem)) {
835       return false;
836     }
837   }
838 
839   // Don't try to attach stubs that wish to be hidden. We don't know how to
840   // have different enumerability in the stubs for the moment.
841   if (op == JSOp::InitHiddenElem) {
842     return true;
843   }
844 
845   // Overwrite the object on the stack (pushed for the decompiler) with the rhs.
846   MOZ_ASSERT(stack[2] == objv);
847   stack[2] = rhs;
848 
849   if (attached) {
850     return true;
851   }
852 
853   // The SetObjectElement call might have entered this IC recursively, so try
854   // to transition.
855   MaybeTransition(cx, frame, stub);
856 
857   bool canAttachStub = stub->state().canAttachStub();
858 
859   if (deferType != DeferType::None && canAttachStub) {
860     SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state(),
861                            objv, index, rhs);
862 
863     MOZ_ASSERT(deferType == DeferType::AddSlot);
864     AttachDecision decision = gen.tryAttachAddSlotStub(oldShape);
865 
866     switch (decision) {
867       case AttachDecision::Attach: {
868         ICScript* icScript = frame->icScript();
869         ICAttachResult result =
870             AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
871                                       frame->script(), icScript, stub);
872         if (result == ICAttachResult::Attached) {
873           attached = true;
874           JitSpew(JitSpew_BaselineIC, "  Attached SetElem CacheIR stub");
875         }
876       } break;
877       case AttachDecision::NoAction:
878         gen.trackAttached(IRGenerator::NotAttached);
879         break;
880       case AttachDecision::TemporarilyUnoptimizable:
881       case AttachDecision::Deferred:
882         MOZ_ASSERT_UNREACHABLE("Invalid attach result");
883         break;
884     }
885   }
886   if (!attached && canAttachStub) {
887     stub->trackNotAttached();
888   }
889   return true;
890 }
891 
emit_SetElem()892 bool FallbackICCodeCompiler::emit_SetElem() {
893   static_assert(R0 == JSReturnOperand);
894 
895   EmitRestoreTailCallReg(masm);
896 
897   // State: R0: object, R1: index, stack: rhs.
898   // For the decompiler, the stack has to be: object, index, rhs,
899   // so we push the index, then overwrite the rhs Value with R0
900   // and push the rhs value.
901   masm.pushValue(R1);
902   masm.loadValue(Address(masm.getStackPointer(), sizeof(Value)), R1);
903   masm.storeValue(R0, Address(masm.getStackPointer(), sizeof(Value)));
904   masm.pushValue(R1);
905 
906   // Push arguments.
907   masm.pushValue(R1);  // RHS
908 
909   // Push index. On x86 and ARM two push instructions are emitted so use a
910   // separate register to store the old stack pointer.
911   masm.moveStackPtrTo(R1.scratchReg());
912   masm.pushValue(Address(R1.scratchReg(), 2 * sizeof(Value)));
913   masm.pushValue(R0);  // Object.
914 
915   // Push pointer to stack values, so that the stub can overwrite the object
916   // (pushed for the decompiler) with the rhs.
917   masm.computeEffectiveAddress(
918       Address(masm.getStackPointer(), 3 * sizeof(Value)), R0.scratchReg());
919   masm.push(R0.scratchReg());
920 
921   masm.push(ICStubReg);
922   pushStubPayload(masm, R0.scratchReg());
923 
924   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, Value*,
925                       HandleValue, HandleValue, HandleValue);
926   return tailCallVM<Fn, DoSetElemFallback>(masm);
927 }
928 
929 //
930 // In_Fallback
931 //
932 
DoInFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue key,HandleValue objValue,MutableHandleValue res)933 bool DoInFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
934                   HandleValue key, HandleValue objValue,
935                   MutableHandleValue res) {
936   stub->incrementEnteredCount();
937   MaybeNotifyWarp(frame->outerScript(), stub);
938   FallbackICSpew(cx, stub, "In");
939 
940   if (!objValue.isObject()) {
941     ReportInNotObjectError(cx, key, objValue);
942     return false;
943   }
944 
945   TryAttachStub<HasPropIRGenerator>("In", cx, frame, stub, CacheKind::In, key,
946                                     objValue);
947 
948   RootedObject obj(cx, &objValue.toObject());
949   bool cond = false;
950   if (!OperatorIn(cx, key, obj, &cond)) {
951     return false;
952   }
953   res.setBoolean(cond);
954 
955   return true;
956 }
957 
emit_In()958 bool FallbackICCodeCompiler::emit_In() {
959   EmitRestoreTailCallReg(masm);
960 
961   // Sync for the decompiler.
962   masm.pushValue(R0);
963   masm.pushValue(R1);
964 
965   // Push arguments.
966   masm.pushValue(R1);
967   masm.pushValue(R0);
968   masm.push(ICStubReg);
969   pushStubPayload(masm, R0.scratchReg());
970 
971   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
972                       HandleValue, MutableHandleValue);
973   return tailCallVM<Fn, DoInFallback>(masm);
974 }
975 
976 //
977 // HasOwn_Fallback
978 //
979 
DoHasOwnFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue keyValue,HandleValue objValue,MutableHandleValue res)980 bool DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
981                       HandleValue keyValue, HandleValue objValue,
982                       MutableHandleValue res) {
983   stub->incrementEnteredCount();
984   MaybeNotifyWarp(frame->outerScript(), stub);
985   FallbackICSpew(cx, stub, "HasOwn");
986 
987   TryAttachStub<HasPropIRGenerator>("HasOwn", cx, frame, stub,
988                                     CacheKind::HasOwn, keyValue, objValue);
989 
990   bool found;
991   if (!HasOwnProperty(cx, objValue, keyValue, &found)) {
992     return false;
993   }
994 
995   res.setBoolean(found);
996   return true;
997 }
998 
emit_HasOwn()999 bool FallbackICCodeCompiler::emit_HasOwn() {
1000   EmitRestoreTailCallReg(masm);
1001 
1002   // Sync for the decompiler.
1003   masm.pushValue(R0);
1004   masm.pushValue(R1);
1005 
1006   // Push arguments.
1007   masm.pushValue(R1);
1008   masm.pushValue(R0);
1009   masm.push(ICStubReg);
1010   pushStubPayload(masm, R0.scratchReg());
1011 
1012   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1013                       HandleValue, MutableHandleValue);
1014   return tailCallVM<Fn, DoHasOwnFallback>(masm);
1015 }
1016 
1017 //
1018 // CheckPrivate_Fallback
1019 //
1020 
DoCheckPrivateFieldFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue objValue,HandleValue keyValue,MutableHandleValue res)1021 bool DoCheckPrivateFieldFallback(JSContext* cx, BaselineFrame* frame,
1022                                  ICFallbackStub* stub, HandleValue objValue,
1023                                  HandleValue keyValue, MutableHandleValue res) {
1024   stub->incrementEnteredCount();
1025   MaybeNotifyWarp(frame->outerScript(), stub);
1026 
1027   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
1028 
1029   FallbackICSpew(cx, stub, "CheckPrivateField");
1030 
1031   MOZ_ASSERT(keyValue.isSymbol() && keyValue.toSymbol()->isPrivateName());
1032 
1033   TryAttachStub<CheckPrivateFieldIRGenerator>("CheckPrivate", cx, frame, stub,
1034                                               CacheKind::CheckPrivateField,
1035                                               keyValue, objValue);
1036 
1037   bool result;
1038   if (!CheckPrivateFieldOperation(cx, pc, objValue, keyValue, &result)) {
1039     return false;
1040   }
1041 
1042   res.setBoolean(result);
1043   return true;
1044 }
1045 
emit_CheckPrivateField()1046 bool FallbackICCodeCompiler::emit_CheckPrivateField() {
1047   EmitRestoreTailCallReg(masm);
1048 
1049   // Sync for the decompiler.
1050   masm.pushValue(R0);
1051   masm.pushValue(R1);
1052 
1053   // Push arguments.
1054   masm.pushValue(R1);
1055   masm.pushValue(R0);
1056   masm.push(ICStubReg);
1057   pushStubPayload(masm, R0.scratchReg());
1058 
1059   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1060                       HandleValue, MutableHandleValue);
1061   return tailCallVM<Fn, DoCheckPrivateFieldFallback>(masm);
1062 }
1063 
1064 //
1065 // GetName_Fallback
1066 //
1067 
DoGetNameFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleObject envChain,MutableHandleValue res)1068 bool DoGetNameFallback(JSContext* cx, BaselineFrame* frame,
1069                        ICFallbackStub* stub, HandleObject envChain,
1070                        MutableHandleValue res) {
1071   stub->incrementEnteredCount();
1072   MaybeNotifyWarp(frame->outerScript(), stub);
1073 
1074   RootedScript script(cx, frame->script());
1075   jsbytecode* pc = StubOffsetToPc(stub, script);
1076   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
1077   FallbackICSpew(cx, stub, "GetName(%s)", CodeName(JSOp(*pc)));
1078 
1079   MOZ_ASSERT(op == JSOp::GetName || op == JSOp::GetGName);
1080 
1081   RootedPropertyName name(cx, script->getName(pc));
1082 
1083   TryAttachStub<GetNameIRGenerator>("GetName", cx, frame, stub, envChain, name);
1084 
1085   static_assert(JSOpLength_GetGName == JSOpLength_GetName,
1086                 "Otherwise our check for JSOp::Typeof isn't ok");
1087   if (JSOp(pc[JSOpLength_GetGName]) == JSOp::Typeof) {
1088     if (!GetEnvironmentName<GetNameMode::TypeOf>(cx, envChain, name, res)) {
1089       return false;
1090     }
1091   } else {
1092     if (!GetEnvironmentName<GetNameMode::Normal>(cx, envChain, name, res)) {
1093       return false;
1094     }
1095   }
1096 
1097   return true;
1098 }
1099 
emit_GetName()1100 bool FallbackICCodeCompiler::emit_GetName() {
1101   static_assert(R0 == JSReturnOperand);
1102 
1103   EmitRestoreTailCallReg(masm);
1104 
1105   masm.push(R0.scratchReg());
1106   masm.push(ICStubReg);
1107   pushStubPayload(masm, R0.scratchReg());
1108 
1109   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleObject,
1110                       MutableHandleValue);
1111   return tailCallVM<Fn, DoGetNameFallback>(masm);
1112 }
1113 
1114 //
1115 // BindName_Fallback
1116 //
1117 
DoBindNameFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleObject envChain,MutableHandleValue res)1118 bool DoBindNameFallback(JSContext* cx, BaselineFrame* frame,
1119                         ICFallbackStub* stub, HandleObject envChain,
1120                         MutableHandleValue res) {
1121   stub->incrementEnteredCount();
1122   MaybeNotifyWarp(frame->outerScript(), stub);
1123 
1124   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
1125   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
1126   FallbackICSpew(cx, stub, "BindName(%s)", CodeName(JSOp(*pc)));
1127 
1128   MOZ_ASSERT(op == JSOp::BindName || op == JSOp::BindGName);
1129 
1130   RootedPropertyName name(cx, frame->script()->getName(pc));
1131 
1132   TryAttachStub<BindNameIRGenerator>("BindName", cx, frame, stub, envChain,
1133                                      name);
1134 
1135   RootedObject scope(cx);
1136   if (!LookupNameUnqualified(cx, name, envChain, &scope)) {
1137     return false;
1138   }
1139 
1140   res.setObject(*scope);
1141   return true;
1142 }
1143 
emit_BindName()1144 bool FallbackICCodeCompiler::emit_BindName() {
1145   static_assert(R0 == JSReturnOperand);
1146 
1147   EmitRestoreTailCallReg(masm);
1148 
1149   masm.push(R0.scratchReg());
1150   masm.push(ICStubReg);
1151   pushStubPayload(masm, R0.scratchReg());
1152 
1153   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleObject,
1154                       MutableHandleValue);
1155   return tailCallVM<Fn, DoBindNameFallback>(masm);
1156 }
1157 
1158 //
1159 // GetIntrinsic_Fallback
1160 //
1161 
DoGetIntrinsicFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,MutableHandleValue res)1162 bool DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame,
1163                             ICFallbackStub* stub, MutableHandleValue res) {
1164   stub->incrementEnteredCount();
1165   MaybeNotifyWarp(frame->outerScript(), stub);
1166 
1167   RootedScript script(cx, frame->script());
1168   jsbytecode* pc = StubOffsetToPc(stub, script);
1169   mozilla::DebugOnly<JSOp> op = JSOp(*pc);
1170   FallbackICSpew(cx, stub, "GetIntrinsic(%s)", CodeName(JSOp(*pc)));
1171 
1172   MOZ_ASSERT(op == JSOp::GetIntrinsic);
1173 
1174   if (!GetIntrinsicOperation(cx, script, pc, res)) {
1175     return false;
1176   }
1177 
1178   TryAttachStub<GetIntrinsicIRGenerator>("GetIntrinsic", cx, frame, stub, res);
1179 
1180   return true;
1181 }
1182 
emit_GetIntrinsic()1183 bool FallbackICCodeCompiler::emit_GetIntrinsic() {
1184   EmitRestoreTailCallReg(masm);
1185 
1186   masm.push(ICStubReg);
1187   pushStubPayload(masm, R0.scratchReg());
1188 
1189   using Fn =
1190       bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, MutableHandleValue);
1191   return tailCallVM<Fn, DoGetIntrinsicFallback>(masm);
1192 }
1193 
1194 //
1195 // GetProp_Fallback
1196 //
1197 
DoGetPropFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,MutableHandleValue val,MutableHandleValue res)1198 bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame,
1199                        ICFallbackStub* stub, MutableHandleValue val,
1200                        MutableHandleValue res) {
1201   stub->incrementEnteredCount();
1202   MaybeNotifyWarp(frame->outerScript(), stub);
1203 
1204   RootedScript script(cx, frame->script());
1205   jsbytecode* pc = StubOffsetToPc(stub, script);
1206   JSOp op = JSOp(*pc);
1207   FallbackICSpew(cx, stub, "GetProp(%s)", CodeName(op));
1208 
1209   MOZ_ASSERT(op == JSOp::GetProp || op == JSOp::GetBoundName);
1210 
1211   RootedPropertyName name(cx, script->getName(pc));
1212   RootedValue idVal(cx, StringValue(name));
1213 
1214   TryAttachStub<GetPropIRGenerator>("GetProp", cx, frame, stub,
1215                                     CacheKind::GetProp, val, idVal);
1216 
1217   if (op == JSOp::GetBoundName) {
1218     RootedObject env(cx, &val.toObject());
1219     RootedId id(cx, NameToId(name));
1220     return GetNameBoundInEnvironment(cx, env, id, res);
1221   }
1222 
1223   MOZ_ASSERT(op == JSOp::GetProp);
1224   if (!GetProperty(cx, val, name, res)) {
1225     return false;
1226   }
1227 
1228   return true;
1229 }
1230 
DoGetPropSuperFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue receiver,MutableHandleValue val,MutableHandleValue res)1231 bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame,
1232                             ICFallbackStub* stub, HandleValue receiver,
1233                             MutableHandleValue val, MutableHandleValue res) {
1234   stub->incrementEnteredCount();
1235   MaybeNotifyWarp(frame->outerScript(), stub);
1236 
1237   RootedScript script(cx, frame->script());
1238   jsbytecode* pc = StubOffsetToPc(stub, script);
1239   FallbackICSpew(cx, stub, "GetPropSuper(%s)", CodeName(JSOp(*pc)));
1240 
1241   MOZ_ASSERT(JSOp(*pc) == JSOp::GetPropSuper);
1242 
1243   RootedPropertyName name(cx, script->getName(pc));
1244   RootedValue idVal(cx, StringValue(name));
1245 
1246   // |val| is [[HomeObject]].[[Prototype]] which must be an Object or null.
1247   MOZ_ASSERT(val.isObjectOrNull());
1248 
1249   int valIndex = -1;
1250   RootedObject valObj(
1251       cx, ToObjectFromStackForPropertyAccess(cx, val, valIndex, name));
1252   if (!valObj) {
1253     return false;
1254   }
1255 
1256   TryAttachStub<GetPropIRGenerator>("GetPropSuper", cx, frame, stub,
1257                                     CacheKind::GetPropSuper, val, idVal);
1258 
1259   if (!GetProperty(cx, valObj, receiver, name, res)) {
1260     return false;
1261   }
1262 
1263   return true;
1264 }
1265 
emitGetProp(bool hasReceiver)1266 bool FallbackICCodeCompiler::emitGetProp(bool hasReceiver) {
1267   static_assert(R0 == JSReturnOperand);
1268 
1269   EmitRestoreTailCallReg(masm);
1270 
1271   // Super property getters use a |this| that differs from base object
1272   if (hasReceiver) {
1273     // Push arguments.
1274     masm.pushValue(R0);
1275     masm.pushValue(R1);
1276     masm.push(ICStubReg);
1277     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
1278 
1279     using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*,
1280                         HandleValue, MutableHandleValue, MutableHandleValue);
1281     if (!tailCallVM<Fn, DoGetPropSuperFallback>(masm)) {
1282       return false;
1283     }
1284   } else {
1285     // Ensure stack is fully synced for the expression decompiler.
1286     masm.pushValue(R0);
1287 
1288     // Push arguments.
1289     masm.pushValue(R0);
1290     masm.push(ICStubReg);
1291     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
1292 
1293     using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*,
1294                         MutableHandleValue, MutableHandleValue);
1295     if (!tailCallVM<Fn, DoGetPropFallback>(masm)) {
1296       return false;
1297     }
1298   }
1299 
1300   // This is the resume point used when bailout rewrites call stack to undo
1301   // Ion inlined frames. The return address pushed onto reconstructed stack
1302   // will point here.
1303   assumeStubFrame();
1304   if (hasReceiver) {
1305     code.initBailoutReturnOffset(BailoutReturnKind::GetPropSuper,
1306                                  masm.currentOffset());
1307   } else {
1308     code.initBailoutReturnOffset(BailoutReturnKind::GetProp,
1309                                  masm.currentOffset());
1310   }
1311 
1312   leaveStubFrame(masm, true);
1313 
1314   EmitReturnFromIC(masm);
1315   return true;
1316 }
1317 
emit_GetProp()1318 bool FallbackICCodeCompiler::emit_GetProp() {
1319   return emitGetProp(/* hasReceiver = */ false);
1320 }
1321 
emit_GetPropSuper()1322 bool FallbackICCodeCompiler::emit_GetPropSuper() {
1323   return emitGetProp(/* hasReceiver = */ true);
1324 }
1325 
1326 //
1327 // SetProp_Fallback
1328 //
1329 
DoSetPropFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,Value * stack,HandleValue lhs,HandleValue rhs)1330 bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame,
1331                        ICFallbackStub* stub, Value* stack, HandleValue lhs,
1332                        HandleValue rhs) {
1333   using DeferType = SetPropIRGenerator::DeferType;
1334 
1335   stub->incrementEnteredCount();
1336   MaybeNotifyWarp(frame->outerScript(), stub);
1337 
1338   RootedScript script(cx, frame->script());
1339   jsbytecode* pc = StubOffsetToPc(stub, script);
1340   JSOp op = JSOp(*pc);
1341   FallbackICSpew(cx, stub, "SetProp(%s)", CodeName(op));
1342 
1343   MOZ_ASSERT(op == JSOp::SetProp || op == JSOp::StrictSetProp ||
1344              op == JSOp::SetName || op == JSOp::StrictSetName ||
1345              op == JSOp::SetGName || op == JSOp::StrictSetGName ||
1346              op == JSOp::InitProp || op == JSOp::InitLockedProp ||
1347              op == JSOp::InitHiddenProp || op == JSOp::InitGLexical);
1348 
1349   RootedPropertyName name(cx, script->getName(pc));
1350   RootedId id(cx, NameToId(name));
1351 
1352   int lhsIndex = -2;
1353   RootedObject obj(cx,
1354                    ToObjectFromStackForPropertyAccess(cx, lhs, lhsIndex, id));
1355   if (!obj) {
1356     return false;
1357   }
1358   RootedShape oldShape(cx, obj->shape());
1359 
1360   DeferType deferType = DeferType::None;
1361   bool attached = false;
1362   MaybeTransition(cx, frame, stub);
1363 
1364   if (stub->state().canAttachStub()) {
1365     RootedValue idVal(cx, StringValue(name));
1366     SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state(),
1367                            lhs, idVal, rhs);
1368     switch (gen.tryAttachStub()) {
1369       case AttachDecision::Attach: {
1370         ICScript* icScript = frame->icScript();
1371         ICAttachResult result =
1372             AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
1373                                       frame->script(), icScript, stub);
1374         if (result == ICAttachResult::Attached) {
1375           attached = true;
1376           JitSpew(JitSpew_BaselineIC, "  Attached SetProp CacheIR stub");
1377         }
1378       } break;
1379       case AttachDecision::NoAction:
1380         break;
1381       case AttachDecision::TemporarilyUnoptimizable:
1382         attached = true;
1383         break;
1384       case AttachDecision::Deferred:
1385         deferType = gen.deferType();
1386         MOZ_ASSERT(deferType != DeferType::None);
1387         break;
1388     }
1389   }
1390 
1391   if (op == JSOp::InitProp || op == JSOp::InitLockedProp ||
1392       op == JSOp::InitHiddenProp) {
1393     if (!InitPropertyOperation(cx, op, obj, name, rhs)) {
1394       return false;
1395     }
1396   } else if (op == JSOp::SetName || op == JSOp::StrictSetName ||
1397              op == JSOp::SetGName || op == JSOp::StrictSetGName) {
1398     if (!SetNameOperation(cx, script, pc, obj, rhs)) {
1399       return false;
1400     }
1401   } else if (op == JSOp::InitGLexical) {
1402     ExtensibleLexicalEnvironmentObject* lexicalEnv;
1403     if (script->hasNonSyntacticScope()) {
1404       lexicalEnv = &NearestEnclosingExtensibleLexicalEnvironment(
1405           frame->environmentChain());
1406     } else {
1407       lexicalEnv = &cx->global()->lexicalEnvironment();
1408     }
1409     InitGlobalLexicalOperation(cx, lexicalEnv, script, pc, rhs);
1410   } else {
1411     MOZ_ASSERT(op == JSOp::SetProp || op == JSOp::StrictSetProp);
1412 
1413     ObjectOpResult result;
1414     if (!SetProperty(cx, obj, id, rhs, lhs, result) ||
1415         !result.checkStrictModeError(cx, obj, id, op == JSOp::StrictSetProp)) {
1416       return false;
1417     }
1418   }
1419 
1420   // Overwrite the LHS on the stack (pushed for the decompiler) with the RHS.
1421   MOZ_ASSERT(stack[1] == lhs);
1422   stack[1] = rhs;
1423 
1424   if (attached) {
1425     return true;
1426   }
1427 
1428   // The SetProperty call might have entered this IC recursively, so try
1429   // to transition.
1430   MaybeTransition(cx, frame, stub);
1431 
1432   bool canAttachStub = stub->state().canAttachStub();
1433 
1434   if (deferType != DeferType::None && canAttachStub) {
1435     RootedValue idVal(cx, StringValue(name));
1436     SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state(),
1437                            lhs, idVal, rhs);
1438 
1439     MOZ_ASSERT(deferType == DeferType::AddSlot);
1440     AttachDecision decision = gen.tryAttachAddSlotStub(oldShape);
1441 
1442     switch (decision) {
1443       case AttachDecision::Attach: {
1444         ICScript* icScript = frame->icScript();
1445         ICAttachResult result =
1446             AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
1447                                       frame->script(), icScript, stub);
1448         if (result == ICAttachResult::Attached) {
1449           attached = true;
1450           JitSpew(JitSpew_BaselineIC, "  Attached SetElem CacheIR stub");
1451         }
1452       } break;
1453       case AttachDecision::NoAction:
1454         gen.trackAttached(IRGenerator::NotAttached);
1455         break;
1456       case AttachDecision::TemporarilyUnoptimizable:
1457       case AttachDecision::Deferred:
1458         MOZ_ASSERT_UNREACHABLE("Invalid attach result");
1459         break;
1460     }
1461   }
1462   if (!attached && canAttachStub) {
1463     stub->trackNotAttached();
1464   }
1465 
1466   return true;
1467 }
1468 
emit_SetProp()1469 bool FallbackICCodeCompiler::emit_SetProp() {
1470   static_assert(R0 == JSReturnOperand);
1471 
1472   EmitRestoreTailCallReg(masm);
1473 
1474   // Ensure stack is fully synced for the expression decompiler.
1475   // Overwrite the RHS value on top of the stack with the object, then push
1476   // the RHS in R1 on top of that.
1477   masm.storeValue(R0, Address(masm.getStackPointer(), 0));
1478   masm.pushValue(R1);
1479 
1480   // Push arguments.
1481   masm.pushValue(R1);
1482   masm.pushValue(R0);
1483 
1484   // Push pointer to stack values, so that the stub can overwrite the object
1485   // (pushed for the decompiler) with the RHS.
1486   masm.computeEffectiveAddress(
1487       Address(masm.getStackPointer(), 2 * sizeof(Value)), R0.scratchReg());
1488   masm.push(R0.scratchReg());
1489 
1490   masm.push(ICStubReg);
1491   pushStubPayload(masm, R0.scratchReg());
1492 
1493   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, Value*,
1494                       HandleValue, HandleValue);
1495   if (!tailCallVM<Fn, DoSetPropFallback>(masm)) {
1496     return false;
1497   }
1498 
1499   // This is the resume point used when bailout rewrites call stack to undo
1500   // Ion inlined frames. The return address pushed onto reconstructed stack
1501   // will point here.
1502   assumeStubFrame();
1503   code.initBailoutReturnOffset(BailoutReturnKind::SetProp,
1504                                masm.currentOffset());
1505 
1506   leaveStubFrame(masm, true);
1507   EmitReturnFromIC(masm);
1508 
1509   return true;
1510 }
1511 
1512 //
1513 // Call_Fallback
1514 //
1515 
DoCallFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,uint32_t argc,Value * vp,MutableHandleValue res)1516 bool DoCallFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
1517                     uint32_t argc, Value* vp, MutableHandleValue res) {
1518   stub->incrementEnteredCount();
1519   MaybeNotifyWarp(frame->outerScript(), stub);
1520 
1521   RootedScript script(cx, frame->script());
1522   jsbytecode* pc = StubOffsetToPc(stub, script);
1523   JSOp op = JSOp(*pc);
1524   FallbackICSpew(cx, stub, "Call(%s)", CodeName(op));
1525 
1526   MOZ_ASSERT(argc == GET_ARGC(pc));
1527   bool constructing = (op == JSOp::New || op == JSOp::SuperCall);
1528   bool ignoresReturnValue = (op == JSOp::CallIgnoresRv);
1529 
1530   // Ensure vp array is rooted - we may GC in here.
1531   size_t numValues = argc + 2 + constructing;
1532   RootedExternalValueArray vpRoot(cx, numValues, vp);
1533 
1534   CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues,
1535                                      constructing, ignoresReturnValue);
1536   RootedValue callee(cx, vp[0]);
1537   RootedValue newTarget(cx, constructing ? callArgs.newTarget() : NullValue());
1538 
1539   // Transition stub state to megamorphic or generic if warranted.
1540   MaybeTransition(cx, frame, stub);
1541 
1542   bool canAttachStub = stub->state().canAttachStub();
1543   bool handled = false;
1544 
1545   // Only bother to try optimizing JSOp::Call with CacheIR if the chain is still
1546   // allowed to attach stubs.
1547   if (canAttachStub) {
1548     HandleValueArray args = HandleValueArray::fromMarkedLocation(argc, vp + 2);
1549     CallIRGenerator gen(cx, script, pc, op, stub->state(), argc, callee,
1550                         callArgs.thisv(), newTarget, args);
1551     switch (gen.tryAttachStub()) {
1552       case AttachDecision::NoAction:
1553         break;
1554       case AttachDecision::Attach: {
1555         ICScript* icScript = frame->icScript();
1556         ICAttachResult result = AttachBaselineCacheIRStub(
1557             cx, gen.writerRef(), gen.cacheKind(), script, icScript, stub);
1558         if (result == ICAttachResult::Attached) {
1559           handled = true;
1560           JitSpew(JitSpew_BaselineIC, "  Attached Call CacheIR stub");
1561         }
1562       } break;
1563       case AttachDecision::TemporarilyUnoptimizable:
1564         handled = true;
1565         break;
1566       case AttachDecision::Deferred:
1567         MOZ_CRASH("No deferred Call stubs");
1568     }
1569     if (!handled) {
1570       stub->trackNotAttached();
1571     }
1572   }
1573 
1574   if (constructing) {
1575     if (!ConstructFromStack(cx, callArgs)) {
1576       return false;
1577     }
1578     res.set(callArgs.rval());
1579   } else if ((op == JSOp::Eval || op == JSOp::StrictEval) &&
1580              cx->global()->valueIsEval(callee)) {
1581     if (!DirectEval(cx, callArgs.get(0), res)) {
1582       return false;
1583     }
1584   } else {
1585     MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallIgnoresRv ||
1586                op == JSOp::CallIter || op == JSOp::FunCall ||
1587                op == JSOp::FunApply || op == JSOp::Eval ||
1588                op == JSOp::StrictEval);
1589     if (op == JSOp::CallIter && callee.isPrimitive()) {
1590       MOZ_ASSERT(argc == 0, "thisv must be on top of the stack");
1591       ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, callArgs.thisv(), nullptr);
1592       return false;
1593     }
1594 
1595     if (!CallFromStack(cx, callArgs)) {
1596       return false;
1597     }
1598 
1599     res.set(callArgs.rval());
1600   }
1601 
1602   return true;
1603 }
1604 
DoSpreadCallFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,Value * vp,MutableHandleValue res)1605 bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame,
1606                           ICFallbackStub* stub, Value* vp,
1607                           MutableHandleValue res) {
1608   stub->incrementEnteredCount();
1609   MaybeNotifyWarp(frame->outerScript(), stub);
1610 
1611   RootedScript script(cx, frame->script());
1612   jsbytecode* pc = StubOffsetToPc(stub, script);
1613   JSOp op = JSOp(*pc);
1614   bool constructing = (op == JSOp::SpreadNew || op == JSOp::SpreadSuperCall);
1615   FallbackICSpew(cx, stub, "SpreadCall(%s)", CodeName(op));
1616 
1617   // Ensure vp array is rooted - we may GC in here.
1618   RootedExternalValueArray vpRoot(cx, 3 + constructing, vp);
1619 
1620   RootedValue callee(cx, vp[0]);
1621   RootedValue thisv(cx, vp[1]);
1622   RootedValue arr(cx, vp[2]);
1623   RootedValue newTarget(cx, constructing ? vp[3] : NullValue());
1624 
1625   // Transition stub state to megamorphic or generic if warranted.
1626   MaybeTransition(cx, frame, stub);
1627 
1628   // Try attaching a call stub.
1629   bool handled = false;
1630   if (op != JSOp::SpreadEval && op != JSOp::StrictSpreadEval &&
1631       stub->state().canAttachStub()) {
1632     // Try CacheIR first:
1633     RootedArrayObject aobj(cx, &arr.toObject().as<ArrayObject>());
1634     MOZ_ASSERT(aobj->length() == aobj->getDenseInitializedLength());
1635 
1636     HandleValueArray args = HandleValueArray::fromMarkedLocation(
1637         aobj->length(), aobj->getDenseElements());
1638     CallIRGenerator gen(cx, script, pc, op, stub->state(), 1, callee, thisv,
1639                         newTarget, args);
1640     switch (gen.tryAttachStub()) {
1641       case AttachDecision::NoAction:
1642         break;
1643       case AttachDecision::Attach: {
1644         ICScript* icScript = frame->icScript();
1645         ICAttachResult result = AttachBaselineCacheIRStub(
1646             cx, gen.writerRef(), gen.cacheKind(), script, icScript, stub);
1647 
1648         if (result == ICAttachResult::Attached) {
1649           handled = true;
1650           JitSpew(JitSpew_BaselineIC, "  Attached Spread Call CacheIR stub");
1651         }
1652       } break;
1653       case AttachDecision::TemporarilyUnoptimizable:
1654         handled = true;
1655         break;
1656       case AttachDecision::Deferred:
1657         MOZ_ASSERT_UNREACHABLE("No deferred optimizations for spread calls");
1658         break;
1659     }
1660     if (!handled) {
1661       stub->trackNotAttached();
1662     }
1663   }
1664 
1665   return SpreadCallOperation(cx, script, pc, thisv, callee, arr, newTarget,
1666                              res);
1667 }
1668 
pushCallArguments(MacroAssembler & masm,AllocatableGeneralRegisterSet regs,Register argcReg,bool isConstructing)1669 void FallbackICCodeCompiler::pushCallArguments(
1670     MacroAssembler& masm, AllocatableGeneralRegisterSet regs, Register argcReg,
1671     bool isConstructing) {
1672   MOZ_ASSERT(!regs.has(argcReg));
1673 
1674   // argPtr initially points to the last argument.
1675   Register argPtr = regs.takeAny();
1676   masm.moveStackPtrTo(argPtr);
1677 
1678   // Skip 4 pointers pushed on top of the arguments: the frame descriptor,
1679   // return address, old frame pointer and stub reg.
1680   size_t valueOffset = STUB_FRAME_SIZE;
1681 
1682   // We have to push |this|, callee, new.target (if constructing) and argc
1683   // arguments. Handle the number of Values we know statically first.
1684 
1685   size_t numNonArgValues = 2 + isConstructing;
1686   for (size_t i = 0; i < numNonArgValues; i++) {
1687     masm.pushValue(Address(argPtr, valueOffset));
1688     valueOffset += sizeof(Value);
1689   }
1690 
1691   // If there are no arguments we're done.
1692   Label done;
1693   masm.branchTest32(Assembler::Zero, argcReg, argcReg, &done);
1694 
1695   // Push argc Values.
1696   Label loop;
1697   Register count = regs.takeAny();
1698   masm.addPtr(Imm32(valueOffset), argPtr);
1699   masm.move32(argcReg, count);
1700   masm.bind(&loop);
1701   {
1702     masm.pushValue(Address(argPtr, 0));
1703     masm.addPtr(Imm32(sizeof(Value)), argPtr);
1704 
1705     masm.branchSub32(Assembler::NonZero, Imm32(1), count, &loop);
1706   }
1707   masm.bind(&done);
1708 }
1709 
emitCall(bool isSpread,bool isConstructing)1710 bool FallbackICCodeCompiler::emitCall(bool isSpread, bool isConstructing) {
1711   static_assert(R0 == JSReturnOperand);
1712 
1713   // Values are on the stack left-to-right. Calling convention wants them
1714   // right-to-left so duplicate them on the stack in reverse order.
1715   // |this| and callee are pushed last.
1716 
1717   AllocatableGeneralRegisterSet regs = BaselineICAvailableGeneralRegs(0);
1718 
1719   if (MOZ_UNLIKELY(isSpread)) {
1720     // Push a stub frame so that we can perform a non-tail call.
1721     enterStubFrame(masm, R1.scratchReg());
1722 
1723     // Use BaselineFrameReg instead of BaselineStackReg, because
1724     // BaselineFrameReg and BaselineStackReg hold the same value just after
1725     // calling enterStubFrame.
1726 
1727     // newTarget
1728     uint32_t valueOffset = 0;
1729     if (isConstructing) {
1730       masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
1731       valueOffset++;
1732     }
1733 
1734     // array
1735     masm.pushValue(Address(BaselineFrameReg,
1736                            valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
1737     valueOffset++;
1738 
1739     // this
1740     masm.pushValue(Address(BaselineFrameReg,
1741                            valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
1742     valueOffset++;
1743 
1744     // callee
1745     masm.pushValue(Address(BaselineFrameReg,
1746                            valueOffset * sizeof(Value) + STUB_FRAME_SIZE));
1747     valueOffset++;
1748 
1749     masm.push(masm.getStackPointer());
1750     masm.push(ICStubReg);
1751 
1752     PushStubPayload(masm, R0.scratchReg());
1753 
1754     using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, Value*,
1755                         MutableHandleValue);
1756     if (!callVM<Fn, DoSpreadCallFallback>(masm)) {
1757       return false;
1758     }
1759 
1760     leaveStubFrame(masm);
1761     EmitReturnFromIC(masm);
1762 
1763     // SpreadCall is not yet supported in Ion, so do not generate asmcode for
1764     // bailout.
1765     return true;
1766   }
1767 
1768   // Push a stub frame so that we can perform a non-tail call.
1769   enterStubFrame(masm, R1.scratchReg());
1770 
1771   regs.take(R0.scratchReg());  // argc.
1772 
1773   pushCallArguments(masm, regs, R0.scratchReg(), isConstructing);
1774 
1775   masm.push(masm.getStackPointer());
1776   masm.push(R0.scratchReg());
1777   masm.push(ICStubReg);
1778 
1779   PushStubPayload(masm, R0.scratchReg());
1780 
1781   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, uint32_t,
1782                       Value*, MutableHandleValue);
1783   if (!callVM<Fn, DoCallFallback>(masm)) {
1784     return false;
1785   }
1786 
1787   leaveStubFrame(masm);
1788   EmitReturnFromIC(masm);
1789 
1790   // This is the resume point used when bailout rewrites call stack to undo
1791   // Ion inlined frames. The return address pushed onto reconstructed stack
1792   // will point here.
1793   assumeStubFrame();
1794 
1795   MOZ_ASSERT(!isSpread);
1796 
1797   if (isConstructing) {
1798     code.initBailoutReturnOffset(BailoutReturnKind::New, masm.currentOffset());
1799   } else {
1800     code.initBailoutReturnOffset(BailoutReturnKind::Call, masm.currentOffset());
1801   }
1802 
1803   // Load passed-in ThisV into R1 just in case it's needed.  Need to do this
1804   // before we leave the stub frame since that info will be lost.
1805   // Current stack:  [...., ThisV, ActualArgc, CalleeToken, Descriptor ]
1806   masm.loadValue(Address(masm.getStackPointer(), 3 * sizeof(size_t)), R1);
1807 
1808   leaveStubFrame(masm, true);
1809 
1810   // If this is a |constructing| call, if the callee returns a non-object, we
1811   // replace it with the |this| object passed in.
1812   if (isConstructing) {
1813     static_assert(JSReturnOperand == R0);
1814     Label skipThisReplace;
1815 
1816     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
1817     masm.moveValue(R1, R0);
1818 #ifdef DEBUG
1819     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
1820     masm.assumeUnreachable("Failed to return object in constructing call.");
1821 #endif
1822     masm.bind(&skipThisReplace);
1823   }
1824 
1825   EmitReturnFromIC(masm);
1826   return true;
1827 }
1828 
emit_Call()1829 bool FallbackICCodeCompiler::emit_Call() {
1830   return emitCall(/* isSpread = */ false, /* isConstructing = */ false);
1831 }
1832 
emit_CallConstructing()1833 bool FallbackICCodeCompiler::emit_CallConstructing() {
1834   return emitCall(/* isSpread = */ false, /* isConstructing = */ true);
1835 }
1836 
emit_SpreadCall()1837 bool FallbackICCodeCompiler::emit_SpreadCall() {
1838   return emitCall(/* isSpread = */ true, /* isConstructing = */ false);
1839 }
1840 
emit_SpreadCallConstructing()1841 bool FallbackICCodeCompiler::emit_SpreadCallConstructing() {
1842   return emitCall(/* isSpread = */ true, /* isConstructing = */ true);
1843 }
1844 
1845 //
1846 // GetIterator_Fallback
1847 //
1848 
DoGetIteratorFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue value,MutableHandleValue res)1849 bool DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame,
1850                            ICFallbackStub* stub, HandleValue value,
1851                            MutableHandleValue res) {
1852   stub->incrementEnteredCount();
1853   MaybeNotifyWarp(frame->outerScript(), stub);
1854   FallbackICSpew(cx, stub, "GetIterator");
1855 
1856   TryAttachStub<GetIteratorIRGenerator>("GetIterator", cx, frame, stub, value);
1857 
1858   JSObject* iterobj = ValueToIterator(cx, value);
1859   if (!iterobj) {
1860     return false;
1861   }
1862 
1863   res.setObject(*iterobj);
1864   return true;
1865 }
1866 
emit_GetIterator()1867 bool FallbackICCodeCompiler::emit_GetIterator() {
1868   EmitRestoreTailCallReg(masm);
1869 
1870   // Sync stack for the decompiler.
1871   masm.pushValue(R0);
1872 
1873   masm.pushValue(R0);
1874   masm.push(ICStubReg);
1875   pushStubPayload(masm, R0.scratchReg());
1876 
1877   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1878                       MutableHandleValue);
1879   return tailCallVM<Fn, DoGetIteratorFallback>(masm);
1880 }
1881 
1882 //
1883 // OptimizeSpreadCall_Fallback
1884 //
1885 
DoOptimizeSpreadCallFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue value,MutableHandleValue res)1886 bool DoOptimizeSpreadCallFallback(JSContext* cx, BaselineFrame* frame,
1887                                   ICFallbackStub* stub, HandleValue value,
1888                                   MutableHandleValue res) {
1889   stub->incrementEnteredCount();
1890   MaybeNotifyWarp(frame->outerScript(), stub);
1891   FallbackICSpew(cx, stub, "OptimizeSpreadCall");
1892 
1893   TryAttachStub<OptimizeSpreadCallIRGenerator>("OptimizeSpreadCall", cx, frame,
1894                                                stub, value);
1895 
1896   return OptimizeSpreadCall(cx, value, res);
1897 }
1898 
emit_OptimizeSpreadCall()1899 bool FallbackICCodeCompiler::emit_OptimizeSpreadCall() {
1900   EmitRestoreTailCallReg(masm);
1901 
1902   masm.pushValue(R0);
1903   masm.push(ICStubReg);
1904   pushStubPayload(masm, R0.scratchReg());
1905 
1906   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1907                       MutableHandleValue);
1908   return tailCallVM<Fn, DoOptimizeSpreadCallFallback>(masm);
1909 }
1910 
1911 //
1912 // InstanceOf_Fallback
1913 //
1914 
DoInstanceOfFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue lhs,HandleValue rhs,MutableHandleValue res)1915 bool DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame,
1916                           ICFallbackStub* stub, HandleValue lhs,
1917                           HandleValue rhs, MutableHandleValue res) {
1918   stub->incrementEnteredCount();
1919   MaybeNotifyWarp(frame->outerScript(), stub);
1920   FallbackICSpew(cx, stub, "InstanceOf");
1921 
1922   if (!rhs.isObject()) {
1923     ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, nullptr);
1924     return false;
1925   }
1926 
1927   RootedObject obj(cx, &rhs.toObject());
1928   bool cond = false;
1929   if (!HasInstance(cx, obj, lhs, &cond)) {
1930     return false;
1931   }
1932 
1933   res.setBoolean(cond);
1934 
1935   if (!obj->is<JSFunction>()) {
1936     // ensure we've recorded at least one failure, so we can detect there was a
1937     // non-optimizable case
1938     if (!stub->state().hasFailures()) {
1939       stub->trackNotAttached();
1940     }
1941     return true;
1942   }
1943 
1944   TryAttachStub<InstanceOfIRGenerator>("InstanceOf", cx, frame, stub, lhs, obj);
1945   return true;
1946 }
1947 
emit_InstanceOf()1948 bool FallbackICCodeCompiler::emit_InstanceOf() {
1949   EmitRestoreTailCallReg(masm);
1950 
1951   // Sync stack for the decompiler.
1952   masm.pushValue(R0);
1953   masm.pushValue(R1);
1954 
1955   masm.pushValue(R1);
1956   masm.pushValue(R0);
1957   masm.push(ICStubReg);
1958   pushStubPayload(masm, R0.scratchReg());
1959 
1960   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1961                       HandleValue, MutableHandleValue);
1962   return tailCallVM<Fn, DoInstanceOfFallback>(masm);
1963 }
1964 
1965 //
1966 // TypeOf_Fallback
1967 //
1968 
DoTypeOfFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue val,MutableHandleValue res)1969 bool DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
1970                       HandleValue val, MutableHandleValue res) {
1971   stub->incrementEnteredCount();
1972   MaybeNotifyWarp(frame->outerScript(), stub);
1973   FallbackICSpew(cx, stub, "TypeOf");
1974 
1975   TryAttachStub<TypeOfIRGenerator>("TypeOf", cx, frame, stub, val);
1976 
1977   JSType type = js::TypeOfValue(val);
1978   RootedString string(cx, TypeName(type, cx->names()));
1979   res.setString(string);
1980   return true;
1981 }
1982 
emit_TypeOf()1983 bool FallbackICCodeCompiler::emit_TypeOf() {
1984   EmitRestoreTailCallReg(masm);
1985 
1986   masm.pushValue(R0);
1987   masm.push(ICStubReg);
1988   pushStubPayload(masm, R0.scratchReg());
1989 
1990   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
1991                       MutableHandleValue);
1992   return tailCallVM<Fn, DoTypeOfFallback>(masm);
1993 }
1994 
1995 //
1996 // ToPropertyKey_Fallback
1997 //
1998 
DoToPropertyKeyFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue val,MutableHandleValue res)1999 bool DoToPropertyKeyFallback(JSContext* cx, BaselineFrame* frame,
2000                              ICFallbackStub* stub, HandleValue val,
2001                              MutableHandleValue res) {
2002   stub->incrementEnteredCount();
2003   MaybeNotifyWarp(frame->outerScript(), stub);
2004   FallbackICSpew(cx, stub, "ToPropertyKey");
2005 
2006   TryAttachStub<ToPropertyKeyIRGenerator>("ToPropertyKey", cx, frame, stub,
2007                                           val);
2008 
2009   return ToPropertyKeyOperation(cx, val, res);
2010 }
2011 
emit_ToPropertyKey()2012 bool FallbackICCodeCompiler::emit_ToPropertyKey() {
2013   EmitRestoreTailCallReg(masm);
2014 
2015   masm.pushValue(R0);
2016   masm.push(ICStubReg);
2017   pushStubPayload(masm, R0.scratchReg());
2018 
2019   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
2020                       MutableHandleValue);
2021   return tailCallVM<Fn, DoToPropertyKeyFallback>(masm);
2022 }
2023 
2024 //
2025 // Rest_Fallback
2026 //
2027 
DoRestFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,MutableHandleValue res)2028 bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
2029                     MutableHandleValue res) {
2030   unsigned numFormals = frame->numFormalArgs() - 1;
2031   unsigned numActuals = frame->numActualArgs();
2032   unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
2033   Value* rest = frame->argv() + numFormals;
2034 
2035   ArrayObject* obj = NewDenseCopiedArray(cx, numRest, rest);
2036   if (!obj) {
2037     return false;
2038   }
2039   res.setObject(*obj);
2040   return true;
2041 }
2042 
emit_Rest()2043 bool FallbackICCodeCompiler::emit_Rest() {
2044   EmitRestoreTailCallReg(masm);
2045 
2046   masm.push(ICStubReg);
2047   pushStubPayload(masm, R0.scratchReg());
2048 
2049   using Fn =
2050       bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, MutableHandleValue);
2051   return tailCallVM<Fn, DoRestFallback>(masm);
2052 }
2053 
2054 //
2055 // UnaryArith_Fallback
2056 //
2057 
DoUnaryArithFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue val,MutableHandleValue res)2058 bool DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame,
2059                           ICFallbackStub* stub, HandleValue val,
2060                           MutableHandleValue res) {
2061   stub->incrementEnteredCount();
2062   MaybeNotifyWarp(frame->outerScript(), stub);
2063 
2064   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
2065   JSOp op = JSOp(*pc);
2066   FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName(op));
2067 
2068   switch (op) {
2069     case JSOp::BitNot: {
2070       res.set(val);
2071       if (!BitNot(cx, res, res)) {
2072         return false;
2073       }
2074       break;
2075     }
2076     case JSOp::Pos: {
2077       res.set(val);
2078       if (!ToNumber(cx, res)) {
2079         return false;
2080       }
2081       break;
2082     }
2083     case JSOp::Neg: {
2084       res.set(val);
2085       if (!NegOperation(cx, res, res)) {
2086         return false;
2087       }
2088       break;
2089     }
2090     case JSOp::Inc: {
2091       if (!IncOperation(cx, val, res)) {
2092         return false;
2093       }
2094       break;
2095     }
2096     case JSOp::Dec: {
2097       if (!DecOperation(cx, val, res)) {
2098         return false;
2099       }
2100       break;
2101     }
2102     case JSOp::ToNumeric: {
2103       res.set(val);
2104       if (!ToNumeric(cx, res)) {
2105         return false;
2106       }
2107       break;
2108     }
2109     default:
2110       MOZ_CRASH("Unexpected op");
2111   }
2112   MOZ_ASSERT(res.isNumeric());
2113 
2114   TryAttachStub<UnaryArithIRGenerator>("UnaryArith", cx, frame, stub, op, val,
2115                                        res);
2116   return true;
2117 }
2118 
emit_UnaryArith()2119 bool FallbackICCodeCompiler::emit_UnaryArith() {
2120   static_assert(R0 == JSReturnOperand);
2121 
2122   // Restore the tail call register.
2123   EmitRestoreTailCallReg(masm);
2124 
2125   // Ensure stack is fully synced for the expression decompiler.
2126   masm.pushValue(R0);
2127 
2128   // Push arguments.
2129   masm.pushValue(R0);
2130   masm.push(ICStubReg);
2131   pushStubPayload(masm, R0.scratchReg());
2132 
2133   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
2134                       MutableHandleValue);
2135   return tailCallVM<Fn, DoUnaryArithFallback>(masm);
2136 }
2137 
2138 //
2139 // BinaryArith_Fallback
2140 //
2141 
DoBinaryArithFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue lhs,HandleValue rhs,MutableHandleValue ret)2142 bool DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame,
2143                            ICFallbackStub* stub, HandleValue lhs,
2144                            HandleValue rhs, MutableHandleValue ret) {
2145   stub->incrementEnteredCount();
2146   MaybeNotifyWarp(frame->outerScript(), stub);
2147 
2148   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
2149   JSOp op = JSOp(*pc);
2150   FallbackICSpew(
2151       cx, stub, "CacheIRBinaryArith(%s,%d,%d)", CodeName(op),
2152       int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()),
2153       int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType()));
2154 
2155   // Don't pass lhs/rhs directly, we need the original values when
2156   // generating stubs.
2157   RootedValue lhsCopy(cx, lhs);
2158   RootedValue rhsCopy(cx, rhs);
2159 
2160   // Perform the arith operation.
2161   switch (op) {
2162     case JSOp::Add:
2163       // Do an add.
2164       if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) {
2165         return false;
2166       }
2167       break;
2168     case JSOp::Sub:
2169       if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) {
2170         return false;
2171       }
2172       break;
2173     case JSOp::Mul:
2174       if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) {
2175         return false;
2176       }
2177       break;
2178     case JSOp::Div:
2179       if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) {
2180         return false;
2181       }
2182       break;
2183     case JSOp::Mod:
2184       if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) {
2185         return false;
2186       }
2187       break;
2188     case JSOp::Pow:
2189       if (!PowValues(cx, &lhsCopy, &rhsCopy, ret)) {
2190         return false;
2191       }
2192       break;
2193     case JSOp::BitOr: {
2194       if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) {
2195         return false;
2196       }
2197       break;
2198     }
2199     case JSOp::BitXor: {
2200       if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) {
2201         return false;
2202       }
2203       break;
2204     }
2205     case JSOp::BitAnd: {
2206       if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) {
2207         return false;
2208       }
2209       break;
2210     }
2211     case JSOp::Lsh: {
2212       if (!BitLsh(cx, &lhsCopy, &rhsCopy, ret)) {
2213         return false;
2214       }
2215       break;
2216     }
2217     case JSOp::Rsh: {
2218       if (!BitRsh(cx, &lhsCopy, &rhsCopy, ret)) {
2219         return false;
2220       }
2221       break;
2222     }
2223     case JSOp::Ursh: {
2224       if (!UrshValues(cx, &lhsCopy, &rhsCopy, ret)) {
2225         return false;
2226       }
2227       break;
2228     }
2229     default:
2230       MOZ_CRASH("Unhandled baseline arith op");
2231   }
2232 
2233   TryAttachStub<BinaryArithIRGenerator>("BinaryArith", cx, frame, stub, op, lhs,
2234                                         rhs, ret);
2235   return true;
2236 }
2237 
emit_BinaryArith()2238 bool FallbackICCodeCompiler::emit_BinaryArith() {
2239   static_assert(R0 == JSReturnOperand);
2240 
2241   // Restore the tail call register.
2242   EmitRestoreTailCallReg(masm);
2243 
2244   // Ensure stack is fully synced for the expression decompiler.
2245   masm.pushValue(R0);
2246   masm.pushValue(R1);
2247 
2248   // Push arguments.
2249   masm.pushValue(R1);
2250   masm.pushValue(R0);
2251   masm.push(ICStubReg);
2252   pushStubPayload(masm, R0.scratchReg());
2253 
2254   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
2255                       HandleValue, MutableHandleValue);
2256   return tailCallVM<Fn, DoBinaryArithFallback>(masm);
2257 }
2258 
2259 //
2260 // Compare_Fallback
2261 //
DoCompareFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,HandleValue lhs,HandleValue rhs,MutableHandleValue ret)2262 bool DoCompareFallback(JSContext* cx, BaselineFrame* frame,
2263                        ICFallbackStub* stub, HandleValue lhs, HandleValue rhs,
2264                        MutableHandleValue ret) {
2265   stub->incrementEnteredCount();
2266   MaybeNotifyWarp(frame->outerScript(), stub);
2267 
2268   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
2269   JSOp op = JSOp(*pc);
2270 
2271   FallbackICSpew(cx, stub, "Compare(%s)", CodeName(op));
2272 
2273   // Don't pass lhs/rhs directly, we need the original values when
2274   // generating stubs.
2275   RootedValue lhsCopy(cx, lhs);
2276   RootedValue rhsCopy(cx, rhs);
2277 
2278   // Perform the compare operation.
2279   bool out;
2280   switch (op) {
2281     case JSOp::Lt:
2282       if (!LessThan(cx, &lhsCopy, &rhsCopy, &out)) {
2283         return false;
2284       }
2285       break;
2286     case JSOp::Le:
2287       if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) {
2288         return false;
2289       }
2290       break;
2291     case JSOp::Gt:
2292       if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out)) {
2293         return false;
2294       }
2295       break;
2296     case JSOp::Ge:
2297       if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) {
2298         return false;
2299       }
2300       break;
2301     case JSOp::Eq:
2302       if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, &out)) {
2303         return false;
2304       }
2305       break;
2306     case JSOp::Ne:
2307       if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, &out)) {
2308         return false;
2309       }
2310       out = !out;
2311       break;
2312     case JSOp::StrictEq:
2313       if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, &out)) {
2314         return false;
2315       }
2316       break;
2317     case JSOp::StrictNe:
2318       if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, &out)) {
2319         return false;
2320       }
2321       out = !out;
2322       break;
2323     default:
2324       MOZ_ASSERT_UNREACHABLE("Unhandled baseline compare op");
2325       return false;
2326   }
2327 
2328   ret.setBoolean(out);
2329 
2330   TryAttachStub<CompareIRGenerator>("Compare", cx, frame, stub, op, lhs, rhs);
2331   return true;
2332 }
2333 
emit_Compare()2334 bool FallbackICCodeCompiler::emit_Compare() {
2335   static_assert(R0 == JSReturnOperand);
2336 
2337   // Restore the tail call register.
2338   EmitRestoreTailCallReg(masm);
2339 
2340   // Ensure stack is fully synced for the expression decompiler.
2341   masm.pushValue(R0);
2342   masm.pushValue(R1);
2343 
2344   // Push arguments.
2345   masm.pushValue(R1);
2346   masm.pushValue(R0);
2347   masm.push(ICStubReg);
2348   pushStubPayload(masm, R0.scratchReg());
2349 
2350   using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, HandleValue,
2351                       HandleValue, MutableHandleValue);
2352   return tailCallVM<Fn, DoCompareFallback>(masm);
2353 }
2354 
2355 //
2356 // NewArray_Fallback
2357 //
2358 
DoNewArrayFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,MutableHandleValue res)2359 bool DoNewArrayFallback(JSContext* cx, BaselineFrame* frame,
2360                         ICFallbackStub* stub, MutableHandleValue res) {
2361   stub->incrementEnteredCount();
2362   MaybeNotifyWarp(frame->outerScript(), stub);
2363   FallbackICSpew(cx, stub, "NewArray");
2364 
2365   jsbytecode* pc = StubOffsetToPc(stub, frame->script());
2366 
2367   uint32_t length = GET_UINT32(pc);
2368   MOZ_ASSERT(length <= INT32_MAX,
2369              "the bytecode emitter must fail to compile code that would "
2370              "produce a length exceeding int32_t range");
2371 
2372   RootedArrayObject array(cx, NewArrayOperation(cx, length));
2373   if (!array) {
2374     return false;
2375   }
2376 
2377   TryAttachStub<NewArrayIRGenerator>("NewArray", cx, frame, stub, JSOp(*pc),
2378                                      array, frame);
2379 
2380   res.setObject(*array);
2381   return true;
2382 }
2383 
emit_NewArray()2384 bool FallbackICCodeCompiler::emit_NewArray() {
2385   EmitRestoreTailCallReg(masm);
2386 
2387   masm.push(ICStubReg);  // stub.
2388   masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2389 
2390   using Fn =
2391       bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, MutableHandleValue);
2392   return tailCallVM<Fn, DoNewArrayFallback>(masm);
2393 }
2394 
2395 //
2396 // NewObject_Fallback
2397 //
DoNewObjectFallback(JSContext * cx,BaselineFrame * frame,ICFallbackStub * stub,MutableHandleValue res)2398 bool DoNewObjectFallback(JSContext* cx, BaselineFrame* frame,
2399                          ICFallbackStub* stub, MutableHandleValue res) {
2400   stub->incrementEnteredCount();
2401   MaybeNotifyWarp(frame->outerScript(), stub);
2402   FallbackICSpew(cx, stub, "NewObject");
2403 
2404   RootedScript script(cx, frame->script());
2405   jsbytecode* pc = StubOffsetToPc(stub, script);
2406 
2407   RootedObject obj(cx, NewObjectOperation(cx, script, pc));
2408   if (!obj) {
2409     return false;
2410   }
2411 
2412   TryAttachStub<NewObjectIRGenerator>("NewObject", cx, frame, stub, JSOp(*pc),
2413                                       obj, frame);
2414 
2415   res.setObject(*obj);
2416   return true;
2417 }
2418 
emit_NewObject()2419 bool FallbackICCodeCompiler::emit_NewObject() {
2420   EmitRestoreTailCallReg(masm);
2421 
2422   masm.push(ICStubReg);  // stub.
2423   pushStubPayload(masm, R0.scratchReg());
2424 
2425   using Fn =
2426       bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, MutableHandleValue);
2427   return tailCallVM<Fn, DoNewObjectFallback>(masm);
2428 }
2429 
generateBaselineICFallbackCode(JSContext * cx)2430 bool JitRuntime::generateBaselineICFallbackCode(JSContext* cx) {
2431   StackMacroAssembler masm;
2432   AutoCreatedBy acb(masm, "JitRuntime::generateBaselineICFallbackCode");
2433 
2434   BaselineICFallbackCode& fallbackCode = baselineICFallbackCode_.ref();
2435   FallbackICCodeCompiler compiler(cx, fallbackCode, masm);
2436 
2437   JitSpew(JitSpew_Codegen, "# Emitting Baseline IC fallback code");
2438 
2439 #define EMIT_CODE(kind)                                            \
2440   {                                                                \
2441     AutoCreatedBy acb(masm, "kind=" #kind);                        \
2442     uint32_t offset = startTrampolineCode(masm);                   \
2443     InitMacroAssemblerForICStub(masm);                             \
2444     if (!compiler.emit_##kind()) {                                 \
2445       return false;                                                \
2446     }                                                              \
2447     fallbackCode.initOffset(BaselineICFallbackKind::kind, offset); \
2448   }
2449   IC_BASELINE_FALLBACK_CODE_KIND_LIST(EMIT_CODE)
2450 #undef EMIT_CODE
2451 
2452   Linker linker(masm);
2453   JitCode* code = linker.newCode(cx, CodeKind::Other);
2454   if (!code) {
2455     return false;
2456   }
2457 
2458 #ifdef JS_ION_PERF
2459   writePerfSpewerJitCodeProfile(code, "BaselineICFallback");
2460 #endif
2461 #ifdef MOZ_VTUNE
2462   vtune::MarkStub(code, "BaselineICFallback");
2463 #endif
2464 
2465   fallbackCode.initCode(code);
2466   return true;
2467 }
2468 
2469 }  // namespace jit
2470 }  // namespace js
2471