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