js/src/jit/BaselineCompiler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial