michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/BaselineCompiler.h" michael@0: michael@0: #include "jit/BaselineHelpers.h" michael@0: #include "jit/BaselineIC.h" michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/FixedList.h" michael@0: #include "jit/IonAnalysis.h" michael@0: #include "jit/IonLinker.h" michael@0: #include "jit/IonSpewer.h" michael@0: #ifdef JS_ION_PERF michael@0: # include "jit/PerfSpewer.h" michael@0: #endif michael@0: #include "jit/VMFunctions.h" michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/Interpreter-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script) michael@0: : BaselineCompilerSpecific(cx, alloc, script), michael@0: modifiesArguments_(false) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::init() michael@0: { michael@0: if (!analysis_.init(alloc_, cx->runtime()->gsnCache)) michael@0: return false; michael@0: michael@0: if (!labels_.init(alloc_, script->length())) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < script->length(); i++) michael@0: new (&labels_[i]) Label(); michael@0: michael@0: if (!frame.init(alloc_)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::addPCMappingEntry(bool addIndexEntry) michael@0: { michael@0: // Don't add multiple entries for a single pc. michael@0: size_t nentries = pcMappingEntries_.length(); michael@0: if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == script->pcToOffset(pc)) michael@0: return true; michael@0: michael@0: PCMappingEntry entry; michael@0: entry.pcOffset = script->pcToOffset(pc); michael@0: entry.nativeOffset = masm.currentOffset(); michael@0: entry.slotInfo = getStackTopSlotInfo(); michael@0: entry.addIndexEntry = addIndexEntry; michael@0: michael@0: return pcMappingEntries_.append(entry); michael@0: } michael@0: michael@0: MethodStatus michael@0: BaselineCompiler::compile() michael@0: { michael@0: IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)", michael@0: script->filename(), script->lineno(), script); michael@0: michael@0: IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d", michael@0: script->filename(), script->lineno()); michael@0: michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script)); michael@0: AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation); michael@0: michael@0: if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx)) michael@0: return Method_Error; michael@0: michael@0: // Pin analysis info during compilation. michael@0: types::AutoEnterAnalysis autoEnterAnalysis(cx); michael@0: michael@0: JS_ASSERT(!script->hasBaselineScript()); michael@0: michael@0: if (!emitPrologue()) michael@0: return Method_Error; michael@0: michael@0: MethodStatus status = emitBody(); michael@0: if (status != Method_Compiled) michael@0: return status; michael@0: michael@0: if (!emitEpilogue()) michael@0: return Method_Error; michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (!emitOutOfLinePostBarrierSlot()) michael@0: return Method_Error; michael@0: #endif michael@0: michael@0: if (masm.oom()) michael@0: return Method_Error; michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("Baseline"); michael@0: JitCode *code = linker.newCode(cx, JSC::BASELINE_CODE); michael@0: if (!code) michael@0: return Method_Error; michael@0: michael@0: JSObject *templateScope = nullptr; michael@0: if (script->functionNonDelazifying()) { michael@0: RootedFunction fun(cx, script->functionNonDelazifying()); michael@0: if (fun->isHeavyweight()) { michael@0: RootedScript scriptRoot(cx, script); michael@0: templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap); michael@0: if (!templateScope) michael@0: return Method_Error; michael@0: michael@0: if (fun->isNamedLambda()) { michael@0: RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); michael@0: if (!declEnvObject) michael@0: return Method_Error; michael@0: templateScope->as().setEnclosingScope(declEnvObject); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Encode the pc mapping table. See PCMappingIndexEntry for michael@0: // more information. michael@0: Vector pcMappingIndexEntries(cx); michael@0: CompactBufferWriter pcEntries; michael@0: uint32_t previousOffset = 0; michael@0: michael@0: for (size_t i = 0; i < pcMappingEntries_.length(); i++) { michael@0: PCMappingEntry &entry = pcMappingEntries_[i]; michael@0: entry.fixupNativeOffset(masm); michael@0: michael@0: if (entry.addIndexEntry) { michael@0: PCMappingIndexEntry indexEntry; michael@0: indexEntry.pcOffset = entry.pcOffset; michael@0: indexEntry.nativeOffset = entry.nativeOffset; michael@0: indexEntry.bufferOffset = pcEntries.length(); michael@0: if (!pcMappingIndexEntries.append(indexEntry)) michael@0: return Method_Error; michael@0: previousOffset = entry.nativeOffset; michael@0: } michael@0: michael@0: // Use the high bit of the SlotInfo byte to indicate the michael@0: // native code offset (relative to the previous op) > 0 and michael@0: // comes next in the buffer. michael@0: JS_ASSERT((entry.slotInfo.toByte() & 0x80) == 0); michael@0: michael@0: if (entry.nativeOffset == previousOffset) { michael@0: pcEntries.writeByte(entry.slotInfo.toByte()); michael@0: } else { michael@0: JS_ASSERT(entry.nativeOffset > previousOffset); michael@0: pcEntries.writeByte(0x80 | entry.slotInfo.toByte()); michael@0: pcEntries.writeUnsigned(entry.nativeOffset - previousOffset); michael@0: } michael@0: michael@0: previousOffset = entry.nativeOffset; michael@0: } michael@0: michael@0: if (pcEntries.oom()) michael@0: return Method_Error; michael@0: michael@0: prologueOffset_.fixup(&masm); michael@0: epilogueOffset_.fixup(&masm); michael@0: spsPushToggleOffset_.fixup(&masm); michael@0: postDebugPrologueOffset_.fixup(&masm); michael@0: michael@0: // Note: There is an extra entry in the bytecode type map for the search hint, see below. michael@0: size_t bytecodeTypeMapEntries = script->nTypeSets() + 1; michael@0: michael@0: BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(), michael@0: epilogueOffset_.offset(), michael@0: spsPushToggleOffset_.offset(), michael@0: postDebugPrologueOffset_.offset(), michael@0: icEntries_.length(), michael@0: pcMappingIndexEntries.length(), michael@0: pcEntries.length(), michael@0: bytecodeTypeMapEntries); michael@0: if (!baselineScript) michael@0: return Method_Error; michael@0: michael@0: baselineScript->setMethod(code); michael@0: baselineScript->setTemplateScope(templateScope); michael@0: michael@0: IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d", michael@0: (void *) baselineScript, (void *) code->raw(), michael@0: script->filename(), script->lineno()); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerBaselineProfile(script, code); michael@0: #endif michael@0: michael@0: JS_ASSERT(pcMappingIndexEntries.length() > 0); michael@0: baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]); michael@0: michael@0: JS_ASSERT(pcEntries.length() > 0); michael@0: baselineScript->copyPCMappingEntries(pcEntries); michael@0: michael@0: // Copy IC entries michael@0: if (icEntries_.length()) michael@0: baselineScript->copyICEntries(script, &icEntries_[0], masm); michael@0: michael@0: // Adopt fallback stubs from the compiler into the baseline script. michael@0: baselineScript->adoptFallbackStubs(&stubSpace_); michael@0: michael@0: // Patch IC loads using IC entries michael@0: for (size_t i = 0; i < icLoadLabels_.length(); i++) { michael@0: CodeOffsetLabel label = icLoadLabels_[i].label; michael@0: label.fixup(&masm); michael@0: size_t icEntry = icLoadLabels_[i].icEntry; michael@0: ICEntry *entryAddr = &(baselineScript->icEntry(icEntry)); michael@0: Assembler::patchDataWithValueCheck(CodeLocationLabel(code, label), michael@0: ImmPtr(entryAddr), michael@0: ImmPtr((void*)-1)); michael@0: } michael@0: michael@0: if (modifiesArguments_) michael@0: baselineScript->setModifiesArguments(); michael@0: michael@0: // All barriers are emitted off-by-default, toggle them on if needed. michael@0: if (cx->zone()->needsBarrier()) michael@0: baselineScript->toggleBarriers(true); michael@0: michael@0: // All SPS instrumentation is emitted toggled off. Toggle them on if needed. michael@0: if (cx->runtime()->spsProfiler.enabled()) michael@0: baselineScript->toggleSPS(true); michael@0: michael@0: uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap(); michael@0: types::FillBytecodeTypeMap(script, bytecodeMap); michael@0: michael@0: // The last entry in the last index found, and is used to avoid binary michael@0: // searches for the sought entry when queries are in linear order. michael@0: bytecodeMap[script->nTypeSets()] = 0; michael@0: michael@0: if (script->compartment()->debugMode()) michael@0: baselineScript->setDebugMode(); michael@0: michael@0: script->setBaselineScript(cx, baselineScript); michael@0: michael@0: return Method_Compiled; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitPrologue() michael@0: { michael@0: masm.push(BaselineFrameReg); michael@0: masm.mov(BaselineStackReg, BaselineFrameReg); michael@0: michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg); michael@0: masm.checkStackAlignment(); michael@0: michael@0: // Initialize BaselineFrame. For eval scripts, the scope chain michael@0: // is passed in R1, so we have to be careful not to clobber michael@0: // it. michael@0: michael@0: // Initialize BaselineFrame::flags. michael@0: uint32_t flags = 0; michael@0: if (script->isForEval()) michael@0: flags |= BaselineFrame::EVAL; michael@0: masm.store32(Imm32(flags), frame.addressOfFlags()); michael@0: michael@0: if (script->isForEval()) michael@0: masm.storePtr(ImmGCPtr(script), frame.addressOfEvalScript()); michael@0: michael@0: // Handle scope chain pre-initialization (in case GC gets run michael@0: // during stack check). For global and eval scripts, the scope michael@0: // chain is in R1. For function scripts, the scope chain is in michael@0: // the callee, nullptr is stored for now so that GC doesn't choke michael@0: // on a bogus ScopeChain value in the frame. michael@0: if (function()) michael@0: masm.storePtr(ImmPtr(nullptr), frame.addressOfScopeChain()); michael@0: else michael@0: masm.storePtr(R1.scratchReg(), frame.addressOfScopeChain()); michael@0: michael@0: // Functions with a large number of locals require two stack checks. michael@0: // The VMCall for a fallible stack check can only occur after the michael@0: // scope chain has been initialized, as that is required for proper michael@0: // exception handling if the VMCall returns false. The scope chain michael@0: // initialization can only happen after the UndefinedValues for the michael@0: // local slots have been pushed. michael@0: // However by that time, the stack might have grown too much. michael@0: // In these cases, we emit an extra, early, infallible check michael@0: // before pushing the locals. The early check sets a flag on the michael@0: // frame if the stack check fails (but otherwise doesn't throw an michael@0: // exception). If the flag is set, then the jitcode skips past michael@0: // the pushing of the locals, and directly to scope chain initialization michael@0: // followed by the actual stack check, which will throw the correct michael@0: // exception. michael@0: Label earlyStackCheckFailed; michael@0: if (needsEarlyStackCheck()) { michael@0: if (!emitStackCheck(/* earlyCheck = */ true)) michael@0: return false; michael@0: masm.branchTest32(Assembler::NonZero, michael@0: frame.addressOfFlags(), michael@0: Imm32(BaselineFrame::OVER_RECURSED), michael@0: &earlyStackCheckFailed); michael@0: } michael@0: michael@0: // Initialize locals to |undefined|. Use R0 to minimize code size. michael@0: // If the number of locals to push is < LOOP_UNROLL_FACTOR, then the michael@0: // initialization pushes are emitted directly and inline. Otherwise, michael@0: // they're emitted in a partially unrolled loop. michael@0: if (frame.nlocals() > 0) { michael@0: size_t LOOP_UNROLL_FACTOR = 4; michael@0: size_t toPushExtra = frame.nlocals() % LOOP_UNROLL_FACTOR; michael@0: michael@0: masm.moveValue(UndefinedValue(), R0); michael@0: michael@0: // Handle any extra pushes left over by the optional unrolled loop below. michael@0: for (size_t i = 0; i < toPushExtra; i++) michael@0: masm.pushValue(R0); michael@0: michael@0: // Partially unrolled loop of pushes. michael@0: if (frame.nlocals() >= LOOP_UNROLL_FACTOR) { michael@0: size_t toPush = frame.nlocals() - toPushExtra; michael@0: JS_ASSERT(toPush % LOOP_UNROLL_FACTOR == 0); michael@0: JS_ASSERT(toPush >= LOOP_UNROLL_FACTOR); michael@0: masm.move32(Imm32(toPush), R1.scratchReg()); michael@0: // Emit unrolled loop with 4 pushes per iteration. michael@0: Label pushLoop; michael@0: masm.bind(&pushLoop); michael@0: for (size_t i = 0; i < LOOP_UNROLL_FACTOR; i++) michael@0: masm.pushValue(R0); michael@0: masm.branchSub32(Assembler::NonZero, michael@0: Imm32(LOOP_UNROLL_FACTOR), R1.scratchReg(), &pushLoop); michael@0: } michael@0: } michael@0: michael@0: if (needsEarlyStackCheck()) michael@0: masm.bind(&earlyStackCheckFailed); michael@0: michael@0: #ifdef JS_TRACE_LOGGING michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: Register loggerReg = RegisterSet::Volatile().takeGeneral(); michael@0: masm.Push(loggerReg); michael@0: masm.movePtr(ImmPtr(logger), loggerReg); michael@0: masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script.get())); michael@0: masm.tracelogStart(loggerReg, TraceLogger::Baseline); michael@0: masm.Pop(loggerReg); michael@0: #endif michael@0: michael@0: // Record the offset of the prologue, because Ion can bailout before michael@0: // the scope chain is initialized. michael@0: prologueOffset_ = masm.currentOffset(); michael@0: michael@0: // Initialize the scope chain before any operation that may michael@0: // call into the VM and trigger a GC. michael@0: if (!initScopeChain()) michael@0: return false; michael@0: michael@0: if (!emitStackCheck()) michael@0: return false; michael@0: michael@0: if (!emitDebugPrologue()) michael@0: return false; michael@0: michael@0: if (!emitUseCountIncrement()) michael@0: return false; michael@0: michael@0: if (!emitArgumentTypeChecks()) michael@0: return false; michael@0: michael@0: if (!emitSPSPush()) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitEpilogue() michael@0: { michael@0: // Record the offset of the epilogue, so we can do early return from michael@0: // Debugger handlers during on-stack recompile. michael@0: epilogueOffset_ = masm.currentOffset(); michael@0: michael@0: masm.bind(&return_); michael@0: michael@0: #ifdef JS_TRACE_LOGGING michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: Register loggerReg = RegisterSet::Volatile().takeGeneral(); michael@0: masm.Push(loggerReg); michael@0: masm.movePtr(ImmPtr(logger), loggerReg); michael@0: masm.tracelogStop(loggerReg, TraceLogger::Baseline); michael@0: // Stop the script. Using a stop without checking the textId, since we michael@0: // we didn't save the textId for the script. michael@0: masm.tracelogStop(loggerReg); michael@0: masm.Pop(loggerReg); michael@0: #endif michael@0: michael@0: // Pop SPS frame if necessary michael@0: emitSPSPop(); michael@0: michael@0: masm.mov(BaselineFrameReg, BaselineStackReg); michael@0: masm.pop(BaselineFrameReg); michael@0: michael@0: masm.ret(); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // On input: michael@0: // R2.scratchReg() contains object being written to. michael@0: // Otherwise, baseline stack will be synced, so all other registers are usable as scratch. michael@0: // This calls: michael@0: // void PostWriteBarrier(JSRuntime *rt, JSObject *obj); michael@0: bool michael@0: BaselineCompiler::emitOutOfLinePostBarrierSlot() michael@0: { michael@0: masm.bind(&postBarrierSlot_); michael@0: michael@0: Register objReg = R2.scratchReg(); michael@0: GeneralRegisterSet regs(GeneralRegisterSet::All()); michael@0: regs.take(objReg); michael@0: regs.take(BaselineFrameReg); michael@0: Register scratch = regs.takeAny(); michael@0: #if defined(JS_CODEGEN_ARM) michael@0: // On ARM, save the link register before calling. It contains the return michael@0: // address. The |masm.ret()| later will pop this into |pc| to return. michael@0: masm.push(lr); michael@0: #elif defined(JS_CODEGEN_MIPS) michael@0: masm.push(ra); michael@0: #endif michael@0: michael@0: masm.setupUnalignedABICall(2, scratch); michael@0: masm.movePtr(ImmPtr(cx->runtime()), scratch); michael@0: masm.passABIArg(scratch); michael@0: masm.passABIArg(objReg); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier)); michael@0: michael@0: masm.ret(); michael@0: return true; michael@0: } michael@0: #endif // JSGC_GENERATIONAL michael@0: michael@0: bool michael@0: BaselineCompiler::emitIC(ICStub *stub, ICEntry::Kind kind) michael@0: { michael@0: ICEntry *entry = allocateICEntry(stub, kind); michael@0: if (!entry) michael@0: return false; michael@0: michael@0: CodeOffsetLabel patchOffset; michael@0: EmitCallIC(&patchOffset, masm); michael@0: entry->setReturnOffset(masm.currentOffset()); michael@0: if (!addICLoadLabel(patchOffset)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*CheckOverRecursedWithExtraFn)(JSContext *, BaselineFrame *, uint32_t, uint32_t); michael@0: static const VMFunction CheckOverRecursedWithExtraInfo = michael@0: FunctionInfo(CheckOverRecursedWithExtra); michael@0: michael@0: bool michael@0: BaselineCompiler::emitStackCheck(bool earlyCheck) michael@0: { michael@0: Label skipCall; michael@0: uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit; michael@0: uint32_t slotsSize = script->nslots() * sizeof(Value); michael@0: uint32_t tolerance = earlyCheck ? slotsSize : 0; michael@0: michael@0: masm.movePtr(BaselineStackReg, R1.scratchReg()); michael@0: michael@0: // If this is the early stack check, locals haven't been pushed yet. Adjust the michael@0: // stack pointer to account for the locals that would be pushed before performing michael@0: // the guard around the vmcall to the stack check. michael@0: if (earlyCheck) michael@0: masm.subPtr(Imm32(tolerance), R1.scratchReg()); michael@0: michael@0: // If this is the late stack check for a frame which contains an early stack check, michael@0: // then the early stack check might have failed and skipped past the pushing of locals michael@0: // on the stack. michael@0: // michael@0: // If this is a possibility, then the OVER_RECURSED flag should be checked, and the michael@0: // VMCall to CheckOverRecursed done unconditionally if it's set. michael@0: Label forceCall; michael@0: if (!earlyCheck && needsEarlyStackCheck()) { michael@0: masm.branchTest32(Assembler::NonZero, michael@0: frame.addressOfFlags(), michael@0: Imm32(BaselineFrame::OVER_RECURSED), michael@0: &forceCall); michael@0: } michael@0: michael@0: masm.branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(limitAddr), R1.scratchReg(), michael@0: &skipCall); michael@0: michael@0: if (!earlyCheck && needsEarlyStackCheck()) michael@0: masm.bind(&forceCall); michael@0: michael@0: prepareVMCall(); michael@0: pushArg(Imm32(earlyCheck)); michael@0: pushArg(Imm32(tolerance)); michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); michael@0: pushArg(R1.scratchReg()); michael@0: michael@0: CallVMPhase phase = POST_INITIALIZE; michael@0: if (earlyCheck) michael@0: phase = PRE_INITIALIZE; michael@0: else if (needsEarlyStackCheck()) michael@0: phase = CHECK_OVER_RECURSED; michael@0: michael@0: if (!callVM(CheckOverRecursedWithExtraInfo, phase)) michael@0: return false; michael@0: michael@0: masm.bind(&skipCall); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *); michael@0: static const VMFunction DebugPrologueInfo = FunctionInfo(jit::DebugPrologue); michael@0: michael@0: bool michael@0: BaselineCompiler::emitDebugPrologue() michael@0: { michael@0: if (debugMode_) { michael@0: // Load pointer to BaselineFrame in R0. michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: pushArg(ImmPtr(pc)); michael@0: pushArg(R0.scratchReg()); michael@0: if (!callVM(DebugPrologueInfo)) michael@0: return false; michael@0: michael@0: // Fix up the fake ICEntry appended by callVM for on-stack recompilation. michael@0: icEntries_.back().setForDebugPrologue(); michael@0: michael@0: // If the stub returns |true|, we have to return the value stored in the michael@0: // frame's return value slot. michael@0: Label done; michael@0: masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done); michael@0: { michael@0: masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); michael@0: masm.jump(&return_); michael@0: } michael@0: masm.bind(&done); michael@0: } michael@0: michael@0: postDebugPrologueOffset_ = masm.currentOffset(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*StrictEvalPrologueFn)(JSContext *, BaselineFrame *); michael@0: static const VMFunction StrictEvalPrologueInfo = michael@0: FunctionInfo(jit::StrictEvalPrologue); michael@0: michael@0: typedef bool (*HeavyweightFunPrologueFn)(JSContext *, BaselineFrame *); michael@0: static const VMFunction HeavyweightFunPrologueInfo = michael@0: FunctionInfo(jit::HeavyweightFunPrologue); michael@0: michael@0: bool michael@0: BaselineCompiler::initScopeChain() michael@0: { michael@0: CallVMPhase phase = POST_INITIALIZE; michael@0: if (needsEarlyStackCheck()) michael@0: phase = CHECK_OVER_RECURSED; michael@0: michael@0: RootedFunction fun(cx, function()); michael@0: if (fun) { michael@0: // Use callee->environment as scope chain. Note that we do michael@0: // this also for heavy-weight functions, so that the scope michael@0: // chain slot is properly initialized if the call triggers GC. michael@0: Register callee = R0.scratchReg(); michael@0: Register scope = R1.scratchReg(); michael@0: masm.loadPtr(frame.addressOfCallee(), callee); michael@0: masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope); michael@0: masm.storePtr(scope, frame.addressOfScopeChain()); michael@0: michael@0: if (fun->isHeavyweight()) { michael@0: // Call into the VM to create a new call object. michael@0: prepareVMCall(); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(HeavyweightFunPrologueInfo, phase)) michael@0: return false; michael@0: } michael@0: } else { michael@0: // ScopeChain pointer in BaselineFrame has already been initialized michael@0: // in prologue. michael@0: michael@0: if (script->isForEval() && script->strict()) { michael@0: // Strict eval needs its own call object. michael@0: prepareVMCall(); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(StrictEvalPrologueInfo, phase)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*InterruptCheckFn)(JSContext *); michael@0: static const VMFunction InterruptCheckInfo = FunctionInfo(InterruptCheck); michael@0: michael@0: bool michael@0: BaselineCompiler::emitInterruptCheck() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: Label done; michael@0: void *interrupt = (void *)&cx->runtime()->interrupt; michael@0: masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done); michael@0: michael@0: prepareVMCall(); michael@0: if (!callVM(InterruptCheckInfo)) michael@0: return false; michael@0: michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitUseCountIncrement(bool allowOsr) michael@0: { michael@0: // Emit no use count increments or bailouts if Ion is not michael@0: // enabled, or if the script will never be Ion-compileable michael@0: michael@0: if (!ionCompileable_ && !ionOSRCompileable_) michael@0: return true; michael@0: michael@0: Register scriptReg = R2.scratchReg(); michael@0: Register countReg = R0.scratchReg(); michael@0: Address useCountAddr(scriptReg, JSScript::offsetOfUseCount()); michael@0: michael@0: masm.movePtr(ImmGCPtr(script), scriptReg); michael@0: masm.load32(useCountAddr, countReg); michael@0: masm.add32(Imm32(1), countReg); michael@0: masm.store32(countReg, useCountAddr); michael@0: michael@0: // If this is a loop inside a catch or finally block, increment the use michael@0: // count but don't attempt OSR (Ion only compiles the try block). michael@0: if (analysis_.info(pc).loopEntryInCatchOrFinally) { michael@0: JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY); michael@0: return true; michael@0: } michael@0: michael@0: // OSR not possible at this loop entry. michael@0: if (!allowOsr) { michael@0: JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY); michael@0: return true; michael@0: } michael@0: michael@0: Label skipCall; michael@0: michael@0: const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel()); michael@0: uint32_t minUses = info->usesBeforeCompile(script, pc); michael@0: masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall); michael@0: michael@0: masm.branchPtr(Assembler::Equal, michael@0: Address(scriptReg, JSScript::offsetOfIonScript()), michael@0: ImmPtr(ION_COMPILING_SCRIPT), &skipCall); michael@0: michael@0: // Call IC. michael@0: ICUseCount_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: masm.bind(&skipCall); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitArgumentTypeChecks() michael@0: { michael@0: if (!function()) michael@0: return true; michael@0: michael@0: frame.pushThis(); michael@0: frame.popRegsAndSync(1); michael@0: michael@0: ICTypeMonitor_Fallback::Compiler compiler(cx, (uint32_t) 0); michael@0: if (!emitNonOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < function()->nargs(); i++) { michael@0: frame.pushArg(i); michael@0: frame.popRegsAndSync(1); michael@0: michael@0: ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1); michael@0: if (!emitNonOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitDebugTrap() michael@0: { michael@0: JS_ASSERT(debugMode_); michael@0: JS_ASSERT(frame.numUnsyncedSlots() == 0); michael@0: michael@0: bool enabled = script->stepModeEnabled() || script->hasBreakpointsAt(pc); michael@0: michael@0: // Emit patchable call to debug trap handler. michael@0: JitCode *handler = cx->runtime()->jitRuntime()->debugTrapHandler(cx); michael@0: mozilla::DebugOnly offset = masm.toggledCall(handler, enabled); michael@0: michael@0: #ifdef DEBUG michael@0: // Patchable call offset has to match the pc mapping offset. michael@0: PCMappingEntry &entry = pcMappingEntries_.back(); michael@0: JS_ASSERT((&offset)->offset() == entry.nativeOffset); michael@0: #endif michael@0: michael@0: // Add an IC entry for the return offset -> pc mapping. michael@0: ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap); michael@0: icEntry.setReturnOffset(masm.currentOffset()); michael@0: if (!icEntries_.append(icEntry)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitSPSPush() michael@0: { michael@0: // Enter the IC, guarded by a toggled jump (initially disabled). michael@0: Label noPush; michael@0: CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush); michael@0: JS_ASSERT(frame.numUnsyncedSlots() == 0); michael@0: ICProfiler_Fallback::Compiler compiler(cx); michael@0: if (!emitNonOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: masm.bind(&noPush); michael@0: michael@0: // Store the start offset in the appropriate location. michael@0: JS_ASSERT(spsPushToggleOffset_.offset() == 0); michael@0: spsPushToggleOffset_ = toggleOffset; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BaselineCompiler::emitSPSPop() michael@0: { michael@0: // If profiler entry was pushed on this frame, pop it. michael@0: Label noPop; michael@0: masm.branchTest32(Assembler::Zero, frame.addressOfFlags(), michael@0: Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop); michael@0: masm.spsPopFrameSafe(&cx->runtime()->spsProfiler, R1.scratchReg()); michael@0: masm.bind(&noPop); michael@0: } michael@0: michael@0: MethodStatus michael@0: BaselineCompiler::emitBody() michael@0: { michael@0: JS_ASSERT(pc == script->code()); michael@0: michael@0: bool lastOpUnreachable = false; michael@0: uint32_t emittedOps = 0; michael@0: mozilla::DebugOnly prevpc = pc; michael@0: michael@0: while (true) { michael@0: JSOp op = JSOp(*pc); michael@0: IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s", michael@0: int(script->pcToOffset(pc)), js_CodeName[op]); michael@0: michael@0: BytecodeInfo *info = analysis_.maybeInfo(pc); michael@0: michael@0: // Skip unreachable ops. michael@0: if (!info) { michael@0: // Test if last instructions and stop emitting in that case. michael@0: pc += GetBytecodeLength(pc); michael@0: if (pc >= script->codeEnd()) michael@0: break; michael@0: michael@0: lastOpUnreachable = true; michael@0: prevpc = pc; michael@0: continue; michael@0: } michael@0: michael@0: // Fully sync the stack if there are incoming jumps. michael@0: if (info->jumpTarget) { michael@0: frame.syncStack(0); michael@0: frame.setStackDepth(info->stackDepth); michael@0: } michael@0: michael@0: // Always sync in debug mode. michael@0: if (debugMode_) michael@0: frame.syncStack(0); michael@0: michael@0: // At the beginning of any op, at most the top 2 stack-values are unsynced. michael@0: if (frame.stackDepth() > 2) michael@0: frame.syncStack(2); michael@0: michael@0: frame.assertValidState(*info); michael@0: michael@0: masm.bind(labelOf(pc)); michael@0: michael@0: // Add a PC -> native mapping entry for the current op. These entries are michael@0: // used when we need the native code address for a given pc, for instance michael@0: // for bailouts from Ion, the debugger and exception handling. See michael@0: // PCMappingIndexEntry for more information. michael@0: bool addIndexEntry = (pc == script->code() || lastOpUnreachable || emittedOps > 100); michael@0: if (addIndexEntry) michael@0: emittedOps = 0; michael@0: if (!addPCMappingEntry(addIndexEntry)) michael@0: return Method_Error; michael@0: michael@0: // Emit traps for breakpoints and step mode. michael@0: if (debugMode_ && !emitDebugTrap()) michael@0: return Method_Error; michael@0: michael@0: switch (op) { michael@0: default: michael@0: IonSpew(IonSpew_BaselineAbort, "Unhandled op: %s", js_CodeName[op]); michael@0: return Method_CantCompile; michael@0: michael@0: #define EMIT_OP(OP) \ michael@0: case OP: \ michael@0: if (!this->emit_##OP()) \ michael@0: return Method_Error; \ michael@0: break; michael@0: OPCODE_LIST(EMIT_OP) michael@0: #undef EMIT_OP michael@0: } michael@0: michael@0: // Test if last instructions and stop emitting in that case. michael@0: pc += GetBytecodeLength(pc); michael@0: if (pc >= script->codeEnd()) michael@0: break; michael@0: michael@0: emittedOps++; michael@0: lastOpUnreachable = false; michael@0: #ifdef DEBUG michael@0: prevpc = pc; michael@0: #endif michael@0: } michael@0: michael@0: JS_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL); michael@0: return Method_Compiled; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NOP() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LABEL() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_POP() michael@0: { michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_POPN() michael@0: { michael@0: frame.popn(GET_UINT16(pc)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DUPAT() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: // DUPAT takes a value on the stack and re-pushes it on top. It's like michael@0: // GETLOCAL but it addresses from the top of the stack instead of from the michael@0: // stack frame. michael@0: michael@0: int depth = -(GET_UINT24(pc) + 1); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DUP() michael@0: { michael@0: // Keep top stack value in R0, sync the rest so that we can use R1. We use michael@0: // separate registers because every register can be used by at most one michael@0: // StackValue. michael@0: frame.popRegsAndSync(1); michael@0: masm.moveValue(R0, R1); michael@0: michael@0: // inc/dec ops use DUP followed by ONE, ADD. Push R0 last to avoid a move. michael@0: frame.push(R1); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DUP2() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); michael@0: michael@0: frame.push(R0); michael@0: frame.push(R1); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SWAP() michael@0: { michael@0: // Keep top stack values in R0 and R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: frame.push(R1); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_PICK() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: // Pick takes a value on the stack and moves it to the top. michael@0: // For instance, pick 2: michael@0: // before: A B C D E michael@0: // after : A B D E C michael@0: michael@0: // First, move value at -(amount + 1) into R0. michael@0: int depth = -(GET_INT8(pc) + 1); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0); michael@0: michael@0: // Move the other values down. michael@0: depth++; michael@0: for (; depth < 0; depth++) { michael@0: Address source = frame.addressOfStackValue(frame.peek(depth)); michael@0: Address dest = frame.addressOfStackValue(frame.peek(depth - 1)); michael@0: masm.loadValue(source, R1); michael@0: masm.storeValue(R1, dest); michael@0: } michael@0: michael@0: // Push R0. michael@0: frame.pop(); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GOTO() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: jsbytecode *target = pc + GET_JUMP_OFFSET(pc); michael@0: masm.jump(labelOf(target)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitToBoolean() michael@0: { michael@0: Label skipIC; michael@0: masm.branchTestBoolean(Assembler::Equal, R0, &skipIC); michael@0: michael@0: // Call IC michael@0: ICToBool_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: masm.bind(&skipIC); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitTest(bool branchIfTrue) michael@0: { michael@0: bool knownBoolean = frame.peek(-1)->isKnownBoolean(); michael@0: michael@0: // Keep top stack value in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: if (!knownBoolean && !emitToBoolean()) michael@0: return false; michael@0: michael@0: // IC will leave a BooleanValue in R0, just need to branch on it. michael@0: masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_IFEQ() michael@0: { michael@0: return emitTest(false); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_IFNE() michael@0: { michael@0: return emitTest(true); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitAndOr(bool branchIfTrue) michael@0: { michael@0: bool knownBoolean = frame.peek(-1)->isKnownBoolean(); michael@0: michael@0: // AND and OR leave the original value on the stack. michael@0: frame.syncStack(0); michael@0: michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); michael@0: if (!knownBoolean && !emitToBoolean()) michael@0: return false; michael@0: michael@0: masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_AND() michael@0: { michael@0: return emitAndOr(false); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_OR() michael@0: { michael@0: return emitAndOr(true); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NOT() michael@0: { michael@0: bool knownBoolean = frame.peek(-1)->isKnownBoolean(); michael@0: michael@0: // Keep top stack value in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: if (!knownBoolean && !emitToBoolean()) michael@0: return false; michael@0: michael@0: masm.notBoolean(R0); michael@0: michael@0: frame.push(R0, JSVAL_TYPE_BOOLEAN); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_POS() michael@0: { michael@0: // Keep top stack value in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: // Inline path for int32 and double. michael@0: Label done; michael@0: masm.branchTestNumber(Assembler::Equal, R0, &done); michael@0: michael@0: // Call IC. michael@0: ICToNumber_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: masm.bind(&done); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LOOPHEAD() michael@0: { michael@0: return emitInterruptCheck(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LOOPENTRY() michael@0: { michael@0: frame.syncStack(0); michael@0: return emitUseCountIncrement(LoopEntryCanIonOsr(pc)); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_VOID() michael@0: { michael@0: frame.pop(); michael@0: frame.push(UndefinedValue()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_UNDEFINED() michael@0: { michael@0: frame.push(UndefinedValue()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_HOLE() michael@0: { michael@0: frame.push(MagicValue(JS_ELEMENTS_HOLE)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NULL() michael@0: { michael@0: frame.push(NullValue()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_THIS() michael@0: { michael@0: if (function() && function()->isArrow()) { michael@0: // Arrow functions store their (lexical) |this| value in an michael@0: // extended slot. michael@0: frame.syncStack(0); michael@0: Register scratch = R0.scratchReg(); michael@0: masm.loadPtr(frame.addressOfCallee(), scratch); michael@0: masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: // Keep this value in R0 michael@0: frame.pushThis(); michael@0: michael@0: // In strict mode code or self-hosted functions, |this| is left alone. michael@0: if (script->strict() || (function() && function()->isSelfHostedBuiltin())) michael@0: return true; michael@0: michael@0: Label skipIC; michael@0: // Keep |thisv| in R0 michael@0: frame.popRegsAndSync(1); michael@0: // If |this| is already an object, skip the IC. michael@0: masm.branchTestObject(Assembler::Equal, R0, &skipIC); michael@0: michael@0: // Call IC michael@0: ICThis_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: masm.storeValue(R0, frame.addressOfThis()); michael@0: michael@0: // R0 is new pushed |this| value. michael@0: masm.bind(&skipIC); michael@0: frame.push(R0); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TRUE() michael@0: { michael@0: frame.push(BooleanValue(true)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_FALSE() michael@0: { michael@0: frame.push(BooleanValue(false)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ZERO() michael@0: { michael@0: frame.push(Int32Value(0)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ONE() michael@0: { michael@0: frame.push(Int32Value(1)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INT8() michael@0: { michael@0: frame.push(Int32Value(GET_INT8(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INT32() michael@0: { michael@0: frame.push(Int32Value(GET_INT32(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_UINT16() michael@0: { michael@0: frame.push(Int32Value(GET_UINT16(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_UINT24() michael@0: { michael@0: frame.push(Int32Value(GET_UINT24(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DOUBLE() michael@0: { michael@0: frame.push(script->getConst(GET_UINT32_INDEX(pc))); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_STRING() michael@0: { michael@0: frame.push(StringValue(script->getAtom(pc))); michael@0: return true; michael@0: } michael@0: michael@0: typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind); michael@0: static const VMFunction DeepCloneObjectLiteralInfo = michael@0: FunctionInfo(DeepCloneObjectLiteral); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_OBJECT() michael@0: { michael@0: if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) { michael@0: RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc))); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(ImmWord(js::MaybeSingletonObject)); michael@0: pushArg(ImmGCPtr(obj)); michael@0: michael@0: if (!callVM(DeepCloneObjectLiteralInfo)) michael@0: return false; michael@0: michael@0: // Box and push return value. michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: JS::CompartmentOptionsRef(cx).setSingletonsAsValues(); michael@0: frame.push(ObjectValue(*script->getObject(pc))); michael@0: return true; michael@0: } michael@0: michael@0: typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *); michael@0: static const VMFunction CloneRegExpObjectInfo = michael@0: FunctionInfo(CloneRegExpObject); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_REGEXP() michael@0: { michael@0: RootedObject reObj(cx, script->getRegExp(pc)); michael@0: michael@0: prepareVMCall(); michael@0: pushArg(ImmGCPtr(reObj)); michael@0: if (!callVM(CloneRegExpObjectInfo)) michael@0: return false; michael@0: michael@0: // Box and push return value. michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject); michael@0: static const VMFunction LambdaInfo = FunctionInfo(js::Lambda); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LAMBDA() michael@0: { michael@0: RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); michael@0: michael@0: prepareVMCall(); michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(ImmGCPtr(fun)); michael@0: michael@0: if (!callVM(LambdaInfo)) michael@0: return false; michael@0: michael@0: // Box and push return value. michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue); michael@0: static const VMFunction LambdaArrowInfo = FunctionInfo(js::LambdaArrow); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LAMBDA_ARROW() michael@0: { michael@0: // Keep pushed |this| in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); michael@0: michael@0: prepareVMCall(); michael@0: masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg()); michael@0: michael@0: pushArg(R0); michael@0: pushArg(R1.scratchReg()); michael@0: pushArg(ImmGCPtr(fun)); michael@0: michael@0: if (!callVM(LambdaArrowInfo)) michael@0: return false; michael@0: michael@0: // Box and push return value. michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BaselineCompiler::storeValue(const StackValue *source, const Address &dest, michael@0: const ValueOperand &scratch) michael@0: { michael@0: switch (source->kind()) { michael@0: case StackValue::Constant: michael@0: masm.storeValue(source->constant(), dest); michael@0: break; michael@0: case StackValue::Register: michael@0: masm.storeValue(source->reg(), dest); michael@0: break; michael@0: case StackValue::LocalSlot: michael@0: masm.loadValue(frame.addressOfLocal(source->localSlot()), scratch); michael@0: masm.storeValue(scratch, dest); michael@0: break; michael@0: case StackValue::ArgSlot: michael@0: masm.loadValue(frame.addressOfArg(source->argSlot()), scratch); michael@0: masm.storeValue(scratch, dest); michael@0: break; michael@0: case StackValue::ThisSlot: michael@0: masm.loadValue(frame.addressOfThis(), scratch); michael@0: masm.storeValue(scratch, dest); michael@0: break; michael@0: case StackValue::Stack: michael@0: masm.loadValue(frame.addressOfStackValue(source), scratch); michael@0: masm.storeValue(scratch, dest); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BITOR() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BITXOR() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BITAND() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LSH() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_RSH() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_URSH() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ADD() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SUB() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_MUL() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DIV() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_MOD() michael@0: { michael@0: return emitBinaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitBinaryArith() michael@0: { michael@0: // Keep top JSStack value in R0 and R2 michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Call IC michael@0: ICBinaryArith_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitUnaryArith() michael@0: { michael@0: // Keep top stack value in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: // Call IC michael@0: ICUnaryArith_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BITNOT() michael@0: { michael@0: return emitUnaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NEG() michael@0: { michael@0: return emitUnaryArith(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LT() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LE() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GT() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GE() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_EQ() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NE() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitCompare() michael@0: { michael@0: // CODEGEN michael@0: michael@0: // Keep top JSStack value in R0 and R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Call IC. michael@0: ICCompare_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0, JSVAL_TYPE_BOOLEAN); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_STRICTEQ() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_STRICTNE() michael@0: { michael@0: return emitCompare(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CONDSWITCH() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CASE() michael@0: { michael@0: frame.popRegsAndSync(2); michael@0: frame.push(R0); michael@0: frame.syncStack(0); michael@0: michael@0: // Call IC. michael@0: ICCompare_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: Register payload = masm.extractInt32(R0, R0.scratchReg()); michael@0: jsbytecode *target = pc + GET_JUMP_OFFSET(pc); michael@0: michael@0: Label done; michael@0: masm.branch32(Assembler::Equal, payload, Imm32(0), &done); michael@0: { michael@0: // Pop the switch value if the case matches. michael@0: masm.addPtr(Imm32(sizeof(Value)), StackPointer); michael@0: masm.jump(labelOf(target)); michael@0: } michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEFAULT() michael@0: { michael@0: frame.pop(); michael@0: return emit_JSOP_GOTO(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LINENO() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NEWARRAY() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: uint32_t length = GET_UINT24(pc); michael@0: RootedTypeObject type(cx); michael@0: if (!types::UseNewTypeForInitializer(script, pc, JSProto_Array)) { michael@0: type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); michael@0: if (!type) michael@0: return false; michael@0: } michael@0: michael@0: // Pass length in R0, type in R1. michael@0: masm.move32(Imm32(length), R0.scratchReg()); michael@0: masm.movePtr(ImmGCPtr(type), R1.scratchReg()); michael@0: michael@0: JSObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject); michael@0: if (!templateObject) michael@0: return false; michael@0: templateObject->setType(type); michael@0: michael@0: ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITELEM_ARRAY() michael@0: { michael@0: // Keep the object and rhs on the stack. michael@0: frame.syncStack(0); michael@0: michael@0: // Load object in R0, index in R1. michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); michael@0: masm.moveValue(Int32Value(GET_UINT24(pc)), R1); michael@0: michael@0: // Call IC. michael@0: ICSetElem_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Pop the rhs, so that the object is on the top of the stack. michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NEWOBJECT() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: RootedTypeObject type(cx); michael@0: if (!types::UseNewTypeForInitializer(script, pc, JSProto_Object)) { michael@0: type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object); michael@0: if (!type) michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseObject(cx, script->getObject(pc)); michael@0: RootedObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject)); michael@0: if (!templateObject) michael@0: return false; michael@0: michael@0: if (type) { michael@0: templateObject->setType(type); michael@0: } else { michael@0: if (!JSObject::setSingletonType(cx, templateObject)) michael@0: return false; michael@0: } michael@0: michael@0: ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NEWINIT() michael@0: { michael@0: frame.syncStack(0); michael@0: JSProtoKey key = JSProtoKey(GET_UINT8(pc)); michael@0: michael@0: RootedTypeObject type(cx); michael@0: if (!types::UseNewTypeForInitializer(script, pc, key)) { michael@0: type = types::TypeScript::InitObject(cx, script, pc, key); michael@0: if (!type) michael@0: return false; michael@0: } michael@0: michael@0: if (key == JSProto_Array) { michael@0: // Pass length in R0, type in R1. michael@0: masm.move32(Imm32(0), R0.scratchReg()); michael@0: masm.movePtr(ImmGCPtr(type), R1.scratchReg()); michael@0: michael@0: JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); michael@0: if (!templateObject) michael@0: return false; michael@0: templateObject->setType(type); michael@0: michael@0: ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: } else { michael@0: JS_ASSERT(key == JSProto_Object); michael@0: michael@0: RootedObject templateObject(cx); michael@0: templateObject = NewBuiltinClassInstance(cx, &JSObject::class_, TenuredObject); michael@0: if (!templateObject) michael@0: return false; michael@0: michael@0: if (type) { michael@0: templateObject->setType(type); michael@0: } else { michael@0: if (!JSObject::setSingletonType(cx, templateObject)) michael@0: return false; michael@0: } michael@0: michael@0: ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: } michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITELEM() michael@0: { michael@0: // Store RHS in the scratch slot. michael@0: storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2); michael@0: frame.pop(); michael@0: michael@0: // Keep object and index in R0 and R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Push the object to store the result of the IC. michael@0: frame.push(R0); michael@0: frame.syncStack(0); michael@0: michael@0: // Keep RHS on the stack. michael@0: frame.pushScratchValue(); michael@0: michael@0: // Call IC. michael@0: ICSetElem_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Pop the rhs, so that the object is on the top of the stack. michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*MutateProtoFn)(JSContext *cx, HandleObject obj, HandleValue newProto); michael@0: static const VMFunction MutateProtoInfo = FunctionInfo(MutatePrototype); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_MUTATEPROTO() michael@0: { michael@0: // Keep values on the stack for the decompiler. michael@0: frame.syncStack(0); michael@0: michael@0: masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg()); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R1); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(MutateProtoInfo)) michael@0: return false; michael@0: michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITPROP() michael@0: { michael@0: // Keep lhs in R0, rhs in R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Push the object to store the result of the IC. michael@0: frame.push(R0); michael@0: frame.syncStack(0); michael@0: michael@0: // Call IC. michael@0: ICSetProp_Fallback::Compiler compiler(cx); michael@0: return emitOpIC(compiler.getStub(&stubSpace_)); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ENDINIT() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &); michael@0: static const VMFunction NewbornArrayPushInfo = FunctionInfo(NewbornArrayPush); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ARRAYPUSH() michael@0: { michael@0: // Keep value in R0, object in R1. michael@0: frame.popRegsAndSync(2); michael@0: masm.unboxObject(R1, R1.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R0); michael@0: pushArg(R1.scratchReg()); michael@0: michael@0: return callVM(NewbornArrayPushInfo); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETELEM() michael@0: { michael@0: // Keep top two stack values in R0 and R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Call IC. michael@0: ICGetElem_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CALLELEM() michael@0: { michael@0: return emit_JSOP_GETELEM(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETELEM() michael@0: { michael@0: // Store RHS in the scratch slot. michael@0: storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2); michael@0: frame.pop(); michael@0: michael@0: // Keep object and index in R0 and R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Keep RHS on the stack. michael@0: frame.pushScratchValue(); michael@0: michael@0: // Call IC. michael@0: ICSetElem_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *); michael@0: static const VMFunction DeleteElementStrictInfo = FunctionInfo(DeleteElement); michael@0: static const VMFunction DeleteElementNonStrictInfo = FunctionInfo(DeleteElement); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DELELEM() michael@0: { michael@0: // Keep values on the stack for the decompiler. michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R1); michael@0: pushArg(R0); michael@0: michael@0: if (!callVM(script->strict() ? DeleteElementStrictInfo : DeleteElementNonStrictInfo)) michael@0: return false; michael@0: michael@0: masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1); michael@0: frame.popn(2); michael@0: frame.push(R1); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_IN() michael@0: { michael@0: frame.popRegsAndSync(2); michael@0: michael@0: ICIn_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETGNAME() michael@0: { michael@0: RootedPropertyName name(cx, script->getName(pc)); michael@0: michael@0: if (name == cx->names().undefined) { michael@0: frame.push(UndefinedValue()); michael@0: return true; michael@0: } michael@0: if (name == cx->names().NaN) { michael@0: frame.push(cx->runtime()->NaNValue); michael@0: return true; michael@0: } michael@0: if (name == cx->names().Infinity) { michael@0: frame.push(cx->runtime()->positiveInfinityValue); michael@0: return true; michael@0: } michael@0: michael@0: frame.syncStack(0); michael@0: michael@0: masm.movePtr(ImmGCPtr(&script->global()), R0.scratchReg()); michael@0: michael@0: // Call IC. michael@0: ICGetName_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BINDGNAME() michael@0: { michael@0: frame.push(ObjectValue(script->global())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETPROP() michael@0: { michael@0: // Keep lhs in R0, rhs in R1. michael@0: frame.popRegsAndSync(2); michael@0: michael@0: // Call IC. michael@0: ICSetProp_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // The IC will return the RHS value in R0, mark it as pushed value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETNAME() michael@0: { michael@0: return emit_JSOP_SETPROP(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETGNAME() michael@0: { michael@0: return emit_JSOP_SETPROP(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETPROP() michael@0: { michael@0: // Keep object in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: // Call IC. michael@0: ICGetProp_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CALLPROP() michael@0: { michael@0: return emit_JSOP_GETPROP(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LENGTH() michael@0: { michael@0: return emit_JSOP_GETPROP(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETXPROP() michael@0: { michael@0: return emit_JSOP_GETPROP(); michael@0: } michael@0: michael@0: typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *); michael@0: static const VMFunction DeletePropertyStrictInfo = FunctionInfo(DeleteProperty); michael@0: static const VMFunction DeletePropertyNonStrictInfo = FunctionInfo(DeleteProperty); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DELPROP() michael@0: { michael@0: // Keep value on the stack for the decompiler. michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: pushArg(R0); michael@0: michael@0: if (!callVM(script->strict() ? DeletePropertyStrictInfo : DeletePropertyNonStrictInfo)) michael@0: return false; michael@0: michael@0: masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1); michael@0: frame.pop(); michael@0: frame.push(R1); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BaselineCompiler::getScopeCoordinateObject(Register reg) michael@0: { michael@0: ScopeCoordinate sc(pc); michael@0: michael@0: masm.loadPtr(frame.addressOfScopeChain(), reg); michael@0: for (unsigned i = sc.hops(); i; i--) michael@0: masm.extractObject(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg); michael@0: } michael@0: michael@0: Address michael@0: BaselineCompiler::getScopeCoordinateAddressFromObject(Register objReg, Register reg) michael@0: { michael@0: ScopeCoordinate sc(pc); michael@0: Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc); michael@0: michael@0: Address addr; michael@0: if (shape->numFixedSlots() <= sc.slot()) { michael@0: masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), reg); michael@0: return Address(reg, (sc.slot() - shape->numFixedSlots()) * sizeof(Value)); michael@0: } michael@0: michael@0: return Address(objReg, JSObject::getFixedSlotOffset(sc.slot())); michael@0: } michael@0: michael@0: Address michael@0: BaselineCompiler::getScopeCoordinateAddress(Register reg) michael@0: { michael@0: getScopeCoordinateObject(reg); michael@0: return getScopeCoordinateAddressFromObject(reg, reg); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETALIASEDVAR() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: Address address = getScopeCoordinateAddress(R0.scratchReg()); michael@0: masm.loadValue(address, R0); michael@0: michael@0: ICTypeMonitor_Fallback::Compiler compiler(cx, (ICMonitoredFallbackStub *) nullptr); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETALIASEDVAR() michael@0: { michael@0: JSScript *outerScript = ScopeCoordinateFunctionScript(script, pc); michael@0: if (outerScript && outerScript->treatAsRunOnce()) { michael@0: // Type updates for this operation might need to be tracked, so treat michael@0: // this as a SETPROP. michael@0: michael@0: // Load rhs into R1. michael@0: frame.syncStack(1); michael@0: frame.popValue(R1); michael@0: michael@0: // Load and box lhs into R0. michael@0: getScopeCoordinateObject(R2.scratchReg()); michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, R2.scratchReg(), R0); michael@0: michael@0: // Call SETPROP IC. michael@0: ICSetProp_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // The IC will return the RHS value in R0, mark it as pushed value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: // Keep rvalue in R0. michael@0: frame.popRegsAndSync(1); michael@0: Register objReg = R2.scratchReg(); michael@0: michael@0: getScopeCoordinateObject(objReg); michael@0: Address address = getScopeCoordinateAddressFromObject(objReg, R1.scratchReg()); michael@0: masm.patchableCallPreBarrier(address, MIRType_Value); michael@0: masm.storeValue(R0, address); michael@0: frame.push(R0); michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // Fully sync the stack if post-barrier is needed. michael@0: // Scope coordinate object is already in R2.scratchReg(). michael@0: frame.syncStack(0); michael@0: Register temp = R1.scratchReg(); michael@0: michael@0: Label skipBarrier; michael@0: masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier); michael@0: masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier); michael@0: michael@0: masm.call(&postBarrierSlot_); michael@0: michael@0: masm.bind(&skipBarrier); michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NAME() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: // Call IC. michael@0: ICGetName_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_BINDNAME() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: // Call IC. michael@0: ICBindName_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*DeleteNameFn)(JSContext *, HandlePropertyName, HandleObject, michael@0: MutableHandleValue); michael@0: static const VMFunction DeleteNameInfo = FunctionInfo(DeleteNameOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DELNAME() michael@0: { michael@0: frame.syncStack(0); michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: michael@0: if (!callVM(DeleteNameInfo)) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETINTRINSIC() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: ICGetIntrinsic_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject); michael@0: static const VMFunction DefVarOrConstInfo = FunctionInfo(DefVarOrConst); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEFVAR() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: unsigned attrs = JSPROP_ENUMERATE; michael@0: if (!script->isForEval()) michael@0: attrs |= JSPROP_PERMANENT; michael@0: if (JSOp(*pc) == JSOP_DEFCONST) michael@0: attrs |= JSPROP_READONLY; michael@0: JS_ASSERT(attrs <= UINT32_MAX); michael@0: michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(Imm32(attrs)); michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: michael@0: return callVM(DefVarOrConstInfo); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEFCONST() michael@0: { michael@0: return emit_JSOP_DEFVAR(); michael@0: } michael@0: michael@0: typedef bool (*SetConstFn)(JSContext *, HandlePropertyName, HandleObject, HandleValue); michael@0: static const VMFunction SetConstInfo = FunctionInfo(SetConst); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETCONST() michael@0: { michael@0: frame.popRegsAndSync(1); michael@0: frame.push(R0); michael@0: frame.syncStack(0); michael@0: michael@0: masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R0); michael@0: pushArg(R1.scratchReg()); michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: michael@0: return callVM(SetConstInfo); michael@0: } michael@0: michael@0: typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction); michael@0: static const VMFunction DefFunOperationInfo = FunctionInfo(DefFunOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEFFUN() michael@0: { michael@0: RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); michael@0: michael@0: frame.syncStack(0); michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(ImmGCPtr(fun)); michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(ImmGCPtr(script)); michael@0: michael@0: return callVM(DefFunOperationInfo); michael@0: } michael@0: michael@0: typedef bool (*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName, michael@0: HandleObject); michael@0: static const VMFunction InitPropGetterSetterInfo = michael@0: FunctionInfo(InitGetterSetterOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emitInitPropGetterSetter() michael@0: { michael@0: JS_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER || michael@0: JSOp(*pc) == JSOP_INITPROP_SETTER); michael@0: michael@0: // Keep values on the stack for the decompiler. michael@0: frame.syncStack(0); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); michael@0: masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R1.scratchReg()); michael@0: michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: pushArg(R1.scratchReg()); michael@0: pushArg(ImmPtr(pc)); michael@0: michael@0: if (!callVM(InitPropGetterSetterInfo)) michael@0: return false; michael@0: michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITPROP_GETTER() michael@0: { michael@0: return emitInitPropGetterSetter(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITPROP_SETTER() michael@0: { michael@0: return emitInitPropGetterSetter(); michael@0: } michael@0: michael@0: typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue, michael@0: HandleObject); michael@0: static const VMFunction InitElemGetterSetterInfo = michael@0: FunctionInfo(InitGetterSetterOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emitInitElemGetterSetter() michael@0: { michael@0: JS_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER || michael@0: JSOp(*pc) == JSOP_INITELEM_SETTER); michael@0: michael@0: // Load index and value in R0 and R1, but keep values on the stack for the michael@0: // decompiler. michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); michael@0: masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R1.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(R1.scratchReg()); michael@0: pushArg(R0); michael@0: masm.extractObject(frame.addressOfStackValue(frame.peek(-3)), R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: pushArg(ImmPtr(pc)); michael@0: michael@0: if (!callVM(InitElemGetterSetterInfo)) michael@0: return false; michael@0: michael@0: frame.popn(2); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITELEM_GETTER() michael@0: { michael@0: return emitInitElemGetterSetter(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INITELEM_SETTER() michael@0: { michael@0: return emitInitElemGetterSetter(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETLOCAL() michael@0: { michael@0: frame.pushLocal(GET_LOCALNO(pc)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETLOCAL() michael@0: { michael@0: // Ensure no other StackValue refers to the old value, for instance i + (i = 3). michael@0: // This also allows us to use R0 as scratch below. michael@0: frame.syncStack(1); michael@0: michael@0: uint32_t local = GET_LOCALNO(pc); michael@0: storeValue(frame.peek(-1), frame.addressOfLocal(local), R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get) michael@0: { michael@0: // Fast path: the script does not use |arguments|, or is strict. In strict michael@0: // mode, formals do not alias the arguments object. michael@0: if (!script->argumentsHasVarBinding() || script->strict()) { michael@0: if (get) { michael@0: frame.pushArg(arg); michael@0: } else { michael@0: // See the comment in emit_JSOP_SETLOCAL. michael@0: frame.syncStack(1); michael@0: storeValue(frame.peek(-1), frame.addressOfArg(arg), R0); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Sync so that we can use R0. michael@0: frame.syncStack(0); michael@0: michael@0: // If the script is known to have an arguments object, we can just use it. michael@0: // Else, we *may* have an arguments object (because we can't invalidate michael@0: // when needsArgsObj becomes |true|), so we have to test HAS_ARGS_OBJ. michael@0: Label done; michael@0: if (!script->needsArgsObj()) { michael@0: Label hasArgsObj; michael@0: masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(), michael@0: Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj); michael@0: if (get) michael@0: masm.loadValue(frame.addressOfArg(arg), R0); michael@0: else michael@0: storeValue(frame.peek(-1), frame.addressOfArg(arg), R0); michael@0: masm.jump(&done); michael@0: masm.bind(&hasArgsObj); michael@0: } michael@0: michael@0: // Load the arguments object data vector. michael@0: Register reg = R2.scratchReg(); michael@0: masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg); michael@0: masm.loadPrivate(Address(reg, ArgumentsObject::getDataSlotOffset()), reg); michael@0: michael@0: // Load/store the argument. michael@0: Address argAddr(reg, ArgumentsData::offsetOfArgs() + arg * sizeof(Value)); michael@0: if (get) { michael@0: masm.loadValue(argAddr, R0); michael@0: frame.push(R0); michael@0: } else { michael@0: masm.patchableCallPreBarrier(argAddr, MIRType_Value); michael@0: storeValue(frame.peek(-1), argAddr, R0); michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // Fully sync the stack if post-barrier is needed. michael@0: frame.syncStack(0); michael@0: Register temp = R1.scratchReg(); michael@0: michael@0: // Reload the arguments object michael@0: Register reg = R2.scratchReg(); michael@0: masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg); michael@0: michael@0: Label skipBarrier; michael@0: masm.branchPtrInNurseryRange(reg, temp, &skipBarrier); michael@0: michael@0: masm.call(&postBarrierSlot_); michael@0: michael@0: masm.bind(&skipBarrier); michael@0: #endif michael@0: } michael@0: michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GETARG() michael@0: { michael@0: uint32_t arg = GET_ARGNO(pc); michael@0: return emitFormalArgAccess(arg, /* get = */ true); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETARG() michael@0: { michael@0: // Ionmonkey can't inline functions with SETARG with magic arguments. michael@0: if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals()) michael@0: script->setUninlineable(); michael@0: michael@0: modifiesArguments_ = true; michael@0: michael@0: uint32_t arg = GET_ARGNO(pc); michael@0: return emitFormalArgAccess(arg, /* get = */ false); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emitCall() michael@0: { michael@0: JS_ASSERT(IsCallPC(pc)); michael@0: michael@0: uint32_t argc = GET_ARGC(pc); michael@0: michael@0: frame.syncStack(0); michael@0: masm.mov(ImmWord(argc), R0.scratchReg()); michael@0: michael@0: // Call IC michael@0: ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ JSOp(*pc) == JSOP_NEW); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Update FrameInfo. michael@0: frame.popn(argc + 2); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CALL() michael@0: { michael@0: return emitCall(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_NEW() michael@0: { michael@0: return emitCall(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_FUNCALL() michael@0: { michael@0: return emitCall(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_FUNAPPLY() michael@0: { michael@0: return emitCall(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_EVAL() michael@0: { michael@0: return emitCall(); michael@0: } michael@0: michael@0: typedef bool (*ImplicitThisFn)(JSContext *, HandleObject, HandlePropertyName, michael@0: MutableHandleValue); michael@0: static const VMFunction ImplicitThisInfo = FunctionInfo(ImplicitThisOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_IMPLICITTHIS() michael@0: { michael@0: frame.syncStack(0); michael@0: masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: pushArg(ImmGCPtr(script->getName(pc))); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(ImplicitThisInfo)) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_INSTANCEOF() michael@0: { michael@0: frame.popRegsAndSync(2); michael@0: michael@0: ICInstanceOf_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TYPEOF() michael@0: { michael@0: frame.popRegsAndSync(1); michael@0: michael@0: ICTypeOf_Fallback::Compiler stubCompiler(cx); michael@0: if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TYPEOFEXPR() michael@0: { michael@0: return emit_JSOP_TYPEOF(); michael@0: } michael@0: michael@0: typedef bool (*SetCallFn)(JSContext *); michael@0: static const VMFunction SetCallInfo = FunctionInfo(js::SetCallOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETCALL() michael@0: { michael@0: prepareVMCall(); michael@0: return callVM(SetCallInfo); michael@0: } michael@0: michael@0: typedef bool (*ThrowFn)(JSContext *, HandleValue); michael@0: static const VMFunction ThrowInfo = FunctionInfo(js::Throw); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_THROW() michael@0: { michael@0: // Keep value to throw in R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: prepareVMCall(); michael@0: pushArg(R0); michael@0: michael@0: return callVM(ThrowInfo); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TRY() michael@0: { michael@0: // Ionmonkey can't inline function with JSOP_TRY. michael@0: script->setUninlineable(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_FINALLY() michael@0: { michael@0: // JSOP_FINALLY has a def count of 2, but these values are already on the michael@0: // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state. michael@0: frame.setStackDepth(frame.stackDepth() + 2); michael@0: michael@0: // To match the interpreter, emit an interrupt check at the start of the michael@0: // finally block. michael@0: return emitInterruptCheck(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_GOSUB() michael@0: { michael@0: // Push |false| so that RETSUB knows the value on top of the michael@0: // stack is not an exception but the offset to the op following michael@0: // this GOSUB. michael@0: frame.push(BooleanValue(false)); michael@0: michael@0: int32_t nextOffset = script->pcToOffset(GetNextPc(pc)); michael@0: frame.push(Int32Value(nextOffset)); michael@0: michael@0: // Jump to the finally block. michael@0: frame.syncStack(0); michael@0: jsbytecode *target = pc + GET_JUMP_OFFSET(pc); michael@0: masm.jump(labelOf(target)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_RETSUB() michael@0: { michael@0: frame.popRegsAndSync(2); michael@0: michael@0: ICRetSub_Fallback::Compiler stubCompiler(cx); michael@0: return emitOpIC(stubCompiler.getStub(&stubSpace_)); michael@0: } michael@0: michael@0: typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle); michael@0: static const VMFunction PushBlockScopeInfo = FunctionInfo(jit::PushBlockScope); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE() michael@0: { michael@0: StaticBlockObject &blockObj = script->getObject(pc)->as(); michael@0: michael@0: // Call a stub to push the block on the block chain. michael@0: prepareVMCall(); michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: michael@0: pushArg(ImmGCPtr(&blockObj)); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: return callVM(PushBlockScopeInfo); michael@0: } michael@0: michael@0: typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *); michael@0: static const VMFunction PopBlockScopeInfo = FunctionInfo(jit::PopBlockScope); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_POPBLOCKSCOPE() michael@0: { michael@0: // Call a stub to pop the block from the block chain. michael@0: prepareVMCall(); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: return callVM(PopBlockScopeInfo); michael@0: } michael@0: michael@0: typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *); michael@0: static const VMFunction DebugLeaveBlockInfo = FunctionInfo(jit::DebugLeaveBlock); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK() michael@0: { michael@0: if (!debugMode_) michael@0: return true; michael@0: michael@0: prepareVMCall(); michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(ImmPtr(pc)); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: return callVM(DebugLeaveBlockInfo); michael@0: } michael@0: michael@0: typedef bool (*EnterWithFn)(JSContext *, BaselineFrame *, HandleValue, Handle); michael@0: static const VMFunction EnterWithInfo = FunctionInfo(jit::EnterWith); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ENTERWITH() michael@0: { michael@0: StaticWithObject &withObj = script->getObject(pc)->as(); michael@0: michael@0: // Pop "with" object to R0. michael@0: frame.popRegsAndSync(1); michael@0: michael@0: // Call a stub to push the object onto the scope chain. michael@0: prepareVMCall(); michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); michael@0: michael@0: pushArg(ImmGCPtr(&withObj)); michael@0: pushArg(R0); michael@0: pushArg(R1.scratchReg()); michael@0: michael@0: return callVM(EnterWithInfo); michael@0: } michael@0: michael@0: typedef bool (*LeaveWithFn)(JSContext *, BaselineFrame *); michael@0: static const VMFunction LeaveWithInfo = FunctionInfo(jit::LeaveWith); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_LEAVEWITH() michael@0: { michael@0: // Call a stub to pop the with object from the scope chain. michael@0: prepareVMCall(); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: return callVM(LeaveWithInfo); michael@0: } michael@0: michael@0: typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue); michael@0: static const VMFunction GetAndClearExceptionInfo = michael@0: FunctionInfo(GetAndClearException); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_EXCEPTION() michael@0: { michael@0: prepareVMCall(); michael@0: michael@0: if (!callVM(GetAndClearExceptionInfo)) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*OnDebuggerStatementFn)(JSContext *, BaselineFrame *, jsbytecode *pc, bool *); michael@0: static const VMFunction OnDebuggerStatementInfo = michael@0: FunctionInfo(jit::OnDebuggerStatement); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_DEBUGGER() michael@0: { michael@0: if (!debugMode_) michael@0: return true; michael@0: michael@0: prepareVMCall(); michael@0: pushArg(ImmPtr(pc)); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(OnDebuggerStatementInfo)) michael@0: return false; michael@0: michael@0: // If the stub returns |true|, return the frame's return value. michael@0: Label done; michael@0: masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done); michael@0: { michael@0: masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); michael@0: masm.jump(&return_); michael@0: } michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool); michael@0: static const VMFunction DebugEpilogueInfo = FunctionInfo(jit::DebugEpilogue); michael@0: michael@0: bool michael@0: BaselineCompiler::emitReturn() michael@0: { michael@0: if (debugMode_) { michael@0: // Move return value into the frame's rval slot. michael@0: masm.storeValue(JSReturnOperand, frame.addressOfReturnValue()); michael@0: masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags()); michael@0: michael@0: // Load BaselineFrame pointer in R0. michael@0: frame.syncStack(0); michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: michael@0: prepareVMCall(); michael@0: pushArg(Imm32(1)); michael@0: pushArg(ImmPtr(pc)); michael@0: pushArg(R0.scratchReg()); michael@0: if (!callVM(DebugEpilogueInfo)) michael@0: return false; michael@0: michael@0: // Fix up the fake ICEntry appended by callVM for on-stack recompilation. michael@0: icEntries_.back().setForDebugEpilogue(); michael@0: michael@0: masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); michael@0: } michael@0: michael@0: // Only emit the jump if this JSOP_RETRVAL is not the last instruction. michael@0: // Not needed for last instruction, because last instruction flows michael@0: // into return label. michael@0: if (pc + GetBytecodeLength(pc) < script->codeEnd()) michael@0: masm.jump(&return_); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_RETURN() michael@0: { michael@0: JS_ASSERT(frame.stackDepth() == 1); michael@0: michael@0: frame.popValue(JSReturnOperand); michael@0: return emitReturn(); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_RETRVAL() michael@0: { michael@0: JS_ASSERT(frame.stackDepth() == 0); michael@0: michael@0: masm.moveValue(UndefinedValue(), JSReturnOperand); michael@0: michael@0: if (!script->noScriptRval()) { michael@0: // Return the value in the return value slot, if any. michael@0: Label done; michael@0: Address flags = frame.addressOfFlags(); michael@0: masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done); michael@0: masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); michael@0: masm.bind(&done); michael@0: } michael@0: michael@0: return emitReturn(); michael@0: } michael@0: michael@0: typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue, michael@0: MutableHandleValue); michael@0: static const VMFunction ToIdInfo = FunctionInfo(js::ToIdOperation); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TOID() michael@0: { michael@0: // Load index in R0, but keep values on the stack for the decompiler. michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); michael@0: michael@0: // No-op if index is int32. michael@0: Label done; michael@0: masm.branchTestInt32(Assembler::Equal, R0, &done); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1); michael@0: michael@0: pushArg(R0); michael@0: pushArg(R1); michael@0: pushArg(ImmPtr(pc)); michael@0: pushArg(ImmGCPtr(script)); michael@0: michael@0: if (!callVM(ToIdInfo)) michael@0: return false; michael@0: michael@0: masm.bind(&done); michael@0: frame.pop(); // Pop index. michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_TABLESWITCH() michael@0: { michael@0: frame.popRegsAndSync(1); michael@0: michael@0: // Call IC. michael@0: ICTableSwitch::Compiler compiler(cx, pc); michael@0: return emitOpIC(compiler.getStub(&stubSpace_)); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ITER() michael@0: { michael@0: frame.popRegsAndSync(1); michael@0: michael@0: ICIteratorNew_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_MOREITER() michael@0: { michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); michael@0: michael@0: ICIteratorMore_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ITERNEXT() michael@0: { michael@0: frame.syncStack(0); michael@0: masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); michael@0: michael@0: ICIteratorNext_Fallback::Compiler compiler(cx); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ENDITER() michael@0: { michael@0: frame.popRegsAndSync(1); michael@0: michael@0: ICIteratorClose_Fallback::Compiler compiler(cx); michael@0: return emitOpIC(compiler.getStub(&stubSpace_)); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_SETRVAL() michael@0: { michael@0: // Store to the frame's return value slot. michael@0: storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2); michael@0: masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags()); michael@0: frame.pop(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_CALLEE() michael@0: { michael@0: JS_ASSERT(function()); michael@0: frame.syncStack(0); michael@0: masm.loadPtr(frame.addressOfCallee(), R0.scratchReg()); michael@0: masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*NewArgumentsObjectFn)(JSContext *, BaselineFrame *, MutableHandleValue); michael@0: static const VMFunction NewArgumentsObjectInfo = michael@0: FunctionInfo(jit::NewArgumentsObject); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_ARGUMENTS() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: Label done; michael@0: if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) { michael@0: // We assume the script does not need an arguments object. However, this michael@0: // assumption can be invalidated later, see argumentsOptimizationFailed michael@0: // in JSScript. Because we can't invalidate baseline JIT code, we set a michael@0: // flag on BaselineScript when that happens and guard on it here. michael@0: masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0); michael@0: michael@0: // Load script->baseline. michael@0: Register scratch = R1.scratchReg(); michael@0: masm.movePtr(ImmGCPtr(script), scratch); michael@0: masm.loadPtr(Address(scratch, JSScript::offsetOfBaselineScript()), scratch); michael@0: michael@0: // If we don't need an arguments object, skip the VM call. michael@0: masm.branchTest32(Assembler::Zero, Address(scratch, BaselineScript::offsetOfFlags()), michael@0: Imm32(BaselineScript::NEEDS_ARGS_OBJ), &done); michael@0: } michael@0: michael@0: prepareVMCall(); michael@0: michael@0: masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: if (!callVM(NewArgumentsObjectInfo)) michael@0: return false; michael@0: michael@0: masm.bind(&done); michael@0: frame.push(R0); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript); michael@0: static const VMFunction RunOnceScriptPrologueInfo = michael@0: FunctionInfo(js::RunOnceScriptPrologue); michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_RUNONCE() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: prepareVMCall(); michael@0: michael@0: masm.movePtr(ImmGCPtr(script), R0.scratchReg()); michael@0: pushArg(R0.scratchReg()); michael@0: michael@0: return callVM(RunOnceScriptPrologueInfo); michael@0: } michael@0: michael@0: bool michael@0: BaselineCompiler::emit_JSOP_REST() michael@0: { michael@0: frame.syncStack(0); michael@0: michael@0: JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); michael@0: if (!templateObject) michael@0: return false; michael@0: types::FixRestArgumentsType(cx, templateObject); michael@0: michael@0: // Call IC. michael@0: ICRest_Fallback::Compiler compiler(cx, templateObject); michael@0: if (!emitOpIC(compiler.getStub(&stubSpace_))) michael@0: return false; michael@0: michael@0: // Mark R0 as pushed stack value. michael@0: frame.push(R0); michael@0: return true; michael@0: }