Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
7 #include "jit/BaselineCompiler.h"
9 #include "jit/BaselineHelpers.h"
10 #include "jit/BaselineIC.h"
11 #include "jit/BaselineJIT.h"
12 #include "jit/FixedList.h"
13 #include "jit/IonAnalysis.h"
14 #include "jit/IonLinker.h"
15 #include "jit/IonSpewer.h"
16 #ifdef JS_ION_PERF
17 # include "jit/PerfSpewer.h"
18 #endif
19 #include "jit/VMFunctions.h"
20 #include "vm/TraceLogging.h"
22 #include "jsscriptinlines.h"
24 #include "vm/Interpreter-inl.h"
26 using namespace js;
27 using namespace js::jit;
29 BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script)
30 : BaselineCompilerSpecific(cx, alloc, script),
31 modifiesArguments_(false)
32 {
33 }
35 bool
36 BaselineCompiler::init()
37 {
38 if (!analysis_.init(alloc_, cx->runtime()->gsnCache))
39 return false;
41 if (!labels_.init(alloc_, script->length()))
42 return false;
44 for (size_t i = 0; i < script->length(); i++)
45 new (&labels_[i]) Label();
47 if (!frame.init(alloc_))
48 return false;
50 return true;
51 }
53 bool
54 BaselineCompiler::addPCMappingEntry(bool addIndexEntry)
55 {
56 // Don't add multiple entries for a single pc.
57 size_t nentries = pcMappingEntries_.length();
58 if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == script->pcToOffset(pc))
59 return true;
61 PCMappingEntry entry;
62 entry.pcOffset = script->pcToOffset(pc);
63 entry.nativeOffset = masm.currentOffset();
64 entry.slotInfo = getStackTopSlotInfo();
65 entry.addIndexEntry = addIndexEntry;
67 return pcMappingEntries_.append(entry);
68 }
70 MethodStatus
71 BaselineCompiler::compile()
72 {
73 IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
74 script->filename(), script->lineno(), script);
76 IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d",
77 script->filename(), script->lineno());
79 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
80 AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
81 AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
83 if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
84 return Method_Error;
86 // Pin analysis info during compilation.
87 types::AutoEnterAnalysis autoEnterAnalysis(cx);
89 JS_ASSERT(!script->hasBaselineScript());
91 if (!emitPrologue())
92 return Method_Error;
94 MethodStatus status = emitBody();
95 if (status != Method_Compiled)
96 return status;
98 if (!emitEpilogue())
99 return Method_Error;
101 #ifdef JSGC_GENERATIONAL
102 if (!emitOutOfLinePostBarrierSlot())
103 return Method_Error;
104 #endif
106 if (masm.oom())
107 return Method_Error;
109 Linker linker(masm);
110 AutoFlushICache afc("Baseline");
111 JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE);
112 if (!code)
113 return Method_Error;
115 JSObject *templateScope = nullptr;
116 if (script->functionNonDelazifying()) {
117 RootedFunction fun(cx, script->functionNonDelazifying());
118 if (fun->isHeavyweight()) {
119 RootedScript scriptRoot(cx, script);
120 templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap);
121 if (!templateScope)
122 return Method_Error;
124 if (fun->isNamedLambda()) {
125 RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
126 if (!declEnvObject)
127 return Method_Error;
128 templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject);
129 }
130 }
131 }
133 // Encode the pc mapping table. See PCMappingIndexEntry for
134 // more information.
135 Vector<PCMappingIndexEntry> pcMappingIndexEntries(cx);
136 CompactBufferWriter pcEntries;
137 uint32_t previousOffset = 0;
139 for (size_t i = 0; i < pcMappingEntries_.length(); i++) {
140 PCMappingEntry &entry = pcMappingEntries_[i];
141 entry.fixupNativeOffset(masm);
143 if (entry.addIndexEntry) {
144 PCMappingIndexEntry indexEntry;
145 indexEntry.pcOffset = entry.pcOffset;
146 indexEntry.nativeOffset = entry.nativeOffset;
147 indexEntry.bufferOffset = pcEntries.length();
148 if (!pcMappingIndexEntries.append(indexEntry))
149 return Method_Error;
150 previousOffset = entry.nativeOffset;
151 }
153 // Use the high bit of the SlotInfo byte to indicate the
154 // native code offset (relative to the previous op) > 0 and
155 // comes next in the buffer.
156 JS_ASSERT((entry.slotInfo.toByte() & 0x80) == 0);
158 if (entry.nativeOffset == previousOffset) {
159 pcEntries.writeByte(entry.slotInfo.toByte());
160 } else {
161 JS_ASSERT(entry.nativeOffset > previousOffset);
162 pcEntries.writeByte(0x80 | entry.slotInfo.toByte());
163 pcEntries.writeUnsigned(entry.nativeOffset - previousOffset);
164 }
166 previousOffset = entry.nativeOffset;
167 }
169 if (pcEntries.oom())
170 return Method_Error;
172 prologueOffset_.fixup(&masm);
173 epilogueOffset_.fixup(&masm);
174 spsPushToggleOffset_.fixup(&masm);
175 postDebugPrologueOffset_.fixup(&masm);
177 // Note: There is an extra entry in the bytecode type map for the search hint, see below.
178 size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
180 BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(),
181 epilogueOffset_.offset(),
182 spsPushToggleOffset_.offset(),
183 postDebugPrologueOffset_.offset(),
184 icEntries_.length(),
185 pcMappingIndexEntries.length(),
186 pcEntries.length(),
187 bytecodeTypeMapEntries);
188 if (!baselineScript)
189 return Method_Error;
191 baselineScript->setMethod(code);
192 baselineScript->setTemplateScope(templateScope);
194 IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
195 (void *) baselineScript, (void *) code->raw(),
196 script->filename(), script->lineno());
198 #ifdef JS_ION_PERF
199 writePerfSpewerBaselineProfile(script, code);
200 #endif
202 JS_ASSERT(pcMappingIndexEntries.length() > 0);
203 baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
205 JS_ASSERT(pcEntries.length() > 0);
206 baselineScript->copyPCMappingEntries(pcEntries);
208 // Copy IC entries
209 if (icEntries_.length())
210 baselineScript->copyICEntries(script, &icEntries_[0], masm);
212 // Adopt fallback stubs from the compiler into the baseline script.
213 baselineScript->adoptFallbackStubs(&stubSpace_);
215 // Patch IC loads using IC entries
216 for (size_t i = 0; i < icLoadLabels_.length(); i++) {
217 CodeOffsetLabel label = icLoadLabels_[i].label;
218 label.fixup(&masm);
219 size_t icEntry = icLoadLabels_[i].icEntry;
220 ICEntry *entryAddr = &(baselineScript->icEntry(icEntry));
221 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, label),
222 ImmPtr(entryAddr),
223 ImmPtr((void*)-1));
224 }
226 if (modifiesArguments_)
227 baselineScript->setModifiesArguments();
229 // All barriers are emitted off-by-default, toggle them on if needed.
230 if (cx->zone()->needsBarrier())
231 baselineScript->toggleBarriers(true);
233 // All SPS instrumentation is emitted toggled off. Toggle them on if needed.
234 if (cx->runtime()->spsProfiler.enabled())
235 baselineScript->toggleSPS(true);
237 uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
238 types::FillBytecodeTypeMap(script, bytecodeMap);
240 // The last entry in the last index found, and is used to avoid binary
241 // searches for the sought entry when queries are in linear order.
242 bytecodeMap[script->nTypeSets()] = 0;
244 if (script->compartment()->debugMode())
245 baselineScript->setDebugMode();
247 script->setBaselineScript(cx, baselineScript);
249 return Method_Compiled;
250 }
252 bool
253 BaselineCompiler::emitPrologue()
254 {
255 masm.push(BaselineFrameReg);
256 masm.mov(BaselineStackReg, BaselineFrameReg);
258 masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg);
259 masm.checkStackAlignment();
261 // Initialize BaselineFrame. For eval scripts, the scope chain
262 // is passed in R1, so we have to be careful not to clobber
263 // it.
265 // Initialize BaselineFrame::flags.
266 uint32_t flags = 0;
267 if (script->isForEval())
268 flags |= BaselineFrame::EVAL;
269 masm.store32(Imm32(flags), frame.addressOfFlags());
271 if (script->isForEval())
272 masm.storePtr(ImmGCPtr(script), frame.addressOfEvalScript());
274 // Handle scope chain pre-initialization (in case GC gets run
275 // during stack check). For global and eval scripts, the scope
276 // chain is in R1. For function scripts, the scope chain is in
277 // the callee, nullptr is stored for now so that GC doesn't choke
278 // on a bogus ScopeChain value in the frame.
279 if (function())
280 masm.storePtr(ImmPtr(nullptr), frame.addressOfScopeChain());
281 else
282 masm.storePtr(R1.scratchReg(), frame.addressOfScopeChain());
284 // Functions with a large number of locals require two stack checks.
285 // The VMCall for a fallible stack check can only occur after the
286 // scope chain has been initialized, as that is required for proper
287 // exception handling if the VMCall returns false. The scope chain
288 // initialization can only happen after the UndefinedValues for the
289 // local slots have been pushed.
290 // However by that time, the stack might have grown too much.
291 // In these cases, we emit an extra, early, infallible check
292 // before pushing the locals. The early check sets a flag on the
293 // frame if the stack check fails (but otherwise doesn't throw an
294 // exception). If the flag is set, then the jitcode skips past
295 // the pushing of the locals, and directly to scope chain initialization
296 // followed by the actual stack check, which will throw the correct
297 // exception.
298 Label earlyStackCheckFailed;
299 if (needsEarlyStackCheck()) {
300 if (!emitStackCheck(/* earlyCheck = */ true))
301 return false;
302 masm.branchTest32(Assembler::NonZero,
303 frame.addressOfFlags(),
304 Imm32(BaselineFrame::OVER_RECURSED),
305 &earlyStackCheckFailed);
306 }
308 // Initialize locals to |undefined|. Use R0 to minimize code size.
309 // If the number of locals to push is < LOOP_UNROLL_FACTOR, then the
310 // initialization pushes are emitted directly and inline. Otherwise,
311 // they're emitted in a partially unrolled loop.
312 if (frame.nlocals() > 0) {
313 size_t LOOP_UNROLL_FACTOR = 4;
314 size_t toPushExtra = frame.nlocals() % LOOP_UNROLL_FACTOR;
316 masm.moveValue(UndefinedValue(), R0);
318 // Handle any extra pushes left over by the optional unrolled loop below.
319 for (size_t i = 0; i < toPushExtra; i++)
320 masm.pushValue(R0);
322 // Partially unrolled loop of pushes.
323 if (frame.nlocals() >= LOOP_UNROLL_FACTOR) {
324 size_t toPush = frame.nlocals() - toPushExtra;
325 JS_ASSERT(toPush % LOOP_UNROLL_FACTOR == 0);
326 JS_ASSERT(toPush >= LOOP_UNROLL_FACTOR);
327 masm.move32(Imm32(toPush), R1.scratchReg());
328 // Emit unrolled loop with 4 pushes per iteration.
329 Label pushLoop;
330 masm.bind(&pushLoop);
331 for (size_t i = 0; i < LOOP_UNROLL_FACTOR; i++)
332 masm.pushValue(R0);
333 masm.branchSub32(Assembler::NonZero,
334 Imm32(LOOP_UNROLL_FACTOR), R1.scratchReg(), &pushLoop);
335 }
336 }
338 if (needsEarlyStackCheck())
339 masm.bind(&earlyStackCheckFailed);
341 #ifdef JS_TRACE_LOGGING
342 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
343 Register loggerReg = RegisterSet::Volatile().takeGeneral();
344 masm.Push(loggerReg);
345 masm.movePtr(ImmPtr(logger), loggerReg);
346 masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script.get()));
347 masm.tracelogStart(loggerReg, TraceLogger::Baseline);
348 masm.Pop(loggerReg);
349 #endif
351 // Record the offset of the prologue, because Ion can bailout before
352 // the scope chain is initialized.
353 prologueOffset_ = masm.currentOffset();
355 // Initialize the scope chain before any operation that may
356 // call into the VM and trigger a GC.
357 if (!initScopeChain())
358 return false;
360 if (!emitStackCheck())
361 return false;
363 if (!emitDebugPrologue())
364 return false;
366 if (!emitUseCountIncrement())
367 return false;
369 if (!emitArgumentTypeChecks())
370 return false;
372 if (!emitSPSPush())
373 return false;
375 return true;
376 }
378 bool
379 BaselineCompiler::emitEpilogue()
380 {
381 // Record the offset of the epilogue, so we can do early return from
382 // Debugger handlers during on-stack recompile.
383 epilogueOffset_ = masm.currentOffset();
385 masm.bind(&return_);
387 #ifdef JS_TRACE_LOGGING
388 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
389 Register loggerReg = RegisterSet::Volatile().takeGeneral();
390 masm.Push(loggerReg);
391 masm.movePtr(ImmPtr(logger), loggerReg);
392 masm.tracelogStop(loggerReg, TraceLogger::Baseline);
393 // Stop the script. Using a stop without checking the textId, since we
394 // we didn't save the textId for the script.
395 masm.tracelogStop(loggerReg);
396 masm.Pop(loggerReg);
397 #endif
399 // Pop SPS frame if necessary
400 emitSPSPop();
402 masm.mov(BaselineFrameReg, BaselineStackReg);
403 masm.pop(BaselineFrameReg);
405 masm.ret();
406 return true;
407 }
409 #ifdef JSGC_GENERATIONAL
410 // On input:
411 // R2.scratchReg() contains object being written to.
412 // Otherwise, baseline stack will be synced, so all other registers are usable as scratch.
413 // This calls:
414 // void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
415 bool
416 BaselineCompiler::emitOutOfLinePostBarrierSlot()
417 {
418 masm.bind(&postBarrierSlot_);
420 Register objReg = R2.scratchReg();
421 GeneralRegisterSet regs(GeneralRegisterSet::All());
422 regs.take(objReg);
423 regs.take(BaselineFrameReg);
424 Register scratch = regs.takeAny();
425 #if defined(JS_CODEGEN_ARM)
426 // On ARM, save the link register before calling. It contains the return
427 // address. The |masm.ret()| later will pop this into |pc| to return.
428 masm.push(lr);
429 #elif defined(JS_CODEGEN_MIPS)
430 masm.push(ra);
431 #endif
433 masm.setupUnalignedABICall(2, scratch);
434 masm.movePtr(ImmPtr(cx->runtime()), scratch);
435 masm.passABIArg(scratch);
436 masm.passABIArg(objReg);
437 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
439 masm.ret();
440 return true;
441 }
442 #endif // JSGC_GENERATIONAL
444 bool
445 BaselineCompiler::emitIC(ICStub *stub, ICEntry::Kind kind)
446 {
447 ICEntry *entry = allocateICEntry(stub, kind);
448 if (!entry)
449 return false;
451 CodeOffsetLabel patchOffset;
452 EmitCallIC(&patchOffset, masm);
453 entry->setReturnOffset(masm.currentOffset());
454 if (!addICLoadLabel(patchOffset))
455 return false;
457 return true;
458 }
460 typedef bool (*CheckOverRecursedWithExtraFn)(JSContext *, BaselineFrame *, uint32_t, uint32_t);
461 static const VMFunction CheckOverRecursedWithExtraInfo =
462 FunctionInfo<CheckOverRecursedWithExtraFn>(CheckOverRecursedWithExtra);
464 bool
465 BaselineCompiler::emitStackCheck(bool earlyCheck)
466 {
467 Label skipCall;
468 uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit;
469 uint32_t slotsSize = script->nslots() * sizeof(Value);
470 uint32_t tolerance = earlyCheck ? slotsSize : 0;
472 masm.movePtr(BaselineStackReg, R1.scratchReg());
474 // If this is the early stack check, locals haven't been pushed yet. Adjust the
475 // stack pointer to account for the locals that would be pushed before performing
476 // the guard around the vmcall to the stack check.
477 if (earlyCheck)
478 masm.subPtr(Imm32(tolerance), R1.scratchReg());
480 // If this is the late stack check for a frame which contains an early stack check,
481 // then the early stack check might have failed and skipped past the pushing of locals
482 // on the stack.
483 //
484 // If this is a possibility, then the OVER_RECURSED flag should be checked, and the
485 // VMCall to CheckOverRecursed done unconditionally if it's set.
486 Label forceCall;
487 if (!earlyCheck && needsEarlyStackCheck()) {
488 masm.branchTest32(Assembler::NonZero,
489 frame.addressOfFlags(),
490 Imm32(BaselineFrame::OVER_RECURSED),
491 &forceCall);
492 }
494 masm.branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(limitAddr), R1.scratchReg(),
495 &skipCall);
497 if (!earlyCheck && needsEarlyStackCheck())
498 masm.bind(&forceCall);
500 prepareVMCall();
501 pushArg(Imm32(earlyCheck));
502 pushArg(Imm32(tolerance));
503 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
504 pushArg(R1.scratchReg());
506 CallVMPhase phase = POST_INITIALIZE;
507 if (earlyCheck)
508 phase = PRE_INITIALIZE;
509 else if (needsEarlyStackCheck())
510 phase = CHECK_OVER_RECURSED;
512 if (!callVM(CheckOverRecursedWithExtraInfo, phase))
513 return false;
515 masm.bind(&skipCall);
516 return true;
517 }
519 typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *);
520 static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue);
522 bool
523 BaselineCompiler::emitDebugPrologue()
524 {
525 if (debugMode_) {
526 // Load pointer to BaselineFrame in R0.
527 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
529 prepareVMCall();
530 pushArg(ImmPtr(pc));
531 pushArg(R0.scratchReg());
532 if (!callVM(DebugPrologueInfo))
533 return false;
535 // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
536 icEntries_.back().setForDebugPrologue();
538 // If the stub returns |true|, we have to return the value stored in the
539 // frame's return value slot.
540 Label done;
541 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
542 {
543 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
544 masm.jump(&return_);
545 }
546 masm.bind(&done);
547 }
549 postDebugPrologueOffset_ = masm.currentOffset();
551 return true;
552 }
554 typedef bool (*StrictEvalPrologueFn)(JSContext *, BaselineFrame *);
555 static const VMFunction StrictEvalPrologueInfo =
556 FunctionInfo<StrictEvalPrologueFn>(jit::StrictEvalPrologue);
558 typedef bool (*HeavyweightFunPrologueFn)(JSContext *, BaselineFrame *);
559 static const VMFunction HeavyweightFunPrologueInfo =
560 FunctionInfo<HeavyweightFunPrologueFn>(jit::HeavyweightFunPrologue);
562 bool
563 BaselineCompiler::initScopeChain()
564 {
565 CallVMPhase phase = POST_INITIALIZE;
566 if (needsEarlyStackCheck())
567 phase = CHECK_OVER_RECURSED;
569 RootedFunction fun(cx, function());
570 if (fun) {
571 // Use callee->environment as scope chain. Note that we do
572 // this also for heavy-weight functions, so that the scope
573 // chain slot is properly initialized if the call triggers GC.
574 Register callee = R0.scratchReg();
575 Register scope = R1.scratchReg();
576 masm.loadPtr(frame.addressOfCallee(), callee);
577 masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope);
578 masm.storePtr(scope, frame.addressOfScopeChain());
580 if (fun->isHeavyweight()) {
581 // Call into the VM to create a new call object.
582 prepareVMCall();
584 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
585 pushArg(R0.scratchReg());
587 if (!callVM(HeavyweightFunPrologueInfo, phase))
588 return false;
589 }
590 } else {
591 // ScopeChain pointer in BaselineFrame has already been initialized
592 // in prologue.
594 if (script->isForEval() && script->strict()) {
595 // Strict eval needs its own call object.
596 prepareVMCall();
598 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
599 pushArg(R0.scratchReg());
601 if (!callVM(StrictEvalPrologueInfo, phase))
602 return false;
603 }
604 }
606 return true;
607 }
609 typedef bool (*InterruptCheckFn)(JSContext *);
610 static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
612 bool
613 BaselineCompiler::emitInterruptCheck()
614 {
615 frame.syncStack(0);
617 Label done;
618 void *interrupt = (void *)&cx->runtime()->interrupt;
619 masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
621 prepareVMCall();
622 if (!callVM(InterruptCheckInfo))
623 return false;
625 masm.bind(&done);
626 return true;
627 }
629 bool
630 BaselineCompiler::emitUseCountIncrement(bool allowOsr)
631 {
632 // Emit no use count increments or bailouts if Ion is not
633 // enabled, or if the script will never be Ion-compileable
635 if (!ionCompileable_ && !ionOSRCompileable_)
636 return true;
638 Register scriptReg = R2.scratchReg();
639 Register countReg = R0.scratchReg();
640 Address useCountAddr(scriptReg, JSScript::offsetOfUseCount());
642 masm.movePtr(ImmGCPtr(script), scriptReg);
643 masm.load32(useCountAddr, countReg);
644 masm.add32(Imm32(1), countReg);
645 masm.store32(countReg, useCountAddr);
647 // If this is a loop inside a catch or finally block, increment the use
648 // count but don't attempt OSR (Ion only compiles the try block).
649 if (analysis_.info(pc).loopEntryInCatchOrFinally) {
650 JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
651 return true;
652 }
654 // OSR not possible at this loop entry.
655 if (!allowOsr) {
656 JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
657 return true;
658 }
660 Label skipCall;
662 const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel());
663 uint32_t minUses = info->usesBeforeCompile(script, pc);
664 masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall);
666 masm.branchPtr(Assembler::Equal,
667 Address(scriptReg, JSScript::offsetOfIonScript()),
668 ImmPtr(ION_COMPILING_SCRIPT), &skipCall);
670 // Call IC.
671 ICUseCount_Fallback::Compiler stubCompiler(cx);
672 if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_)))
673 return false;
675 masm.bind(&skipCall);
677 return true;
678 }
680 bool
681 BaselineCompiler::emitArgumentTypeChecks()
682 {
683 if (!function())
684 return true;
686 frame.pushThis();
687 frame.popRegsAndSync(1);
689 ICTypeMonitor_Fallback::Compiler compiler(cx, (uint32_t) 0);
690 if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
691 return false;
693 for (size_t i = 0; i < function()->nargs(); i++) {
694 frame.pushArg(i);
695 frame.popRegsAndSync(1);
697 ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1);
698 if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
699 return false;
700 }
702 return true;
703 }
705 bool
706 BaselineCompiler::emitDebugTrap()
707 {
708 JS_ASSERT(debugMode_);
709 JS_ASSERT(frame.numUnsyncedSlots() == 0);
711 bool enabled = script->stepModeEnabled() || script->hasBreakpointsAt(pc);
713 // Emit patchable call to debug trap handler.
714 JitCode *handler = cx->runtime()->jitRuntime()->debugTrapHandler(cx);
715 mozilla::DebugOnly<CodeOffsetLabel> offset = masm.toggledCall(handler, enabled);
717 #ifdef DEBUG
718 // Patchable call offset has to match the pc mapping offset.
719 PCMappingEntry &entry = pcMappingEntries_.back();
720 JS_ASSERT((&offset)->offset() == entry.nativeOffset);
721 #endif
723 // Add an IC entry for the return offset -> pc mapping.
724 ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap);
725 icEntry.setReturnOffset(masm.currentOffset());
726 if (!icEntries_.append(icEntry))
727 return false;
729 return true;
730 }
732 bool
733 BaselineCompiler::emitSPSPush()
734 {
735 // Enter the IC, guarded by a toggled jump (initially disabled).
736 Label noPush;
737 CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush);
738 JS_ASSERT(frame.numUnsyncedSlots() == 0);
739 ICProfiler_Fallback::Compiler compiler(cx);
740 if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
741 return false;
742 masm.bind(&noPush);
744 // Store the start offset in the appropriate location.
745 JS_ASSERT(spsPushToggleOffset_.offset() == 0);
746 spsPushToggleOffset_ = toggleOffset;
747 return true;
748 }
750 void
751 BaselineCompiler::emitSPSPop()
752 {
753 // If profiler entry was pushed on this frame, pop it.
754 Label noPop;
755 masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
756 Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop);
757 masm.spsPopFrameSafe(&cx->runtime()->spsProfiler, R1.scratchReg());
758 masm.bind(&noPop);
759 }
761 MethodStatus
762 BaselineCompiler::emitBody()
763 {
764 JS_ASSERT(pc == script->code());
766 bool lastOpUnreachable = false;
767 uint32_t emittedOps = 0;
768 mozilla::DebugOnly<jsbytecode *> prevpc = pc;
770 while (true) {
771 JSOp op = JSOp(*pc);
772 IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s",
773 int(script->pcToOffset(pc)), js_CodeName[op]);
775 BytecodeInfo *info = analysis_.maybeInfo(pc);
777 // Skip unreachable ops.
778 if (!info) {
779 // Test if last instructions and stop emitting in that case.
780 pc += GetBytecodeLength(pc);
781 if (pc >= script->codeEnd())
782 break;
784 lastOpUnreachable = true;
785 prevpc = pc;
786 continue;
787 }
789 // Fully sync the stack if there are incoming jumps.
790 if (info->jumpTarget) {
791 frame.syncStack(0);
792 frame.setStackDepth(info->stackDepth);
793 }
795 // Always sync in debug mode.
796 if (debugMode_)
797 frame.syncStack(0);
799 // At the beginning of any op, at most the top 2 stack-values are unsynced.
800 if (frame.stackDepth() > 2)
801 frame.syncStack(2);
803 frame.assertValidState(*info);
805 masm.bind(labelOf(pc));
807 // Add a PC -> native mapping entry for the current op. These entries are
808 // used when we need the native code address for a given pc, for instance
809 // for bailouts from Ion, the debugger and exception handling. See
810 // PCMappingIndexEntry for more information.
811 bool addIndexEntry = (pc == script->code() || lastOpUnreachable || emittedOps > 100);
812 if (addIndexEntry)
813 emittedOps = 0;
814 if (!addPCMappingEntry(addIndexEntry))
815 return Method_Error;
817 // Emit traps for breakpoints and step mode.
818 if (debugMode_ && !emitDebugTrap())
819 return Method_Error;
821 switch (op) {
822 default:
823 IonSpew(IonSpew_BaselineAbort, "Unhandled op: %s", js_CodeName[op]);
824 return Method_CantCompile;
826 #define EMIT_OP(OP) \
827 case OP: \
828 if (!this->emit_##OP()) \
829 return Method_Error; \
830 break;
831 OPCODE_LIST(EMIT_OP)
832 #undef EMIT_OP
833 }
835 // Test if last instructions and stop emitting in that case.
836 pc += GetBytecodeLength(pc);
837 if (pc >= script->codeEnd())
838 break;
840 emittedOps++;
841 lastOpUnreachable = false;
842 #ifdef DEBUG
843 prevpc = pc;
844 #endif
845 }
847 JS_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL);
848 return Method_Compiled;
849 }
851 bool
852 BaselineCompiler::emit_JSOP_NOP()
853 {
854 return true;
855 }
857 bool
858 BaselineCompiler::emit_JSOP_LABEL()
859 {
860 return true;
861 }
863 bool
864 BaselineCompiler::emit_JSOP_POP()
865 {
866 frame.pop();
867 return true;
868 }
870 bool
871 BaselineCompiler::emit_JSOP_POPN()
872 {
873 frame.popn(GET_UINT16(pc));
874 return true;
875 }
877 bool
878 BaselineCompiler::emit_JSOP_DUPAT()
879 {
880 frame.syncStack(0);
882 // DUPAT takes a value on the stack and re-pushes it on top. It's like
883 // GETLOCAL but it addresses from the top of the stack instead of from the
884 // stack frame.
886 int depth = -(GET_UINT24(pc) + 1);
887 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
888 frame.push(R0);
889 return true;
890 }
892 bool
893 BaselineCompiler::emit_JSOP_DUP()
894 {
895 // Keep top stack value in R0, sync the rest so that we can use R1. We use
896 // separate registers because every register can be used by at most one
897 // StackValue.
898 frame.popRegsAndSync(1);
899 masm.moveValue(R0, R1);
901 // inc/dec ops use DUP followed by ONE, ADD. Push R0 last to avoid a move.
902 frame.push(R1);
903 frame.push(R0);
904 return true;
905 }
907 bool
908 BaselineCompiler::emit_JSOP_DUP2()
909 {
910 frame.syncStack(0);
912 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
913 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
915 frame.push(R0);
916 frame.push(R1);
917 return true;
918 }
920 bool
921 BaselineCompiler::emit_JSOP_SWAP()
922 {
923 // Keep top stack values in R0 and R1.
924 frame.popRegsAndSync(2);
926 frame.push(R1);
927 frame.push(R0);
928 return true;
929 }
931 bool
932 BaselineCompiler::emit_JSOP_PICK()
933 {
934 frame.syncStack(0);
936 // Pick takes a value on the stack and moves it to the top.
937 // For instance, pick 2:
938 // before: A B C D E
939 // after : A B D E C
941 // First, move value at -(amount + 1) into R0.
942 int depth = -(GET_INT8(pc) + 1);
943 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
945 // Move the other values down.
946 depth++;
947 for (; depth < 0; depth++) {
948 Address source = frame.addressOfStackValue(frame.peek(depth));
949 Address dest = frame.addressOfStackValue(frame.peek(depth - 1));
950 masm.loadValue(source, R1);
951 masm.storeValue(R1, dest);
952 }
954 // Push R0.
955 frame.pop();
956 frame.push(R0);
957 return true;
958 }
960 bool
961 BaselineCompiler::emit_JSOP_GOTO()
962 {
963 frame.syncStack(0);
965 jsbytecode *target = pc + GET_JUMP_OFFSET(pc);
966 masm.jump(labelOf(target));
967 return true;
968 }
970 bool
971 BaselineCompiler::emitToBoolean()
972 {
973 Label skipIC;
974 masm.branchTestBoolean(Assembler::Equal, R0, &skipIC);
976 // Call IC
977 ICToBool_Fallback::Compiler stubCompiler(cx);
978 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
979 return false;
981 masm.bind(&skipIC);
982 return true;
983 }
985 bool
986 BaselineCompiler::emitTest(bool branchIfTrue)
987 {
988 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
990 // Keep top stack value in R0.
991 frame.popRegsAndSync(1);
993 if (!knownBoolean && !emitToBoolean())
994 return false;
996 // IC will leave a BooleanValue in R0, just need to branch on it.
997 masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
998 return true;
999 }
1001 bool
1002 BaselineCompiler::emit_JSOP_IFEQ()
1003 {
1004 return emitTest(false);
1005 }
1007 bool
1008 BaselineCompiler::emit_JSOP_IFNE()
1009 {
1010 return emitTest(true);
1011 }
1013 bool
1014 BaselineCompiler::emitAndOr(bool branchIfTrue)
1015 {
1016 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
1018 // AND and OR leave the original value on the stack.
1019 frame.syncStack(0);
1021 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
1022 if (!knownBoolean && !emitToBoolean())
1023 return false;
1025 masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc)));
1026 return true;
1027 }
1029 bool
1030 BaselineCompiler::emit_JSOP_AND()
1031 {
1032 return emitAndOr(false);
1033 }
1035 bool
1036 BaselineCompiler::emit_JSOP_OR()
1037 {
1038 return emitAndOr(true);
1039 }
1041 bool
1042 BaselineCompiler::emit_JSOP_NOT()
1043 {
1044 bool knownBoolean = frame.peek(-1)->isKnownBoolean();
1046 // Keep top stack value in R0.
1047 frame.popRegsAndSync(1);
1049 if (!knownBoolean && !emitToBoolean())
1050 return false;
1052 masm.notBoolean(R0);
1054 frame.push(R0, JSVAL_TYPE_BOOLEAN);
1055 return true;
1056 }
1058 bool
1059 BaselineCompiler::emit_JSOP_POS()
1060 {
1061 // Keep top stack value in R0.
1062 frame.popRegsAndSync(1);
1064 // Inline path for int32 and double.
1065 Label done;
1066 masm.branchTestNumber(Assembler::Equal, R0, &done);
1068 // Call IC.
1069 ICToNumber_Fallback::Compiler stubCompiler(cx);
1070 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1071 return false;
1073 masm.bind(&done);
1074 frame.push(R0);
1075 return true;
1076 }
1078 bool
1079 BaselineCompiler::emit_JSOP_LOOPHEAD()
1080 {
1081 return emitInterruptCheck();
1082 }
1084 bool
1085 BaselineCompiler::emit_JSOP_LOOPENTRY()
1086 {
1087 frame.syncStack(0);
1088 return emitUseCountIncrement(LoopEntryCanIonOsr(pc));
1089 }
1091 bool
1092 BaselineCompiler::emit_JSOP_VOID()
1093 {
1094 frame.pop();
1095 frame.push(UndefinedValue());
1096 return true;
1097 }
1099 bool
1100 BaselineCompiler::emit_JSOP_UNDEFINED()
1101 {
1102 frame.push(UndefinedValue());
1103 return true;
1104 }
1106 bool
1107 BaselineCompiler::emit_JSOP_HOLE()
1108 {
1109 frame.push(MagicValue(JS_ELEMENTS_HOLE));
1110 return true;
1111 }
1113 bool
1114 BaselineCompiler::emit_JSOP_NULL()
1115 {
1116 frame.push(NullValue());
1117 return true;
1118 }
1120 bool
1121 BaselineCompiler::emit_JSOP_THIS()
1122 {
1123 if (function() && function()->isArrow()) {
1124 // Arrow functions store their (lexical) |this| value in an
1125 // extended slot.
1126 frame.syncStack(0);
1127 Register scratch = R0.scratchReg();
1128 masm.loadPtr(frame.addressOfCallee(), scratch);
1129 masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0);
1130 frame.push(R0);
1131 return true;
1132 }
1134 // Keep this value in R0
1135 frame.pushThis();
1137 // In strict mode code or self-hosted functions, |this| is left alone.
1138 if (script->strict() || (function() && function()->isSelfHostedBuiltin()))
1139 return true;
1141 Label skipIC;
1142 // Keep |thisv| in R0
1143 frame.popRegsAndSync(1);
1144 // If |this| is already an object, skip the IC.
1145 masm.branchTestObject(Assembler::Equal, R0, &skipIC);
1147 // Call IC
1148 ICThis_Fallback::Compiler stubCompiler(cx);
1149 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1150 return false;
1152 masm.storeValue(R0, frame.addressOfThis());
1154 // R0 is new pushed |this| value.
1155 masm.bind(&skipIC);
1156 frame.push(R0);
1158 return true;
1159 }
1161 bool
1162 BaselineCompiler::emit_JSOP_TRUE()
1163 {
1164 frame.push(BooleanValue(true));
1165 return true;
1166 }
1168 bool
1169 BaselineCompiler::emit_JSOP_FALSE()
1170 {
1171 frame.push(BooleanValue(false));
1172 return true;
1173 }
1175 bool
1176 BaselineCompiler::emit_JSOP_ZERO()
1177 {
1178 frame.push(Int32Value(0));
1179 return true;
1180 }
1182 bool
1183 BaselineCompiler::emit_JSOP_ONE()
1184 {
1185 frame.push(Int32Value(1));
1186 return true;
1187 }
1189 bool
1190 BaselineCompiler::emit_JSOP_INT8()
1191 {
1192 frame.push(Int32Value(GET_INT8(pc)));
1193 return true;
1194 }
1196 bool
1197 BaselineCompiler::emit_JSOP_INT32()
1198 {
1199 frame.push(Int32Value(GET_INT32(pc)));
1200 return true;
1201 }
1203 bool
1204 BaselineCompiler::emit_JSOP_UINT16()
1205 {
1206 frame.push(Int32Value(GET_UINT16(pc)));
1207 return true;
1208 }
1210 bool
1211 BaselineCompiler::emit_JSOP_UINT24()
1212 {
1213 frame.push(Int32Value(GET_UINT24(pc)));
1214 return true;
1215 }
1217 bool
1218 BaselineCompiler::emit_JSOP_DOUBLE()
1219 {
1220 frame.push(script->getConst(GET_UINT32_INDEX(pc)));
1221 return true;
1222 }
1224 bool
1225 BaselineCompiler::emit_JSOP_STRING()
1226 {
1227 frame.push(StringValue(script->getAtom(pc)));
1228 return true;
1229 }
1231 typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
1232 static const VMFunction DeepCloneObjectLiteralInfo =
1233 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
1235 bool
1236 BaselineCompiler::emit_JSOP_OBJECT()
1237 {
1238 if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) {
1239 RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
1240 if (!obj)
1241 return false;
1243 prepareVMCall();
1245 pushArg(ImmWord(js::MaybeSingletonObject));
1246 pushArg(ImmGCPtr(obj));
1248 if (!callVM(DeepCloneObjectLiteralInfo))
1249 return false;
1251 // Box and push return value.
1252 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1253 frame.push(R0);
1254 return true;
1255 }
1257 JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
1258 frame.push(ObjectValue(*script->getObject(pc)));
1259 return true;
1260 }
1262 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *);
1263 static const VMFunction CloneRegExpObjectInfo =
1264 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
1266 bool
1267 BaselineCompiler::emit_JSOP_REGEXP()
1268 {
1269 RootedObject reObj(cx, script->getRegExp(pc));
1271 prepareVMCall();
1272 pushArg(ImmGCPtr(reObj));
1273 if (!callVM(CloneRegExpObjectInfo))
1274 return false;
1276 // Box and push return value.
1277 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1278 frame.push(R0);
1279 return true;
1280 }
1282 typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject);
1283 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda);
1285 bool
1286 BaselineCompiler::emit_JSOP_LAMBDA()
1287 {
1288 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
1290 prepareVMCall();
1291 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
1293 pushArg(R0.scratchReg());
1294 pushArg(ImmGCPtr(fun));
1296 if (!callVM(LambdaInfo))
1297 return false;
1299 // Box and push return value.
1300 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1301 frame.push(R0);
1302 return true;
1303 }
1305 typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue);
1306 static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
1308 bool
1309 BaselineCompiler::emit_JSOP_LAMBDA_ARROW()
1310 {
1311 // Keep pushed |this| in R0.
1312 frame.popRegsAndSync(1);
1314 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
1316 prepareVMCall();
1317 masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg());
1319 pushArg(R0);
1320 pushArg(R1.scratchReg());
1321 pushArg(ImmGCPtr(fun));
1323 if (!callVM(LambdaArrowInfo))
1324 return false;
1326 // Box and push return value.
1327 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
1328 frame.push(R0);
1329 return true;
1330 }
1332 void
1333 BaselineCompiler::storeValue(const StackValue *source, const Address &dest,
1334 const ValueOperand &scratch)
1335 {
1336 switch (source->kind()) {
1337 case StackValue::Constant:
1338 masm.storeValue(source->constant(), dest);
1339 break;
1340 case StackValue::Register:
1341 masm.storeValue(source->reg(), dest);
1342 break;
1343 case StackValue::LocalSlot:
1344 masm.loadValue(frame.addressOfLocal(source->localSlot()), scratch);
1345 masm.storeValue(scratch, dest);
1346 break;
1347 case StackValue::ArgSlot:
1348 masm.loadValue(frame.addressOfArg(source->argSlot()), scratch);
1349 masm.storeValue(scratch, dest);
1350 break;
1351 case StackValue::ThisSlot:
1352 masm.loadValue(frame.addressOfThis(), scratch);
1353 masm.storeValue(scratch, dest);
1354 break;
1355 case StackValue::Stack:
1356 masm.loadValue(frame.addressOfStackValue(source), scratch);
1357 masm.storeValue(scratch, dest);
1358 break;
1359 default:
1360 MOZ_ASSUME_UNREACHABLE("Invalid kind");
1361 }
1362 }
1364 bool
1365 BaselineCompiler::emit_JSOP_BITOR()
1366 {
1367 return emitBinaryArith();
1368 }
1370 bool
1371 BaselineCompiler::emit_JSOP_BITXOR()
1372 {
1373 return emitBinaryArith();
1374 }
1376 bool
1377 BaselineCompiler::emit_JSOP_BITAND()
1378 {
1379 return emitBinaryArith();
1380 }
1382 bool
1383 BaselineCompiler::emit_JSOP_LSH()
1384 {
1385 return emitBinaryArith();
1386 }
1388 bool
1389 BaselineCompiler::emit_JSOP_RSH()
1390 {
1391 return emitBinaryArith();
1392 }
1394 bool
1395 BaselineCompiler::emit_JSOP_URSH()
1396 {
1397 return emitBinaryArith();
1398 }
1400 bool
1401 BaselineCompiler::emit_JSOP_ADD()
1402 {
1403 return emitBinaryArith();
1404 }
1406 bool
1407 BaselineCompiler::emit_JSOP_SUB()
1408 {
1409 return emitBinaryArith();
1410 }
1412 bool
1413 BaselineCompiler::emit_JSOP_MUL()
1414 {
1415 return emitBinaryArith();
1416 }
1418 bool
1419 BaselineCompiler::emit_JSOP_DIV()
1420 {
1421 return emitBinaryArith();
1422 }
1424 bool
1425 BaselineCompiler::emit_JSOP_MOD()
1426 {
1427 return emitBinaryArith();
1428 }
1430 bool
1431 BaselineCompiler::emitBinaryArith()
1432 {
1433 // Keep top JSStack value in R0 and R2
1434 frame.popRegsAndSync(2);
1436 // Call IC
1437 ICBinaryArith_Fallback::Compiler stubCompiler(cx);
1438 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1439 return false;
1441 // Mark R0 as pushed stack value.
1442 frame.push(R0);
1443 return true;
1444 }
1446 bool
1447 BaselineCompiler::emitUnaryArith()
1448 {
1449 // Keep top stack value in R0.
1450 frame.popRegsAndSync(1);
1452 // Call IC
1453 ICUnaryArith_Fallback::Compiler stubCompiler(cx);
1454 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1455 return false;
1457 // Mark R0 as pushed stack value.
1458 frame.push(R0);
1459 return true;
1460 }
1462 bool
1463 BaselineCompiler::emit_JSOP_BITNOT()
1464 {
1465 return emitUnaryArith();
1466 }
1468 bool
1469 BaselineCompiler::emit_JSOP_NEG()
1470 {
1471 return emitUnaryArith();
1472 }
1474 bool
1475 BaselineCompiler::emit_JSOP_LT()
1476 {
1477 return emitCompare();
1478 }
1480 bool
1481 BaselineCompiler::emit_JSOP_LE()
1482 {
1483 return emitCompare();
1484 }
1486 bool
1487 BaselineCompiler::emit_JSOP_GT()
1488 {
1489 return emitCompare();
1490 }
1492 bool
1493 BaselineCompiler::emit_JSOP_GE()
1494 {
1495 return emitCompare();
1496 }
1498 bool
1499 BaselineCompiler::emit_JSOP_EQ()
1500 {
1501 return emitCompare();
1502 }
1504 bool
1505 BaselineCompiler::emit_JSOP_NE()
1506 {
1507 return emitCompare();
1508 }
1510 bool
1511 BaselineCompiler::emitCompare()
1512 {
1513 // CODEGEN
1515 // Keep top JSStack value in R0 and R1.
1516 frame.popRegsAndSync(2);
1518 // Call IC.
1519 ICCompare_Fallback::Compiler stubCompiler(cx);
1520 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1521 return false;
1523 // Mark R0 as pushed stack value.
1524 frame.push(R0, JSVAL_TYPE_BOOLEAN);
1525 return true;
1526 }
1528 bool
1529 BaselineCompiler::emit_JSOP_STRICTEQ()
1530 {
1531 return emitCompare();
1532 }
1534 bool
1535 BaselineCompiler::emit_JSOP_STRICTNE()
1536 {
1537 return emitCompare();
1538 }
1540 bool
1541 BaselineCompiler::emit_JSOP_CONDSWITCH()
1542 {
1543 return true;
1544 }
1546 bool
1547 BaselineCompiler::emit_JSOP_CASE()
1548 {
1549 frame.popRegsAndSync(2);
1550 frame.push(R0);
1551 frame.syncStack(0);
1553 // Call IC.
1554 ICCompare_Fallback::Compiler stubCompiler(cx);
1555 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1556 return false;
1558 Register payload = masm.extractInt32(R0, R0.scratchReg());
1559 jsbytecode *target = pc + GET_JUMP_OFFSET(pc);
1561 Label done;
1562 masm.branch32(Assembler::Equal, payload, Imm32(0), &done);
1563 {
1564 // Pop the switch value if the case matches.
1565 masm.addPtr(Imm32(sizeof(Value)), StackPointer);
1566 masm.jump(labelOf(target));
1567 }
1568 masm.bind(&done);
1569 return true;
1570 }
1572 bool
1573 BaselineCompiler::emit_JSOP_DEFAULT()
1574 {
1575 frame.pop();
1576 return emit_JSOP_GOTO();
1577 }
1579 bool
1580 BaselineCompiler::emit_JSOP_LINENO()
1581 {
1582 return true;
1583 }
1585 bool
1586 BaselineCompiler::emit_JSOP_NEWARRAY()
1587 {
1588 frame.syncStack(0);
1590 uint32_t length = GET_UINT24(pc);
1591 RootedTypeObject type(cx);
1592 if (!types::UseNewTypeForInitializer(script, pc, JSProto_Array)) {
1593 type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array);
1594 if (!type)
1595 return false;
1596 }
1598 // Pass length in R0, type in R1.
1599 masm.move32(Imm32(length), R0.scratchReg());
1600 masm.movePtr(ImmGCPtr(type), R1.scratchReg());
1602 JSObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject);
1603 if (!templateObject)
1604 return false;
1605 templateObject->setType(type);
1607 ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
1608 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1609 return false;
1611 frame.push(R0);
1612 return true;
1613 }
1615 bool
1616 BaselineCompiler::emit_JSOP_INITELEM_ARRAY()
1617 {
1618 // Keep the object and rhs on the stack.
1619 frame.syncStack(0);
1621 // Load object in R0, index in R1.
1622 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
1623 masm.moveValue(Int32Value(GET_UINT24(pc)), R1);
1625 // Call IC.
1626 ICSetElem_Fallback::Compiler stubCompiler(cx);
1627 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1628 return false;
1630 // Pop the rhs, so that the object is on the top of the stack.
1631 frame.pop();
1632 return true;
1633 }
1635 bool
1636 BaselineCompiler::emit_JSOP_NEWOBJECT()
1637 {
1638 frame.syncStack(0);
1640 RootedTypeObject type(cx);
1641 if (!types::UseNewTypeForInitializer(script, pc, JSProto_Object)) {
1642 type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object);
1643 if (!type)
1644 return false;
1645 }
1647 RootedObject baseObject(cx, script->getObject(pc));
1648 RootedObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
1649 if (!templateObject)
1650 return false;
1652 if (type) {
1653 templateObject->setType(type);
1654 } else {
1655 if (!JSObject::setSingletonType(cx, templateObject))
1656 return false;
1657 }
1659 ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject);
1660 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1661 return false;
1663 frame.push(R0);
1664 return true;
1665 }
1667 bool
1668 BaselineCompiler::emit_JSOP_NEWINIT()
1669 {
1670 frame.syncStack(0);
1671 JSProtoKey key = JSProtoKey(GET_UINT8(pc));
1673 RootedTypeObject type(cx);
1674 if (!types::UseNewTypeForInitializer(script, pc, key)) {
1675 type = types::TypeScript::InitObject(cx, script, pc, key);
1676 if (!type)
1677 return false;
1678 }
1680 if (key == JSProto_Array) {
1681 // Pass length in R0, type in R1.
1682 masm.move32(Imm32(0), R0.scratchReg());
1683 masm.movePtr(ImmGCPtr(type), R1.scratchReg());
1685 JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
1686 if (!templateObject)
1687 return false;
1688 templateObject->setType(type);
1690 ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
1691 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1692 return false;
1693 } else {
1694 JS_ASSERT(key == JSProto_Object);
1696 RootedObject templateObject(cx);
1697 templateObject = NewBuiltinClassInstance(cx, &JSObject::class_, TenuredObject);
1698 if (!templateObject)
1699 return false;
1701 if (type) {
1702 templateObject->setType(type);
1703 } else {
1704 if (!JSObject::setSingletonType(cx, templateObject))
1705 return false;
1706 }
1708 ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject);
1709 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1710 return false;
1711 }
1713 frame.push(R0);
1714 return true;
1715 }
1717 bool
1718 BaselineCompiler::emit_JSOP_INITELEM()
1719 {
1720 // Store RHS in the scratch slot.
1721 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
1722 frame.pop();
1724 // Keep object and index in R0 and R1.
1725 frame.popRegsAndSync(2);
1727 // Push the object to store the result of the IC.
1728 frame.push(R0);
1729 frame.syncStack(0);
1731 // Keep RHS on the stack.
1732 frame.pushScratchValue();
1734 // Call IC.
1735 ICSetElem_Fallback::Compiler stubCompiler(cx);
1736 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1737 return false;
1739 // Pop the rhs, so that the object is on the top of the stack.
1740 frame.pop();
1741 return true;
1742 }
1744 typedef bool (*MutateProtoFn)(JSContext *cx, HandleObject obj, HandleValue newProto);
1745 static const VMFunction MutateProtoInfo = FunctionInfo<MutateProtoFn>(MutatePrototype);
1747 bool
1748 BaselineCompiler::emit_JSOP_MUTATEPROTO()
1749 {
1750 // Keep values on the stack for the decompiler.
1751 frame.syncStack(0);
1753 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg());
1754 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
1756 prepareVMCall();
1758 pushArg(R1);
1759 pushArg(R0.scratchReg());
1761 if (!callVM(MutateProtoInfo))
1762 return false;
1764 frame.pop();
1765 return true;
1766 }
1768 bool
1769 BaselineCompiler::emit_JSOP_INITPROP()
1770 {
1771 // Keep lhs in R0, rhs in R1.
1772 frame.popRegsAndSync(2);
1774 // Push the object to store the result of the IC.
1775 frame.push(R0);
1776 frame.syncStack(0);
1778 // Call IC.
1779 ICSetProp_Fallback::Compiler compiler(cx);
1780 return emitOpIC(compiler.getStub(&stubSpace_));
1781 }
1783 bool
1784 BaselineCompiler::emit_JSOP_ENDINIT()
1785 {
1786 return true;
1787 }
1789 typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &);
1790 static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush);
1792 bool
1793 BaselineCompiler::emit_JSOP_ARRAYPUSH()
1794 {
1795 // Keep value in R0, object in R1.
1796 frame.popRegsAndSync(2);
1797 masm.unboxObject(R1, R1.scratchReg());
1799 prepareVMCall();
1801 pushArg(R0);
1802 pushArg(R1.scratchReg());
1804 return callVM(NewbornArrayPushInfo);
1805 }
1807 bool
1808 BaselineCompiler::emit_JSOP_GETELEM()
1809 {
1810 // Keep top two stack values in R0 and R1.
1811 frame.popRegsAndSync(2);
1813 // Call IC.
1814 ICGetElem_Fallback::Compiler stubCompiler(cx);
1815 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1816 return false;
1818 // Mark R0 as pushed stack value.
1819 frame.push(R0);
1820 return true;
1821 }
1823 bool
1824 BaselineCompiler::emit_JSOP_CALLELEM()
1825 {
1826 return emit_JSOP_GETELEM();
1827 }
1829 bool
1830 BaselineCompiler::emit_JSOP_SETELEM()
1831 {
1832 // Store RHS in the scratch slot.
1833 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2);
1834 frame.pop();
1836 // Keep object and index in R0 and R1.
1837 frame.popRegsAndSync(2);
1839 // Keep RHS on the stack.
1840 frame.pushScratchValue();
1842 // Call IC.
1843 ICSetElem_Fallback::Compiler stubCompiler(cx);
1844 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1845 return false;
1847 return true;
1848 }
1850 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
1851 static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<true>);
1852 static const VMFunction DeleteElementNonStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<false>);
1854 bool
1855 BaselineCompiler::emit_JSOP_DELELEM()
1856 {
1857 // Keep values on the stack for the decompiler.
1858 frame.syncStack(0);
1859 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
1860 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1);
1862 prepareVMCall();
1864 pushArg(R1);
1865 pushArg(R0);
1867 if (!callVM(script->strict() ? DeleteElementStrictInfo : DeleteElementNonStrictInfo))
1868 return false;
1870 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
1871 frame.popn(2);
1872 frame.push(R1);
1873 return true;
1874 }
1876 bool
1877 BaselineCompiler::emit_JSOP_IN()
1878 {
1879 frame.popRegsAndSync(2);
1881 ICIn_Fallback::Compiler stubCompiler(cx);
1882 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1883 return false;
1885 frame.push(R0);
1886 return true;
1887 }
1889 bool
1890 BaselineCompiler::emit_JSOP_GETGNAME()
1891 {
1892 RootedPropertyName name(cx, script->getName(pc));
1894 if (name == cx->names().undefined) {
1895 frame.push(UndefinedValue());
1896 return true;
1897 }
1898 if (name == cx->names().NaN) {
1899 frame.push(cx->runtime()->NaNValue);
1900 return true;
1901 }
1902 if (name == cx->names().Infinity) {
1903 frame.push(cx->runtime()->positiveInfinityValue);
1904 return true;
1905 }
1907 frame.syncStack(0);
1909 masm.movePtr(ImmGCPtr(&script->global()), R0.scratchReg());
1911 // Call IC.
1912 ICGetName_Fallback::Compiler stubCompiler(cx);
1913 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
1914 return false;
1916 // Mark R0 as pushed stack value.
1917 frame.push(R0);
1918 return true;
1919 }
1921 bool
1922 BaselineCompiler::emit_JSOP_BINDGNAME()
1923 {
1924 frame.push(ObjectValue(script->global()));
1925 return true;
1926 }
1928 bool
1929 BaselineCompiler::emit_JSOP_SETPROP()
1930 {
1931 // Keep lhs in R0, rhs in R1.
1932 frame.popRegsAndSync(2);
1934 // Call IC.
1935 ICSetProp_Fallback::Compiler compiler(cx);
1936 if (!emitOpIC(compiler.getStub(&stubSpace_)))
1937 return false;
1939 // The IC will return the RHS value in R0, mark it as pushed value.
1940 frame.push(R0);
1941 return true;
1942 }
1944 bool
1945 BaselineCompiler::emit_JSOP_SETNAME()
1946 {
1947 return emit_JSOP_SETPROP();
1948 }
1950 bool
1951 BaselineCompiler::emit_JSOP_SETGNAME()
1952 {
1953 return emit_JSOP_SETPROP();
1954 }
1956 bool
1957 BaselineCompiler::emit_JSOP_GETPROP()
1958 {
1959 // Keep object in R0.
1960 frame.popRegsAndSync(1);
1962 // Call IC.
1963 ICGetProp_Fallback::Compiler compiler(cx);
1964 if (!emitOpIC(compiler.getStub(&stubSpace_)))
1965 return false;
1967 // Mark R0 as pushed stack value.
1968 frame.push(R0);
1969 return true;
1970 }
1972 bool
1973 BaselineCompiler::emit_JSOP_CALLPROP()
1974 {
1975 return emit_JSOP_GETPROP();
1976 }
1978 bool
1979 BaselineCompiler::emit_JSOP_LENGTH()
1980 {
1981 return emit_JSOP_GETPROP();
1982 }
1984 bool
1985 BaselineCompiler::emit_JSOP_GETXPROP()
1986 {
1987 return emit_JSOP_GETPROP();
1988 }
1990 typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *);
1991 static const VMFunction DeletePropertyStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
1992 static const VMFunction DeletePropertyNonStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
1994 bool
1995 BaselineCompiler::emit_JSOP_DELPROP()
1996 {
1997 // Keep value on the stack for the decompiler.
1998 frame.syncStack(0);
1999 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2001 prepareVMCall();
2003 pushArg(ImmGCPtr(script->getName(pc)));
2004 pushArg(R0);
2006 if (!callVM(script->strict() ? DeletePropertyStrictInfo : DeletePropertyNonStrictInfo))
2007 return false;
2009 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
2010 frame.pop();
2011 frame.push(R1);
2012 return true;
2013 }
2015 void
2016 BaselineCompiler::getScopeCoordinateObject(Register reg)
2017 {
2018 ScopeCoordinate sc(pc);
2020 masm.loadPtr(frame.addressOfScopeChain(), reg);
2021 for (unsigned i = sc.hops(); i; i--)
2022 masm.extractObject(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg);
2023 }
2025 Address
2026 BaselineCompiler::getScopeCoordinateAddressFromObject(Register objReg, Register reg)
2027 {
2028 ScopeCoordinate sc(pc);
2029 Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc);
2031 Address addr;
2032 if (shape->numFixedSlots() <= sc.slot()) {
2033 masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), reg);
2034 return Address(reg, (sc.slot() - shape->numFixedSlots()) * sizeof(Value));
2035 }
2037 return Address(objReg, JSObject::getFixedSlotOffset(sc.slot()));
2038 }
2040 Address
2041 BaselineCompiler::getScopeCoordinateAddress(Register reg)
2042 {
2043 getScopeCoordinateObject(reg);
2044 return getScopeCoordinateAddressFromObject(reg, reg);
2045 }
2047 bool
2048 BaselineCompiler::emit_JSOP_GETALIASEDVAR()
2049 {
2050 frame.syncStack(0);
2052 Address address = getScopeCoordinateAddress(R0.scratchReg());
2053 masm.loadValue(address, R0);
2055 ICTypeMonitor_Fallback::Compiler compiler(cx, (ICMonitoredFallbackStub *) nullptr);
2056 if (!emitOpIC(compiler.getStub(&stubSpace_)))
2057 return false;
2059 frame.push(R0);
2060 return true;
2061 }
2063 bool
2064 BaselineCompiler::emit_JSOP_SETALIASEDVAR()
2065 {
2066 JSScript *outerScript = ScopeCoordinateFunctionScript(script, pc);
2067 if (outerScript && outerScript->treatAsRunOnce()) {
2068 // Type updates for this operation might need to be tracked, so treat
2069 // this as a SETPROP.
2071 // Load rhs into R1.
2072 frame.syncStack(1);
2073 frame.popValue(R1);
2075 // Load and box lhs into R0.
2076 getScopeCoordinateObject(R2.scratchReg());
2077 masm.tagValue(JSVAL_TYPE_OBJECT, R2.scratchReg(), R0);
2079 // Call SETPROP IC.
2080 ICSetProp_Fallback::Compiler compiler(cx);
2081 if (!emitOpIC(compiler.getStub(&stubSpace_)))
2082 return false;
2084 // The IC will return the RHS value in R0, mark it as pushed value.
2085 frame.push(R0);
2086 return true;
2087 }
2089 // Keep rvalue in R0.
2090 frame.popRegsAndSync(1);
2091 Register objReg = R2.scratchReg();
2093 getScopeCoordinateObject(objReg);
2094 Address address = getScopeCoordinateAddressFromObject(objReg, R1.scratchReg());
2095 masm.patchableCallPreBarrier(address, MIRType_Value);
2096 masm.storeValue(R0, address);
2097 frame.push(R0);
2099 #ifdef JSGC_GENERATIONAL
2100 // Fully sync the stack if post-barrier is needed.
2101 // Scope coordinate object is already in R2.scratchReg().
2102 frame.syncStack(0);
2103 Register temp = R1.scratchReg();
2105 Label skipBarrier;
2106 masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier);
2107 masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier);
2109 masm.call(&postBarrierSlot_);
2111 masm.bind(&skipBarrier);
2112 #endif
2114 return true;
2115 }
2117 bool
2118 BaselineCompiler::emit_JSOP_NAME()
2119 {
2120 frame.syncStack(0);
2122 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2124 // Call IC.
2125 ICGetName_Fallback::Compiler stubCompiler(cx);
2126 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2127 return false;
2129 // Mark R0 as pushed stack value.
2130 frame.push(R0);
2131 return true;
2132 }
2134 bool
2135 BaselineCompiler::emit_JSOP_BINDNAME()
2136 {
2137 frame.syncStack(0);
2139 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2141 // Call IC.
2142 ICBindName_Fallback::Compiler stubCompiler(cx);
2143 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2144 return false;
2146 // Mark R0 as pushed stack value.
2147 frame.push(R0);
2148 return true;
2149 }
2151 typedef bool (*DeleteNameFn)(JSContext *, HandlePropertyName, HandleObject,
2152 MutableHandleValue);
2153 static const VMFunction DeleteNameInfo = FunctionInfo<DeleteNameFn>(DeleteNameOperation);
2155 bool
2156 BaselineCompiler::emit_JSOP_DELNAME()
2157 {
2158 frame.syncStack(0);
2159 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2161 prepareVMCall();
2163 pushArg(R0.scratchReg());
2164 pushArg(ImmGCPtr(script->getName(pc)));
2166 if (!callVM(DeleteNameInfo))
2167 return false;
2169 frame.push(R0);
2170 return true;
2171 }
2173 bool
2174 BaselineCompiler::emit_JSOP_GETINTRINSIC()
2175 {
2176 frame.syncStack(0);
2178 ICGetIntrinsic_Fallback::Compiler stubCompiler(cx);
2179 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2180 return false;
2182 frame.push(R0);
2183 return true;
2184 }
2186 typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject);
2187 static const VMFunction DefVarOrConstInfo = FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
2189 bool
2190 BaselineCompiler::emit_JSOP_DEFVAR()
2191 {
2192 frame.syncStack(0);
2194 unsigned attrs = JSPROP_ENUMERATE;
2195 if (!script->isForEval())
2196 attrs |= JSPROP_PERMANENT;
2197 if (JSOp(*pc) == JSOP_DEFCONST)
2198 attrs |= JSPROP_READONLY;
2199 JS_ASSERT(attrs <= UINT32_MAX);
2201 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2203 prepareVMCall();
2205 pushArg(R0.scratchReg());
2206 pushArg(Imm32(attrs));
2207 pushArg(ImmGCPtr(script->getName(pc)));
2209 return callVM(DefVarOrConstInfo);
2210 }
2212 bool
2213 BaselineCompiler::emit_JSOP_DEFCONST()
2214 {
2215 return emit_JSOP_DEFVAR();
2216 }
2218 typedef bool (*SetConstFn)(JSContext *, HandlePropertyName, HandleObject, HandleValue);
2219 static const VMFunction SetConstInfo = FunctionInfo<SetConstFn>(SetConst);
2221 bool
2222 BaselineCompiler::emit_JSOP_SETCONST()
2223 {
2224 frame.popRegsAndSync(1);
2225 frame.push(R0);
2226 frame.syncStack(0);
2228 masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg());
2230 prepareVMCall();
2232 pushArg(R0);
2233 pushArg(R1.scratchReg());
2234 pushArg(ImmGCPtr(script->getName(pc)));
2236 return callVM(SetConstInfo);
2237 }
2239 typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction);
2240 static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(DefFunOperation);
2242 bool
2243 BaselineCompiler::emit_JSOP_DEFFUN()
2244 {
2245 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
2247 frame.syncStack(0);
2248 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2250 prepareVMCall();
2252 pushArg(ImmGCPtr(fun));
2253 pushArg(R0.scratchReg());
2254 pushArg(ImmGCPtr(script));
2256 return callVM(DefFunOperationInfo);
2257 }
2259 typedef bool (*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName,
2260 HandleObject);
2261 static const VMFunction InitPropGetterSetterInfo =
2262 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation);
2264 bool
2265 BaselineCompiler::emitInitPropGetterSetter()
2266 {
2267 JS_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER ||
2268 JSOp(*pc) == JSOP_INITPROP_SETTER);
2270 // Keep values on the stack for the decompiler.
2271 frame.syncStack(0);
2273 prepareVMCall();
2275 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
2276 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R1.scratchReg());
2278 pushArg(R0.scratchReg());
2279 pushArg(ImmGCPtr(script->getName(pc)));
2280 pushArg(R1.scratchReg());
2281 pushArg(ImmPtr(pc));
2283 if (!callVM(InitPropGetterSetterInfo))
2284 return false;
2286 frame.pop();
2287 return true;
2288 }
2290 bool
2291 BaselineCompiler::emit_JSOP_INITPROP_GETTER()
2292 {
2293 return emitInitPropGetterSetter();
2294 }
2296 bool
2297 BaselineCompiler::emit_JSOP_INITPROP_SETTER()
2298 {
2299 return emitInitPropGetterSetter();
2300 }
2302 typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue,
2303 HandleObject);
2304 static const VMFunction InitElemGetterSetterInfo =
2305 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation);
2307 bool
2308 BaselineCompiler::emitInitElemGetterSetter()
2309 {
2310 JS_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER ||
2311 JSOp(*pc) == JSOP_INITELEM_SETTER);
2313 // Load index and value in R0 and R1, but keep values on the stack for the
2314 // decompiler.
2315 frame.syncStack(0);
2316 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
2317 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R1.scratchReg());
2319 prepareVMCall();
2321 pushArg(R1.scratchReg());
2322 pushArg(R0);
2323 masm.extractObject(frame.addressOfStackValue(frame.peek(-3)), R0.scratchReg());
2324 pushArg(R0.scratchReg());
2325 pushArg(ImmPtr(pc));
2327 if (!callVM(InitElemGetterSetterInfo))
2328 return false;
2330 frame.popn(2);
2331 return true;
2332 }
2334 bool
2335 BaselineCompiler::emit_JSOP_INITELEM_GETTER()
2336 {
2337 return emitInitElemGetterSetter();
2338 }
2340 bool
2341 BaselineCompiler::emit_JSOP_INITELEM_SETTER()
2342 {
2343 return emitInitElemGetterSetter();
2344 }
2346 bool
2347 BaselineCompiler::emit_JSOP_GETLOCAL()
2348 {
2349 frame.pushLocal(GET_LOCALNO(pc));
2350 return true;
2351 }
2353 bool
2354 BaselineCompiler::emit_JSOP_SETLOCAL()
2355 {
2356 // Ensure no other StackValue refers to the old value, for instance i + (i = 3).
2357 // This also allows us to use R0 as scratch below.
2358 frame.syncStack(1);
2360 uint32_t local = GET_LOCALNO(pc);
2361 storeValue(frame.peek(-1), frame.addressOfLocal(local), R0);
2362 return true;
2363 }
2365 bool
2366 BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
2367 {
2368 // Fast path: the script does not use |arguments|, or is strict. In strict
2369 // mode, formals do not alias the arguments object.
2370 if (!script->argumentsHasVarBinding() || script->strict()) {
2371 if (get) {
2372 frame.pushArg(arg);
2373 } else {
2374 // See the comment in emit_JSOP_SETLOCAL.
2375 frame.syncStack(1);
2376 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
2377 }
2379 return true;
2380 }
2382 // Sync so that we can use R0.
2383 frame.syncStack(0);
2385 // If the script is known to have an arguments object, we can just use it.
2386 // Else, we *may* have an arguments object (because we can't invalidate
2387 // when needsArgsObj becomes |true|), so we have to test HAS_ARGS_OBJ.
2388 Label done;
2389 if (!script->needsArgsObj()) {
2390 Label hasArgsObj;
2391 masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(),
2392 Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj);
2393 if (get)
2394 masm.loadValue(frame.addressOfArg(arg), R0);
2395 else
2396 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
2397 masm.jump(&done);
2398 masm.bind(&hasArgsObj);
2399 }
2401 // Load the arguments object data vector.
2402 Register reg = R2.scratchReg();
2403 masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
2404 masm.loadPrivate(Address(reg, ArgumentsObject::getDataSlotOffset()), reg);
2406 // Load/store the argument.
2407 Address argAddr(reg, ArgumentsData::offsetOfArgs() + arg * sizeof(Value));
2408 if (get) {
2409 masm.loadValue(argAddr, R0);
2410 frame.push(R0);
2411 } else {
2412 masm.patchableCallPreBarrier(argAddr, MIRType_Value);
2413 storeValue(frame.peek(-1), argAddr, R0);
2415 #ifdef JSGC_GENERATIONAL
2416 // Fully sync the stack if post-barrier is needed.
2417 frame.syncStack(0);
2418 Register temp = R1.scratchReg();
2420 // Reload the arguments object
2421 Register reg = R2.scratchReg();
2422 masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
2424 Label skipBarrier;
2425 masm.branchPtrInNurseryRange(reg, temp, &skipBarrier);
2427 masm.call(&postBarrierSlot_);
2429 masm.bind(&skipBarrier);
2430 #endif
2431 }
2433 masm.bind(&done);
2434 return true;
2435 }
2437 bool
2438 BaselineCompiler::emit_JSOP_GETARG()
2439 {
2440 uint32_t arg = GET_ARGNO(pc);
2441 return emitFormalArgAccess(arg, /* get = */ true);
2442 }
2444 bool
2445 BaselineCompiler::emit_JSOP_SETARG()
2446 {
2447 // Ionmonkey can't inline functions with SETARG with magic arguments.
2448 if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals())
2449 script->setUninlineable();
2451 modifiesArguments_ = true;
2453 uint32_t arg = GET_ARGNO(pc);
2454 return emitFormalArgAccess(arg, /* get = */ false);
2455 }
2457 bool
2458 BaselineCompiler::emitCall()
2459 {
2460 JS_ASSERT(IsCallPC(pc));
2462 uint32_t argc = GET_ARGC(pc);
2464 frame.syncStack(0);
2465 masm.mov(ImmWord(argc), R0.scratchReg());
2467 // Call IC
2468 ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ JSOp(*pc) == JSOP_NEW);
2469 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2470 return false;
2472 // Update FrameInfo.
2473 frame.popn(argc + 2);
2474 frame.push(R0);
2475 return true;
2476 }
2478 bool
2479 BaselineCompiler::emit_JSOP_CALL()
2480 {
2481 return emitCall();
2482 }
2484 bool
2485 BaselineCompiler::emit_JSOP_NEW()
2486 {
2487 return emitCall();
2488 }
2490 bool
2491 BaselineCompiler::emit_JSOP_FUNCALL()
2492 {
2493 return emitCall();
2494 }
2496 bool
2497 BaselineCompiler::emit_JSOP_FUNAPPLY()
2498 {
2499 return emitCall();
2500 }
2502 bool
2503 BaselineCompiler::emit_JSOP_EVAL()
2504 {
2505 return emitCall();
2506 }
2508 typedef bool (*ImplicitThisFn)(JSContext *, HandleObject, HandlePropertyName,
2509 MutableHandleValue);
2510 static const VMFunction ImplicitThisInfo = FunctionInfo<ImplicitThisFn>(ImplicitThisOperation);
2512 bool
2513 BaselineCompiler::emit_JSOP_IMPLICITTHIS()
2514 {
2515 frame.syncStack(0);
2516 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
2518 prepareVMCall();
2520 pushArg(ImmGCPtr(script->getName(pc)));
2521 pushArg(R0.scratchReg());
2523 if (!callVM(ImplicitThisInfo))
2524 return false;
2526 frame.push(R0);
2527 return true;
2528 }
2530 bool
2531 BaselineCompiler::emit_JSOP_INSTANCEOF()
2532 {
2533 frame.popRegsAndSync(2);
2535 ICInstanceOf_Fallback::Compiler stubCompiler(cx);
2536 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2537 return false;
2539 frame.push(R0);
2540 return true;
2541 }
2543 bool
2544 BaselineCompiler::emit_JSOP_TYPEOF()
2545 {
2546 frame.popRegsAndSync(1);
2548 ICTypeOf_Fallback::Compiler stubCompiler(cx);
2549 if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
2550 return false;
2552 frame.push(R0);
2553 return true;
2554 }
2556 bool
2557 BaselineCompiler::emit_JSOP_TYPEOFEXPR()
2558 {
2559 return emit_JSOP_TYPEOF();
2560 }
2562 typedef bool (*SetCallFn)(JSContext *);
2563 static const VMFunction SetCallInfo = FunctionInfo<SetCallFn>(js::SetCallOperation);
2565 bool
2566 BaselineCompiler::emit_JSOP_SETCALL()
2567 {
2568 prepareVMCall();
2569 return callVM(SetCallInfo);
2570 }
2572 typedef bool (*ThrowFn)(JSContext *, HandleValue);
2573 static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw);
2575 bool
2576 BaselineCompiler::emit_JSOP_THROW()
2577 {
2578 // Keep value to throw in R0.
2579 frame.popRegsAndSync(1);
2581 prepareVMCall();
2582 pushArg(R0);
2584 return callVM(ThrowInfo);
2585 }
2587 bool
2588 BaselineCompiler::emit_JSOP_TRY()
2589 {
2590 // Ionmonkey can't inline function with JSOP_TRY.
2591 script->setUninlineable();
2592 return true;
2593 }
2595 bool
2596 BaselineCompiler::emit_JSOP_FINALLY()
2597 {
2598 // JSOP_FINALLY has a def count of 2, but these values are already on the
2599 // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
2600 frame.setStackDepth(frame.stackDepth() + 2);
2602 // To match the interpreter, emit an interrupt check at the start of the
2603 // finally block.
2604 return emitInterruptCheck();
2605 }
2607 bool
2608 BaselineCompiler::emit_JSOP_GOSUB()
2609 {
2610 // Push |false| so that RETSUB knows the value on top of the
2611 // stack is not an exception but the offset to the op following
2612 // this GOSUB.
2613 frame.push(BooleanValue(false));
2615 int32_t nextOffset = script->pcToOffset(GetNextPc(pc));
2616 frame.push(Int32Value(nextOffset));
2618 // Jump to the finally block.
2619 frame.syncStack(0);
2620 jsbytecode *target = pc + GET_JUMP_OFFSET(pc);
2621 masm.jump(labelOf(target));
2622 return true;
2623 }
2625 bool
2626 BaselineCompiler::emit_JSOP_RETSUB()
2627 {
2628 frame.popRegsAndSync(2);
2630 ICRetSub_Fallback::Compiler stubCompiler(cx);
2631 return emitOpIC(stubCompiler.getStub(&stubSpace_));
2632 }
2634 typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
2635 static const VMFunction PushBlockScopeInfo = FunctionInfo<PushBlockScopeFn>(jit::PushBlockScope);
2637 bool
2638 BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE()
2639 {
2640 StaticBlockObject &blockObj = script->getObject(pc)->as<StaticBlockObject>();
2642 // Call a stub to push the block on the block chain.
2643 prepareVMCall();
2644 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2646 pushArg(ImmGCPtr(&blockObj));
2647 pushArg(R0.scratchReg());
2649 return callVM(PushBlockScopeInfo);
2650 }
2652 typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *);
2653 static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
2655 bool
2656 BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
2657 {
2658 // Call a stub to pop the block from the block chain.
2659 prepareVMCall();
2661 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2662 pushArg(R0.scratchReg());
2664 return callVM(PopBlockScopeInfo);
2665 }
2667 typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
2668 static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock);
2670 bool
2671 BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
2672 {
2673 if (!debugMode_)
2674 return true;
2676 prepareVMCall();
2677 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2678 pushArg(ImmPtr(pc));
2679 pushArg(R0.scratchReg());
2681 return callVM(DebugLeaveBlockInfo);
2682 }
2684 typedef bool (*EnterWithFn)(JSContext *, BaselineFrame *, HandleValue, Handle<StaticWithObject *>);
2685 static const VMFunction EnterWithInfo = FunctionInfo<EnterWithFn>(jit::EnterWith);
2687 bool
2688 BaselineCompiler::emit_JSOP_ENTERWITH()
2689 {
2690 StaticWithObject &withObj = script->getObject(pc)->as<StaticWithObject>();
2692 // Pop "with" object to R0.
2693 frame.popRegsAndSync(1);
2695 // Call a stub to push the object onto the scope chain.
2696 prepareVMCall();
2697 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
2699 pushArg(ImmGCPtr(&withObj));
2700 pushArg(R0);
2701 pushArg(R1.scratchReg());
2703 return callVM(EnterWithInfo);
2704 }
2706 typedef bool (*LeaveWithFn)(JSContext *, BaselineFrame *);
2707 static const VMFunction LeaveWithInfo = FunctionInfo<LeaveWithFn>(jit::LeaveWith);
2709 bool
2710 BaselineCompiler::emit_JSOP_LEAVEWITH()
2711 {
2712 // Call a stub to pop the with object from the scope chain.
2713 prepareVMCall();
2715 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2716 pushArg(R0.scratchReg());
2718 return callVM(LeaveWithInfo);
2719 }
2721 typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue);
2722 static const VMFunction GetAndClearExceptionInfo =
2723 FunctionInfo<GetAndClearExceptionFn>(GetAndClearException);
2725 bool
2726 BaselineCompiler::emit_JSOP_EXCEPTION()
2727 {
2728 prepareVMCall();
2730 if (!callVM(GetAndClearExceptionInfo))
2731 return false;
2733 frame.push(R0);
2734 return true;
2735 }
2737 typedef bool (*OnDebuggerStatementFn)(JSContext *, BaselineFrame *, jsbytecode *pc, bool *);
2738 static const VMFunction OnDebuggerStatementInfo =
2739 FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement);
2741 bool
2742 BaselineCompiler::emit_JSOP_DEBUGGER()
2743 {
2744 if (!debugMode_)
2745 return true;
2747 prepareVMCall();
2748 pushArg(ImmPtr(pc));
2750 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2751 pushArg(R0.scratchReg());
2753 if (!callVM(OnDebuggerStatementInfo))
2754 return false;
2756 // If the stub returns |true|, return the frame's return value.
2757 Label done;
2758 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
2759 {
2760 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
2761 masm.jump(&return_);
2762 }
2763 masm.bind(&done);
2764 return true;
2765 }
2767 typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool);
2768 static const VMFunction DebugEpilogueInfo = FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogue);
2770 bool
2771 BaselineCompiler::emitReturn()
2772 {
2773 if (debugMode_) {
2774 // Move return value into the frame's rval slot.
2775 masm.storeValue(JSReturnOperand, frame.addressOfReturnValue());
2776 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
2778 // Load BaselineFrame pointer in R0.
2779 frame.syncStack(0);
2780 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2782 prepareVMCall();
2783 pushArg(Imm32(1));
2784 pushArg(ImmPtr(pc));
2785 pushArg(R0.scratchReg());
2786 if (!callVM(DebugEpilogueInfo))
2787 return false;
2789 // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
2790 icEntries_.back().setForDebugEpilogue();
2792 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
2793 }
2795 // Only emit the jump if this JSOP_RETRVAL is not the last instruction.
2796 // Not needed for last instruction, because last instruction flows
2797 // into return label.
2798 if (pc + GetBytecodeLength(pc) < script->codeEnd())
2799 masm.jump(&return_);
2801 return true;
2802 }
2804 bool
2805 BaselineCompiler::emit_JSOP_RETURN()
2806 {
2807 JS_ASSERT(frame.stackDepth() == 1);
2809 frame.popValue(JSReturnOperand);
2810 return emitReturn();
2811 }
2813 bool
2814 BaselineCompiler::emit_JSOP_RETRVAL()
2815 {
2816 JS_ASSERT(frame.stackDepth() == 0);
2818 masm.moveValue(UndefinedValue(), JSReturnOperand);
2820 if (!script->noScriptRval()) {
2821 // Return the value in the return value slot, if any.
2822 Label done;
2823 Address flags = frame.addressOfFlags();
2824 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done);
2825 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
2826 masm.bind(&done);
2827 }
2829 return emitReturn();
2830 }
2832 typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue,
2833 MutableHandleValue);
2834 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(js::ToIdOperation);
2836 bool
2837 BaselineCompiler::emit_JSOP_TOID()
2838 {
2839 // Load index in R0, but keep values on the stack for the decompiler.
2840 frame.syncStack(0);
2841 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2843 // No-op if index is int32.
2844 Label done;
2845 masm.branchTestInt32(Assembler::Equal, R0, &done);
2847 prepareVMCall();
2849 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
2851 pushArg(R0);
2852 pushArg(R1);
2853 pushArg(ImmPtr(pc));
2854 pushArg(ImmGCPtr(script));
2856 if (!callVM(ToIdInfo))
2857 return false;
2859 masm.bind(&done);
2860 frame.pop(); // Pop index.
2861 frame.push(R0);
2862 return true;
2863 }
2865 bool
2866 BaselineCompiler::emit_JSOP_TABLESWITCH()
2867 {
2868 frame.popRegsAndSync(1);
2870 // Call IC.
2871 ICTableSwitch::Compiler compiler(cx, pc);
2872 return emitOpIC(compiler.getStub(&stubSpace_));
2873 }
2875 bool
2876 BaselineCompiler::emit_JSOP_ITER()
2877 {
2878 frame.popRegsAndSync(1);
2880 ICIteratorNew_Fallback::Compiler compiler(cx);
2881 if (!emitOpIC(compiler.getStub(&stubSpace_)))
2882 return false;
2884 frame.push(R0);
2885 return true;
2886 }
2888 bool
2889 BaselineCompiler::emit_JSOP_MOREITER()
2890 {
2891 frame.syncStack(0);
2892 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2894 ICIteratorMore_Fallback::Compiler compiler(cx);
2895 if (!emitOpIC(compiler.getStub(&stubSpace_)))
2896 return false;
2898 frame.push(R0);
2899 return true;
2900 }
2902 bool
2903 BaselineCompiler::emit_JSOP_ITERNEXT()
2904 {
2905 frame.syncStack(0);
2906 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
2908 ICIteratorNext_Fallback::Compiler compiler(cx);
2909 if (!emitOpIC(compiler.getStub(&stubSpace_)))
2910 return false;
2912 frame.push(R0);
2913 return true;
2914 }
2916 bool
2917 BaselineCompiler::emit_JSOP_ENDITER()
2918 {
2919 frame.popRegsAndSync(1);
2921 ICIteratorClose_Fallback::Compiler compiler(cx);
2922 return emitOpIC(compiler.getStub(&stubSpace_));
2923 }
2925 bool
2926 BaselineCompiler::emit_JSOP_SETRVAL()
2927 {
2928 // Store to the frame's return value slot.
2929 storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
2930 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
2931 frame.pop();
2932 return true;
2933 }
2935 bool
2936 BaselineCompiler::emit_JSOP_CALLEE()
2937 {
2938 JS_ASSERT(function());
2939 frame.syncStack(0);
2940 masm.loadPtr(frame.addressOfCallee(), R0.scratchReg());
2941 masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0);
2942 frame.push(R0);
2943 return true;
2944 }
2946 typedef bool (*NewArgumentsObjectFn)(JSContext *, BaselineFrame *, MutableHandleValue);
2947 static const VMFunction NewArgumentsObjectInfo =
2948 FunctionInfo<NewArgumentsObjectFn>(jit::NewArgumentsObject);
2950 bool
2951 BaselineCompiler::emit_JSOP_ARGUMENTS()
2952 {
2953 frame.syncStack(0);
2955 Label done;
2956 if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) {
2957 // We assume the script does not need an arguments object. However, this
2958 // assumption can be invalidated later, see argumentsOptimizationFailed
2959 // in JSScript. Because we can't invalidate baseline JIT code, we set a
2960 // flag on BaselineScript when that happens and guard on it here.
2961 masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0);
2963 // Load script->baseline.
2964 Register scratch = R1.scratchReg();
2965 masm.movePtr(ImmGCPtr(script), scratch);
2966 masm.loadPtr(Address(scratch, JSScript::offsetOfBaselineScript()), scratch);
2968 // If we don't need an arguments object, skip the VM call.
2969 masm.branchTest32(Assembler::Zero, Address(scratch, BaselineScript::offsetOfFlags()),
2970 Imm32(BaselineScript::NEEDS_ARGS_OBJ), &done);
2971 }
2973 prepareVMCall();
2975 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
2976 pushArg(R0.scratchReg());
2978 if (!callVM(NewArgumentsObjectInfo))
2979 return false;
2981 masm.bind(&done);
2982 frame.push(R0);
2983 return true;
2984 }
2986 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
2987 static const VMFunction RunOnceScriptPrologueInfo =
2988 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
2990 bool
2991 BaselineCompiler::emit_JSOP_RUNONCE()
2992 {
2993 frame.syncStack(0);
2995 prepareVMCall();
2997 masm.movePtr(ImmGCPtr(script), R0.scratchReg());
2998 pushArg(R0.scratchReg());
3000 return callVM(RunOnceScriptPrologueInfo);
3001 }
3003 bool
3004 BaselineCompiler::emit_JSOP_REST()
3005 {
3006 frame.syncStack(0);
3008 JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
3009 if (!templateObject)
3010 return false;
3011 types::FixRestArgumentsType(cx, templateObject);
3013 // Call IC.
3014 ICRest_Fallback::Compiler compiler(cx, templateObject);
3015 if (!emitOpIC(compiler.getStub(&stubSpace_)))
3016 return false;
3018 // Mark R0 as pushed stack value.
3019 frame.push(R0);
3020 return true;
3021 }