1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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/BaselineCompiler.h"
8
9 #include "mozilla/Casting.h"
10
11 #include "jit/BaselineIC.h"
12 #include "jit/BaselineJIT.h"
13 #include "jit/FixedList.h"
14 #include "jit/IonAnalysis.h"
15 #include "jit/JitcodeMap.h"
16 #include "jit/JitSpewer.h"
17 #include "jit/Linker.h"
18 #ifdef JS_ION_PERF
19 #include "jit/PerfSpewer.h"
20 #endif
21 #include "jit/SharedICHelpers.h"
22 #include "jit/VMFunctions.h"
23 #include "js/UniquePtr.h"
24 #include "vm/AsyncFunction.h"
25 #include "vm/AsyncIteration.h"
26 #include "vm/EnvironmentObject.h"
27 #include "vm/Interpreter.h"
28 #include "vm/JSFunction.h"
29 #include "vm/TraceLogging.h"
30 #include "vtune/VTuneWrapper.h"
31
32 #include "jit/BaselineFrameInfo-inl.h"
33 #include "jit/MacroAssembler-inl.h"
34 #include "vm/Interpreter-inl.h"
35 #include "vm/JSScript-inl.h"
36 #include "vm/NativeObject-inl.h"
37 #include "vm/TypeInference-inl.h"
38
39 using namespace js;
40 using namespace js::jit;
41
42 using mozilla::AssertedCast;
43
BaselineCompiler(JSContext * cx,TempAllocator & alloc,JSScript * script)44 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc,
45 JSScript* script)
46 : BaselineCompilerSpecific(cx, alloc, script),
47 yieldAndAwaitOffsets_(cx),
48 modifiesArguments_(false) {}
49
init()50 bool BaselineCompiler::init() {
51 if (!analysis_.init(alloc_, cx->caches().gsnCache)) return false;
52
53 if (!labels_.init(alloc_, script->length())) return false;
54
55 for (size_t i = 0; i < script->length(); i++) new (&labels_[i]) Label();
56
57 if (!frame.init(alloc_)) return false;
58
59 return true;
60 }
61
addPCMappingEntry(bool addIndexEntry)62 bool BaselineCompiler::addPCMappingEntry(bool addIndexEntry) {
63 // Don't add multiple entries for a single pc.
64 size_t nentries = pcMappingEntries_.length();
65 if (nentries > 0 &&
66 pcMappingEntries_[nentries - 1].pcOffset == script->pcToOffset(pc))
67 return true;
68
69 PCMappingEntry entry;
70 entry.pcOffset = script->pcToOffset(pc);
71 entry.nativeOffset = masm.currentOffset();
72 entry.slotInfo = getStackTopSlotInfo();
73 entry.addIndexEntry = addIndexEntry;
74
75 return pcMappingEntries_.append(entry);
76 }
77
compile()78 MethodStatus BaselineCompiler::compile() {
79 JitSpew(JitSpew_BaselineScripts, "Baseline compiling script %s:%zu (%p)",
80 script->filename(), script->lineno(), script);
81
82 JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%zu",
83 script->filename(), script->lineno());
84
85 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
86 TraceLoggerEvent scriptEvent(TraceLogger_AnnotateScripts, script);
87 AutoTraceLog logScript(logger, scriptEvent);
88 AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
89
90 AutoKeepTypeScripts keepTypes(cx);
91 if (!script->ensureHasTypes(cx, keepTypes) ||
92 !script->ensureHasAnalyzedArgsUsage(cx))
93 return Method_Error;
94
95 // When code coverage is only enabled for optimizations, or when a Debugger
96 // set the collectCoverageInfo flag, we have to create the ScriptCounts if
97 // they do not exist.
98 if (!script->hasScriptCounts() && cx->compartment()->collectCoverage()) {
99 if (!script->initScriptCounts(cx)) return Method_Error;
100 }
101
102 // Pin analysis info during compilation.
103 AutoEnterAnalysis autoEnterAnalysis(cx);
104
105 MOZ_ASSERT(!script->hasBaselineScript());
106
107 if (!emitPrologue()) return Method_Error;
108
109 MethodStatus status = emitBody();
110 if (status != Method_Compiled) return status;
111
112 if (!emitEpilogue()) return Method_Error;
113
114 if (!emitOutOfLinePostBarrierSlot()) return Method_Error;
115
116 Linker linker(masm);
117 if (masm.oom()) {
118 ReportOutOfMemory(cx);
119 return Method_Error;
120 }
121
122 AutoFlushICache afc("Baseline");
123 JitCode* code = linker.newCode(cx, CodeKind::Baseline);
124 if (!code) return Method_Error;
125
126 Rooted<EnvironmentObject*> templateEnv(cx);
127 if (script->functionNonDelazifying()) {
128 RootedFunction fun(cx, script->functionNonDelazifying());
129
130 if (fun->needsNamedLambdaEnvironment()) {
131 templateEnv =
132 NamedLambdaObject::createTemplateObject(cx, fun, gc::TenuredHeap);
133 if (!templateEnv) return Method_Error;
134 }
135
136 if (fun->needsCallObject()) {
137 RootedScript scriptRoot(cx, script);
138 templateEnv = CallObject::createTemplateObject(
139 cx, scriptRoot, templateEnv, gc::TenuredHeap);
140 if (!templateEnv) return Method_Error;
141 }
142 }
143
144 // Encode the pc mapping table. See PCMappingIndexEntry for
145 // more information.
146 Vector<PCMappingIndexEntry> pcMappingIndexEntries(cx);
147 CompactBufferWriter pcEntries;
148 uint32_t previousOffset = 0;
149
150 for (size_t i = 0; i < pcMappingEntries_.length(); i++) {
151 PCMappingEntry& entry = pcMappingEntries_[i];
152
153 if (entry.addIndexEntry) {
154 PCMappingIndexEntry indexEntry;
155 indexEntry.pcOffset = entry.pcOffset;
156 indexEntry.nativeOffset = entry.nativeOffset;
157 indexEntry.bufferOffset = pcEntries.length();
158 if (!pcMappingIndexEntries.append(indexEntry)) {
159 ReportOutOfMemory(cx);
160 return Method_Error;
161 }
162 previousOffset = entry.nativeOffset;
163 }
164
165 // Use the high bit of the SlotInfo byte to indicate the
166 // native code offset (relative to the previous op) > 0 and
167 // comes next in the buffer.
168 MOZ_ASSERT((entry.slotInfo.toByte() & 0x80) == 0);
169
170 if (entry.nativeOffset == previousOffset) {
171 pcEntries.writeByte(entry.slotInfo.toByte());
172 } else {
173 MOZ_ASSERT(entry.nativeOffset > previousOffset);
174 pcEntries.writeByte(0x80 | entry.slotInfo.toByte());
175 pcEntries.writeUnsigned(entry.nativeOffset - previousOffset);
176 }
177
178 previousOffset = entry.nativeOffset;
179 }
180
181 if (pcEntries.oom()) {
182 ReportOutOfMemory(cx);
183 return Method_Error;
184 }
185
186 // Note: There is an extra entry in the bytecode type map for the search hint,
187 // see below.
188 size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
189 UniquePtr<BaselineScript> baselineScript(
190 BaselineScript::New(
191 script, prologueOffset_.offset(), epilogueOffset_.offset(),
192 profilerEnterFrameToggleOffset_.offset(),
193 profilerExitFrameToggleOffset_.offset(),
194 postDebugPrologueOffset_.offset(), icEntries_.length(),
195 pcMappingIndexEntries.length(), pcEntries.length(),
196 bytecodeTypeMapEntries, yieldAndAwaitOffsets_.length(),
197 traceLoggerToggleOffsets_.length()),
198 JS::DeletePolicy<BaselineScript>(cx->runtime()));
199 if (!baselineScript) {
200 ReportOutOfMemory(cx);
201 return Method_Error;
202 }
203
204 baselineScript->setMethod(code);
205 baselineScript->setTemplateEnvironment(templateEnv);
206
207 JitSpew(JitSpew_BaselineScripts,
208 "Created BaselineScript %p (raw %p) for %s:%zu",
209 (void*)baselineScript.get(), (void*)code->raw(), script->filename(),
210 script->lineno());
211
212 MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
213 baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
214
215 MOZ_ASSERT(pcEntries.length() > 0);
216 baselineScript->copyPCMappingEntries(pcEntries);
217
218 // Copy IC entries
219 if (icEntries_.length())
220 baselineScript->copyICEntries(script, &icEntries_[0]);
221
222 // Adopt fallback stubs from the compiler into the baseline script.
223 baselineScript->adoptFallbackStubs(&stubSpace_);
224
225 // If profiler instrumentation is enabled, toggle instrumentation on.
226 if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(
227 cx->runtime()))
228 baselineScript->toggleProfilerInstrumentation(true);
229
230 // Patch IC loads using IC entries.
231 for (size_t i = 0; i < icLoadLabels_.length(); i++) {
232 CodeOffset label = icLoadLabels_[i].label;
233 size_t icEntry = icLoadLabels_[i].icEntry;
234 BaselineICEntry* entryAddr = &(baselineScript->icEntry(icEntry));
235 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label),
236 ImmPtr(entryAddr), ImmPtr((void*)-1));
237 }
238
239 if (modifiesArguments_) baselineScript->setModifiesArguments();
240 if (analysis_.usesEnvironmentChain())
241 baselineScript->setUsesEnvironmentChain();
242
243 #ifdef JS_TRACE_LOGGING
244 // Initialize the tracelogger instrumentation.
245 baselineScript->initTraceLogger(script, traceLoggerToggleOffsets_);
246 #endif
247
248 uint32_t* bytecodeMap = baselineScript->bytecodeTypeMap();
249 FillBytecodeTypeMap(script, bytecodeMap);
250
251 // The last entry in the last index found, and is used to avoid binary
252 // searches for the sought entry when queries are in linear order.
253 bytecodeMap[script->nTypeSets()] = 0;
254
255 baselineScript->copyYieldAndAwaitEntries(script, yieldAndAwaitOffsets_);
256
257 if (compileDebugInstrumentation_)
258 baselineScript->setHasDebugInstrumentation();
259
260 // Always register a native => bytecode mapping entry, since profiler can be
261 // turned on with baseline jitcode on stack, and baseline jitcode cannot be
262 // invalidated.
263 {
264 JitSpew(JitSpew_Profiling,
265 "Added JitcodeGlobalEntry for baseline script %s:%zu (%p)",
266 script->filename(), script->lineno(), baselineScript.get());
267
268 // Generate profiling string.
269 char* str = JitcodeGlobalEntry::createScriptString(cx, script);
270 if (!str) return Method_Error;
271
272 JitcodeGlobalEntry::BaselineEntry entry;
273 entry.init(code, code->raw(), code->rawEnd(), script, str);
274
275 JitcodeGlobalTable* globalTable =
276 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
277 if (!globalTable->addEntry(entry)) {
278 entry.destroy();
279 ReportOutOfMemory(cx);
280 return Method_Error;
281 }
282
283 // Mark the jitcode as having a bytecode map.
284 code->setHasBytecodeMap();
285 }
286
287 script->setBaselineScript(cx->runtime(), baselineScript.release());
288
289 #ifdef JS_ION_PERF
290 writePerfSpewerBaselineProfile(script, code);
291 #endif
292
293 #ifdef MOZ_VTUNE
294 vtune::MarkScript(code, script, "baseline");
295 #endif
296
297 return Method_Compiled;
298 }
299
emitInitializeLocals()300 void BaselineCompiler::emitInitializeLocals() {
301 // Initialize all locals to |undefined|. Lexical bindings are temporal
302 // dead zoned in bytecode.
303
304 size_t n = frame.nlocals();
305 if (n == 0) return;
306
307 // Use R0 to minimize code size. If the number of locals to push is <
308 // LOOP_UNROLL_FACTOR, then the initialization pushes are emitted directly
309 // and inline. Otherwise, they're emitted in a partially unrolled loop.
310 static const size_t LOOP_UNROLL_FACTOR = 4;
311 size_t toPushExtra = n % LOOP_UNROLL_FACTOR;
312
313 masm.moveValue(UndefinedValue(), R0);
314
315 // Handle any extra pushes left over by the optional unrolled loop below.
316 for (size_t i = 0; i < toPushExtra; i++) masm.pushValue(R0);
317
318 // Partially unrolled loop of pushes.
319 if (n >= LOOP_UNROLL_FACTOR) {
320 size_t toPush = n - toPushExtra;
321 MOZ_ASSERT(toPush % LOOP_UNROLL_FACTOR == 0);
322 MOZ_ASSERT(toPush >= LOOP_UNROLL_FACTOR);
323 masm.move32(Imm32(toPush), R1.scratchReg());
324 // Emit unrolled loop with 4 pushes per iteration.
325 Label pushLoop;
326 masm.bind(&pushLoop);
327 for (size_t i = 0; i < LOOP_UNROLL_FACTOR; i++) masm.pushValue(R0);
328 masm.branchSub32(Assembler::NonZero, Imm32(LOOP_UNROLL_FACTOR),
329 R1.scratchReg(), &pushLoop);
330 }
331 }
332
emitPrologue()333 bool BaselineCompiler::emitPrologue() {
334 #ifdef JS_USE_LINK_REGISTER
335 // Push link register from generateEnterJIT()'s BLR.
336 masm.pushReturnAddress();
337 masm.checkStackAlignment();
338 #endif
339 emitProfilerEnterFrame();
340
341 masm.push(BaselineFrameReg);
342 masm.moveStackPtrTo(BaselineFrameReg);
343 masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
344
345 // Initialize BaselineFrame. For eval scripts, the env chain
346 // is passed in R1, so we have to be careful not to clobber it.
347
348 // Initialize BaselineFrame::flags.
349 masm.store32(Imm32(0), frame.addressOfFlags());
350
351 // Handle env chain pre-initialization (in case GC gets run
352 // during stack check). For global and eval scripts, the env
353 // chain is in R1. For function scripts, the env chain is in
354 // the callee, nullptr is stored for now so that GC doesn't choke
355 // on a bogus EnvironmentChain value in the frame.
356 if (function())
357 masm.storePtr(ImmPtr(nullptr), frame.addressOfEnvironmentChain());
358 else
359 masm.storePtr(R1.scratchReg(), frame.addressOfEnvironmentChain());
360
361 // Functions with a large number of locals require two stack checks.
362 // The VMCall for a fallible stack check can only occur after the
363 // env chain has been initialized, as that is required for proper
364 // exception handling if the VMCall returns false. The env chain
365 // initialization can only happen after the UndefinedValues for the
366 // local slots have been pushed.
367 // However by that time, the stack might have grown too much.
368 // In these cases, we emit an extra, early, infallible check
369 // before pushing the locals. The early check sets a flag on the
370 // frame if the stack check fails (but otherwise doesn't throw an
371 // exception). If the flag is set, then the jitcode skips past
372 // the pushing of the locals, and directly to env chain initialization
373 // followed by the actual stack check, which will throw the correct
374 // exception.
375 Label earlyStackCheckFailed;
376 if (needsEarlyStackCheck()) {
377 if (!emitStackCheck(/* earlyCheck = */ true)) return false;
378 masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
379 Imm32(BaselineFrame::OVER_RECURSED),
380 &earlyStackCheckFailed);
381 }
382
383 emitInitializeLocals();
384
385 if (needsEarlyStackCheck()) masm.bind(&earlyStackCheckFailed);
386
387 #ifdef JS_TRACE_LOGGING
388 if (!emitTraceLoggerEnter()) return false;
389 #endif
390
391 // Record the offset of the prologue, because Ion can bailout before
392 // the env chain is initialized.
393 prologueOffset_ = CodeOffset(masm.currentOffset());
394
395 // When compiling with Debugger instrumentation, set the debuggeeness of
396 // the frame before any operation that can call into the VM.
397 emitIsDebuggeeCheck();
398
399 // Initialize the env chain before any operation that may
400 // call into the VM and trigger a GC.
401 if (!initEnvironmentChain()) return false;
402
403 if (!emitStackCheck()) return false;
404
405 if (!emitDebugPrologue()) return false;
406
407 if (!emitWarmUpCounterIncrement()) return false;
408
409 if (!emitArgumentTypeChecks()) return false;
410
411 return true;
412 }
413
emitEpilogue()414 bool BaselineCompiler::emitEpilogue() {
415 // Record the offset of the epilogue, so we can do early return from
416 // Debugger handlers during on-stack recompile.
417 epilogueOffset_ = CodeOffset(masm.currentOffset());
418
419 masm.bind(&return_);
420
421 #ifdef JS_TRACE_LOGGING
422 if (!emitTraceLoggerExit()) return false;
423 #endif
424
425 masm.moveToStackPtr(BaselineFrameReg);
426 masm.pop(BaselineFrameReg);
427
428 emitProfilerExitFrame();
429
430 masm.ret();
431 return true;
432 }
433
434 // On input:
435 // R2.scratchReg() contains object being written to.
436 // Called with the baseline stack synced, except for R0 which is preserved.
437 // All other registers are usable as scratch.
438 // This calls:
439 // void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
emitOutOfLinePostBarrierSlot()440 bool BaselineCompiler::emitOutOfLinePostBarrierSlot() {
441 masm.bind(&postBarrierSlot_);
442
443 Register objReg = R2.scratchReg();
444 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
445 regs.take(R0);
446 regs.take(objReg);
447 regs.take(BaselineFrameReg);
448 Register scratch = regs.takeAny();
449 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
450 // On ARM, save the link register before calling. It contains the return
451 // address. The |masm.ret()| later will pop this into |pc| to return.
452 masm.push(lr);
453 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
454 masm.push(ra);
455 #endif
456 masm.pushValue(R0);
457
458 masm.setupUnalignedABICall(scratch);
459 masm.movePtr(ImmPtr(cx->runtime()), scratch);
460 masm.passABIArg(scratch);
461 masm.passABIArg(objReg);
462 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
463
464 masm.popValue(R0);
465 masm.ret();
466 return true;
467 }
468
emitIC(ICStub * stub,ICEntry::Kind kind)469 bool BaselineCompiler::emitIC(ICStub* stub, ICEntry::Kind kind) {
470 BaselineICEntry* entry = allocateICEntry(stub, kind);
471 if (!entry) return false;
472
473 CodeOffset patchOffset;
474 EmitCallIC(&patchOffset, masm);
475 entry->setReturnOffset(CodeOffset(masm.currentOffset()));
476 if (!addICLoadLabel(patchOffset)) return false;
477
478 return true;
479 }
480
481 typedef bool (*CheckOverRecursedWithExtraFn)(JSContext*, BaselineFrame*,
482 uint32_t, uint32_t);
483 static const VMFunction CheckOverRecursedWithExtraInfo =
484 FunctionInfo<CheckOverRecursedWithExtraFn>(CheckOverRecursedWithExtra,
485 "CheckOverRecursedWithExtra");
486
emitStackCheck(bool earlyCheck)487 bool BaselineCompiler::emitStackCheck(bool earlyCheck) {
488 Label skipCall;
489 uint32_t slotsSize = script->nslots() * sizeof(Value);
490 uint32_t tolerance = earlyCheck ? slotsSize : 0;
491
492 masm.moveStackPtrTo(R1.scratchReg());
493
494 // If this is the early stack check, locals haven't been pushed yet. Adjust
495 // the stack pointer to account for the locals that would be pushed before
496 // performing the guard around the vmcall to the stack check.
497 if (earlyCheck) masm.subPtr(Imm32(tolerance), R1.scratchReg());
498
499 // If this is the late stack check for a frame which contains an early stack
500 // check, then the early stack check might have failed and skipped past the
501 // pushing of locals on the stack.
502 //
503 // If this is a possibility, then the OVER_RECURSED flag should be checked,
504 // and the VMCall to CheckOverRecursed done unconditionally if it's set.
505 Label forceCall;
506 if (!earlyCheck && needsEarlyStackCheck()) {
507 masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
508 Imm32(BaselineFrame::OVER_RECURSED), &forceCall);
509 }
510
511 void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
512 masm.loadPtr(AbsoluteAddress(contextAddr), R0.scratchReg());
513 masm.branchPtr(Assembler::BelowOrEqual,
514 Address(R0.scratchReg(), offsetof(JSContext, jitStackLimit)),
515 R1.scratchReg(), &skipCall);
516
517 if (!earlyCheck && needsEarlyStackCheck()) masm.bind(&forceCall);
518
519 prepareVMCall();
520 pushArg(Imm32(earlyCheck));
521 pushArg(Imm32(tolerance));
522 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
523 pushArg(R1.scratchReg());
524
525 CallVMPhase phase = POST_INITIALIZE;
526 if (earlyCheck)
527 phase = PRE_INITIALIZE;
528 else if (needsEarlyStackCheck())
529 phase = CHECK_OVER_RECURSED;
530
531 if (!callVMNonOp(CheckOverRecursedWithExtraInfo, phase)) return false;
532
533 icEntries_.back().setFakeKind(earlyCheck ? ICEntry::Kind_EarlyStackCheck
534 : ICEntry::Kind_StackCheck);
535
536 masm.bind(&skipCall);
537 return true;
538 }
539
emitIsDebuggeeCheck()540 void BaselineCompiler::emitIsDebuggeeCheck() {
541 if (compileDebugInstrumentation_) {
542 masm.Push(BaselineFrameReg);
543 masm.setupUnalignedABICall(R0.scratchReg());
544 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
545 masm.passABIArg(R0.scratchReg());
546 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
547 masm.Pop(BaselineFrameReg);
548 }
549 }
550
551 typedef bool (*DebugPrologueFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
552 static const VMFunction DebugPrologueInfo =
553 FunctionInfo<DebugPrologueFn>(jit::DebugPrologue, "DebugPrologue");
554
emitDebugPrologue()555 bool BaselineCompiler::emitDebugPrologue() {
556 if (compileDebugInstrumentation_) {
557 // Load pointer to BaselineFrame in R0.
558 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
559
560 prepareVMCall();
561 pushArg(ImmPtr(pc));
562 pushArg(R0.scratchReg());
563 if (!callVM(DebugPrologueInfo)) return false;
564
565 // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
566 icEntries_.back().setFakeKind(ICEntry::Kind_DebugPrologue);
567
568 // If the stub returns |true|, we have to return the value stored in the
569 // frame's return value slot.
570 Label done;
571 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
572 {
573 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
574 masm.jump(&return_);
575 }
576 masm.bind(&done);
577 }
578
579 postDebugPrologueOffset_ = CodeOffset(masm.currentOffset());
580
581 return true;
582 }
583
584 typedef bool (*CheckGlobalOrEvalDeclarationConflictsFn)(JSContext*,
585 BaselineFrame*);
586 static const VMFunction CheckGlobalOrEvalDeclarationConflictsInfo =
587 FunctionInfo<CheckGlobalOrEvalDeclarationConflictsFn>(
588 jit::CheckGlobalOrEvalDeclarationConflicts,
589 "CheckGlobalOrEvalDeclarationConflicts");
590
591 typedef bool (*InitFunctionEnvironmentObjectsFn)(JSContext*, BaselineFrame*);
592 static const VMFunction InitFunctionEnvironmentObjectsInfo =
593 FunctionInfo<InitFunctionEnvironmentObjectsFn>(
594 jit::InitFunctionEnvironmentObjects, "InitFunctionEnvironmentObjects");
595
initEnvironmentChain()596 bool BaselineCompiler::initEnvironmentChain() {
597 CallVMPhase phase = POST_INITIALIZE;
598 if (needsEarlyStackCheck()) phase = CHECK_OVER_RECURSED;
599
600 RootedFunction fun(cx, function());
601 if (fun) {
602 // Use callee->environment as env chain. Note that we do this also
603 // for needsSomeEnvironmentObject functions, so that the env chain
604 // slot is properly initialized if the call triggers GC.
605 Register callee = R0.scratchReg();
606 Register scope = R1.scratchReg();
607 masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), callee);
608 masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope);
609 masm.storePtr(scope, frame.addressOfEnvironmentChain());
610
611 if (fun->needsFunctionEnvironmentObjects()) {
612 // Call into the VM to create the proper environment objects.
613 prepareVMCall();
614
615 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
616 pushArg(R0.scratchReg());
617
618 if (!callVMNonOp(InitFunctionEnvironmentObjectsInfo, phase)) return false;
619 }
620 } else if (module()) {
621 // Modules use a pre-created scope object.
622 Register scope = R1.scratchReg();
623 masm.movePtr(ImmGCPtr(&module()->initialEnvironment()), scope);
624 masm.storePtr(scope, frame.addressOfEnvironmentChain());
625 } else {
626 // EnvironmentChain pointer in BaselineFrame has already been initialized
627 // in prologue, but we need to check for redeclaration errors.
628
629 prepareVMCall();
630 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
631 pushArg(R0.scratchReg());
632
633 if (!callVMNonOp(CheckGlobalOrEvalDeclarationConflictsInfo, phase))
634 return false;
635 }
636
637 return true;
638 }
639
640 typedef bool (*InterruptCheckFn)(JSContext*);
641 static const VMFunction InterruptCheckInfo =
642 FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
643
emitInterruptCheck()644 bool BaselineCompiler::emitInterruptCheck() {
645 frame.syncStack(0);
646
647 Label done;
648 void* context = cx->zone()->group()->addressOfOwnerContext();
649 masm.loadPtr(AbsoluteAddress(context), R0.scratchReg());
650 masm.branch32(Assembler::Equal,
651 Address(R0.scratchReg(), offsetof(JSContext, interrupt_)),
652 Imm32(0), &done);
653
654 prepareVMCall();
655 if (!callVM(InterruptCheckInfo)) return false;
656
657 masm.bind(&done);
658 return true;
659 }
660
661 typedef bool (*IonCompileScriptForBaselineFn)(JSContext*, BaselineFrame*,
662 jsbytecode*);
663 static const VMFunction IonCompileScriptForBaselineInfo =
664 FunctionInfo<IonCompileScriptForBaselineFn>(IonCompileScriptForBaseline,
665 "IonCompileScriptForBaseline");
666
emitWarmUpCounterIncrement(bool allowOsr)667 bool BaselineCompiler::emitWarmUpCounterIncrement(bool allowOsr) {
668 // Emit no warm-up counter increments or bailouts if Ion is not
669 // enabled, or if the script will never be Ion-compileable
670
671 if (!ionCompileable_) return true;
672
673 frame.assertSyncedStack();
674
675 Register scriptReg = R2.scratchReg();
676 Register countReg = R0.scratchReg();
677 Address warmUpCounterAddr(scriptReg, JSScript::offsetOfWarmUpCounter());
678
679 masm.movePtr(ImmGCPtr(script), scriptReg);
680 masm.load32(warmUpCounterAddr, countReg);
681 masm.add32(Imm32(1), countReg);
682 masm.store32(countReg, warmUpCounterAddr);
683
684 // If this is a loop inside a catch or finally block, increment the warmup
685 // counter but don't attempt OSR (Ion only compiles the try block).
686 if (analysis_.info(pc).loopEntryInCatchOrFinally) {
687 MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
688 return true;
689 }
690
691 // OSR not possible at this loop entry.
692 if (!allowOsr) {
693 MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
694 return true;
695 }
696
697 Label skipCall;
698
699 const OptimizationInfo* info =
700 IonOptimizations.get(IonOptimizations.firstLevel());
701 uint32_t warmUpThreshold = info->compilerWarmUpThreshold(script, pc);
702 masm.branch32(Assembler::LessThan, countReg, Imm32(warmUpThreshold),
703 &skipCall);
704
705 masm.branchPtr(Assembler::Equal,
706 Address(scriptReg, JSScript::offsetOfIonScript()),
707 ImmPtr(ION_COMPILING_SCRIPT), &skipCall);
708
709 // Try to compile and/or finish a compilation.
710 if (JSOp(*pc) == JSOP_LOOPENTRY) {
711 // During the loop entry we can try to OSR into ion.
712 // The ic has logic for this.
713 ICWarmUpCounter_Fallback::Compiler stubCompiler(cx);
714 if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_))) return false;
715 } else {
716 // To call stubs we need to have an opcode. This code handles the
717 // prologue and there is no dedicatd opcode present. Therefore use an
718 // annotated vm call.
719 prepareVMCall();
720
721 masm.Push(ImmPtr(pc));
722 masm.PushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
723
724 if (!callVM(IonCompileScriptForBaselineInfo)) return false;
725
726 // Annotate the ICEntry as warmup counter.
727 icEntries_.back().setFakeKind(ICEntry::Kind_WarmupCounter);
728 }
729 masm.bind(&skipCall);
730
731 return true;
732 }
733
emitArgumentTypeChecks()734 bool BaselineCompiler::emitArgumentTypeChecks() {
735 if (!function()) return true;
736
737 frame.pushThis();
738 frame.popRegsAndSync(1);
739
740 ICTypeMonitor_Fallback::Compiler compiler(cx, uint32_t(0));
741 if (!emitNonOpIC(compiler.getStub(&stubSpace_))) return false;
742
743 for (size_t i = 0; i < function()->nargs(); i++) {
744 frame.pushArg(i);
745 frame.popRegsAndSync(1);
746
747 ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1);
748 if (!emitNonOpIC(compiler.getStub(&stubSpace_))) return false;
749 }
750
751 return true;
752 }
753
emitDebugTrap()754 bool BaselineCompiler::emitDebugTrap() {
755 MOZ_ASSERT(compileDebugInstrumentation_);
756 MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
757
758 bool enabled = script->stepModeEnabled() || script->hasBreakpointsAt(pc);
759
760 // Emit patchable call to debug trap handler.
761 JitCode* handler = cx->runtime()->jitRuntime()->debugTrapHandler(cx);
762 if (!handler) return false;
763 mozilla::DebugOnly<CodeOffset> offset = masm.toggledCall(handler, enabled);
764
765 #ifdef DEBUG
766 // Patchable call offset has to match the pc mapping offset.
767 PCMappingEntry& entry = pcMappingEntries_.back();
768 MOZ_ASSERT((&offset)->offset() == entry.nativeOffset);
769 #endif
770
771 // Add an IC entry for the return offset -> pc mapping.
772 return appendICEntry(ICEntry::Kind_DebugTrap, masm.currentOffset());
773 }
774
775 #ifdef JS_TRACE_LOGGING
emitTraceLoggerEnter()776 bool BaselineCompiler::emitTraceLoggerEnter() {
777 AllocatableRegisterSet regs(RegisterSet::Volatile());
778 Register loggerReg = regs.takeAnyGeneral();
779 Register scriptReg = regs.takeAnyGeneral();
780
781 Label noTraceLogger;
782 if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger)))
783 return false;
784
785 masm.Push(loggerReg);
786 masm.Push(scriptReg);
787
788 masm.loadTraceLogger(loggerReg);
789
790 // Script start.
791 masm.movePtr(ImmGCPtr(script), scriptReg);
792 masm.loadPtr(Address(scriptReg, JSScript::offsetOfBaselineScript()),
793 scriptReg);
794 Address scriptEvent(scriptReg,
795 BaselineScript::offsetOfTraceLoggerScriptEvent());
796 masm.computeEffectiveAddress(scriptEvent, scriptReg);
797 masm.tracelogStartEvent(loggerReg, scriptReg);
798
799 // Engine start.
800 masm.tracelogStartId(loggerReg, TraceLogger_Baseline, /* force = */ true);
801
802 masm.Pop(scriptReg);
803 masm.Pop(loggerReg);
804
805 masm.bind(&noTraceLogger);
806
807 return true;
808 }
809
emitTraceLoggerExit()810 bool BaselineCompiler::emitTraceLoggerExit() {
811 AllocatableRegisterSet regs(RegisterSet::Volatile());
812 Register loggerReg = regs.takeAnyGeneral();
813
814 Label noTraceLogger;
815 if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger)))
816 return false;
817
818 masm.Push(loggerReg);
819 masm.loadTraceLogger(loggerReg);
820
821 masm.tracelogStopId(loggerReg, TraceLogger_Baseline, /* force = */ true);
822 masm.tracelogStopId(loggerReg, TraceLogger_Scripts, /* force = */ true);
823
824 masm.Pop(loggerReg);
825
826 masm.bind(&noTraceLogger);
827
828 return true;
829 }
830
emitTraceLoggerResume(Register baselineScript,AllocatableGeneralRegisterSet & regs)831 bool BaselineCompiler::emitTraceLoggerResume(
832 Register baselineScript, AllocatableGeneralRegisterSet& regs) {
833 Register scriptId = regs.takeAny();
834 Register loggerReg = regs.takeAny();
835
836 Label noTraceLogger;
837 if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger)))
838 return false;
839
840 masm.loadTraceLogger(loggerReg);
841
842 Address scriptEvent(baselineScript,
843 BaselineScript::offsetOfTraceLoggerScriptEvent());
844 masm.computeEffectiveAddress(scriptEvent, scriptId);
845 masm.tracelogStartEvent(loggerReg, scriptId);
846 masm.tracelogStartId(loggerReg, TraceLogger_Baseline, /* force = */ true);
847
848 regs.add(loggerReg);
849 regs.add(scriptId);
850
851 masm.bind(&noTraceLogger);
852
853 return true;
854 }
855 #endif
856
emitProfilerEnterFrame()857 void BaselineCompiler::emitProfilerEnterFrame() {
858 // Store stack position to lastProfilingFrame variable, guarded by a toggled
859 // jump. Starts off initially disabled.
860 Label noInstrument;
861 CodeOffset toggleOffset = masm.toggledJump(&noInstrument);
862 masm.profilerEnterFrame(masm.getStackPointer(), R0.scratchReg());
863 masm.bind(&noInstrument);
864
865 // Store the start offset in the appropriate location.
866 MOZ_ASSERT(!profilerEnterFrameToggleOffset_.bound());
867 profilerEnterFrameToggleOffset_ = toggleOffset;
868 }
869
emitProfilerExitFrame()870 void BaselineCompiler::emitProfilerExitFrame() {
871 // Store previous frame to lastProfilingFrame variable, guarded by a toggled
872 // jump. Starts off initially disabled.
873 Label noInstrument;
874 CodeOffset toggleOffset = masm.toggledJump(&noInstrument);
875 masm.profilerExitFrame();
876 masm.bind(&noInstrument);
877
878 // Store the start offset in the appropriate location.
879 MOZ_ASSERT(!profilerExitFrameToggleOffset_.bound());
880 profilerExitFrameToggleOffset_ = toggleOffset;
881 }
882
emitBody()883 MethodStatus BaselineCompiler::emitBody() {
884 MOZ_ASSERT(pc == script->code());
885
886 bool lastOpUnreachable = false;
887 uint32_t emittedOps = 0;
888 mozilla::DebugOnly<jsbytecode*> prevpc = pc;
889
890 while (true) {
891 JSOp op = JSOp(*pc);
892 JitSpew(JitSpew_BaselineOp, "Compiling op @ %d: %s",
893 int(script->pcToOffset(pc)), CodeName[op]);
894
895 BytecodeInfo* info = analysis_.maybeInfo(pc);
896
897 // Skip unreachable ops.
898 if (!info) {
899 // Test if last instructions and stop emitting in that case.
900 pc += GetBytecodeLength(pc);
901 if (pc >= script->codeEnd()) break;
902
903 lastOpUnreachable = true;
904 prevpc = pc;
905 continue;
906 }
907
908 if (info->jumpTarget) {
909 // Fully sync the stack if there are incoming jumps.
910 frame.syncStack(0);
911 frame.setStackDepth(info->stackDepth);
912 masm.bind(labelOf(pc));
913 } else if (MOZ_UNLIKELY(compileDebugInstrumentation_)) {
914 // Also fully sync the stack if the debugger is enabled.
915 frame.syncStack(0);
916 } else {
917 // At the beginning of any op, at most the top 2 stack-values are
918 // unsynced.
919 if (frame.stackDepth() > 2) frame.syncStack(2);
920 }
921
922 frame.assertValidState(*info);
923
924 // Add a PC -> native mapping entry for the current op. These entries are
925 // used when we need the native code address for a given pc, for instance
926 // for bailouts from Ion, the debugger and exception handling. See
927 // PCMappingIndexEntry for more information.
928 bool addIndexEntry =
929 (pc == script->code() || lastOpUnreachable || emittedOps > 100);
930 if (addIndexEntry) emittedOps = 0;
931 if (MOZ_UNLIKELY(!addPCMappingEntry(addIndexEntry))) {
932 ReportOutOfMemory(cx);
933 return Method_Error;
934 }
935
936 // Emit traps for breakpoints and step mode.
937 if (MOZ_UNLIKELY(compileDebugInstrumentation_) && !emitDebugTrap())
938 return Method_Error;
939
940 switch (op) {
941 // ===== NOT Yet Implemented =====
942 case JSOP_FORCEINTERPRETER:
943 // Intentionally not implemented.
944 case JSOP_SETINTRINSIC:
945 // Run-once opcode during self-hosting initialization.
946 case JSOP_UNUSED126:
947 case JSOP_UNUSED206:
948 case JSOP_UNUSED223:
949 case JSOP_LIMIT:
950 // === !! WARNING WARNING WARNING !! ===
951 // Do you really want to sacrifice performance by not implementing
952 // this operation in the BaselineCompiler?
953 JitSpew(JitSpew_BaselineAbort, "Unhandled op: %s", CodeName[op]);
954 return Method_CantCompile;
955
956 #define EMIT_OP(OP) \
957 case OP: \
958 if (MOZ_UNLIKELY(!this->emit_##OP())) return Method_Error; \
959 break;
960 OPCODE_LIST(EMIT_OP)
961 #undef EMIT_OP
962 }
963
964 // If the main instruction is not a jump target, then we emit the
965 // corresponding code coverage counter.
966 if (pc == script->main() && !BytecodeIsJumpTarget(op)) {
967 if (!emit_JSOP_JUMPTARGET()) return Method_Error;
968 }
969
970 // Test if last instructions and stop emitting in that case.
971 pc += GetBytecodeLength(pc);
972 if (pc >= script->codeEnd()) break;
973
974 emittedOps++;
975 lastOpUnreachable = false;
976 #ifdef DEBUG
977 prevpc = pc;
978 #endif
979 }
980
981 MOZ_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL);
982 return Method_Compiled;
983 }
984
emit_JSOP_NOP()985 bool BaselineCompiler::emit_JSOP_NOP() { return true; }
986
emit_JSOP_ITERNEXT()987 bool BaselineCompiler::emit_JSOP_ITERNEXT() { return true; }
988
emit_JSOP_NOP_DESTRUCTURING()989 bool BaselineCompiler::emit_JSOP_NOP_DESTRUCTURING() { return true; }
990
emit_JSOP_TRY_DESTRUCTURING_ITERCLOSE()991 bool BaselineCompiler::emit_JSOP_TRY_DESTRUCTURING_ITERCLOSE() { return true; }
992
emit_JSOP_LABEL()993 bool BaselineCompiler::emit_JSOP_LABEL() { return true; }
994
emit_JSOP_POP()995 bool BaselineCompiler::emit_JSOP_POP() {
996 frame.pop();
997 return true;
998 }
999
emit_JSOP_POPN()1000 bool BaselineCompiler::emit_JSOP_POPN() {
1001 frame.popn(GET_UINT16(pc));
1002 return true;
1003 }
1004
emit_JSOP_DUPAT()1005 bool BaselineCompiler::emit_JSOP_DUPAT() {
1006 frame.syncStack(0);
1007
1008 // DUPAT takes a value on the stack and re-pushes it on top. It's like
1009 // GETLOCAL but it addresses from the top of the stack instead of from the
1010 // stack frame.
1011
1012 int depth = -(GET_UINT24(pc) + 1);
1013 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
1014 frame.push(R0);
1015 return true;
1016 }
1017
emit_JSOP_DUP()1018 bool BaselineCompiler::emit_JSOP_DUP() {
1019 // Keep top stack value in R0, sync the rest so that we can use R1. We use
1020 // separate registers because every register can be used by at most one
1021 // StackValue.
1022 frame.popRegsAndSync(1);
1023 masm.moveValue(R0, R1);
1024
1025 // inc/dec ops use DUP followed by ONE, ADD. Push R0 last to avoid a move.
1026 frame.push(R1);
1027 frame.push(R0);
1028 return true;
1029 }
1030
emit_JSOP_DUP2()1031 bool BaselineCompiler::emit_JSOP_DUP2() {
1032 frame.syncStack(0);
1033
1034 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
1035 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
1036
1037 frame.push(R0);
1038 frame.push(R1);
1039 return true;
1040 }
1041
emit_JSOP_SWAP()1042 bool BaselineCompiler::emit_JSOP_SWAP() {
1043 // Keep top stack values in R0 and R1.
1044 frame.popRegsAndSync(2);
1045
1046 frame.push(R1);
1047 frame.push(R0);
1048 return true;
1049 }
1050
emit_JSOP_PICK()1051 bool BaselineCompiler::emit_JSOP_PICK() {
1052 frame.syncStack(0);
1053
1054 // Pick takes a value on the stack and moves it to the top.
1055 // For instance, pick 2:
1056 // before: A B C D E
1057 // after : A B D E C
1058
1059 // First, move value at -(amount + 1) into R0.
1060 int32_t depth = -(GET_INT8(pc) + 1);
1061 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
1062
1063 // Move the other values down.
1064 depth++;
1065 for (; depth < 0; depth++) {
1066 Address source = frame.addressOfStackValue(frame.peek(depth));
1067 Address dest = frame.addressOfStackValue(frame.peek(depth - 1));
1068 masm.loadValue(source, R1);
1069 masm.storeValue(R1, dest);
1070 }
1071
1072 // Push R0.
1073 frame.pop();
1074 frame.push(R0);
1075 return true;
1076 }
1077
emit_JSOP_UNPICK()1078 bool BaselineCompiler::emit_JSOP_UNPICK() {
1079 frame.syncStack(0);
1080
1081 // Pick takes the top of the stack value and moves it under the nth value.
1082 // For instance, unpick 2:
1083 // before: A B C D E
1084 // after : A B E C D
1085
1086 // First, move value at -1 into R0.
1087 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1088
1089 // Move the other values up.
1090 int32_t depth = -(GET_INT8(pc) + 1);
1091 for (int32_t i = -1; i > depth; i--) {
1092 Address source = frame.addressOfStackValue(frame.peek(i - 1));
1093 Address dest = frame.addressOfStackValue(frame.peek(i));
1094 masm.loadValue(source, R1);
1095 masm.storeValue(R1, dest);
1096 }
1097
1098 // Store R0 under the nth value.
1099 Address dest = frame.addressOfStackValue(frame.peek(depth));
1100 masm.storeValue(R0, dest);
1101 return true;
1102 }
1103
emit_JSOP_GOTO()1104 bool BaselineCompiler::emit_JSOP_GOTO() {
1105 frame.syncStack(0);
1106
1107 jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
1108 masm.jump(labelOf(target));
1109 return true;
1110 }
1111
emitToBoolean()1112 bool BaselineCompiler::emitToBoolean() {
1113 Label skipIC;
1114 masm.branchTestBoolean(Assembler::Equal, R0, &skipIC);
1115
1116 // Call IC
1117 ICToBool_Fallback::Compiler stubCompiler(cx);
1118 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1119
1120 masm.bind(&skipIC);
1121 return true;
1122 }
1123
emitTest(bool branchIfTrue)1124 bool BaselineCompiler::emitTest(bool branchIfTrue) {
1125 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
1126
1127 // Keep top stack value in R0.
1128 frame.popRegsAndSync(1);
1129
1130 if (!knownBoolean && !emitToBoolean()) return false;
1131
1132 // IC will leave a BooleanValue in R0, just need to branch on it.
1133 masm.branchTestBooleanTruthy(branchIfTrue, R0,
1134 labelOf(pc + GET_JUMP_OFFSET(pc)));
1135 return true;
1136 }
1137
emit_JSOP_IFEQ()1138 bool BaselineCompiler::emit_JSOP_IFEQ() { return emitTest(false); }
1139
emit_JSOP_IFNE()1140 bool BaselineCompiler::emit_JSOP_IFNE() { return emitTest(true); }
1141
emitAndOr(bool branchIfTrue)1142 bool BaselineCompiler::emitAndOr(bool branchIfTrue) {
1143 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
1144
1145 // AND and OR leave the original value on the stack.
1146 frame.syncStack(0);
1147
1148 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1149 if (!knownBoolean && !emitToBoolean()) return false;
1150
1151 masm.branchTestBooleanTruthy(branchIfTrue, R0,
1152 labelOf(pc + GET_JUMP_OFFSET(pc)));
1153 return true;
1154 }
1155
emit_JSOP_AND()1156 bool BaselineCompiler::emit_JSOP_AND() { return emitAndOr(false); }
1157
emit_JSOP_OR()1158 bool BaselineCompiler::emit_JSOP_OR() { return emitAndOr(true); }
1159
emit_JSOP_NOT()1160 bool BaselineCompiler::emit_JSOP_NOT() {
1161 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
1162
1163 // Keep top stack value in R0.
1164 frame.popRegsAndSync(1);
1165
1166 if (!knownBoolean && !emitToBoolean()) return false;
1167
1168 masm.notBoolean(R0);
1169
1170 frame.push(R0, JSVAL_TYPE_BOOLEAN);
1171 return true;
1172 }
1173
emit_JSOP_POS()1174 bool BaselineCompiler::emit_JSOP_POS() {
1175 // Keep top stack value in R0.
1176 frame.popRegsAndSync(1);
1177
1178 // Inline path for int32 and double.
1179 Label done;
1180 masm.branchTestNumber(Assembler::Equal, R0, &done);
1181
1182 // Call IC.
1183 ICToNumber_Fallback::Compiler stubCompiler(cx);
1184 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1185
1186 masm.bind(&done);
1187 frame.push(R0);
1188 return true;
1189 }
1190
emit_JSOP_LOOPHEAD()1191 bool BaselineCompiler::emit_JSOP_LOOPHEAD() {
1192 if (!emit_JSOP_JUMPTARGET()) return false;
1193 return emitInterruptCheck();
1194 }
1195
emit_JSOP_LOOPENTRY()1196 bool BaselineCompiler::emit_JSOP_LOOPENTRY() {
1197 if (!emit_JSOP_JUMPTARGET()) return false;
1198 frame.syncStack(0);
1199 return emitWarmUpCounterIncrement(LoopEntryCanIonOsr(pc));
1200 }
1201
emit_JSOP_VOID()1202 bool BaselineCompiler::emit_JSOP_VOID() {
1203 frame.pop();
1204 frame.push(UndefinedValue());
1205 return true;
1206 }
1207
emit_JSOP_UNDEFINED()1208 bool BaselineCompiler::emit_JSOP_UNDEFINED() {
1209 // If this ever changes, change what JSOP_GIMPLICITTHIS does too.
1210 frame.push(UndefinedValue());
1211 return true;
1212 }
1213
emit_JSOP_HOLE()1214 bool BaselineCompiler::emit_JSOP_HOLE() {
1215 frame.push(MagicValue(JS_ELEMENTS_HOLE));
1216 return true;
1217 }
1218
emit_JSOP_NULL()1219 bool BaselineCompiler::emit_JSOP_NULL() {
1220 frame.push(NullValue());
1221 return true;
1222 }
1223
1224 typedef bool (*ThrowCheckIsObjectFn)(JSContext*, CheckIsObjectKind);
1225 static const VMFunction ThrowCheckIsObjectInfo =
1226 FunctionInfo<ThrowCheckIsObjectFn>(ThrowCheckIsObject,
1227 "ThrowCheckIsObject");
1228
emit_JSOP_CHECKISOBJ()1229 bool BaselineCompiler::emit_JSOP_CHECKISOBJ() {
1230 frame.syncStack(0);
1231 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1232
1233 Label ok;
1234 masm.branchTestObject(Assembler::Equal, R0, &ok);
1235
1236 prepareVMCall();
1237
1238 pushArg(Imm32(GET_UINT8(pc)));
1239 if (!callVM(ThrowCheckIsObjectInfo)) return false;
1240
1241 masm.bind(&ok);
1242 return true;
1243 }
1244
1245 typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
1246 static const VMFunction CheckIsCallableInfo =
1247 FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
1248
emit_JSOP_CHECKISCALLABLE()1249 bool BaselineCompiler::emit_JSOP_CHECKISCALLABLE() {
1250 frame.syncStack(0);
1251 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1252
1253 prepareVMCall();
1254
1255 pushArg(Imm32(GET_UINT8(pc)));
1256 pushArg(R0);
1257 if (!callVM(CheckIsCallableInfo)) return false;
1258
1259 return true;
1260 }
1261
1262 typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
1263 static const VMFunction ThrowUninitializedThisInfo =
1264 FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
1265 "BaselineThrowUninitializedThis");
1266
1267 typedef bool (*ThrowInitializedThisFn)(JSContext*);
1268 static const VMFunction ThrowInitializedThisInfo =
1269 FunctionInfo<ThrowInitializedThisFn>(BaselineThrowInitializedThis,
1270 "BaselineThrowInitializedThis");
1271
emit_JSOP_CHECKTHIS()1272 bool BaselineCompiler::emit_JSOP_CHECKTHIS() {
1273 frame.syncStack(0);
1274 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1275
1276 return emitCheckThis(R0);
1277 }
1278
emit_JSOP_CHECKTHISREINIT()1279 bool BaselineCompiler::emit_JSOP_CHECKTHISREINIT() {
1280 frame.syncStack(0);
1281 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1282
1283 return emitCheckThis(R0, /* reinit = */ true);
1284 }
1285
emitCheckThis(ValueOperand val,bool reinit)1286 bool BaselineCompiler::emitCheckThis(ValueOperand val, bool reinit) {
1287 Label thisOK;
1288 if (reinit)
1289 masm.branchTestMagic(Assembler::Equal, val, &thisOK);
1290 else
1291 masm.branchTestMagic(Assembler::NotEqual, val, &thisOK);
1292
1293 prepareVMCall();
1294
1295 if (reinit) {
1296 if (!callVM(ThrowInitializedThisInfo)) return false;
1297 } else {
1298 masm.loadBaselineFramePtr(BaselineFrameReg, val.scratchReg());
1299 pushArg(val.scratchReg());
1300
1301 if (!callVM(ThrowUninitializedThisInfo)) return false;
1302 }
1303
1304 masm.bind(&thisOK);
1305 return true;
1306 }
1307
1308 typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
1309 static const VMFunction ThrowBadDerivedReturnInfo =
1310 FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn,
1311 "ThrowBadDerivedReturn");
1312
emit_JSOP_CHECKRETURN()1313 bool BaselineCompiler::emit_JSOP_CHECKRETURN() {
1314 MOZ_ASSERT(script->isDerivedClassConstructor());
1315
1316 // Load |this| in R0, return value in R1.
1317 frame.popRegsAndSync(1);
1318 emitLoadReturnValue(R1);
1319
1320 Label done, returnOK;
1321 masm.branchTestObject(Assembler::Equal, R1, &done);
1322 masm.branchTestUndefined(Assembler::Equal, R1, &returnOK);
1323
1324 prepareVMCall();
1325 pushArg(R1);
1326 if (!callVM(ThrowBadDerivedReturnInfo)) return false;
1327 masm.assumeUnreachable("Should throw on bad derived constructor return");
1328
1329 masm.bind(&returnOK);
1330
1331 if (!emitCheckThis(R0)) return false;
1332
1333 // Store |this| in the return value slot.
1334 masm.storeValue(R0, frame.addressOfReturnValue());
1335 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
1336
1337 masm.bind(&done);
1338 return true;
1339 }
1340
1341 typedef bool (*GetFunctionThisFn)(JSContext*, BaselineFrame*,
1342 MutableHandleValue);
1343 static const VMFunction GetFunctionThisInfo = FunctionInfo<GetFunctionThisFn>(
1344 jit::BaselineGetFunctionThis, "BaselineGetFunctionThis");
1345
emit_JSOP_FUNCTIONTHIS()1346 bool BaselineCompiler::emit_JSOP_FUNCTIONTHIS() {
1347 MOZ_ASSERT(function());
1348 MOZ_ASSERT(!function()->isArrow());
1349
1350 frame.pushThis();
1351
1352 // In strict mode code or self-hosted functions, |this| is left alone.
1353 if (script->strict() || (function() && function()->isSelfHostedBuiltin()))
1354 return true;
1355
1356 // Load |thisv| in R0. Skip the call if it's already an object.
1357 Label skipCall;
1358 frame.popRegsAndSync(1);
1359 masm.branchTestObject(Assembler::Equal, R0, &skipCall);
1360
1361 prepareVMCall();
1362 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
1363
1364 pushArg(R1.scratchReg());
1365
1366 if (!callVM(GetFunctionThisInfo)) return false;
1367
1368 masm.bind(&skipCall);
1369 frame.push(R0);
1370 return true;
1371 }
1372
1373 typedef void (*GetNonSyntacticGlobalThisFn)(JSContext*, HandleObject,
1374 MutableHandleValue);
1375 static const VMFunction GetNonSyntacticGlobalThisInfo =
1376 FunctionInfo<GetNonSyntacticGlobalThisFn>(js::GetNonSyntacticGlobalThis,
1377 "GetNonSyntacticGlobalThis");
1378
emit_JSOP_GLOBALTHIS()1379 bool BaselineCompiler::emit_JSOP_GLOBALTHIS() {
1380 frame.syncStack(0);
1381
1382 if (!script->hasNonSyntacticScope()) {
1383 LexicalEnvironmentObject* globalLexical =
1384 &script->global().lexicalEnvironment();
1385 masm.moveValue(globalLexical->thisValue(), R0);
1386 frame.push(R0);
1387 return true;
1388 }
1389
1390 prepareVMCall();
1391
1392 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
1393 pushArg(R0.scratchReg());
1394
1395 if (!callVM(GetNonSyntacticGlobalThisInfo)) return false;
1396
1397 frame.push(R0);
1398 return true;
1399 }
1400
emit_JSOP_TRUE()1401 bool BaselineCompiler::emit_JSOP_TRUE() {
1402 frame.push(BooleanValue(true));
1403 return true;
1404 }
1405
emit_JSOP_FALSE()1406 bool BaselineCompiler::emit_JSOP_FALSE() {
1407 frame.push(BooleanValue(false));
1408 return true;
1409 }
1410
emit_JSOP_ZERO()1411 bool BaselineCompiler::emit_JSOP_ZERO() {
1412 frame.push(Int32Value(0));
1413 return true;
1414 }
1415
emit_JSOP_ONE()1416 bool BaselineCompiler::emit_JSOP_ONE() {
1417 frame.push(Int32Value(1));
1418 return true;
1419 }
1420
emit_JSOP_INT8()1421 bool BaselineCompiler::emit_JSOP_INT8() {
1422 frame.push(Int32Value(GET_INT8(pc)));
1423 return true;
1424 }
1425
emit_JSOP_INT32()1426 bool BaselineCompiler::emit_JSOP_INT32() {
1427 frame.push(Int32Value(GET_INT32(pc)));
1428 return true;
1429 }
1430
emit_JSOP_UINT16()1431 bool BaselineCompiler::emit_JSOP_UINT16() {
1432 frame.push(Int32Value(GET_UINT16(pc)));
1433 return true;
1434 }
1435
emit_JSOP_UINT24()1436 bool BaselineCompiler::emit_JSOP_UINT24() {
1437 frame.push(Int32Value(GET_UINT24(pc)));
1438 return true;
1439 }
1440
emit_JSOP_DOUBLE()1441 bool BaselineCompiler::emit_JSOP_DOUBLE() {
1442 frame.push(script->getConst(GET_UINT32_INDEX(pc)));
1443 return true;
1444 }
1445
emit_JSOP_STRING()1446 bool BaselineCompiler::emit_JSOP_STRING() {
1447 frame.push(StringValue(script->getAtom(pc)));
1448 return true;
1449 }
1450
emit_JSOP_SYMBOL()1451 bool BaselineCompiler::emit_JSOP_SYMBOL() {
1452 unsigned which = GET_UINT8(pc);
1453 JS::Symbol* sym = cx->runtime()->wellKnownSymbols->get(which);
1454 frame.push(SymbolValue(sym));
1455 return true;
1456 }
1457
1458 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject,
1459 NewObjectKind);
1460 static const VMFunction DeepCloneObjectLiteralInfo =
1461 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral,
1462 "DeepCloneObjectLiteral");
1463
emit_JSOP_OBJECT()1464 bool BaselineCompiler::emit_JSOP_OBJECT() {
1465 JSCompartment* comp = cx->compartment();
1466 if (comp->creationOptions().cloneSingletons()) {
1467 RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
1468 if (!obj) return false;
1469
1470 prepareVMCall();
1471
1472 pushArg(ImmWord(TenuredObject));
1473 pushArg(ImmGCPtr(obj));
1474
1475 if (!callVM(DeepCloneObjectLiteralInfo)) return false;
1476
1477 // Box and push return value.
1478 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1479 frame.push(R0);
1480 return true;
1481 }
1482
1483 comp->behaviors().setSingletonsAsValues();
1484 frame.push(ObjectValue(*script->getObject(pc)));
1485 return true;
1486 }
1487
emit_JSOP_CALLSITEOBJ()1488 bool BaselineCompiler::emit_JSOP_CALLSITEOBJ() {
1489 RootedObject cso(cx, script->getObject(pc));
1490 RootedObject raw(cx, script->getObject(GET_UINT32_INDEX(pc) + 1));
1491 if (!cso || !raw) return false;
1492
1493 if (!ProcessCallSiteObjOperation(cx, cso, raw)) return false;
1494
1495 frame.push(ObjectValue(*cso));
1496 return true;
1497 }
1498
1499 typedef JSObject* (*CloneRegExpObjectFn)(JSContext*, Handle<RegExpObject*>);
1500 static const VMFunction CloneRegExpObjectInfo =
1501 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject, "CloneRegExpObject");
1502
emit_JSOP_REGEXP()1503 bool BaselineCompiler::emit_JSOP_REGEXP() {
1504 RootedObject reObj(cx, script->getRegExp(pc));
1505
1506 prepareVMCall();
1507 pushArg(ImmGCPtr(reObj));
1508 if (!callVM(CloneRegExpObjectInfo)) return false;
1509
1510 // Box and push return value.
1511 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1512 frame.push(R0);
1513 return true;
1514 }
1515
1516 typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject);
1517 static const VMFunction LambdaInfo =
1518 FunctionInfo<LambdaFn>(js::Lambda, "Lambda");
1519
emit_JSOP_LAMBDA()1520 bool BaselineCompiler::emit_JSOP_LAMBDA() {
1521 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
1522
1523 prepareVMCall();
1524 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
1525
1526 pushArg(R0.scratchReg());
1527 pushArg(ImmGCPtr(fun));
1528
1529 if (!callVM(LambdaInfo)) return false;
1530
1531 // Box and push return value.
1532 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1533 frame.push(R0);
1534 return true;
1535 }
1536
1537 typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject,
1538 HandleValue);
1539 static const VMFunction LambdaArrowInfo =
1540 FunctionInfo<LambdaArrowFn>(js::LambdaArrow, "LambdaArrow");
1541
emit_JSOP_LAMBDA_ARROW()1542 bool BaselineCompiler::emit_JSOP_LAMBDA_ARROW() {
1543 // Keep pushed newTarget in R0.
1544 frame.popRegsAndSync(1);
1545
1546 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
1547
1548 prepareVMCall();
1549 masm.loadPtr(frame.addressOfEnvironmentChain(), R2.scratchReg());
1550
1551 pushArg(R0);
1552 pushArg(R2.scratchReg());
1553 pushArg(ImmGCPtr(fun));
1554
1555 if (!callVM(LambdaArrowInfo)) return false;
1556
1557 // Box and push return value.
1558 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1559 frame.push(R0);
1560 return true;
1561 }
1562
1563 typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue,
1564 FunctionPrefixKind);
1565 static const VMFunction SetFunNameInfo =
1566 FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
1567
emit_JSOP_SETFUNNAME()1568 bool BaselineCompiler::emit_JSOP_SETFUNNAME() {
1569 frame.popRegsAndSync(2);
1570
1571 frame.push(R0);
1572 frame.syncStack(0);
1573
1574 FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(pc));
1575 masm.unboxObject(R0, R0.scratchReg());
1576
1577 prepareVMCall();
1578
1579 pushArg(Imm32(int32_t(prefixKind)));
1580 pushArg(R1);
1581 pushArg(R0.scratchReg());
1582 return callVM(SetFunNameInfo);
1583 }
1584
storeValue(const StackValue * source,const Address & dest,const ValueOperand & scratch)1585 void BaselineCompiler::storeValue(const StackValue* source, const Address& dest,
1586 const ValueOperand& scratch) {
1587 switch (source->kind()) {
1588 case StackValue::Constant:
1589 masm.storeValue(source->constant(), dest);
1590 break;
1591 case StackValue::Register:
1592 masm.storeValue(source->reg(), dest);
1593 break;
1594 case StackValue::LocalSlot:
1595 masm.loadValue(frame.addressOfLocal(source->localSlot()), scratch);
1596 masm.storeValue(scratch, dest);
1597 break;
1598 case StackValue::ArgSlot:
1599 masm.loadValue(frame.addressOfArg(source->argSlot()), scratch);
1600 masm.storeValue(scratch, dest);
1601 break;
1602 case StackValue::ThisSlot:
1603 masm.loadValue(frame.addressOfThis(), scratch);
1604 masm.storeValue(scratch, dest);
1605 break;
1606 case StackValue::EvalNewTargetSlot:
1607 MOZ_ASSERT(script->isForEval());
1608 masm.loadValue(frame.addressOfEvalNewTarget(), scratch);
1609 masm.storeValue(scratch, dest);
1610 break;
1611 case StackValue::Stack:
1612 masm.loadValue(frame.addressOfStackValue(source), scratch);
1613 masm.storeValue(scratch, dest);
1614 break;
1615 default:
1616 MOZ_CRASH("Invalid kind");
1617 }
1618 }
1619
emit_JSOP_BITOR()1620 bool BaselineCompiler::emit_JSOP_BITOR() { return emitBinaryArith(); }
1621
emit_JSOP_BITXOR()1622 bool BaselineCompiler::emit_JSOP_BITXOR() { return emitBinaryArith(); }
1623
emit_JSOP_BITAND()1624 bool BaselineCompiler::emit_JSOP_BITAND() { return emitBinaryArith(); }
1625
emit_JSOP_LSH()1626 bool BaselineCompiler::emit_JSOP_LSH() { return emitBinaryArith(); }
1627
emit_JSOP_RSH()1628 bool BaselineCompiler::emit_JSOP_RSH() { return emitBinaryArith(); }
1629
emit_JSOP_URSH()1630 bool BaselineCompiler::emit_JSOP_URSH() { return emitBinaryArith(); }
1631
emit_JSOP_ADD()1632 bool BaselineCompiler::emit_JSOP_ADD() { return emitBinaryArith(); }
1633
emit_JSOP_SUB()1634 bool BaselineCompiler::emit_JSOP_SUB() { return emitBinaryArith(); }
1635
emit_JSOP_MUL()1636 bool BaselineCompiler::emit_JSOP_MUL() { return emitBinaryArith(); }
1637
emit_JSOP_DIV()1638 bool BaselineCompiler::emit_JSOP_DIV() { return emitBinaryArith(); }
1639
emit_JSOP_MOD()1640 bool BaselineCompiler::emit_JSOP_MOD() { return emitBinaryArith(); }
1641
emit_JSOP_POW()1642 bool BaselineCompiler::emit_JSOP_POW() { return emitBinaryArith(); }
1643
emitBinaryArith()1644 bool BaselineCompiler::emitBinaryArith() {
1645 // Keep top JSStack value in R0 and R2
1646 frame.popRegsAndSync(2);
1647
1648 // Call IC
1649 ICBinaryArith_Fallback::Compiler stubCompiler(
1650 cx, ICStubCompiler::Engine::Baseline);
1651 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1652
1653 // Mark R0 as pushed stack value.
1654 frame.push(R0);
1655 return true;
1656 }
1657
emitUnaryArith()1658 bool BaselineCompiler::emitUnaryArith() {
1659 // Keep top stack value in R0.
1660 frame.popRegsAndSync(1);
1661
1662 // Call IC
1663 ICUnaryArith_Fallback::Compiler stubCompiler(
1664 cx, ICStubCompiler::Engine::Baseline);
1665 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1666
1667 // Mark R0 as pushed stack value.
1668 frame.push(R0);
1669 return true;
1670 }
1671
emit_JSOP_BITNOT()1672 bool BaselineCompiler::emit_JSOP_BITNOT() { return emitUnaryArith(); }
1673
emit_JSOP_NEG()1674 bool BaselineCompiler::emit_JSOP_NEG() { return emitUnaryArith(); }
1675
emit_JSOP_LT()1676 bool BaselineCompiler::emit_JSOP_LT() { return emitCompare(); }
1677
emit_JSOP_LE()1678 bool BaselineCompiler::emit_JSOP_LE() { return emitCompare(); }
1679
emit_JSOP_GT()1680 bool BaselineCompiler::emit_JSOP_GT() { return emitCompare(); }
1681
emit_JSOP_GE()1682 bool BaselineCompiler::emit_JSOP_GE() { return emitCompare(); }
1683
emit_JSOP_EQ()1684 bool BaselineCompiler::emit_JSOP_EQ() { return emitCompare(); }
1685
emit_JSOP_NE()1686 bool BaselineCompiler::emit_JSOP_NE() { return emitCompare(); }
1687
emitCompare()1688 bool BaselineCompiler::emitCompare() {
1689 // CODEGEN
1690
1691 // Keep top JSStack value in R0 and R1.
1692 frame.popRegsAndSync(2);
1693
1694 // Call IC.
1695 ICCompare_Fallback::Compiler stubCompiler(cx,
1696 ICStubCompiler::Engine::Baseline);
1697 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1698
1699 // Mark R0 as pushed stack value.
1700 frame.push(R0, JSVAL_TYPE_BOOLEAN);
1701 return true;
1702 }
1703
emit_JSOP_STRICTEQ()1704 bool BaselineCompiler::emit_JSOP_STRICTEQ() { return emitCompare(); }
1705
emit_JSOP_STRICTNE()1706 bool BaselineCompiler::emit_JSOP_STRICTNE() { return emitCompare(); }
1707
emit_JSOP_CONDSWITCH()1708 bool BaselineCompiler::emit_JSOP_CONDSWITCH() { return true; }
1709
emit_JSOP_CASE()1710 bool BaselineCompiler::emit_JSOP_CASE() {
1711 frame.popRegsAndSync(2);
1712 frame.push(R0);
1713 frame.syncStack(0);
1714
1715 // Call IC.
1716 ICCompare_Fallback::Compiler stubCompiler(cx,
1717 ICStubCompiler::Engine::Baseline);
1718 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1719
1720 Register payload = masm.extractInt32(R0, R0.scratchReg());
1721 jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
1722
1723 Label done;
1724 masm.branch32(Assembler::Equal, payload, Imm32(0), &done);
1725 {
1726 // Pop the switch value if the case matches.
1727 masm.addToStackPtr(Imm32(sizeof(Value)));
1728 masm.jump(labelOf(target));
1729 }
1730 masm.bind(&done);
1731 return true;
1732 }
1733
emit_JSOP_DEFAULT()1734 bool BaselineCompiler::emit_JSOP_DEFAULT() {
1735 frame.pop();
1736 return emit_JSOP_GOTO();
1737 }
1738
emit_JSOP_LINENO()1739 bool BaselineCompiler::emit_JSOP_LINENO() { return true; }
1740
emit_JSOP_NEWARRAY()1741 bool BaselineCompiler::emit_JSOP_NEWARRAY() {
1742 frame.syncStack(0);
1743
1744 uint32_t length = GET_UINT32(pc);
1745 MOZ_ASSERT(length <= INT32_MAX,
1746 "the bytecode emitter must fail to compile code that would "
1747 "produce JSOP_NEWARRAY with a length exceeding int32_t range");
1748
1749 // Pass length in R0.
1750 masm.move32(Imm32(AssertedCast<int32_t>(length)), R0.scratchReg());
1751
1752 ObjectGroup* group =
1753 ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
1754 if (!group) return false;
1755
1756 ICNewArray_Fallback::Compiler stubCompiler(cx, group,
1757 ICStubCompiler::Engine::Baseline);
1758 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1759
1760 frame.push(R0);
1761 return true;
1762 }
1763
1764 typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject,
1765 gc::InitialHeap);
1766 const VMFunction jit::NewArrayCopyOnWriteInfo =
1767 FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray,
1768 "NewDenseCopyOnWriteArray");
1769
emit_JSOP_NEWARRAY_COPYONWRITE()1770 bool BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE() {
1771 RootedScript scriptRoot(cx, script);
1772 JSObject* obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
1773 if (!obj) return false;
1774
1775 prepareVMCall();
1776
1777 pushArg(Imm32(gc::DefaultHeap));
1778 pushArg(ImmGCPtr(obj));
1779
1780 if (!callVM(NewArrayCopyOnWriteInfo)) return false;
1781
1782 // Box and push return value.
1783 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1784 frame.push(R0);
1785 return true;
1786 }
1787
emit_JSOP_INITELEM_ARRAY()1788 bool BaselineCompiler::emit_JSOP_INITELEM_ARRAY() {
1789 // Keep the object and rhs on the stack.
1790 frame.syncStack(0);
1791
1792 // Load object in R0, index in R1.
1793 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
1794 uint32_t index = GET_UINT32(pc);
1795 MOZ_ASSERT(index <= INT32_MAX,
1796 "the bytecode emitter must fail to compile code that would "
1797 "produce JSOP_INITELEM_ARRAY with a length exceeding "
1798 "int32_t range");
1799 masm.moveValue(Int32Value(AssertedCast<int32_t>(index)), R1);
1800
1801 // Call IC.
1802 ICSetElem_Fallback::Compiler stubCompiler(cx);
1803 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1804
1805 // Pop the rhs, so that the object is on the top of the stack.
1806 frame.pop();
1807 return true;
1808 }
1809
emit_JSOP_NEWOBJECT()1810 bool BaselineCompiler::emit_JSOP_NEWOBJECT() {
1811 frame.syncStack(0);
1812
1813 ICNewObject_Fallback::Compiler stubCompiler(cx,
1814 ICStubCompiler::Engine::Baseline);
1815 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1816
1817 frame.push(R0);
1818 return true;
1819 }
1820
emit_JSOP_NEWINIT()1821 bool BaselineCompiler::emit_JSOP_NEWINIT() {
1822 frame.syncStack(0);
1823 JSProtoKey key = JSProtoKey(GET_UINT8(pc));
1824
1825 if (key == JSProto_Array) {
1826 // Pass length in R0.
1827 masm.move32(Imm32(0), R0.scratchReg());
1828
1829 ObjectGroup* group =
1830 ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
1831 if (!group) return false;
1832
1833 ICNewArray_Fallback::Compiler stubCompiler(
1834 cx, group, ICStubCompiler::Engine::Baseline);
1835 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1836 } else {
1837 MOZ_ASSERT(key == JSProto_Object);
1838
1839 ICNewObject_Fallback::Compiler stubCompiler(
1840 cx, ICStubCompiler::Engine::Baseline);
1841 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1842 }
1843
1844 frame.push(R0);
1845 return true;
1846 }
1847
emit_JSOP_INITELEM()1848 bool BaselineCompiler::emit_JSOP_INITELEM() {
1849 // Store RHS in the scratch slot.
1850 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
1851 frame.pop();
1852
1853 // Keep object and index in R0 and R1.
1854 frame.popRegsAndSync(2);
1855
1856 // Push the object to store the result of the IC.
1857 frame.push(R0);
1858 frame.syncStack(0);
1859
1860 // Keep RHS on the stack.
1861 frame.pushScratchValue();
1862
1863 // Call IC.
1864 ICSetElem_Fallback::Compiler stubCompiler(cx);
1865 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1866
1867 // Pop the rhs, so that the object is on the top of the stack.
1868 frame.pop();
1869 return true;
1870 }
1871
emit_JSOP_INITHIDDENELEM()1872 bool BaselineCompiler::emit_JSOP_INITHIDDENELEM() {
1873 return emit_JSOP_INITELEM();
1874 }
1875
1876 typedef bool (*MutateProtoFn)(JSContext* cx, HandlePlainObject obj,
1877 HandleValue newProto);
1878 static const VMFunction MutateProtoInfo =
1879 FunctionInfo<MutateProtoFn>(MutatePrototype, "MutatePrototype");
1880
emit_JSOP_MUTATEPROTO()1881 bool BaselineCompiler::emit_JSOP_MUTATEPROTO() {
1882 // Keep values on the stack for the decompiler.
1883 frame.syncStack(0);
1884
1885 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)),
1886 R0.scratchReg());
1887 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
1888
1889 prepareVMCall();
1890
1891 pushArg(R1);
1892 pushArg(R0.scratchReg());
1893
1894 if (!callVM(MutateProtoInfo)) return false;
1895
1896 frame.pop();
1897 return true;
1898 }
1899
emit_JSOP_INITPROP()1900 bool BaselineCompiler::emit_JSOP_INITPROP() {
1901 // Load lhs in R0, rhs in R1.
1902 frame.syncStack(0);
1903 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
1904 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
1905
1906 // Call IC.
1907 ICSetProp_Fallback::Compiler compiler(cx);
1908 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
1909
1910 // Leave the object on the stack.
1911 frame.pop();
1912 return true;
1913 }
1914
emit_JSOP_INITLOCKEDPROP()1915 bool BaselineCompiler::emit_JSOP_INITLOCKEDPROP() {
1916 return emit_JSOP_INITPROP();
1917 }
1918
emit_JSOP_INITHIDDENPROP()1919 bool BaselineCompiler::emit_JSOP_INITHIDDENPROP() {
1920 return emit_JSOP_INITPROP();
1921 }
1922
emit_JSOP_GETELEM()1923 bool BaselineCompiler::emit_JSOP_GETELEM() {
1924 // Keep top two stack values in R0 and R1.
1925 frame.popRegsAndSync(2);
1926
1927 // Call IC.
1928 ICGetElem_Fallback::Compiler stubCompiler(cx);
1929 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1930
1931 // Mark R0 as pushed stack value.
1932 frame.push(R0);
1933 return true;
1934 }
1935
emit_JSOP_GETELEM_SUPER()1936 bool BaselineCompiler::emit_JSOP_GETELEM_SUPER() {
1937 // Store obj in the scratch slot.
1938 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
1939 frame.pop();
1940
1941 // Keep index and receiver in R0 and R1.
1942 frame.popRegsAndSync(2);
1943
1944 // Keep obj on the stack.
1945 frame.pushScratchValue();
1946
1947 ICGetElem_Fallback::Compiler stubCompiler(cx, /* hasReceiver = */ true);
1948 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1949
1950 frame.pop(); // This value is also popped in InitFromBailout.
1951 frame.push(R0);
1952 return true;
1953 }
1954
emit_JSOP_CALLELEM()1955 bool BaselineCompiler::emit_JSOP_CALLELEM() { return emit_JSOP_GETELEM(); }
1956
emit_JSOP_SETELEM()1957 bool BaselineCompiler::emit_JSOP_SETELEM() {
1958 // Store RHS in the scratch slot.
1959 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
1960 frame.pop();
1961
1962 // Keep object and index in R0 and R1.
1963 frame.popRegsAndSync(2);
1964
1965 // Keep RHS on the stack.
1966 frame.pushScratchValue();
1967
1968 // Call IC.
1969 ICSetElem_Fallback::Compiler stubCompiler(cx);
1970 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
1971
1972 return true;
1973 }
1974
emit_JSOP_STRICTSETELEM()1975 bool BaselineCompiler::emit_JSOP_STRICTSETELEM() { return emit_JSOP_SETELEM(); }
1976
emit_JSOP_SETELEM_SUPER()1977 bool BaselineCompiler::emit_JSOP_SETELEM_SUPER() {
1978 bool strict = IsCheckStrictOp(JSOp(*pc));
1979
1980 // Incoming stack is |propval, receiver, obj, rval|. We need to shuffle
1981 // stack to leave rval when operation is complete.
1982
1983 // Pop rval into R0, then load propval into R1 and replace with rval.
1984 frame.popRegsAndSync(1);
1985 masm.loadValue(frame.addressOfStackValue(frame.peek(-3)), R1);
1986 masm.storeValue(R0, frame.addressOfStackValue(frame.peek(-3)));
1987
1988 prepareVMCall();
1989
1990 pushArg(Imm32(strict));
1991 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R2);
1992 pushArg(R2); // receiver
1993 pushArg(R0); // rval
1994 pushArg(R1); // propval
1995 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
1996 pushArg(R0.scratchReg()); // obj
1997
1998 if (!callVM(SetObjectElementInfo)) return false;
1999
2000 frame.popn(2);
2001 return true;
2002 }
2003
emit_JSOP_STRICTSETELEM_SUPER()2004 bool BaselineCompiler::emit_JSOP_STRICTSETELEM_SUPER() {
2005 return emit_JSOP_SETELEM_SUPER();
2006 }
2007
2008 typedef bool (*DeleteElementFn)(JSContext*, HandleValue, HandleValue, bool*);
2009 static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(
2010 DeleteElementJit<true>, "DeleteElementStrict");
2011 static const VMFunction DeleteElementNonStrictInfo =
2012 FunctionInfo<DeleteElementFn>(DeleteElementJit<false>,
2013 "DeleteElementNonStrict");
2014
emit_JSOP_DELELEM()2015 bool BaselineCompiler::emit_JSOP_DELELEM() {
2016 // Keep values on the stack for the decompiler.
2017 frame.syncStack(0);
2018 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
2019 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
2020
2021 prepareVMCall();
2022
2023 pushArg(R1);
2024 pushArg(R0);
2025
2026 bool strict = JSOp(*pc) == JSOP_STRICTDELELEM;
2027 if (!callVM(strict ? DeleteElementStrictInfo : DeleteElementNonStrictInfo))
2028 return false;
2029
2030 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
2031 frame.popn(2);
2032 frame.push(R1);
2033 return true;
2034 }
2035
emit_JSOP_STRICTDELELEM()2036 bool BaselineCompiler::emit_JSOP_STRICTDELELEM() { return emit_JSOP_DELELEM(); }
2037
emit_JSOP_IN()2038 bool BaselineCompiler::emit_JSOP_IN() {
2039 frame.popRegsAndSync(2);
2040
2041 ICIn_Fallback::Compiler stubCompiler(cx);
2042 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2043
2044 frame.push(R0);
2045 return true;
2046 }
2047
emit_JSOP_HASOWN()2048 bool BaselineCompiler::emit_JSOP_HASOWN() {
2049 frame.popRegsAndSync(2);
2050
2051 ICHasOwn_Fallback::Compiler stubCompiler(cx);
2052 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2053
2054 frame.push(R0);
2055 return true;
2056 }
2057
emit_JSOP_GETGNAME()2058 bool BaselineCompiler::emit_JSOP_GETGNAME() {
2059 if (script->hasNonSyntacticScope()) return emit_JSOP_GETNAME();
2060
2061 RootedPropertyName name(cx, script->getName(pc));
2062
2063 // These names are non-configurable on the global and cannot be shadowed.
2064 if (name == cx->names().undefined) {
2065 frame.push(UndefinedValue());
2066 return true;
2067 }
2068 if (name == cx->names().NaN) {
2069 frame.push(cx->runtime()->NaNValue);
2070 return true;
2071 }
2072 if (name == cx->names().Infinity) {
2073 frame.push(cx->runtime()->positiveInfinityValue);
2074 return true;
2075 }
2076
2077 frame.syncStack(0);
2078
2079 masm.movePtr(ImmGCPtr(&script->global().lexicalEnvironment()),
2080 R0.scratchReg());
2081
2082 // Call IC.
2083 ICGetName_Fallback::Compiler stubCompiler(cx);
2084 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2085
2086 // Mark R0 as pushed stack value.
2087 frame.push(R0);
2088 return true;
2089 }
2090
emit_JSOP_BINDGNAME()2091 bool BaselineCompiler::emit_JSOP_BINDGNAME() {
2092 if (!script->hasNonSyntacticScope()) {
2093 // We can bind name to the global lexical scope if the binding already
2094 // exists, is initialized, and is writable (i.e., an initialized
2095 // 'let') at compile time.
2096 RootedPropertyName name(cx, script->getName(pc));
2097 Rooted<LexicalEnvironmentObject*> env(
2098 cx, &script->global().lexicalEnvironment());
2099 if (Shape* shape = env->lookup(cx, name)) {
2100 if (shape->writable() &&
2101 !env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
2102 frame.push(ObjectValue(*env));
2103 return true;
2104 }
2105 } else if (Shape* shape = script->global().lookup(cx, name)) {
2106 // If the property does not currently exist on the global lexical
2107 // scope, we can bind name to the global object if the property
2108 // exists on the global and is non-configurable, as then it cannot
2109 // be shadowed.
2110 if (!shape->configurable()) {
2111 frame.push(ObjectValue(script->global()));
2112 return true;
2113 }
2114 }
2115
2116 // Otherwise we have to use the environment chain.
2117 }
2118
2119 return emit_JSOP_BINDNAME();
2120 }
2121
2122 typedef JSObject* (*BindVarFn)(JSContext*, HandleObject);
2123 static const VMFunction BindVarInfo =
2124 FunctionInfo<BindVarFn>(jit::BindVar, "BindVar");
2125
emit_JSOP_BINDVAR()2126 bool BaselineCompiler::emit_JSOP_BINDVAR() {
2127 frame.syncStack(0);
2128 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2129
2130 prepareVMCall();
2131 pushArg(R0.scratchReg());
2132
2133 if (!callVM(BindVarInfo)) return false;
2134
2135 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
2136 frame.push(R0);
2137 return true;
2138 }
2139
emit_JSOP_SETPROP()2140 bool BaselineCompiler::emit_JSOP_SETPROP() {
2141 // Keep lhs in R0, rhs in R1.
2142 frame.popRegsAndSync(2);
2143
2144 // Keep RHS on the stack.
2145 frame.push(R1);
2146 frame.syncStack(0);
2147
2148 // Call IC.
2149 ICSetProp_Fallback::Compiler compiler(cx);
2150 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2151
2152 return true;
2153 }
2154
emit_JSOP_STRICTSETPROP()2155 bool BaselineCompiler::emit_JSOP_STRICTSETPROP() { return emit_JSOP_SETPROP(); }
2156
emit_JSOP_SETNAME()2157 bool BaselineCompiler::emit_JSOP_SETNAME() { return emit_JSOP_SETPROP(); }
2158
emit_JSOP_STRICTSETNAME()2159 bool BaselineCompiler::emit_JSOP_STRICTSETNAME() { return emit_JSOP_SETPROP(); }
2160
emit_JSOP_SETGNAME()2161 bool BaselineCompiler::emit_JSOP_SETGNAME() { return emit_JSOP_SETPROP(); }
2162
emit_JSOP_STRICTSETGNAME()2163 bool BaselineCompiler::emit_JSOP_STRICTSETGNAME() {
2164 return emit_JSOP_SETPROP();
2165 }
2166
2167 typedef bool (*SetPropertySuperFn)(JSContext*, HandleObject, HandleValue,
2168 HandlePropertyName, HandleValue, bool);
2169 static const VMFunction SetPropertySuperInfo =
2170 FunctionInfo<SetPropertySuperFn>(js::SetPropertySuper, "SetPropertySuper");
2171
emit_JSOP_SETPROP_SUPER()2172 bool BaselineCompiler::emit_JSOP_SETPROP_SUPER() {
2173 bool strict = IsCheckStrictOp(JSOp(*pc));
2174
2175 // Incoming stack is |receiver, obj, rval|. We need to shuffle stack to
2176 // leave rval when operation is complete.
2177
2178 // Pop rval into R0, then load receiver into R1 and replace with rval.
2179 frame.popRegsAndSync(1);
2180 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
2181 masm.storeValue(R0, frame.addressOfStackValue(frame.peek(-2)));
2182
2183 prepareVMCall();
2184
2185 pushArg(Imm32(strict));
2186 pushArg(R0); // rval
2187 pushArg(ImmGCPtr(script->getName(pc)));
2188 pushArg(R1); // receiver
2189 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
2190 pushArg(R0.scratchReg()); // obj
2191
2192 if (!callVM(SetPropertySuperInfo)) return false;
2193
2194 frame.pop();
2195 return true;
2196 }
2197
emit_JSOP_STRICTSETPROP_SUPER()2198 bool BaselineCompiler::emit_JSOP_STRICTSETPROP_SUPER() {
2199 return emit_JSOP_SETPROP_SUPER();
2200 }
2201
emit_JSOP_GETPROP()2202 bool BaselineCompiler::emit_JSOP_GETPROP() {
2203 // Keep object in R0.
2204 frame.popRegsAndSync(1);
2205
2206 // Call IC.
2207 ICGetProp_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline);
2208 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2209
2210 // Mark R0 as pushed stack value.
2211 frame.push(R0);
2212 return true;
2213 }
2214
emit_JSOP_CALLPROP()2215 bool BaselineCompiler::emit_JSOP_CALLPROP() { return emit_JSOP_GETPROP(); }
2216
emit_JSOP_LENGTH()2217 bool BaselineCompiler::emit_JSOP_LENGTH() { return emit_JSOP_GETPROP(); }
2218
emit_JSOP_GETBOUNDNAME()2219 bool BaselineCompiler::emit_JSOP_GETBOUNDNAME() { return emit_JSOP_GETPROP(); }
2220
emit_JSOP_GETPROP_SUPER()2221 bool BaselineCompiler::emit_JSOP_GETPROP_SUPER() {
2222 // Receiver -> R1, Object -> R0
2223 frame.popRegsAndSync(1);
2224 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
2225 frame.pop();
2226
2227 ICGetProp_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline,
2228 /* hasReceiver = */ true);
2229 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2230
2231 frame.push(R0);
2232 return true;
2233 }
2234
2235 typedef bool (*DeletePropertyFn)(JSContext*, HandleValue, HandlePropertyName,
2236 bool*);
2237 static const VMFunction DeletePropertyStrictInfo =
2238 FunctionInfo<DeletePropertyFn>(DeletePropertyJit<true>,
2239 "DeletePropertyStrict");
2240 static const VMFunction DeletePropertyNonStrictInfo =
2241 FunctionInfo<DeletePropertyFn>(DeletePropertyJit<false>,
2242 "DeletePropertyNonStrict");
2243
emit_JSOP_DELPROP()2244 bool BaselineCompiler::emit_JSOP_DELPROP() {
2245 // Keep value on the stack for the decompiler.
2246 frame.syncStack(0);
2247 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2248
2249 prepareVMCall();
2250
2251 pushArg(ImmGCPtr(script->getName(pc)));
2252 pushArg(R0);
2253
2254 bool strict = JSOp(*pc) == JSOP_STRICTDELPROP;
2255 if (!callVM(strict ? DeletePropertyStrictInfo : DeletePropertyNonStrictInfo))
2256 return false;
2257
2258 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
2259 frame.pop();
2260 frame.push(R1);
2261 return true;
2262 }
2263
emit_JSOP_STRICTDELPROP()2264 bool BaselineCompiler::emit_JSOP_STRICTDELPROP() { return emit_JSOP_DELPROP(); }
2265
getEnvironmentCoordinateObject(Register reg)2266 void BaselineCompiler::getEnvironmentCoordinateObject(Register reg) {
2267 EnvironmentCoordinate ec(pc);
2268
2269 masm.loadPtr(frame.addressOfEnvironmentChain(), reg);
2270 for (unsigned i = ec.hops(); i; i--)
2271 masm.extractObject(
2272 Address(reg, EnvironmentObject::offsetOfEnclosingEnvironment()), reg);
2273 }
2274
getEnvironmentCoordinateAddressFromObject(Register objReg,Register reg)2275 Address BaselineCompiler::getEnvironmentCoordinateAddressFromObject(
2276 Register objReg, Register reg) {
2277 EnvironmentCoordinate ec(pc);
2278 Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc);
2279
2280 Address addr;
2281 if (shape->numFixedSlots() <= ec.slot()) {
2282 masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg);
2283 return Address(reg, (ec.slot() - shape->numFixedSlots()) * sizeof(Value));
2284 }
2285
2286 return Address(objReg, NativeObject::getFixedSlotOffset(ec.slot()));
2287 }
2288
getEnvironmentCoordinateAddress(Register reg)2289 Address BaselineCompiler::getEnvironmentCoordinateAddress(Register reg) {
2290 getEnvironmentCoordinateObject(reg);
2291 return getEnvironmentCoordinateAddressFromObject(reg, reg);
2292 }
2293
emit_JSOP_GETALIASEDVAR()2294 bool BaselineCompiler::emit_JSOP_GETALIASEDVAR() {
2295 frame.syncStack(0);
2296
2297 Address address = getEnvironmentCoordinateAddress(R0.scratchReg());
2298 masm.loadValue(address, R0);
2299
2300 if (ionCompileable_) {
2301 // No need to monitor types if we know Ion can't compile this script.
2302 ICTypeMonitor_Fallback::Compiler compiler(cx, nullptr);
2303 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2304 }
2305
2306 frame.push(R0);
2307 return true;
2308 }
2309
emit_JSOP_SETALIASEDVAR()2310 bool BaselineCompiler::emit_JSOP_SETALIASEDVAR() {
2311 JSScript* outerScript = EnvironmentCoordinateFunctionScript(script, pc);
2312 if (outerScript && outerScript->treatAsRunOnce()) {
2313 // Type updates for this operation might need to be tracked, so treat
2314 // this as a SETPROP.
2315
2316 // Load rhs into R1.
2317 frame.syncStack(0);
2318 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
2319
2320 // Load and box lhs into R0.
2321 getEnvironmentCoordinateObject(R2.scratchReg());
2322 masm.tagValue(JSVAL_TYPE_OBJECT, R2.scratchReg(), R0);
2323
2324 // Call SETPROP IC.
2325 ICSetProp_Fallback::Compiler compiler(cx);
2326 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2327
2328 return true;
2329 }
2330
2331 // Keep rvalue in R0.
2332 frame.popRegsAndSync(1);
2333 Register objReg = R2.scratchReg();
2334
2335 getEnvironmentCoordinateObject(objReg);
2336 Address address =
2337 getEnvironmentCoordinateAddressFromObject(objReg, R1.scratchReg());
2338 masm.guardedCallPreBarrier(address, MIRType::Value);
2339 masm.storeValue(R0, address);
2340 frame.push(R0);
2341
2342 // Only R0 is live at this point.
2343 // Scope coordinate object is already in R2.scratchReg().
2344 Register temp = R1.scratchReg();
2345
2346 Label skipBarrier;
2347 masm.branchPtrInNurseryChunk(Assembler::Equal, objReg, temp, &skipBarrier);
2348 masm.branchValueIsNurseryCell(Assembler::NotEqual, R0, temp, &skipBarrier);
2349
2350 masm.call(&postBarrierSlot_); // Won't clobber R0
2351
2352 masm.bind(&skipBarrier);
2353 return true;
2354 }
2355
emit_JSOP_GETNAME()2356 bool BaselineCompiler::emit_JSOP_GETNAME() {
2357 frame.syncStack(0);
2358
2359 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2360
2361 // Call IC.
2362 ICGetName_Fallback::Compiler stubCompiler(cx);
2363 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2364
2365 // Mark R0 as pushed stack value.
2366 frame.push(R0);
2367 return true;
2368 }
2369
emit_JSOP_BINDNAME()2370 bool BaselineCompiler::emit_JSOP_BINDNAME() {
2371 frame.syncStack(0);
2372
2373 if (*pc == JSOP_BINDGNAME && !script->hasNonSyntacticScope())
2374 masm.movePtr(ImmGCPtr(&script->global().lexicalEnvironment()),
2375 R0.scratchReg());
2376 else
2377 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2378
2379 // Call IC.
2380 ICBindName_Fallback::Compiler stubCompiler(cx);
2381 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2382
2383 // Mark R0 as pushed stack value.
2384 frame.push(R0);
2385 return true;
2386 }
2387
2388 typedef bool (*DeleteNameFn)(JSContext*, HandlePropertyName, HandleObject,
2389 MutableHandleValue);
2390 static const VMFunction DeleteNameInfo =
2391 FunctionInfo<DeleteNameFn>(DeleteNameOperation, "DeleteNameOperation");
2392
emit_JSOP_DELNAME()2393 bool BaselineCompiler::emit_JSOP_DELNAME() {
2394 frame.syncStack(0);
2395 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2396
2397 prepareVMCall();
2398
2399 pushArg(R0.scratchReg());
2400 pushArg(ImmGCPtr(script->getName(pc)));
2401
2402 if (!callVM(DeleteNameInfo)) return false;
2403
2404 frame.push(R0);
2405 return true;
2406 }
2407
emit_JSOP_GETIMPORT()2408 bool BaselineCompiler::emit_JSOP_GETIMPORT() {
2409 ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script);
2410 MOZ_ASSERT(env);
2411
2412 ModuleEnvironmentObject* targetEnv;
2413 Shape* shape;
2414 MOZ_ALWAYS_TRUE(
2415 env->lookupImport(NameToId(script->getName(pc)), &targetEnv, &shape));
2416
2417 EnsureTrackPropertyTypes(cx, targetEnv, shape->propid());
2418
2419 frame.syncStack(0);
2420
2421 uint32_t slot = shape->slot();
2422 Register scratch = R0.scratchReg();
2423 masm.movePtr(ImmGCPtr(targetEnv), scratch);
2424 if (slot < targetEnv->numFixedSlots()) {
2425 masm.loadValue(Address(scratch, NativeObject::getFixedSlotOffset(slot)),
2426 R0);
2427 } else {
2428 masm.loadPtr(Address(scratch, NativeObject::offsetOfSlots()), scratch);
2429 masm.loadValue(
2430 Address(scratch, (slot - targetEnv->numFixedSlots()) * sizeof(Value)),
2431 R0);
2432 }
2433
2434 // Imports are initialized by this point except in rare circumstances, so
2435 // don't emit a check unless we have to.
2436 if (targetEnv->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
2437 if (!emitUninitializedLexicalCheck(R0)) return false;
2438
2439 if (ionCompileable_) {
2440 // No need to monitor types if we know Ion can't compile this script.
2441 ICTypeMonitor_Fallback::Compiler compiler(cx, nullptr);
2442 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
2443 }
2444
2445 frame.push(R0);
2446 return true;
2447 }
2448
emit_JSOP_GETINTRINSIC()2449 bool BaselineCompiler::emit_JSOP_GETINTRINSIC() {
2450 frame.syncStack(0);
2451
2452 ICGetIntrinsic_Fallback::Compiler stubCompiler(cx);
2453 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2454
2455 frame.push(R0);
2456 return true;
2457 }
2458
2459 typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned,
2460 HandleObject);
2461 static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar, "DefVar");
2462
emit_JSOP_DEFVAR()2463 bool BaselineCompiler::emit_JSOP_DEFVAR() {
2464 frame.syncStack(0);
2465
2466 unsigned attrs = JSPROP_ENUMERATE;
2467 if (!script->isForEval()) attrs |= JSPROP_PERMANENT;
2468 MOZ_ASSERT(attrs <= UINT32_MAX);
2469
2470 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2471
2472 prepareVMCall();
2473
2474 pushArg(R0.scratchReg());
2475 pushArg(Imm32(attrs));
2476 pushArg(ImmGCPtr(script->getName(pc)));
2477
2478 return callVM(DefVarInfo);
2479 }
2480
2481 typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned,
2482 HandleObject);
2483 static const VMFunction DefLexicalInfo =
2484 FunctionInfo<DefLexicalFn>(DefLexical, "DefLexical");
2485
emit_JSOP_DEFCONST()2486 bool BaselineCompiler::emit_JSOP_DEFCONST() { return emit_JSOP_DEFLET(); }
2487
emit_JSOP_DEFLET()2488 bool BaselineCompiler::emit_JSOP_DEFLET() {
2489 frame.syncStack(0);
2490
2491 unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
2492 if (*pc == JSOP_DEFCONST) attrs |= JSPROP_READONLY;
2493 MOZ_ASSERT(attrs <= UINT32_MAX);
2494
2495 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2496
2497 prepareVMCall();
2498
2499 pushArg(R0.scratchReg());
2500 pushArg(Imm32(attrs));
2501 pushArg(ImmGCPtr(script->getName(pc)));
2502
2503 return callVM(DefLexicalInfo);
2504 }
2505
2506 typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject,
2507 HandleFunction);
2508 static const VMFunction DefFunOperationInfo =
2509 FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
2510
emit_JSOP_DEFFUN()2511 bool BaselineCompiler::emit_JSOP_DEFFUN() {
2512 frame.popRegsAndSync(1);
2513 masm.unboxObject(R0, R0.scratchReg());
2514 masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
2515
2516 prepareVMCall();
2517
2518 pushArg(R0.scratchReg());
2519 pushArg(R1.scratchReg());
2520 pushArg(ImmGCPtr(script));
2521
2522 return callVM(DefFunOperationInfo);
2523 }
2524
2525 typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
2526 HandlePropertyName, HandleObject);
2527 static const VMFunction InitPropGetterSetterInfo =
2528 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation,
2529 "InitPropGetterSetterOperation");
2530
emitInitPropGetterSetter()2531 bool BaselineCompiler::emitInitPropGetterSetter() {
2532 MOZ_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER ||
2533 JSOp(*pc) == JSOP_INITHIDDENPROP_GETTER ||
2534 JSOp(*pc) == JSOP_INITPROP_SETTER ||
2535 JSOp(*pc) == JSOP_INITHIDDENPROP_SETTER);
2536
2537 // Keep values on the stack for the decompiler.
2538 frame.syncStack(0);
2539
2540 prepareVMCall();
2541
2542 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)),
2543 R0.scratchReg());
2544 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)),
2545 R1.scratchReg());
2546
2547 pushArg(R0.scratchReg());
2548 pushArg(ImmGCPtr(script->getName(pc)));
2549 pushArg(R1.scratchReg());
2550 pushArg(ImmPtr(pc));
2551
2552 if (!callVM(InitPropGetterSetterInfo)) return false;
2553
2554 frame.pop();
2555 return true;
2556 }
2557
emit_JSOP_INITPROP_GETTER()2558 bool BaselineCompiler::emit_JSOP_INITPROP_GETTER() {
2559 return emitInitPropGetterSetter();
2560 }
2561
emit_JSOP_INITHIDDENPROP_GETTER()2562 bool BaselineCompiler::emit_JSOP_INITHIDDENPROP_GETTER() {
2563 return emitInitPropGetterSetter();
2564 }
2565
emit_JSOP_INITPROP_SETTER()2566 bool BaselineCompiler::emit_JSOP_INITPROP_SETTER() {
2567 return emitInitPropGetterSetter();
2568 }
2569
emit_JSOP_INITHIDDENPROP_SETTER()2570 bool BaselineCompiler::emit_JSOP_INITHIDDENPROP_SETTER() {
2571 return emitInitPropGetterSetter();
2572 }
2573
2574 typedef bool (*InitElemGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
2575 HandleValue, HandleObject);
2576 static const VMFunction InitElemGetterSetterInfo =
2577 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation,
2578 "InitElemGetterSetterOperation");
2579
emitInitElemGetterSetter()2580 bool BaselineCompiler::emitInitElemGetterSetter() {
2581 MOZ_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER ||
2582 JSOp(*pc) == JSOP_INITHIDDENELEM_GETTER ||
2583 JSOp(*pc) == JSOP_INITELEM_SETTER ||
2584 JSOp(*pc) == JSOP_INITHIDDENELEM_SETTER);
2585
2586 // Load index and value in R0 and R1, but keep values on the stack for the
2587 // decompiler.
2588 frame.syncStack(0);
2589 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
2590 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)),
2591 R1.scratchReg());
2592
2593 prepareVMCall();
2594
2595 pushArg(R1.scratchReg());
2596 pushArg(R0);
2597 masm.extractObject(frame.addressOfStackValue(frame.peek(-3)),
2598 R0.scratchReg());
2599 pushArg(R0.scratchReg());
2600 pushArg(ImmPtr(pc));
2601
2602 if (!callVM(InitElemGetterSetterInfo)) return false;
2603
2604 frame.popn(2);
2605 return true;
2606 }
2607
emit_JSOP_INITELEM_GETTER()2608 bool BaselineCompiler::emit_JSOP_INITELEM_GETTER() {
2609 return emitInitElemGetterSetter();
2610 }
2611
emit_JSOP_INITHIDDENELEM_GETTER()2612 bool BaselineCompiler::emit_JSOP_INITHIDDENELEM_GETTER() {
2613 return emitInitElemGetterSetter();
2614 }
2615
emit_JSOP_INITELEM_SETTER()2616 bool BaselineCompiler::emit_JSOP_INITELEM_SETTER() {
2617 return emitInitElemGetterSetter();
2618 }
2619
emit_JSOP_INITHIDDENELEM_SETTER()2620 bool BaselineCompiler::emit_JSOP_INITHIDDENELEM_SETTER() {
2621 return emitInitElemGetterSetter();
2622 }
2623
emit_JSOP_INITELEM_INC()2624 bool BaselineCompiler::emit_JSOP_INITELEM_INC() {
2625 // Keep the object and rhs on the stack.
2626 frame.syncStack(0);
2627
2628 // Load object in R0, index in R1.
2629 masm.loadValue(frame.addressOfStackValue(frame.peek(-3)), R0);
2630 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
2631
2632 // Call IC.
2633 ICSetElem_Fallback::Compiler stubCompiler(cx);
2634 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2635
2636 // Pop the rhs
2637 frame.pop();
2638
2639 // Increment index
2640 Address indexAddr = frame.addressOfStackValue(frame.peek(-1));
2641 #ifdef DEBUG
2642 Label isInt32;
2643 masm.branchTestInt32(Assembler::Equal, indexAddr, &isInt32);
2644 masm.assumeUnreachable("INITELEM_INC index must be Int32");
2645 masm.bind(&isInt32);
2646 #endif
2647 masm.incrementInt32Value(indexAddr);
2648 return true;
2649 }
2650
emit_JSOP_GETLOCAL()2651 bool BaselineCompiler::emit_JSOP_GETLOCAL() {
2652 frame.pushLocal(GET_LOCALNO(pc));
2653 return true;
2654 }
2655
emit_JSOP_SETLOCAL()2656 bool BaselineCompiler::emit_JSOP_SETLOCAL() {
2657 // Ensure no other StackValue refers to the old value, for instance i + (i =
2658 // 3). This also allows us to use R0 as scratch below.
2659 frame.syncStack(1);
2660
2661 uint32_t local = GET_LOCALNO(pc);
2662 storeValue(frame.peek(-1), frame.addressOfLocal(local), R0);
2663 return true;
2664 }
2665
emitFormalArgAccess(uint32_t arg,bool get)2666 bool BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get) {
2667 // Fast path: the script does not use |arguments| or formals don't
2668 // alias the arguments object.
2669 if (!script->argumentsAliasesFormals()) {
2670 if (get) {
2671 frame.pushArg(arg);
2672 } else {
2673 // See the comment in emit_JSOP_SETLOCAL.
2674 frame.syncStack(1);
2675 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
2676 }
2677
2678 return true;
2679 }
2680
2681 // Sync so that we can use R0.
2682 frame.syncStack(0);
2683
2684 // If the script is known to have an arguments object, we can just use it.
2685 // Else, we *may* have an arguments object (because we can't invalidate
2686 // when needsArgsObj becomes |true|), so we have to test HAS_ARGS_OBJ.
2687 Label done;
2688 if (!script->needsArgsObj()) {
2689 Label hasArgsObj;
2690 masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
2691 Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj);
2692 if (get)
2693 masm.loadValue(frame.addressOfArg(arg), R0);
2694 else
2695 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
2696 masm.jump(&done);
2697 masm.bind(&hasArgsObj);
2698 }
2699
2700 // Load the arguments object data vector.
2701 Register reg = R2.scratchReg();
2702 masm.loadPtr(
2703 Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
2704 masm.loadPrivate(Address(reg, ArgumentsObject::getDataSlotOffset()), reg);
2705
2706 // Load/store the argument.
2707 Address argAddr(reg, ArgumentsData::offsetOfArgs() + arg * sizeof(Value));
2708 if (get) {
2709 masm.loadValue(argAddr, R0);
2710 frame.push(R0);
2711 } else {
2712 masm.guardedCallPreBarrier(argAddr, MIRType::Value);
2713 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2714 masm.storeValue(R0, argAddr);
2715
2716 MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
2717
2718 Register temp = R1.scratchReg();
2719
2720 // Reload the arguments object
2721 Register reg = R2.scratchReg();
2722 masm.loadPtr(
2723 Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()),
2724 reg);
2725
2726 Label skipBarrier;
2727
2728 masm.branchPtrInNurseryChunk(Assembler::Equal, reg, temp, &skipBarrier);
2729 masm.branchValueIsNurseryCell(Assembler::NotEqual, R0, temp, &skipBarrier);
2730
2731 masm.call(&postBarrierSlot_);
2732
2733 masm.bind(&skipBarrier);
2734 }
2735
2736 masm.bind(&done);
2737 return true;
2738 }
2739
emit_JSOP_GETARG()2740 bool BaselineCompiler::emit_JSOP_GETARG() {
2741 uint32_t arg = GET_ARGNO(pc);
2742 return emitFormalArgAccess(arg, /* get = */ true);
2743 }
2744
emit_JSOP_SETARG()2745 bool BaselineCompiler::emit_JSOP_SETARG() {
2746 // Ionmonkey can't inline functions with SETARG with magic arguments.
2747 if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals())
2748 script->setUninlineable();
2749
2750 modifiesArguments_ = true;
2751
2752 uint32_t arg = GET_ARGNO(pc);
2753 return emitFormalArgAccess(arg, /* get = */ false);
2754 }
2755
emit_JSOP_NEWTARGET()2756 bool BaselineCompiler::emit_JSOP_NEWTARGET() {
2757 if (script->isForEval()) {
2758 frame.pushEvalNewTarget();
2759 return true;
2760 }
2761
2762 MOZ_ASSERT(function());
2763 frame.syncStack(0);
2764
2765 if (function()->isArrow()) {
2766 // Arrow functions store their |new.target| value in an
2767 // extended slot.
2768 Register scratch = R0.scratchReg();
2769 masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
2770 masm.loadValue(
2771 Address(scratch, FunctionExtended::offsetOfArrowNewTargetSlot()), R0);
2772 frame.push(R0);
2773 return true;
2774 }
2775
2776 // if (isConstructing()) push(argv[Max(numActualArgs, numFormalArgs)])
2777 Label notConstructing, done;
2778 masm.branchTestPtr(Assembler::Zero, frame.addressOfCalleeToken(),
2779 Imm32(CalleeToken_FunctionConstructing), ¬Constructing);
2780
2781 Register argvLen = R0.scratchReg();
2782
2783 Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
2784 masm.loadPtr(actualArgs, argvLen);
2785
2786 Label useNFormals;
2787
2788 masm.branchPtr(Assembler::Below, argvLen, Imm32(function()->nargs()),
2789 &useNFormals);
2790
2791 {
2792 BaseValueIndex newTarget(BaselineFrameReg, argvLen,
2793 BaselineFrame::offsetOfArg(0));
2794 masm.loadValue(newTarget, R0);
2795 masm.jump(&done);
2796 }
2797
2798 masm.bind(&useNFormals);
2799
2800 {
2801 Address newTarget(
2802 BaselineFrameReg,
2803 BaselineFrame::offsetOfArg(0) + (function()->nargs() * sizeof(Value)));
2804 masm.loadValue(newTarget, R0);
2805 masm.jump(&done);
2806 }
2807
2808 // else push(undefined)
2809 masm.bind(¬Constructing);
2810 masm.moveValue(UndefinedValue(), R0);
2811
2812 masm.bind(&done);
2813 frame.push(R0);
2814
2815 return true;
2816 }
2817
2818 typedef bool (*ThrowRuntimeLexicalErrorFn)(JSContext* cx, unsigned);
2819 static const VMFunction ThrowRuntimeLexicalErrorInfo =
2820 FunctionInfo<ThrowRuntimeLexicalErrorFn>(jit::ThrowRuntimeLexicalError,
2821 "ThrowRuntimeLexicalError");
2822
emitThrowConstAssignment()2823 bool BaselineCompiler::emitThrowConstAssignment() {
2824 prepareVMCall();
2825 pushArg(Imm32(JSMSG_BAD_CONST_ASSIGN));
2826 return callVM(ThrowRuntimeLexicalErrorInfo);
2827 }
2828
emit_JSOP_THROWSETCONST()2829 bool BaselineCompiler::emit_JSOP_THROWSETCONST() {
2830 return emitThrowConstAssignment();
2831 }
2832
emit_JSOP_THROWSETALIASEDCONST()2833 bool BaselineCompiler::emit_JSOP_THROWSETALIASEDCONST() {
2834 return emitThrowConstAssignment();
2835 }
2836
emit_JSOP_THROWSETCALLEE()2837 bool BaselineCompiler::emit_JSOP_THROWSETCALLEE() {
2838 return emitThrowConstAssignment();
2839 }
2840
emitUninitializedLexicalCheck(const ValueOperand & val)2841 bool BaselineCompiler::emitUninitializedLexicalCheck(const ValueOperand& val) {
2842 Label done;
2843 masm.branchTestMagicValue(Assembler::NotEqual, val, JS_UNINITIALIZED_LEXICAL,
2844 &done);
2845
2846 prepareVMCall();
2847 pushArg(Imm32(JSMSG_UNINITIALIZED_LEXICAL));
2848 if (!callVM(ThrowRuntimeLexicalErrorInfo)) return false;
2849
2850 masm.bind(&done);
2851 return true;
2852 }
2853
emit_JSOP_CHECKLEXICAL()2854 bool BaselineCompiler::emit_JSOP_CHECKLEXICAL() {
2855 frame.syncStack(0);
2856 masm.loadValue(frame.addressOfLocal(GET_LOCALNO(pc)), R0);
2857 return emitUninitializedLexicalCheck(R0);
2858 }
2859
emit_JSOP_INITLEXICAL()2860 bool BaselineCompiler::emit_JSOP_INITLEXICAL() { return emit_JSOP_SETLOCAL(); }
2861
emit_JSOP_INITGLEXICAL()2862 bool BaselineCompiler::emit_JSOP_INITGLEXICAL() {
2863 frame.popRegsAndSync(1);
2864 frame.push(ObjectValue(script->global().lexicalEnvironment()));
2865 frame.push(R0);
2866 return emit_JSOP_SETPROP();
2867 }
2868
emit_JSOP_CHECKALIASEDLEXICAL()2869 bool BaselineCompiler::emit_JSOP_CHECKALIASEDLEXICAL() {
2870 frame.syncStack(0);
2871 masm.loadValue(getEnvironmentCoordinateAddress(R0.scratchReg()), R0);
2872 return emitUninitializedLexicalCheck(R0);
2873 }
2874
emit_JSOP_INITALIASEDLEXICAL()2875 bool BaselineCompiler::emit_JSOP_INITALIASEDLEXICAL() {
2876 return emit_JSOP_SETALIASEDVAR();
2877 }
2878
emit_JSOP_UNINITIALIZED()2879 bool BaselineCompiler::emit_JSOP_UNINITIALIZED() {
2880 frame.push(MagicValue(JS_UNINITIALIZED_LEXICAL));
2881 return true;
2882 }
2883
emitCall()2884 bool BaselineCompiler::emitCall() {
2885 MOZ_ASSERT(IsCallPC(pc));
2886
2887 bool construct = JSOp(*pc) == JSOP_NEW || JSOp(*pc) == JSOP_SUPERCALL;
2888 uint32_t argc = GET_ARGC(pc);
2889
2890 frame.syncStack(0);
2891 masm.move32(Imm32(argc), R0.scratchReg());
2892
2893 // Call IC
2894 ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ construct,
2895 /* isSpread = */ false);
2896 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2897
2898 // Update FrameInfo.
2899 frame.popn(2 + argc + construct);
2900 frame.push(R0);
2901 return true;
2902 }
2903
emitSpreadCall()2904 bool BaselineCompiler::emitSpreadCall() {
2905 MOZ_ASSERT(IsCallPC(pc));
2906
2907 frame.syncStack(0);
2908 masm.move32(Imm32(1), R0.scratchReg());
2909
2910 // Call IC
2911 bool construct =
2912 JSOp(*pc) == JSOP_SPREADNEW || JSOp(*pc) == JSOP_SPREADSUPERCALL;
2913 ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ construct,
2914 /* isSpread = */ true);
2915 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
2916
2917 // Update FrameInfo.
2918 frame.popn(3 + construct);
2919 frame.push(R0);
2920 return true;
2921 }
2922
emit_JSOP_CALL()2923 bool BaselineCompiler::emit_JSOP_CALL() { return emitCall(); }
2924
emit_JSOP_CALL_IGNORES_RV()2925 bool BaselineCompiler::emit_JSOP_CALL_IGNORES_RV() { return emitCall(); }
2926
emit_JSOP_CALLITER()2927 bool BaselineCompiler::emit_JSOP_CALLITER() { return emitCall(); }
2928
emit_JSOP_NEW()2929 bool BaselineCompiler::emit_JSOP_NEW() { return emitCall(); }
2930
emit_JSOP_SUPERCALL()2931 bool BaselineCompiler::emit_JSOP_SUPERCALL() { return emitCall(); }
2932
emit_JSOP_FUNCALL()2933 bool BaselineCompiler::emit_JSOP_FUNCALL() { return emitCall(); }
2934
emit_JSOP_FUNAPPLY()2935 bool BaselineCompiler::emit_JSOP_FUNAPPLY() { return emitCall(); }
2936
emit_JSOP_EVAL()2937 bool BaselineCompiler::emit_JSOP_EVAL() { return emitCall(); }
2938
emit_JSOP_STRICTEVAL()2939 bool BaselineCompiler::emit_JSOP_STRICTEVAL() { return emitCall(); }
2940
emit_JSOP_SPREADCALL()2941 bool BaselineCompiler::emit_JSOP_SPREADCALL() { return emitSpreadCall(); }
2942
emit_JSOP_SPREADNEW()2943 bool BaselineCompiler::emit_JSOP_SPREADNEW() { return emitSpreadCall(); }
2944
emit_JSOP_SPREADSUPERCALL()2945 bool BaselineCompiler::emit_JSOP_SPREADSUPERCALL() { return emitSpreadCall(); }
2946
emit_JSOP_SPREADEVAL()2947 bool BaselineCompiler::emit_JSOP_SPREADEVAL() { return emitSpreadCall(); }
2948
emit_JSOP_STRICTSPREADEVAL()2949 bool BaselineCompiler::emit_JSOP_STRICTSPREADEVAL() { return emitSpreadCall(); }
2950
2951 typedef bool (*OptimizeSpreadCallFn)(JSContext*, HandleValue, bool*);
2952 static const VMFunction OptimizeSpreadCallInfo =
2953 FunctionInfo<OptimizeSpreadCallFn>(OptimizeSpreadCall,
2954 "OptimizeSpreadCall");
2955
emit_JSOP_OPTIMIZE_SPREADCALL()2956 bool BaselineCompiler::emit_JSOP_OPTIMIZE_SPREADCALL() {
2957 frame.syncStack(0);
2958 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2959
2960 prepareVMCall();
2961 pushArg(R0);
2962
2963 if (!callVM(OptimizeSpreadCallInfo)) return false;
2964
2965 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
2966 frame.push(R0);
2967 return true;
2968 }
2969
2970 typedef bool (*ImplicitThisFn)(JSContext*, HandleObject, HandlePropertyName,
2971 MutableHandleValue);
2972 const VMFunction jit::ImplicitThisInfo = FunctionInfo<ImplicitThisFn>(
2973 ImplicitThisOperation, "ImplicitThisOperation");
2974
emit_JSOP_IMPLICITTHIS()2975 bool BaselineCompiler::emit_JSOP_IMPLICITTHIS() {
2976 frame.syncStack(0);
2977 masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
2978
2979 prepareVMCall();
2980
2981 pushArg(ImmGCPtr(script->getName(pc)));
2982 pushArg(R0.scratchReg());
2983
2984 if (!callVM(ImplicitThisInfo)) return false;
2985
2986 frame.push(R0);
2987 return true;
2988 }
2989
emit_JSOP_GIMPLICITTHIS()2990 bool BaselineCompiler::emit_JSOP_GIMPLICITTHIS() {
2991 if (!script->hasNonSyntacticScope()) {
2992 frame.push(UndefinedValue());
2993 return true;
2994 }
2995
2996 return emit_JSOP_IMPLICITTHIS();
2997 }
2998
emit_JSOP_INSTANCEOF()2999 bool BaselineCompiler::emit_JSOP_INSTANCEOF() {
3000 frame.popRegsAndSync(2);
3001
3002 ICInstanceOf_Fallback::Compiler stubCompiler(cx);
3003 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
3004
3005 frame.push(R0);
3006 return true;
3007 }
3008
emit_JSOP_TYPEOF()3009 bool BaselineCompiler::emit_JSOP_TYPEOF() {
3010 frame.popRegsAndSync(1);
3011
3012 ICTypeOf_Fallback::Compiler stubCompiler(cx);
3013 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false;
3014
3015 frame.push(R0);
3016 return true;
3017 }
3018
emit_JSOP_TYPEOFEXPR()3019 bool BaselineCompiler::emit_JSOP_TYPEOFEXPR() { return emit_JSOP_TYPEOF(); }
3020
3021 typedef bool (*ThrowMsgFn)(JSContext*, const unsigned);
3022 static const VMFunction ThrowMsgInfo =
3023 FunctionInfo<ThrowMsgFn>(js::ThrowMsgOperation, "ThrowMsgOperation");
3024
emit_JSOP_THROWMSG()3025 bool BaselineCompiler::emit_JSOP_THROWMSG() {
3026 prepareVMCall();
3027 pushArg(Imm32(GET_UINT16(pc)));
3028 return callVM(ThrowMsgInfo);
3029 }
3030
3031 typedef bool (*ThrowFn)(JSContext*, HandleValue);
3032 static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw, "Throw");
3033
emit_JSOP_THROW()3034 bool BaselineCompiler::emit_JSOP_THROW() {
3035 // Keep value to throw in R0.
3036 frame.popRegsAndSync(1);
3037
3038 prepareVMCall();
3039 pushArg(R0);
3040
3041 return callVM(ThrowInfo);
3042 }
3043
3044 typedef bool (*ThrowingFn)(JSContext*, HandleValue);
3045 static const VMFunction ThrowingInfo =
3046 FunctionInfo<ThrowingFn>(js::ThrowingOperation, "ThrowingOperation");
3047
emit_JSOP_THROWING()3048 bool BaselineCompiler::emit_JSOP_THROWING() {
3049 // Keep value to throw in R0.
3050 frame.popRegsAndSync(1);
3051
3052 prepareVMCall();
3053 pushArg(R0);
3054
3055 return callVM(ThrowingInfo);
3056 }
3057
emit_JSOP_TRY()3058 bool BaselineCompiler::emit_JSOP_TRY() {
3059 if (!emit_JSOP_JUMPTARGET()) return false;
3060
3061 // Ionmonkey can't inline function with JSOP_TRY.
3062 script->setUninlineable();
3063 return true;
3064 }
3065
emit_JSOP_FINALLY()3066 bool BaselineCompiler::emit_JSOP_FINALLY() {
3067 // JSOP_FINALLY has a def count of 2, but these values are already on the
3068 // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
3069 frame.setStackDepth(frame.stackDepth() + 2);
3070
3071 // To match the interpreter, emit an interrupt check at the start of the
3072 // finally block.
3073 return emitInterruptCheck();
3074 }
3075
emit_JSOP_GOSUB()3076 bool BaselineCompiler::emit_JSOP_GOSUB() {
3077 // Push |false| so that RETSUB knows the value on top of the
3078 // stack is not an exception but the offset to the op following
3079 // this GOSUB.
3080 frame.push(BooleanValue(false));
3081
3082 int32_t nextOffset = script->pcToOffset(GetNextPc(pc));
3083 frame.push(Int32Value(nextOffset));
3084
3085 // Jump to the finally block.
3086 frame.syncStack(0);
3087 jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
3088 masm.jump(labelOf(target));
3089 return true;
3090 }
3091
emit_JSOP_RETSUB()3092 bool BaselineCompiler::emit_JSOP_RETSUB() {
3093 frame.popRegsAndSync(2);
3094
3095 ICRetSub_Fallback::Compiler stubCompiler(cx);
3096 return emitOpIC(stubCompiler.getStub(&stubSpace_));
3097 }
3098
3099 typedef bool (*PushLexicalEnvFn)(JSContext*, BaselineFrame*,
3100 Handle<LexicalScope*>);
3101 static const VMFunction PushLexicalEnvInfo =
3102 FunctionInfo<PushLexicalEnvFn>(jit::PushLexicalEnv, "PushLexicalEnv");
3103
emit_JSOP_PUSHLEXICALENV()3104 bool BaselineCompiler::emit_JSOP_PUSHLEXICALENV() {
3105 LexicalScope& scope = script->getScope(pc)->as<LexicalScope>();
3106
3107 // Call a stub to push the block on the block chain.
3108 prepareVMCall();
3109 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3110
3111 pushArg(ImmGCPtr(&scope));
3112 pushArg(R0.scratchReg());
3113
3114 return callVM(PushLexicalEnvInfo);
3115 }
3116
3117 typedef bool (*PopLexicalEnvFn)(JSContext*, BaselineFrame*);
3118 static const VMFunction PopLexicalEnvInfo =
3119 FunctionInfo<PopLexicalEnvFn>(jit::PopLexicalEnv, "PopLexicalEnv");
3120
3121 typedef bool (*DebugLeaveThenPopLexicalEnvFn)(JSContext*, BaselineFrame*,
3122 jsbytecode*);
3123 static const VMFunction DebugLeaveThenPopLexicalEnvInfo =
3124 FunctionInfo<DebugLeaveThenPopLexicalEnvFn>(
3125 jit::DebugLeaveThenPopLexicalEnv, "DebugLeaveThenPopLexicalEnv");
3126
emit_JSOP_POPLEXICALENV()3127 bool BaselineCompiler::emit_JSOP_POPLEXICALENV() {
3128 prepareVMCall();
3129
3130 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3131
3132 if (compileDebugInstrumentation_) {
3133 pushArg(ImmPtr(pc));
3134 pushArg(R0.scratchReg());
3135 return callVM(DebugLeaveThenPopLexicalEnvInfo);
3136 }
3137
3138 pushArg(R0.scratchReg());
3139 return callVM(PopLexicalEnvInfo);
3140 }
3141
3142 typedef bool (*FreshenLexicalEnvFn)(JSContext*, BaselineFrame*);
3143 static const VMFunction FreshenLexicalEnvInfo =
3144 FunctionInfo<FreshenLexicalEnvFn>(jit::FreshenLexicalEnv,
3145 "FreshenLexicalEnv");
3146
3147 typedef bool (*DebugLeaveThenFreshenLexicalEnvFn)(JSContext*, BaselineFrame*,
3148 jsbytecode*);
3149 static const VMFunction DebugLeaveThenFreshenLexicalEnvInfo =
3150 FunctionInfo<DebugLeaveThenFreshenLexicalEnvFn>(
3151 jit::DebugLeaveThenFreshenLexicalEnv,
3152 "DebugLeaveThenFreshenLexicalEnv");
3153
emit_JSOP_FRESHENLEXICALENV()3154 bool BaselineCompiler::emit_JSOP_FRESHENLEXICALENV() {
3155 prepareVMCall();
3156
3157 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3158
3159 if (compileDebugInstrumentation_) {
3160 pushArg(ImmPtr(pc));
3161 pushArg(R0.scratchReg());
3162 return callVM(DebugLeaveThenFreshenLexicalEnvInfo);
3163 }
3164
3165 pushArg(R0.scratchReg());
3166 return callVM(FreshenLexicalEnvInfo);
3167 }
3168
3169 typedef bool (*RecreateLexicalEnvFn)(JSContext*, BaselineFrame*);
3170 static const VMFunction RecreateLexicalEnvInfo =
3171 FunctionInfo<RecreateLexicalEnvFn>(jit::RecreateLexicalEnv,
3172 "RecreateLexicalEnv");
3173
3174 typedef bool (*DebugLeaveThenRecreateLexicalEnvFn)(JSContext*, BaselineFrame*,
3175 jsbytecode*);
3176 static const VMFunction DebugLeaveThenRecreateLexicalEnvInfo =
3177 FunctionInfo<DebugLeaveThenRecreateLexicalEnvFn>(
3178 jit::DebugLeaveThenRecreateLexicalEnv,
3179 "DebugLeaveThenRecreateLexicalEnv");
3180
emit_JSOP_RECREATELEXICALENV()3181 bool BaselineCompiler::emit_JSOP_RECREATELEXICALENV() {
3182 prepareVMCall();
3183
3184 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3185
3186 if (compileDebugInstrumentation_) {
3187 pushArg(ImmPtr(pc));
3188 pushArg(R0.scratchReg());
3189 return callVM(DebugLeaveThenRecreateLexicalEnvInfo);
3190 }
3191
3192 pushArg(R0.scratchReg());
3193 return callVM(RecreateLexicalEnvInfo);
3194 }
3195
3196 typedef bool (*DebugLeaveLexicalEnvFn)(JSContext*, BaselineFrame*, jsbytecode*);
3197 static const VMFunction DebugLeaveLexicalEnvInfo =
3198 FunctionInfo<DebugLeaveLexicalEnvFn>(jit::DebugLeaveLexicalEnv,
3199 "DebugLeaveLexicalEnv");
3200
emit_JSOP_DEBUGLEAVELEXICALENV()3201 bool BaselineCompiler::emit_JSOP_DEBUGLEAVELEXICALENV() {
3202 if (!compileDebugInstrumentation_) return true;
3203
3204 prepareVMCall();
3205 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3206 pushArg(ImmPtr(pc));
3207 pushArg(R0.scratchReg());
3208
3209 return callVM(DebugLeaveLexicalEnvInfo);
3210 }
3211
3212 typedef bool (*PushVarEnvFn)(JSContext*, BaselineFrame*, HandleScope);
3213 static const VMFunction PushVarEnvInfo =
3214 FunctionInfo<PushVarEnvFn>(jit::PushVarEnv, "PushVarEnv");
3215
emit_JSOP_PUSHVARENV()3216 bool BaselineCompiler::emit_JSOP_PUSHVARENV() {
3217 prepareVMCall();
3218 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3219 pushArg(ImmGCPtr(script->getScope(pc)));
3220 pushArg(R0.scratchReg());
3221
3222 return callVM(PushVarEnvInfo);
3223 }
3224
3225 typedef bool (*PopVarEnvFn)(JSContext*, BaselineFrame*);
3226 static const VMFunction PopVarEnvInfo =
3227 FunctionInfo<PopVarEnvFn>(jit::PopVarEnv, "PopVarEnv");
3228
emit_JSOP_POPVARENV()3229 bool BaselineCompiler::emit_JSOP_POPVARENV() {
3230 prepareVMCall();
3231 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3232 pushArg(R0.scratchReg());
3233
3234 return callVM(PopVarEnvInfo);
3235 }
3236
3237 typedef bool (*EnterWithFn)(JSContext*, BaselineFrame*, HandleValue,
3238 Handle<WithScope*>);
3239 static const VMFunction EnterWithInfo =
3240 FunctionInfo<EnterWithFn>(jit::EnterWith, "EnterWith");
3241
emit_JSOP_ENTERWITH()3242 bool BaselineCompiler::emit_JSOP_ENTERWITH() {
3243 WithScope& withScope = script->getScope(pc)->as<WithScope>();
3244
3245 // Pop "with" object to R0.
3246 frame.popRegsAndSync(1);
3247
3248 // Call a stub to push the object onto the environment chain.
3249 prepareVMCall();
3250 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
3251
3252 pushArg(ImmGCPtr(&withScope));
3253 pushArg(R0);
3254 pushArg(R1.scratchReg());
3255
3256 return callVM(EnterWithInfo);
3257 }
3258
3259 typedef bool (*LeaveWithFn)(JSContext*, BaselineFrame*);
3260 static const VMFunction LeaveWithInfo =
3261 FunctionInfo<LeaveWithFn>(jit::LeaveWith, "LeaveWith");
3262
emit_JSOP_LEAVEWITH()3263 bool BaselineCompiler::emit_JSOP_LEAVEWITH() {
3264 // Call a stub to pop the with object from the environment chain.
3265 prepareVMCall();
3266
3267 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3268 pushArg(R0.scratchReg());
3269
3270 return callVM(LeaveWithInfo);
3271 }
3272
3273 typedef bool (*GetAndClearExceptionFn)(JSContext*, MutableHandleValue);
3274 static const VMFunction GetAndClearExceptionInfo =
3275 FunctionInfo<GetAndClearExceptionFn>(GetAndClearException,
3276 "GetAndClearException");
3277
emit_JSOP_EXCEPTION()3278 bool BaselineCompiler::emit_JSOP_EXCEPTION() {
3279 prepareVMCall();
3280
3281 if (!callVM(GetAndClearExceptionInfo)) return false;
3282
3283 frame.push(R0);
3284 return true;
3285 }
3286
3287 typedef bool (*OnDebuggerStatementFn)(JSContext*, BaselineFrame*,
3288 jsbytecode* pc, bool*);
3289 static const VMFunction OnDebuggerStatementInfo =
3290 FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement,
3291 "OnDebuggerStatement");
3292
emit_JSOP_DEBUGGER()3293 bool BaselineCompiler::emit_JSOP_DEBUGGER() {
3294 prepareVMCall();
3295 pushArg(ImmPtr(pc));
3296
3297 frame.assertSyncedStack();
3298 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3299 pushArg(R0.scratchReg());
3300
3301 if (!callVM(OnDebuggerStatementInfo)) return false;
3302
3303 // If the stub returns |true|, return the frame's return value.
3304 Label done;
3305 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
3306 {
3307 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
3308 masm.jump(&return_);
3309 }
3310 masm.bind(&done);
3311 return true;
3312 }
3313
3314 typedef bool (*DebugEpilogueFn)(JSContext*, BaselineFrame*, jsbytecode*);
3315 static const VMFunction DebugEpilogueInfo = FunctionInfo<DebugEpilogueFn>(
3316 jit::DebugEpilogueOnBaselineReturn, "DebugEpilogueOnBaselineReturn");
3317
emitReturn()3318 bool BaselineCompiler::emitReturn() {
3319 if (compileDebugInstrumentation_) {
3320 // Move return value into the frame's rval slot.
3321 masm.storeValue(JSReturnOperand, frame.addressOfReturnValue());
3322 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
3323
3324 // Load BaselineFrame pointer in R0.
3325 frame.syncStack(0);
3326 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3327
3328 prepareVMCall();
3329 pushArg(ImmPtr(pc));
3330 pushArg(R0.scratchReg());
3331 if (!callVM(DebugEpilogueInfo)) return false;
3332
3333 // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
3334 icEntries_.back().setFakeKind(ICEntry::Kind_DebugEpilogue);
3335
3336 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
3337 }
3338
3339 // Only emit the jump if this JSOP_RETRVAL is not the last instruction.
3340 // Not needed for last instruction, because last instruction flows
3341 // into return label.
3342 if (pc + GetBytecodeLength(pc) < script->codeEnd()) masm.jump(&return_);
3343
3344 return true;
3345 }
3346
emit_JSOP_RETURN()3347 bool BaselineCompiler::emit_JSOP_RETURN() {
3348 MOZ_ASSERT(frame.stackDepth() == 1);
3349
3350 frame.popValue(JSReturnOperand);
3351 return emitReturn();
3352 }
3353
emitLoadReturnValue(ValueOperand val)3354 void BaselineCompiler::emitLoadReturnValue(ValueOperand val) {
3355 Label done, noRval;
3356 masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
3357 Imm32(BaselineFrame::HAS_RVAL), &noRval);
3358 masm.loadValue(frame.addressOfReturnValue(), val);
3359 masm.jump(&done);
3360
3361 masm.bind(&noRval);
3362 masm.moveValue(UndefinedValue(), val);
3363
3364 masm.bind(&done);
3365 }
3366
emit_JSOP_RETRVAL()3367 bool BaselineCompiler::emit_JSOP_RETRVAL() {
3368 MOZ_ASSERT(frame.stackDepth() == 0);
3369
3370 masm.moveValue(UndefinedValue(), JSReturnOperand);
3371
3372 if (!script->noScriptRval()) {
3373 // Return the value in the return value slot, if any.
3374 Label done;
3375 Address flags = frame.addressOfFlags();
3376 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
3377 &done);
3378 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
3379 masm.bind(&done);
3380 }
3381
3382 return emitReturn();
3383 }
3384
3385 typedef bool (*ToIdFn)(JSContext*, HandleValue, MutableHandleValue);
3386 static const VMFunction ToIdInfo =
3387 FunctionInfo<ToIdFn>(js::ToIdOperation, "ToIdOperation");
3388
emit_JSOP_TOID()3389 bool BaselineCompiler::emit_JSOP_TOID() {
3390 // Load index in R0, but keep values on the stack for the decompiler.
3391 frame.syncStack(0);
3392 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
3393
3394 // No-op if the index is trivally convertable to an id.
3395 Label done;
3396 masm.branchTestInt32(Assembler::Equal, R0, &done);
3397 masm.branchTestString(Assembler::Equal, R0, &done);
3398 masm.branchTestSymbol(Assembler::Equal, R0, &done);
3399
3400 prepareVMCall();
3401
3402 pushArg(R0);
3403
3404 if (!callVM(ToIdInfo)) return false;
3405
3406 masm.bind(&done);
3407 frame.pop(); // Pop index.
3408 frame.push(R0);
3409 return true;
3410 }
3411
3412 typedef JSObject* (*ToAsyncFn)(JSContext*, HandleFunction);
3413 static const VMFunction ToAsyncInfo =
3414 FunctionInfo<ToAsyncFn>(js::WrapAsyncFunction, "ToAsync");
3415
emit_JSOP_TOASYNC()3416 bool BaselineCompiler::emit_JSOP_TOASYNC() {
3417 frame.syncStack(0);
3418 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
3419
3420 prepareVMCall();
3421 pushArg(R0.scratchReg());
3422
3423 if (!callVM(ToAsyncInfo)) return false;
3424
3425 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
3426 frame.pop();
3427 frame.push(R0);
3428 return true;
3429 }
3430
3431 typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
3432 static const VMFunction ToAsyncGenInfo =
3433 FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
3434
emit_JSOP_TOASYNCGEN()3435 bool BaselineCompiler::emit_JSOP_TOASYNCGEN() {
3436 frame.syncStack(0);
3437 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
3438
3439 prepareVMCall();
3440 pushArg(R0.scratchReg());
3441
3442 if (!callVM(ToAsyncGenInfo)) return false;
3443
3444 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
3445 frame.pop();
3446 frame.push(R0);
3447 return true;
3448 }
3449
3450 typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue);
3451 static const VMFunction ToAsyncIterInfo =
3452 FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
3453
emit_JSOP_TOASYNCITER()3454 bool BaselineCompiler::emit_JSOP_TOASYNCITER() {
3455 frame.syncStack(0);
3456 masm.unboxObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg());
3457 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
3458
3459 prepareVMCall();
3460 pushArg(R1);
3461 pushArg(R0.scratchReg());
3462
3463 if (!callVM(ToAsyncIterInfo)) return false;
3464
3465 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
3466 frame.popn(2);
3467 frame.push(R0);
3468 return true;
3469 }
3470
3471 typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue);
3472 static const VMFunction ThrowObjectCoercibleInfo =
3473 FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible,
3474 "ThrowObjectCoercible");
3475
emit_JSOP_CHECKOBJCOERCIBLE()3476 bool BaselineCompiler::emit_JSOP_CHECKOBJCOERCIBLE() {
3477 frame.syncStack(0);
3478 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
3479
3480 Label fail, done;
3481
3482 masm.branchTestUndefined(Assembler::Equal, R0, &fail);
3483 masm.branchTestNull(Assembler::NotEqual, R0, &done);
3484
3485 masm.bind(&fail);
3486 prepareVMCall();
3487
3488 pushArg(R0);
3489
3490 if (!callVM(ThrowObjectCoercibleInfo)) return false;
3491
3492 masm.bind(&done);
3493 return true;
3494 }
3495
3496 typedef JSString* (*ToStringFn)(JSContext*, HandleValue);
3497 static const VMFunction ToStringInfo =
3498 FunctionInfo<ToStringFn>(ToStringSlow, "ToStringSlow");
3499
emit_JSOP_TOSTRING()3500 bool BaselineCompiler::emit_JSOP_TOSTRING() {
3501 // Keep top stack value in R0.
3502 frame.popRegsAndSync(1);
3503
3504 // Inline path for string.
3505 Label done;
3506 masm.branchTestString(Assembler::Equal, R0, &done);
3507
3508 prepareVMCall();
3509
3510 pushArg(R0);
3511
3512 // Call ToStringSlow which doesn't handle string inputs.
3513 if (!callVM(ToStringInfo)) return false;
3514
3515 masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, R0);
3516
3517 masm.bind(&done);
3518 frame.push(R0);
3519 return true;
3520 }
3521
emit_JSOP_TABLESWITCH()3522 bool BaselineCompiler::emit_JSOP_TABLESWITCH() {
3523 frame.popRegsAndSync(1);
3524
3525 // Call IC.
3526 ICTableSwitch::Compiler compiler(cx, pc);
3527 return emitOpIC(compiler.getStub(&stubSpace_));
3528 }
3529
emit_JSOP_ITER()3530 bool BaselineCompiler::emit_JSOP_ITER() {
3531 frame.popRegsAndSync(1);
3532
3533 ICGetIterator_Fallback::Compiler compiler(cx);
3534 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
3535
3536 frame.push(R0);
3537 return true;
3538 }
3539
emit_JSOP_MOREITER()3540 bool BaselineCompiler::emit_JSOP_MOREITER() {
3541 frame.syncStack(0);
3542 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
3543
3544 ICIteratorMore_Fallback::Compiler compiler(cx);
3545 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
3546
3547 frame.push(R0);
3548 return true;
3549 }
3550
emitIsMagicValue()3551 bool BaselineCompiler::emitIsMagicValue() {
3552 frame.syncStack(0);
3553
3554 Label isMagic, done;
3555 masm.branchTestMagic(Assembler::Equal,
3556 frame.addressOfStackValue(frame.peek(-1)), &isMagic);
3557 masm.moveValue(BooleanValue(false), R0);
3558 masm.jump(&done);
3559
3560 masm.bind(&isMagic);
3561 masm.moveValue(BooleanValue(true), R0);
3562
3563 masm.bind(&done);
3564 frame.push(R0, JSVAL_TYPE_BOOLEAN);
3565 return true;
3566 }
3567
emit_JSOP_ISNOITER()3568 bool BaselineCompiler::emit_JSOP_ISNOITER() { return emitIsMagicValue(); }
3569
emit_JSOP_ENDITER()3570 bool BaselineCompiler::emit_JSOP_ENDITER() {
3571 if (!emit_JSOP_JUMPTARGET()) return false;
3572 frame.popRegsAndSync(1);
3573
3574 ICIteratorClose_Fallback::Compiler compiler(cx);
3575 return emitOpIC(compiler.getStub(&stubSpace_));
3576 }
3577
emit_JSOP_ISGENCLOSING()3578 bool BaselineCompiler::emit_JSOP_ISGENCLOSING() { return emitIsMagicValue(); }
3579
emit_JSOP_GETRVAL()3580 bool BaselineCompiler::emit_JSOP_GETRVAL() {
3581 frame.syncStack(0);
3582
3583 emitLoadReturnValue(R0);
3584
3585 frame.push(R0);
3586 return true;
3587 }
3588
emit_JSOP_SETRVAL()3589 bool BaselineCompiler::emit_JSOP_SETRVAL() {
3590 // Store to the frame's return value slot.
3591 storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
3592 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
3593 frame.pop();
3594 return true;
3595 }
3596
emit_JSOP_CALLEE()3597 bool BaselineCompiler::emit_JSOP_CALLEE() {
3598 MOZ_ASSERT(function());
3599 frame.syncStack(0);
3600 masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(),
3601 R0.scratchReg());
3602 masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0);
3603 frame.push(R0);
3604 return true;
3605 }
3606
getThisEnvironmentCallee(Register reg)3607 void BaselineCompiler::getThisEnvironmentCallee(Register reg) {
3608 // Directly load callee from frame if we have a HomeObject
3609 if (function() && function()->allowSuperProperty()) {
3610 masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), reg);
3611 return;
3612 }
3613
3614 // Locate environment chain
3615 masm.loadPtr(frame.addressOfEnvironmentChain(), reg);
3616
3617 // Walk environment chain until first non-arrow CallObject
3618 for (ScopeIter si(script->innermostScope(pc)); si; si++) {
3619 // Find first non-arrow FunctionScope
3620 if (si.hasSyntacticEnvironment() && si.scope()->is<FunctionScope>()) {
3621 JSFunction* fn = si.scope()->as<FunctionScope>().canonicalFunction();
3622
3623 if (!fn->isArrow()) break;
3624 }
3625
3626 // Traverse environment chain
3627 if (si.scope()->hasEnvironment()) {
3628 Address nextAddr(reg, EnvironmentObject::offsetOfEnclosingEnvironment());
3629 masm.unboxObject(nextAddr, reg);
3630 }
3631 }
3632
3633 // Load callee
3634 masm.unboxObject(Address(reg, CallObject::offsetOfCallee()), reg);
3635 }
3636
3637 typedef JSObject* (*HomeObjectSuperBaseFn)(JSContext*, HandleObject);
3638 static const VMFunction HomeObjectSuperBaseInfo =
3639 FunctionInfo<HomeObjectSuperBaseFn>(HomeObjectSuperBase,
3640 "HomeObjectSuperBase");
3641
emit_JSOP_SUPERBASE()3642 bool BaselineCompiler::emit_JSOP_SUPERBASE() {
3643 frame.syncStack(0);
3644
3645 Register scratch = R0.scratchReg();
3646 Register proto = R1.scratchReg();
3647
3648 // Lookup callee object of environment containing [[ThisValue]]
3649 getThisEnvironmentCallee(scratch);
3650
3651 // Load [[HomeObject]]
3652 Address homeObjAddr(scratch,
3653 FunctionExtended::offsetOfMethodHomeObjectSlot());
3654 #ifdef DEBUG
3655 Label isObject;
3656 masm.branchTestObject(Assembler::Equal, homeObjAddr, &isObject);
3657 masm.assumeUnreachable("[[HomeObject]] must be Object");
3658 masm.bind(&isObject);
3659 #endif
3660 masm.unboxObject(homeObjAddr, scratch);
3661
3662 // Load prototype from [[HomeObject]]
3663 masm.loadObjProto(scratch, proto);
3664
3665 Label hasProto;
3666 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
3667 masm.branchPtr(Assembler::Above, proto, ImmWord(1), &hasProto);
3668
3669 // Use VMCall for missing or lazy proto
3670 prepareVMCall();
3671 pushArg(scratch); // [[HomeObject]]
3672 if (!callVM(HomeObjectSuperBaseInfo)) return false;
3673 masm.movePtr(ReturnReg, proto);
3674
3675 // Box prototype and return
3676 masm.bind(&hasProto);
3677 masm.tagValue(JSVAL_TYPE_OBJECT, proto, R1);
3678 frame.push(R1);
3679 return true;
3680 }
3681
3682 typedef JSObject* (*SuperFunOperationFn)(JSContext*, HandleObject);
3683 static const VMFunction SuperFunOperationInfo =
3684 FunctionInfo<SuperFunOperationFn>(SuperFunOperation, "SuperFunOperation");
3685
emit_JSOP_SUPERFUN()3686 bool BaselineCompiler::emit_JSOP_SUPERFUN() {
3687 frame.syncStack(0);
3688
3689 Register callee = R0.scratchReg();
3690 Register proto = R1.scratchReg();
3691 Register scratch = R2.scratchReg();
3692
3693 // Lookup callee object of environment containing [[ThisValue]]
3694 getThisEnvironmentCallee(callee);
3695
3696 // Load prototype of callee
3697 masm.loadObjProto(callee, proto);
3698
3699 // Use VMCall for missing or lazy proto
3700 Label needVMCall;
3701 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
3702 masm.branchPtr(Assembler::BelowOrEqual, proto, ImmWord(1), &needVMCall);
3703
3704 // Use VMCall for non-JSFunction objects (eg. Proxy)
3705 masm.branchTestObjClass(Assembler::NotEqual, proto, &JSFunction::class_,
3706 scratch, proto, &needVMCall);
3707
3708 // Use VMCall if not constructor
3709 masm.load16ZeroExtend(Address(proto, JSFunction::offsetOfFlags()), scratch);
3710 masm.branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::CONSTRUCTOR),
3711 &needVMCall);
3712
3713 // Valid constructor
3714 Label hasSuperFun;
3715 masm.jump(&hasSuperFun);
3716
3717 // Slow path VM Call
3718 masm.bind(&needVMCall);
3719 prepareVMCall();
3720 pushArg(callee);
3721 if (!callVM(SuperFunOperationInfo)) return false;
3722 masm.movePtr(ReturnReg, proto);
3723
3724 // Box prototype and return
3725 masm.bind(&hasSuperFun);
3726 masm.tagValue(JSVAL_TYPE_OBJECT, proto, R1);
3727 frame.push(R1);
3728 return true;
3729 }
3730
3731 typedef bool (*NewArgumentsObjectFn)(JSContext*, BaselineFrame*,
3732 MutableHandleValue);
3733 static const VMFunction NewArgumentsObjectInfo =
3734 FunctionInfo<NewArgumentsObjectFn>(jit::NewArgumentsObject,
3735 "NewArgumentsObject");
3736
emit_JSOP_ARGUMENTS()3737 bool BaselineCompiler::emit_JSOP_ARGUMENTS() {
3738 frame.syncStack(0);
3739
3740 Label done;
3741 if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) {
3742 // We assume the script does not need an arguments object. However, this
3743 // assumption can be invalidated later, see argumentsOptimizationFailed
3744 // in JSScript. Because we can't invalidate baseline JIT code, we set a
3745 // flag on BaselineScript when that happens and guard on it here.
3746 masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0);
3747
3748 // Load script->baseline.
3749 Register scratch = R1.scratchReg();
3750 masm.movePtr(ImmGCPtr(script), scratch);
3751 masm.loadPtr(Address(scratch, JSScript::offsetOfBaselineScript()), scratch);
3752
3753 // If we don't need an arguments object, skip the VM call.
3754 masm.branchTest32(Assembler::Zero,
3755 Address(scratch, BaselineScript::offsetOfFlags()),
3756 Imm32(BaselineScript::NEEDS_ARGS_OBJ), &done);
3757 }
3758
3759 prepareVMCall();
3760
3761 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3762 pushArg(R0.scratchReg());
3763
3764 if (!callVM(NewArgumentsObjectInfo)) return false;
3765
3766 masm.bind(&done);
3767 frame.push(R0);
3768 return true;
3769 }
3770
3771 typedef bool (*RunOnceScriptPrologueFn)(JSContext*, HandleScript);
3772 static const VMFunction RunOnceScriptPrologueInfo =
3773 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue,
3774 "RunOnceScriptPrologue");
3775
emit_JSOP_RUNONCE()3776 bool BaselineCompiler::emit_JSOP_RUNONCE() {
3777 frame.syncStack(0);
3778
3779 prepareVMCall();
3780
3781 masm.movePtr(ImmGCPtr(script), R0.scratchReg());
3782 pushArg(R0.scratchReg());
3783
3784 return callVM(RunOnceScriptPrologueInfo);
3785 }
3786
emit_JSOP_REST()3787 bool BaselineCompiler::emit_JSOP_REST() {
3788 frame.syncStack(0);
3789
3790 ArrayObject* templateObject = ObjectGroup::newArrayObject(
3791 cx, nullptr, 0, TenuredObject, ObjectGroup::NewArrayKind::UnknownIndex);
3792 if (!templateObject) return false;
3793
3794 // Call IC.
3795 ICRest_Fallback::Compiler compiler(cx, templateObject);
3796 if (!emitOpIC(compiler.getStub(&stubSpace_))) return false;
3797
3798 // Mark R0 as pushed stack value.
3799 frame.push(R0);
3800 return true;
3801 }
3802
3803 typedef JSObject* (*CreateGeneratorFn)(JSContext*, BaselineFrame*);
3804 static const VMFunction CreateGeneratorInfo =
3805 FunctionInfo<CreateGeneratorFn>(jit::CreateGenerator, "CreateGenerator");
3806
emit_JSOP_GENERATOR()3807 bool BaselineCompiler::emit_JSOP_GENERATOR() {
3808 MOZ_ASSERT(frame.stackDepth() == 0);
3809
3810 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3811
3812 prepareVMCall();
3813 pushArg(R0.scratchReg());
3814 if (!callVM(CreateGeneratorInfo)) return false;
3815
3816 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
3817 frame.push(R0);
3818 return true;
3819 }
3820
addYieldAndAwaitOffset()3821 bool BaselineCompiler::addYieldAndAwaitOffset() {
3822 MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD ||
3823 *pc == JSOP_AWAIT);
3824
3825 uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
3826
3827 while (yieldAndAwaitIndex >= yieldAndAwaitOffsets_.length()) {
3828 if (!yieldAndAwaitOffsets_.append(0)) return false;
3829 }
3830
3831 static_assert(
3832 JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH &&
3833 JSOP_INITIALYIELD_LENGTH == JSOP_AWAIT_LENGTH,
3834 "code below assumes INITIALYIELD and YIELD and AWAIT have same length");
3835 yieldAndAwaitOffsets_[yieldAndAwaitIndex] =
3836 script->pcToOffset(pc + JSOP_YIELD_LENGTH);
3837 return true;
3838 }
3839
emit_JSOP_INITIALYIELD()3840 bool BaselineCompiler::emit_JSOP_INITIALYIELD() {
3841 if (!addYieldAndAwaitOffset()) return false;
3842
3843 frame.syncStack(0);
3844 MOZ_ASSERT(frame.stackDepth() == 1);
3845
3846 Register genObj = R2.scratchReg();
3847 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj);
3848
3849 MOZ_ASSERT(GET_UINT24(pc) == 0);
3850 masm.storeValue(
3851 Int32Value(0),
3852 Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
3853
3854 Register envObj = R0.scratchReg();
3855 Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot());
3856 masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
3857 masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
3858 masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
3859
3860 Register temp = R1.scratchReg();
3861 Label skipBarrier;
3862 masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
3863 masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp, &skipBarrier);
3864 masm.push(genObj);
3865 MOZ_ASSERT(genObj == R2.scratchReg());
3866 masm.call(&postBarrierSlot_);
3867 masm.pop(genObj);
3868 masm.bind(&skipBarrier);
3869
3870 masm.tagValue(JSVAL_TYPE_OBJECT, genObj, JSReturnOperand);
3871 return emitReturn();
3872 }
3873
3874 typedef bool (*NormalSuspendFn)(JSContext*, HandleObject, BaselineFrame*,
3875 jsbytecode*, uint32_t);
3876 static const VMFunction NormalSuspendInfo =
3877 FunctionInfo<NormalSuspendFn>(jit::NormalSuspend, "NormalSuspend");
3878
emit_JSOP_YIELD()3879 bool BaselineCompiler::emit_JSOP_YIELD() {
3880 if (!addYieldAndAwaitOffset()) return false;
3881
3882 // Store generator in R0.
3883 frame.popRegsAndSync(1);
3884
3885 Register genObj = R2.scratchReg();
3886 masm.unboxObject(R0, genObj);
3887
3888 MOZ_ASSERT(frame.stackDepth() >= 1);
3889
3890 if (frame.stackDepth() == 1) {
3891 // If the expression stack is empty, we can inline the YIELD.
3892
3893 masm.storeValue(
3894 Int32Value(GET_UINT24(pc)),
3895 Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
3896
3897 Register envObj = R0.scratchReg();
3898 Address envChainSlot(genObj,
3899 GeneratorObject::offsetOfEnvironmentChainSlot());
3900 masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
3901 masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
3902 masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
3903
3904 Register temp = R1.scratchReg();
3905 Label skipBarrier;
3906 masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
3907 masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp,
3908 &skipBarrier);
3909 MOZ_ASSERT(genObj == R2.scratchReg());
3910 masm.call(&postBarrierSlot_);
3911 masm.bind(&skipBarrier);
3912 } else {
3913 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
3914
3915 prepareVMCall();
3916 pushArg(Imm32(frame.stackDepth()));
3917 pushArg(ImmPtr(pc));
3918 pushArg(R1.scratchReg());
3919 pushArg(genObj);
3920
3921 if (!callVM(NormalSuspendInfo)) return false;
3922 }
3923
3924 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), JSReturnOperand);
3925 return emitReturn();
3926 }
3927
emit_JSOP_AWAIT()3928 bool BaselineCompiler::emit_JSOP_AWAIT() { return emit_JSOP_YIELD(); }
3929
3930 typedef bool (*DebugAfterYieldFn)(JSContext*, BaselineFrame*);
3931 static const VMFunction DebugAfterYieldInfo =
3932 FunctionInfo<DebugAfterYieldFn>(jit::DebugAfterYield, "DebugAfterYield");
3933
emit_JSOP_DEBUGAFTERYIELD()3934 bool BaselineCompiler::emit_JSOP_DEBUGAFTERYIELD() {
3935 if (!compileDebugInstrumentation_) return true;
3936
3937 frame.assertSyncedStack();
3938 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
3939 prepareVMCall();
3940 pushArg(R0.scratchReg());
3941 return callVM(DebugAfterYieldInfo);
3942 }
3943
3944 typedef bool (*FinalSuspendFn)(JSContext*, HandleObject, jsbytecode*);
3945 static const VMFunction FinalSuspendInfo =
3946 FunctionInfo<FinalSuspendFn>(jit::FinalSuspend, "FinalSuspend");
3947
emit_JSOP_FINALYIELDRVAL()3948 bool BaselineCompiler::emit_JSOP_FINALYIELDRVAL() {
3949 // Store generator in R0.
3950 frame.popRegsAndSync(1);
3951 masm.unboxObject(R0, R0.scratchReg());
3952
3953 prepareVMCall();
3954 pushArg(ImmPtr(pc));
3955 pushArg(R0.scratchReg());
3956
3957 if (!callVM(FinalSuspendInfo)) return false;
3958
3959 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
3960 return emitReturn();
3961 }
3962
3963 typedef bool (*InterpretResumeFn)(JSContext*, HandleObject, HandleValue,
3964 HandlePropertyName, MutableHandleValue);
3965 static const VMFunction InterpretResumeInfo =
3966 FunctionInfo<InterpretResumeFn>(jit::InterpretResume, "InterpretResume");
3967
3968 typedef bool (*GeneratorThrowFn)(JSContext*, BaselineFrame*,
3969 Handle<GeneratorObject*>, HandleValue,
3970 uint32_t);
3971 static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(
3972 jit::GeneratorThrowOrReturn, "GeneratorThrowOrReturn", TailCall);
3973
emit_JSOP_RESUME()3974 bool BaselineCompiler::emit_JSOP_RESUME() {
3975 GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
3976
3977 frame.syncStack(0);
3978 masm.assertStackAlignment(sizeof(Value), 0);
3979
3980 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3981 regs.take(BaselineFrameReg);
3982
3983 // Load generator object.
3984 Register genObj = regs.takeAny();
3985 masm.unboxObject(frame.addressOfStackValue(frame.peek(-2)), genObj);
3986
3987 // Load callee.
3988 Register callee = regs.takeAny();
3989 masm.unboxObject(Address(genObj, GeneratorObject::offsetOfCalleeSlot()),
3990 callee);
3991
3992 // Load the script. Note that we don't relazify generator scripts, so it's
3993 // guaranteed to be non-lazy.
3994 Register scratch1 = regs.takeAny();
3995 masm.loadPtr(Address(callee, JSFunction::offsetOfScript()), scratch1);
3996
3997 // Load the BaselineScript or call a stub if we don't have one.
3998 Label interpret;
3999 masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1);
4000 masm.branchPtr(Assembler::BelowOrEqual, scratch1,
4001 ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret);
4002
4003 #ifdef JS_TRACE_LOGGING
4004 if (!emitTraceLoggerResume(scratch1, regs)) return false;
4005 #endif
4006
4007 Register constructing = regs.takeAny();
4008 ValueOperand newTarget = regs.takeAnyValue();
4009 masm.loadValue(Address(genObj, GeneratorObject::offsetOfNewTargetSlot()),
4010 newTarget);
4011 masm.move32(Imm32(0), constructing);
4012 {
4013 Label notConstructing;
4014 masm.branchTestObject(Assembler::NotEqual, newTarget, ¬Constructing);
4015 masm.pushValue(newTarget);
4016 masm.move32(Imm32(CalleeToken_FunctionConstructing), constructing);
4017 masm.bind(¬Constructing);
4018 }
4019 regs.add(newTarget);
4020
4021 // Push |undefined| for all formals.
4022 Register scratch2 = regs.takeAny();
4023 Label loop, loopDone;
4024 masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch2);
4025 masm.bind(&loop);
4026 masm.branchTest32(Assembler::Zero, scratch2, scratch2, &loopDone);
4027 {
4028 masm.pushValue(UndefinedValue());
4029 masm.sub32(Imm32(1), scratch2);
4030 masm.jump(&loop);
4031 }
4032 masm.bind(&loopDone);
4033
4034 // Push |undefined| for |this|.
4035 masm.pushValue(UndefinedValue());
4036
4037 // Update BaselineFrame frameSize field and create the frame descriptor.
4038 masm.computeEffectiveAddress(
4039 Address(BaselineFrameReg, BaselineFrame::FramePointerOffset), scratch2);
4040 masm.subStackPtrFrom(scratch2);
4041 masm.store32(scratch2, Address(BaselineFrameReg,
4042 BaselineFrame::reverseOffsetOfFrameSize()));
4043 masm.makeFrameDescriptor(scratch2, JitFrame_BaselineJS,
4044 JitFrameLayout::Size());
4045
4046 masm.Push(Imm32(0)); // actual argc
4047
4048 // Duplicate PushCalleeToken with a variable instead.
4049 masm.orPtr(constructing, callee);
4050 masm.Push(callee);
4051 masm.Push(scratch2); // frame descriptor
4052
4053 regs.add(callee);
4054 regs.add(constructing);
4055
4056 // Load the return value.
4057 ValueOperand retVal = regs.takeAnyValue();
4058 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
4059
4060 // Push a fake return address on the stack. We will resume here when the
4061 // generator returns.
4062 Label genStart, returnTarget;
4063 #ifdef JS_USE_LINK_REGISTER
4064 masm.call(&genStart);
4065 #else
4066 masm.callAndPushReturnAddress(&genStart);
4067 #endif
4068
4069 // Add an IC entry so the return offset -> pc mapping works.
4070 if (!appendICEntry(ICEntry::Kind_Op, masm.currentOffset())) return false;
4071
4072 masm.jump(&returnTarget);
4073 masm.bind(&genStart);
4074 #ifdef JS_USE_LINK_REGISTER
4075 masm.pushReturnAddress();
4076 #endif
4077
4078 // If profiler instrumentation is on, update lastProfilingFrame on
4079 // current JitActivation
4080 {
4081 Register scratchReg = scratch2;
4082 Label skip;
4083 AbsoluteAddress addressOfEnabled(
4084 cx->runtime()->geckoProfiler().addressOfEnabled());
4085 masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip);
4086 masm.loadJSContext(scratchReg);
4087 masm.loadPtr(Address(scratchReg, JSContext::offsetOfProfilingActivation()),
4088 scratchReg);
4089 masm.storeStackPtr(
4090 Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()));
4091 masm.bind(&skip);
4092 }
4093
4094 // Construct BaselineFrame.
4095 masm.push(BaselineFrameReg);
4096 masm.moveStackPtrTo(BaselineFrameReg);
4097 masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
4098 masm.assertStackAlignment(sizeof(Value), 0);
4099
4100 // Store flags and env chain.
4101 masm.store32(Imm32(BaselineFrame::HAS_INITIAL_ENV), frame.addressOfFlags());
4102 masm.unboxObject(
4103 Address(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()),
4104 scratch2);
4105 masm.storePtr(scratch2, frame.addressOfEnvironmentChain());
4106
4107 // Store the arguments object if there is one.
4108 Label noArgsObj;
4109 Address argsObjSlot(genObj, GeneratorObject::offsetOfArgsObjSlot());
4110 masm.branchTestUndefined(Assembler::Equal, argsObjSlot, &noArgsObj);
4111 masm.unboxObject(argsObjSlot, scratch2);
4112 {
4113 masm.storePtr(scratch2, frame.addressOfArgsObj());
4114 masm.or32(Imm32(BaselineFrame::HAS_ARGS_OBJ), frame.addressOfFlags());
4115 }
4116 masm.bind(&noArgsObj);
4117
4118 // Push expression slots if needed.
4119 Label noExprStack;
4120 Address exprStackSlot(genObj, GeneratorObject::offsetOfExpressionStackSlot());
4121 masm.branchTestNull(Assembler::Equal, exprStackSlot, &noExprStack);
4122 {
4123 masm.unboxObject(exprStackSlot, scratch2);
4124
4125 Register initLength = regs.takeAny();
4126 masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
4127 masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()),
4128 initLength);
4129 masm.store32(
4130 Imm32(0),
4131 Address(scratch2, ObjectElements::offsetOfInitializedLength()));
4132
4133 Label loop, loopDone;
4134 masm.bind(&loop);
4135 masm.branchTest32(Assembler::Zero, initLength, initLength, &loopDone);
4136 {
4137 masm.pushValue(Address(scratch2, 0));
4138 masm.guardedCallPreBarrier(Address(scratch2, 0), MIRType::Value);
4139 masm.addPtr(Imm32(sizeof(Value)), scratch2);
4140 masm.sub32(Imm32(1), initLength);
4141 masm.jump(&loop);
4142 }
4143 masm.bind(&loopDone);
4144 regs.add(initLength);
4145 }
4146
4147 masm.bind(&noExprStack);
4148 masm.pushValue(retVal);
4149
4150 if (resumeKind == GeneratorObject::NEXT) {
4151 // Determine the resume address based on the yieldAndAwaitIndex and the
4152 // yieldAndAwaitIndex -> native table in the BaselineScript.
4153 masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()),
4154 scratch2);
4155 masm.addPtr(scratch2, scratch1);
4156 masm.unboxInt32(
4157 Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()),
4158 scratch2);
4159 masm.loadPtr(
4160 BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))),
4161 scratch1);
4162
4163 // Mark as running and jump to the generator's JIT code.
4164 masm.storeValue(
4165 Int32Value(GeneratorObject::YIELD_AND_AWAIT_INDEX_RUNNING),
4166 Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()));
4167 masm.jump(scratch1);
4168 } else {
4169 MOZ_ASSERT(resumeKind == GeneratorObject::THROW ||
4170 resumeKind == GeneratorObject::RETURN);
4171
4172 // Update the frame's frameSize field.
4173 masm.computeEffectiveAddress(
4174 Address(BaselineFrameReg, BaselineFrame::FramePointerOffset), scratch2);
4175 masm.movePtr(scratch2, scratch1);
4176 masm.subStackPtrFrom(scratch2);
4177 masm.store32(scratch2, Address(BaselineFrameReg,
4178 BaselineFrame::reverseOffsetOfFrameSize()));
4179 masm.loadBaselineFramePtr(BaselineFrameReg, scratch2);
4180
4181 prepareVMCall();
4182 pushArg(Imm32(resumeKind));
4183 pushArg(retVal);
4184 pushArg(genObj);
4185 pushArg(scratch2);
4186
4187 TrampolinePtr code =
4188 cx->runtime()->jitRuntime()->getVMWrapper(GeneratorThrowInfo);
4189
4190 // Create the frame descriptor.
4191 masm.subStackPtrFrom(scratch1);
4192 masm.makeFrameDescriptor(scratch1, JitFrame_BaselineJS,
4193 ExitFrameLayout::Size());
4194
4195 // Push the frame descriptor and a dummy return address (it doesn't
4196 // matter what we push here, frame iterators will use the frame pc
4197 // set in jit::GeneratorThrowOrReturn).
4198 masm.push(scratch1);
4199
4200 // On ARM64, the callee will push the return address.
4201 #ifndef JS_CODEGEN_ARM64
4202 masm.push(ImmWord(0));
4203 #endif
4204 masm.jump(code);
4205 }
4206
4207 // If the generator script has no JIT code, call into the VM.
4208 masm.bind(&interpret);
4209
4210 prepareVMCall();
4211 if (resumeKind == GeneratorObject::NEXT) {
4212 pushArg(ImmGCPtr(cx->names().next));
4213 } else if (resumeKind == GeneratorObject::THROW) {
4214 pushArg(ImmGCPtr(cx->names().throw_));
4215 } else {
4216 MOZ_ASSERT(resumeKind == GeneratorObject::RETURN);
4217 pushArg(ImmGCPtr(cx->names().return_));
4218 }
4219
4220 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
4221 pushArg(retVal);
4222 pushArg(genObj);
4223
4224 if (!callVM(InterpretResumeInfo)) return false;
4225
4226 // After the generator returns, we restore the stack pointer, push the
4227 // return value and we're done.
4228 masm.bind(&returnTarget);
4229 masm.computeEffectiveAddress(frame.addressOfStackValue(frame.peek(-1)),
4230 masm.getStackPointer());
4231 frame.popn(2);
4232 frame.push(R0);
4233 return true;
4234 }
4235
4236 typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
4237 static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(
4238 js::Debug_CheckSelfHosted, "Debug_CheckSelfHosted");
4239
emit_JSOP_DEBUGCHECKSELFHOSTED()4240 bool BaselineCompiler::emit_JSOP_DEBUGCHECKSELFHOSTED() {
4241 #ifdef DEBUG
4242 frame.syncStack(0);
4243
4244 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
4245
4246 prepareVMCall();
4247 pushArg(R0);
4248 if (!callVM(CheckSelfHostedInfo)) return false;
4249 #endif
4250 return true;
4251 }
4252
emit_JSOP_IS_CONSTRUCTING()4253 bool BaselineCompiler::emit_JSOP_IS_CONSTRUCTING() {
4254 frame.push(MagicValue(JS_IS_CONSTRUCTING));
4255 return true;
4256 }
4257
emit_JSOP_JUMPTARGET()4258 bool BaselineCompiler::emit_JSOP_JUMPTARGET() {
4259 if (!script->hasScriptCounts()) return true;
4260 PCCounts* counts = script->maybeGetPCCounts(pc);
4261 uint64_t* counterAddr = &counts->numExec();
4262 masm.inc64(AbsoluteAddress(counterAddr));
4263 return true;
4264 }
4265
4266 typedef bool (*CheckClassHeritageOperationFn)(JSContext*, HandleValue);
4267 static const VMFunction CheckClassHeritageOperationInfo =
4268 FunctionInfo<CheckClassHeritageOperationFn>(js::CheckClassHeritageOperation,
4269 "CheckClassHeritageOperation");
4270
emit_JSOP_CHECKCLASSHERITAGE()4271 bool BaselineCompiler::emit_JSOP_CHECKCLASSHERITAGE() {
4272 frame.syncStack(0);
4273
4274 // Leave the heritage value on the stack.
4275 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
4276
4277 prepareVMCall();
4278 pushArg(R0);
4279 return callVM(CheckClassHeritageOperationInfo);
4280 }
4281
emit_JSOP_INITHOMEOBJECT()4282 bool BaselineCompiler::emit_JSOP_INITHOMEOBJECT() {
4283 frame.syncStack(0);
4284
4285 // Load HomeObject off stack
4286 unsigned skipOver = GET_UINT8(pc);
4287 MOZ_ASSERT(frame.stackDepth() >= skipOver + 2);
4288 masm.loadValue(frame.addressOfStackValue(frame.peek(-2 - skipOver)), R0);
4289
4290 // Load function off stack
4291 Register func = R2.scratchReg();
4292 masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), func);
4293
4294 // Set HOMEOBJECT_SLOT
4295 Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4296 #ifdef DEBUG
4297 Label isUndefined;
4298 masm.branchTestUndefined(Assembler::Equal, addr, &isUndefined);
4299 masm.assumeUnreachable("INITHOMEOBJECT expects undefined slot");
4300 masm.bind(&isUndefined);
4301 #endif
4302 masm.storeValue(R0, addr);
4303
4304 Register temp = R1.scratchReg();
4305 Label skipBarrier;
4306 masm.branchPtrInNurseryChunk(Assembler::Equal, func, temp, &skipBarrier);
4307 masm.branchValueIsNurseryObject(Assembler::NotEqual, R0, temp, &skipBarrier);
4308 masm.call(&postBarrierSlot_);
4309 masm.bind(&skipBarrier);
4310
4311 return true;
4312 }
4313
emit_JSOP_BUILTINPROTO()4314 bool BaselineCompiler::emit_JSOP_BUILTINPROTO() {
4315 // The builtin prototype is a constant for a given global.
4316 JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(pc));
4317 MOZ_ASSERT(key < JSProto_LIMIT);
4318 JSObject* builtin = GlobalObject::getOrCreatePrototype(cx, key);
4319 if (!builtin) return false;
4320 frame.push(ObjectValue(*builtin));
4321 return true;
4322 }
4323
4324 typedef JSObject* (*ObjectWithProtoOperationFn)(JSContext*, HandleValue);
4325 static const VMFunction ObjectWithProtoOperationInfo =
4326 FunctionInfo<ObjectWithProtoOperationFn>(js::ObjectWithProtoOperation,
4327 "ObjectWithProtoOperationInfo");
4328
emit_JSOP_OBJWITHPROTO()4329 bool BaselineCompiler::emit_JSOP_OBJWITHPROTO() {
4330 frame.syncStack(0);
4331
4332 // Leave the proto value on the stack for the decompiler
4333 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
4334
4335 prepareVMCall();
4336 pushArg(R0);
4337 if (!callVM(ObjectWithProtoOperationInfo)) return false;
4338
4339 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
4340 frame.pop();
4341 frame.push(R0);
4342 return true;
4343 }
4344
4345 typedef JSObject* (*FunWithProtoFn)(JSContext*, HandleFunction, HandleObject,
4346 HandleObject);
4347 static const VMFunction FunWithProtoInfo = FunctionInfo<FunWithProtoFn>(
4348 js::FunWithProtoOperation, "FunWithProtoOperation");
4349
emit_JSOP_FUNWITHPROTO()4350 bool BaselineCompiler::emit_JSOP_FUNWITHPROTO() {
4351 frame.popRegsAndSync(1);
4352
4353 masm.unboxObject(R0, R0.scratchReg());
4354 masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
4355
4356 prepareVMCall();
4357 pushArg(R0.scratchReg());
4358 pushArg(R1.scratchReg());
4359 pushArg(ImmGCPtr(script->getFunction(GET_UINT32_INDEX(pc))));
4360 if (!callVM(FunWithProtoInfo)) return false;
4361
4362 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
4363 frame.push(R0);
4364 return true;
4365 }
4366
4367 typedef JSFunction* (*MakeDefaultConstructorFn)(JSContext*, HandleScript,
4368 jsbytecode*, HandleObject);
4369 static const VMFunction MakeDefaultConstructorInfo =
4370 FunctionInfo<MakeDefaultConstructorFn>(js::MakeDefaultConstructor,
4371 "MakeDefaultConstructor");
4372
emit_JSOP_CLASSCONSTRUCTOR()4373 bool BaselineCompiler::emit_JSOP_CLASSCONSTRUCTOR() {
4374 frame.syncStack(0);
4375
4376 // Pass nullptr as prototype to MakeDefaultConstructor
4377 prepareVMCall();
4378 pushArg(ImmPtr(nullptr));
4379 pushArg(ImmPtr(pc));
4380 pushArg(ImmGCPtr(script));
4381 if (!callVM(MakeDefaultConstructorInfo)) return false;
4382
4383 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
4384 frame.push(R0);
4385 return true;
4386 }
4387
emit_JSOP_DERIVEDCONSTRUCTOR()4388 bool BaselineCompiler::emit_JSOP_DERIVEDCONSTRUCTOR() {
4389 frame.popRegsAndSync(1);
4390
4391 masm.unboxObject(R0, R0.scratchReg());
4392
4393 prepareVMCall();
4394 pushArg(R0.scratchReg());
4395 pushArg(ImmPtr(pc));
4396 pushArg(ImmGCPtr(script));
4397 if (!callVM(MakeDefaultConstructorInfo)) return false;
4398
4399 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
4400 frame.push(R0);
4401 return true;
4402 }
4403