js/src/jit/shared/CodeGenerator-shared.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/shared/CodeGenerator-shared-inl.h"
     9 #include "mozilla/DebugOnly.h"
    11 #include "jit/IonCaches.h"
    12 #include "jit/IonMacroAssembler.h"
    13 #include "jit/IonSpewer.h"
    14 #include "jit/MIR.h"
    15 #include "jit/MIRGenerator.h"
    16 #include "jit/ParallelFunctions.h"
    17 #include "vm/TraceLogging.h"
    19 #include "jit/IonFrames-inl.h"
    21 using namespace js;
    22 using namespace js::jit;
    24 using mozilla::DebugOnly;
    26 namespace js {
    27 namespace jit {
    29 MacroAssembler &
    30 CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg)
    31 {
    32     if (masmArg)
    33         return *masmArg;
    34     maybeMasm_.construct();
    35     return maybeMasm_.ref();
    36 }
    38 CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg)
    39   : oolIns(nullptr),
    40     maybeMasm_(),
    41     masm(ensureMasm(masmArg)),
    42     gen(gen),
    43     graph(*graph),
    44     current(nullptr),
    45     snapshots_(),
    46     recovers_(),
    47     deoptTable_(nullptr),
    48 #ifdef DEBUG
    49     pushedArgs_(0),
    50 #endif
    51     lastOsiPointOffset_(0),
    52     sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
    53     osrEntryOffset_(0),
    54     skipArgCheckEntryOffset_(0),
    55     frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize())
    56 {
    57     if (!gen->compilingAsmJS())
    58         masm.setInstrumentation(&sps_);
    60     // Since asm.js uses the system ABI which does not necessarily use a
    61     // regular array where all slots are sizeof(Value), it maintains the max
    62     // argument stack depth separately.
    63     if (gen->compilingAsmJS()) {
    64         JS_ASSERT(graph->argumentSlotCount() == 0);
    65         frameDepth_ += gen->maxAsmJSStackArgBytes();
    67         // An MAsmJSCall does not align the stack pointer at calls sites but instead
    68         // relies on the a priori stack adjustment (in the prologue) on platforms
    69         // (like x64) which require the stack to be aligned.
    70 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
    71         bool forceAlign = true;
    72 #else
    73         bool forceAlign = false;
    74 #endif
    75         if (gen->performsAsmJSCall() || forceAlign) {
    76             unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
    77             if (unsigned rem = alignmentAtCall % StackAlignment)
    78                 frameDepth_ += StackAlignment - rem;
    79         }
    81         // FrameSizeClass is only used for bailing, which cannot happen in
    82         // asm.js code.
    83         frameClass_ = FrameSizeClass::None();
    84     } else {
    85         frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
    86     }
    87 }
    89 bool
    90 CodeGeneratorShared::generateOutOfLineCode()
    91 {
    92     for (size_t i = 0; i < outOfLineCode_.length(); i++) {
    93         if (!gen->alloc().ensureBallast())
    94             return false;
    95         masm.setFramePushed(outOfLineCode_[i]->framePushed());
    96         lastPC_ = outOfLineCode_[i]->pc();
    97         if (!sps_.prepareForOOL())
    98             return false;
    99         sps_.setPushed(outOfLineCode_[i]->script());
   100         outOfLineCode_[i]->bind(&masm);
   102         oolIns = outOfLineCode_[i];
   103         if (!outOfLineCode_[i]->generate(this))
   104             return false;
   105         sps_.finishOOL();
   106     }
   107     oolIns = nullptr;
   109     return true;
   110 }
   112 bool
   113 CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code)
   114 {
   115     code->setFramePushed(masm.framePushed());
   116     // If an OOL instruction adds another OOL instruction, then use the original
   117     // instruction's script/pc instead of the basic block's that we're on
   118     // because they're probably not relevant any more.
   119     if (oolIns)
   120         code->setSource(oolIns->script(), oolIns->pc());
   121     else
   122         code->setSource(current ? current->mir()->info().script() : nullptr, lastPC_);
   123     JS_ASSERT_IF(code->script(), code->script()->containsPC(code->pc()));
   124     return outOfLineCode_.append(code);
   125 }
   127 // see OffsetOfFrameSlot
   128 static inline int32_t
   129 ToStackIndex(LAllocation *a)
   130 {
   131     if (a->isStackSlot()) {
   132         JS_ASSERT(a->toStackSlot()->slot() >= 1);
   133         return a->toStackSlot()->slot();
   134     }
   135     JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index());
   136     return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index());
   137 }
   139 bool
   140 CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint,
   141                                        uint32_t *startIndex)
   142 {
   143     IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
   144             resumePoint->numOperands(), (void *) resumePoint, *startIndex);
   145     for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) {
   146         uint32_t i = allocno + *startIndex;
   147         MDefinition *mir = resumePoint->getOperand(allocno);
   149         if (mir->isBox())
   150             mir = mir->toBox()->getOperand(0);
   152         MIRType type = mir->isUnused()
   153                        ? MIRType_MagicOptimizedOut
   154                        : mir->type();
   156         RValueAllocation alloc;
   158         switch (type) {
   159           case MIRType_Undefined:
   160             alloc = RValueAllocation::Undefined();
   161             break;
   162           case MIRType_Null:
   163             alloc = RValueAllocation::Null();
   164             break;
   165           case MIRType_Int32:
   166           case MIRType_String:
   167           case MIRType_Object:
   168           case MIRType_Boolean:
   169           case MIRType_Double:
   170           case MIRType_Float32:
   171           {
   172             LAllocation *payload = snapshot->payloadOfSlot(i);
   173             JSValueType valueType = ValueTypeFromMIRType(type);
   174             if (payload->isMemory()) {
   175                 if (type == MIRType_Float32)
   176                     alloc = RValueAllocation::Float32(ToStackIndex(payload));
   177                 else
   178                     alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
   179             } else if (payload->isGeneralReg()) {
   180                 alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
   181             } else if (payload->isFloatReg()) {
   182                 FloatRegister reg = ToFloatRegister(payload);
   183                 if (type == MIRType_Float32)
   184                     alloc = RValueAllocation::Float32(reg);
   185                 else
   186                     alloc = RValueAllocation::Double(reg);
   187             } else {
   188                 MConstant *constant = mir->toConstant();
   189                 uint32_t index;
   190                 if (!graph.addConstantToPool(constant->value(), &index))
   191                     return false;
   192                 alloc = RValueAllocation::ConstantPool(index);
   193             }
   194             break;
   195           }
   196           case MIRType_MagicOptimizedArguments:
   197           case MIRType_MagicOptimizedOut:
   198           {
   199             uint32_t index;
   200             JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
   201                               ? JS_OPTIMIZED_ARGUMENTS
   202                               : JS_OPTIMIZED_OUT);
   203             Value v = MagicValue(why);
   204             if (!graph.addConstantToPool(v, &index))
   205                 return false;
   206             alloc = RValueAllocation::ConstantPool(index);
   207             break;
   208           }
   209           default:
   210           {
   211             JS_ASSERT(mir->type() == MIRType_Value);
   212             LAllocation *payload = snapshot->payloadOfSlot(i);
   213 #ifdef JS_NUNBOX32
   214             LAllocation *type = snapshot->typeOfSlot(i);
   215             if (type->isRegister()) {
   216                 if (payload->isRegister())
   217                     alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
   218                 else
   219                     alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload));
   220             } else {
   221                 if (payload->isRegister())
   222                     alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload));
   223                 else
   224                     alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload));
   225             }
   226 #elif JS_PUNBOX64
   227             if (payload->isRegister())
   228                 alloc = RValueAllocation::Untyped(ToRegister(payload));
   229             else
   230                 alloc = RValueAllocation::Untyped(ToStackIndex(payload));
   231 #endif
   232             break;
   233           }
   234         }
   236         snapshots_.add(alloc);
   237     }
   239     *startIndex += resumePoint->numOperands();
   240     return true;
   241 }
   243 bool
   244 CodeGeneratorShared::encode(LRecoverInfo *recover)
   245 {
   246     if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
   247         return true;
   249     uint32_t frameCount = recover->mir()->frameCount();
   250     IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)",
   251             (void *)recover, frameCount);
   253     MResumePoint::Mode mode = recover->mir()->mode();
   254     JS_ASSERT(mode != MResumePoint::Outer);
   255     bool resumeAfter = (mode == MResumePoint::ResumeAfter);
   257     RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter);
   259     for (MResumePoint **it = recover->begin(), **end = recover->end();
   260          it != end;
   261          ++it)
   262     {
   263         if (!recovers_.writeFrame(*it))
   264             return false;
   265     }
   267     recovers_.endRecover();
   268     recover->setRecoverOffset(offset);
   269     return !recovers_.oom();
   270 }
   272 bool
   273 CodeGeneratorShared::encode(LSnapshot *snapshot)
   274 {
   275     if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET)
   276         return true;
   278     LRecoverInfo *recoverInfo = snapshot->recoverInfo();
   279     if (!encode(recoverInfo))
   280         return false;
   282     RecoverOffset recoverOffset = recoverInfo->recoverOffset();
   283     MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET);
   285     IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)",
   286             (void *)snapshot, (void*) recoverInfo);
   288     SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind());
   290 #ifdef TRACK_SNAPSHOTS
   291     uint32_t pcOpcode = 0;
   292     uint32_t lirOpcode = 0;
   293     uint32_t lirId = 0;
   294     uint32_t mirOpcode = 0;
   295     uint32_t mirId = 0;
   297     if (LInstruction *ins = instruction()) {
   298         lirOpcode = ins->op();
   299         lirId = ins->id();
   300         if (ins->mirRaw()) {
   301             mirOpcode = ins->mirRaw()->op();
   302             mirId = ins->mirRaw()->id();
   303             if (ins->mirRaw()->trackedPc())
   304                 pcOpcode = *ins->mirRaw()->trackedPc();
   305         }
   306     }
   307     snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
   308 #endif
   310     uint32_t startIndex = 0;
   311     for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
   312          it != end;
   313          ++it)
   314     {
   315         MResumePoint *mir = *it;
   316         if (!encodeAllocations(snapshot, mir, &startIndex))
   317             return false;
   318     }
   320     MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots());
   321     snapshots_.endSnapshot();
   322     snapshot->setSnapshotOffset(offset);
   323     return !snapshots_.oom();
   324 }
   326 bool
   327 CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
   328 {
   329     JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET);
   331     // Can we not use bailout tables at all?
   332     if (!deoptTable_)
   333         return false;
   335     JS_ASSERT(frameClass_ != FrameSizeClass::None());
   337     if (snapshot->bailoutId() != INVALID_BAILOUT_ID)
   338         return true;
   340     // Is the bailout table full?
   341     if (bailouts_.length() >= BAILOUT_TABLE_SIZE)
   342         return false;
   344     unsigned bailoutId = bailouts_.length();
   345     snapshot->setBailoutId(bailoutId);
   346     IonSpew(IonSpew_Snapshots, "Assigned snapshot bailout id %u", bailoutId);
   347     return bailouts_.append(snapshot->snapshotOffset());
   348 }
   350 void
   351 CodeGeneratorShared::encodeSafepoints()
   352 {
   353     for (SafepointIndex *it = safepointIndices_.begin(), *end = safepointIndices_.end();
   354          it != end;
   355          ++it)
   356     {
   357         LSafepoint *safepoint = it->safepoint();
   359         if (!safepoint->encoded()) {
   360             safepoint->fixupOffset(&masm);
   361             safepoints_.encode(safepoint);
   362         }
   364         it->resolve();
   365     }
   366 }
   368 bool
   369 CodeGeneratorShared::markSafepoint(LInstruction *ins)
   370 {
   371     return markSafepointAt(masm.currentOffset(), ins);
   372 }
   374 bool
   375 CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction *ins)
   376 {
   377     JS_ASSERT_IF(!safepointIndices_.empty(),
   378                  offset - safepointIndices_.back().displacement() >= sizeof(uint32_t));
   379     return safepointIndices_.append(SafepointIndex(offset, ins->safepoint()));
   380 }
   382 void
   383 CodeGeneratorShared::ensureOsiSpace()
   384 {
   385     // For a refresher, an invalidation point is of the form:
   386     // 1: call <target>
   387     // 2: ...
   388     // 3: <osipoint>
   389     //
   390     // The four bytes *before* instruction 2 are overwritten with an offset.
   391     // Callers must ensure that the instruction itself has enough bytes to
   392     // support this.
   393     //
   394     // The bytes *at* instruction 3 are overwritten with an invalidation jump.
   395     // jump. These bytes may be in a completely different IR sequence, but
   396     // represent the join point of the call out of the function.
   397     //
   398     // At points where we want to ensure that invalidation won't corrupt an
   399     // important instruction, we make sure to pad with nops.
   400     if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::patchWrite_NearCallSize()) {
   401         int32_t paddingSize = Assembler::patchWrite_NearCallSize();
   402         paddingSize -= masm.currentOffset() - lastOsiPointOffset_;
   403         for (int32_t i = 0; i < paddingSize; ++i)
   404             masm.nop();
   405     }
   406     JS_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::patchWrite_NearCallSize());
   407     lastOsiPointOffset_ = masm.currentOffset();
   408 }
   410 bool
   411 CodeGeneratorShared::markOsiPoint(LOsiPoint *ins, uint32_t *callPointOffset)
   412 {
   413     if (!encode(ins->snapshot()))
   414         return false;
   416     ensureOsiSpace();
   418     *callPointOffset = masm.currentOffset();
   419     SnapshotOffset so = ins->snapshot()->snapshotOffset();
   420     return osiIndices_.append(OsiIndex(*callPointOffset, so));
   421 }
   423 #ifdef CHECK_OSIPOINT_REGISTERS
   424 template <class Op>
   425 static void
   426 HandleRegisterDump(Op op, MacroAssembler &masm, RegisterSet liveRegs, Register activation,
   427                    Register scratch)
   428 {
   429     const size_t baseOffset = JitActivation::offsetOfRegs();
   431     // Handle live GPRs.
   432     for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); iter++) {
   433         Register reg = *iter;
   434         Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
   436         if (reg == activation) {
   437             // To use the original value of the activation register (that's
   438             // now on top of the stack), we need the scratch register.
   439             masm.push(scratch);
   440             masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch);
   441             op(scratch, dump);
   442             masm.pop(scratch);
   443         } else {
   444             op(reg, dump);
   445         }
   446     }
   448     // Handle live FPRs.
   449     for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); iter++) {
   450         FloatRegister reg = *iter;
   451         Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
   452         op(reg, dump);
   453     }
   454 }
   456 class StoreOp
   457 {
   458     MacroAssembler &masm;
   460   public:
   461     StoreOp(MacroAssembler &masm)
   462       : masm(masm)
   463     {}
   465     void operator()(Register reg, Address dump) {
   466         masm.storePtr(reg, dump);
   467     }
   468     void operator()(FloatRegister reg, Address dump) {
   469         masm.storeDouble(reg, dump);
   470     }
   471 };
   473 static void
   474 StoreAllLiveRegs(MacroAssembler &masm, RegisterSet liveRegs)
   475 {
   476     // Store a copy of all live registers before performing the call.
   477     // When we reach the OsiPoint, we can use this to check nothing
   478     // modified them in the meantime.
   480     // Load pointer to the JitActivation in a scratch register.
   481     GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   482     Register scratch = allRegs.takeAny();
   483     masm.push(scratch);
   484     masm.loadJitActivation(scratch);
   486     Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   487     masm.add32(Imm32(1), checkRegs);
   489     StoreOp op(masm);
   490     HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
   492     masm.pop(scratch);
   493 }
   495 class VerifyOp
   496 {
   497     MacroAssembler &masm;
   498     Label *failure_;
   500   public:
   501     VerifyOp(MacroAssembler &masm, Label *failure)
   502       : masm(masm), failure_(failure)
   503     {}
   505     void operator()(Register reg, Address dump) {
   506         masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
   507     }
   508     void operator()(FloatRegister reg, Address dump) {
   509         masm.loadDouble(dump, ScratchFloatReg);
   510         masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_);
   511     }
   512 };
   514 static void
   515 OsiPointRegisterCheckFailed()
   516 {
   517     // Any live register captured by a safepoint (other than temp registers)
   518     // must remain unchanged between the call and the OsiPoint instruction.
   519     MOZ_ASSUME_UNREACHABLE("Modified registers between VM call and OsiPoint");
   520 }
   522 void
   523 CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint)
   524 {
   525     // Ensure the live registers stored by callVM did not change between
   526     // the call and this OsiPoint. Try-catch relies on this invariant.
   528     // Load pointer to the JitActivation in a scratch register.
   529     GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   530     Register scratch = allRegs.takeAny();
   531     masm.push(scratch);
   532     masm.loadJitActivation(scratch);
   534     // If we should not check registers (because the instruction did not call
   535     // into the VM, or a GC happened), we're done.
   536     Label failure, done;
   537     Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   538     masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
   540     // Having more than one VM function call made in one visit function at
   541     // runtime is a sec-ciritcal error, because if we conservatively assume that
   542     // one of the function call can re-enter Ion, then the invalidation process
   543     // will potentially add a call at a random location, by patching the code
   544     // before the return address.
   545     masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
   547     // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
   548     // temps after calling into the VM. This is fine because no other
   549     // instructions (including this OsiPoint) will depend on them. Also
   550     // backtracking can also use the same register for an input and an output.
   551     // These are marked as clobbered and shouldn't get checked.
   552     RegisterSet liveRegs = safepoint->liveRegs();
   553     liveRegs = RegisterSet::Intersect(liveRegs, RegisterSet::Not(safepoint->clobberedRegs()));
   555     VerifyOp op(masm, &failure);
   556     HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
   558     masm.jump(&done);
   560     // Do not profile the callWithABI that occurs below.  This is to avoid a
   561     // rare corner case that occurs when profiling interacts with itself:
   562     //
   563     // When slow profiling assertions are turned on, FunctionBoundary ops
   564     // (which update the profiler pseudo-stack) may emit a callVM, which
   565     // forces them to have an osi point associated with them.  The
   566     // FunctionBoundary for inline function entry is added to the caller's
   567     // graph with a PC from the caller's code, but during codegen it modifies
   568     // SPS instrumentation to add the callee as the current top-most script.
   569     // When codegen gets to the OSIPoint, and the callWithABI below is
   570     // emitted, the codegen thinks that the current frame is the callee, but
   571     // the PC it's using from the OSIPoint refers to the caller.  This causes
   572     // the profiler instrumentation of the callWithABI below to ASSERT, since
   573     // the script and pc are mismatched.  To avoid this, we simply omit
   574     // instrumentation for these callWithABIs.
   575     masm.bind(&failure);
   576     masm.setupUnalignedABICall(0, scratch);
   577     masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, OsiPointRegisterCheckFailed));
   578     masm.breakpoint();
   580     masm.bind(&done);
   581     masm.pop(scratch);
   582 }
   584 bool
   585 CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint)
   586 {
   587     if (!js_JitOptions.checkOsiPointRegisters)
   588         return false;
   590     if (gen->info().executionMode() != SequentialExecution)
   591         return false;
   593     if (safepoint->liveRegs().empty(true) && safepoint->liveRegs().empty(false))
   594         return false; // No registers to check.
   596     return true;
   597 }
   599 void
   600 CodeGeneratorShared::resetOsiPointRegs(LSafepoint *safepoint)
   601 {
   602     if (!shouldVerifyOsiPointRegs(safepoint))
   603         return;
   605     // Set checkRegs to 0. If we perform a VM call, the instruction
   606     // will set it to 1.
   607     GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   608     Register scratch = allRegs.takeAny();
   609     masm.push(scratch);
   610     masm.loadJitActivation(scratch);
   611     Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   612     masm.store32(Imm32(0), checkRegs);
   613     masm.pop(scratch);
   614 }
   615 #endif
   617 // Before doing any call to Cpp, you should ensure that volatile
   618 // registers are evicted by the register allocator.
   619 bool
   620 CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack)
   621 {
   622     // Different execution modes have different sets of VM functions.
   623     JS_ASSERT(fun.executionMode == gen->info().executionMode());
   625     // If we're calling a function with an out parameter type of double, make
   626     // sure we have an FPU.
   627     JS_ASSERT_IF(fun.outParam == Type_Double, GetIonContext()->runtime->jitSupportsFloatingPoint());
   629 #ifdef DEBUG
   630     if (ins->mirRaw()) {
   631         JS_ASSERT(ins->mirRaw()->isInstruction());
   632         MInstruction *mir = ins->mirRaw()->toInstruction();
   633         JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint());
   634     }
   635 #endif
   637 #ifdef JS_TRACE_LOGGING
   638     if (!emitTracelogStartEvent(TraceLogger::VM))
   639         return false;
   640 #endif
   642     // Stack is:
   643     //    ... frame ...
   644     //    [args]
   645 #ifdef DEBUG
   646     JS_ASSERT(pushedArgs_ == fun.explicitArgs);
   647     pushedArgs_ = 0;
   648 #endif
   650     // Get the wrapper of the VM function.
   651     JitCode *wrapper = gen->jitRuntime()->getVMWrapper(fun);
   652     if (!wrapper)
   653         return false;
   655 #ifdef CHECK_OSIPOINT_REGISTERS
   656     if (shouldVerifyOsiPointRegs(ins->safepoint()))
   657         StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
   658 #endif
   660     // Call the wrapper function.  The wrapper is in charge to unwind the stack
   661     // when returning from the call.  Failures are handled with exceptions based
   662     // on the return value of the C functions.  To guard the outcome of the
   663     // returned value, use another LIR instruction.
   664     uint32_t callOffset;
   665     if (dynStack)
   666         callOffset = masm.callWithExitFrame(wrapper, *dynStack);
   667     else
   668         callOffset = masm.callWithExitFrame(wrapper);
   670     if (!markSafepointAt(callOffset, ins))
   671         return false;
   673     // Remove rest of the frame left on the stack. We remove the return address
   674     // which is implicitly poped when returning.
   675     int framePop = sizeof(IonExitFrameLayout) - sizeof(void*);
   677     // Pop arguments from framePushed.
   678     masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
   679     // Stack is:
   680     //    ... frame ...
   682 #ifdef JS_TRACE_LOGGING
   683     if (!emitTracelogStopEvent(TraceLogger::VM))
   684         return false;
   685 #endif
   687     return true;
   688 }
   690 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
   691 {
   692     FloatRegister src_;
   693     Register dest_;
   694     bool needFloat32Conversion_;
   696   public:
   697     OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false)
   698       : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion)
   699     { }
   701     bool accept(CodeGeneratorShared *codegen) {
   702         return codegen->visitOutOfLineTruncateSlow(this);
   703     }
   704     FloatRegister src() const {
   705         return src_;
   706     }
   707     Register dest() const {
   708         return dest_;
   709     }
   710     bool needFloat32Conversion() const {
   711         return needFloat32Conversion_;
   712     }
   714 };
   716 OutOfLineCode *
   717 CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest)
   718 {
   719     OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest);
   720     if (!addOutOfLineCode(ool))
   721         return nullptr;
   722     return ool;
   723 }
   725 bool
   726 CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register &dest)
   727 {
   728     OutOfLineCode *ool = oolTruncateDouble(src, dest);
   729     if (!ool)
   730         return false;
   732     masm.branchTruncateDouble(src, dest, ool->entry());
   733     masm.bind(ool->rejoin());
   734     return true;
   735 }
   737 bool
   738 CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest)
   739 {
   740     OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true);
   741     if (!addOutOfLineCode(ool))
   742         return false;
   744     masm.branchTruncateFloat32(src, dest, ool->entry());
   745     masm.bind(ool->rejoin());
   746     return true;
   747 }
   749 bool
   750 CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool)
   751 {
   752     FloatRegister src = ool->src();
   753     Register dest = ool->dest();
   755     saveVolatile(dest);
   757     if (ool->needFloat32Conversion()) {
   758         masm.push(src);
   759         masm.convertFloat32ToDouble(src, src);
   760     }
   762     masm.setupUnalignedABICall(1, dest);
   763     masm.passABIArg(src, MoveOp::DOUBLE);
   764     if (gen->compilingAsmJS())
   765         masm.callWithABI(AsmJSImm_ToInt32);
   766     else
   767         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
   768     masm.storeCallResult(dest);
   770     if (ool->needFloat32Conversion())
   771         masm.pop(src);
   773     restoreVolatile(dest);
   775     masm.jump(ool->rejoin());
   776     return true;
   777 }
   779 bool
   780 CodeGeneratorShared::omitOverRecursedCheck() const
   781 {
   782     // If the current function makes no calls (which means it isn't recursive)
   783     // and it uses only a small amount of stack space, it doesn't need a
   784     // stack overflow check. Note that the actual number here is somewhat
   785     // arbitrary, and codegen actually uses small bounded amounts of
   786     // additional stack space in some cases too.
   787     return frameSize() < 64 && !gen->performsCall();
   788 }
   790 void
   791 CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type)
   792 {
   793     if (index->isConstant()) {
   794         Address address(base, ToInt32(index) * sizeof(Value));
   795         masm.patchableCallPreBarrier(address, type);
   796     } else {
   797         BaseIndex address(base, ToRegister(index), TimesEight);
   798         masm.patchableCallPreBarrier(address, type);
   799     }
   800 }
   802 void
   803 CodeGeneratorShared::emitPreBarrier(Address address, MIRType type)
   804 {
   805     masm.patchableCallPreBarrier(address, type);
   806 }
   808 void
   809 CodeGeneratorShared::dropArguments(unsigned argc)
   810 {
   811     pushedArgumentSlots_.shrinkBy(argc);
   812 }
   814 bool
   815 CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
   816 {
   817     for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) {
   818         if (!safepoint->addValueSlot(pushedArgumentSlots_[i]))
   819             return false;
   820     }
   821     return true;
   822 }
   824 OutOfLineAbortPar *
   825 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
   826                                  jsbytecode *bytecode)
   827 {
   828     OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode);
   829     if (!ool || !addOutOfLineCode(ool))
   830         return nullptr;
   831     return ool;
   832 }
   834 OutOfLineAbortPar *
   835 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir)
   836 {
   837     MDefinition *mir = lir->mirRaw();
   838     MBasicBlock *block = mir->block();
   839     jsbytecode *pc = mir->trackedPc();
   840     if (!pc) {
   841         if (lir->snapshot())
   842             pc = lir->snapshot()->mir()->pc();
   843         else
   844             pc = block->pc();
   845     }
   846     return oolAbortPar(cause, block, pc);
   847 }
   849 OutOfLinePropagateAbortPar *
   850 CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir)
   851 {
   852     OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir);
   853     if (!ool || !addOutOfLineCode(ool))
   854         return nullptr;
   855     return ool;
   856 }
   858 bool
   859 OutOfLineAbortPar::generate(CodeGeneratorShared *codegen)
   860 {
   861     codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
   862     return codegen->visitOutOfLineAbortPar(this);
   863 }
   865 bool
   866 OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen)
   867 {
   868     codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
   869     return codegen->visitOutOfLinePropagateAbortPar(this);
   870 }
   872 bool
   873 CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
   874                                   const char *bailoutName)
   875 {
   876     JS_ASSERT_IF(!lir, bailoutName);
   878     if (!IonSpewEnabled(IonSpew_Trace))
   879         return true;
   881     uint32_t execMode = (uint32_t) gen->info().executionMode();
   882     uint32_t lirIndex;
   883     const char *lirOpName;
   884     const char *mirOpName;
   885     JSScript *script;
   886     jsbytecode *pc;
   888     masm.PushRegsInMask(RegisterSet::Volatile());
   889     masm.reserveStack(sizeof(IonLIRTraceData));
   891     // This first move is here so that when you scan the disassembly,
   892     // you can easily pick out where each instruction begins.  The
   893     // next few items indicate to you the Basic Block / LIR.
   894     masm.move32(Imm32(0xDEADBEEF), CallTempReg0);
   896     if (lir) {
   897         lirIndex = lir->id();
   898         lirOpName = lir->opName();
   899         if (MDefinition *mir = lir->mirRaw()) {
   900             mirOpName = mir->opName();
   901             script = mir->block()->info().script();
   902             pc = mir->trackedPc();
   903         } else {
   904             mirOpName = nullptr;
   905             script = nullptr;
   906             pc = nullptr;
   907         }
   908     } else {
   909         blockIndex = lirIndex = 0xDEADBEEF;
   910         lirOpName = mirOpName = bailoutName;
   911         script = nullptr;
   912         pc = nullptr;
   913     }
   915     masm.store32(Imm32(blockIndex),
   916                  Address(StackPointer, offsetof(IonLIRTraceData, blockIndex)));
   917     masm.store32(Imm32(lirIndex),
   918                  Address(StackPointer, offsetof(IonLIRTraceData, lirIndex)));
   919     masm.store32(Imm32(execMode),
   920                  Address(StackPointer, offsetof(IonLIRTraceData, execModeInt)));
   921     masm.storePtr(ImmPtr(lirOpName),
   922                   Address(StackPointer, offsetof(IonLIRTraceData, lirOpName)));
   923     masm.storePtr(ImmPtr(mirOpName),
   924                   Address(StackPointer, offsetof(IonLIRTraceData, mirOpName)));
   925     masm.storePtr(ImmGCPtr(script),
   926                   Address(StackPointer, offsetof(IonLIRTraceData, script)));
   927     masm.storePtr(ImmPtr(pc),
   928                   Address(StackPointer, offsetof(IonLIRTraceData, pc)));
   930     masm.movePtr(StackPointer, CallTempReg0);
   931     masm.setupUnalignedABICall(1, CallTempReg1);
   932     masm.passABIArg(CallTempReg0);
   933     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR));
   935     masm.freeStack(sizeof(IonLIRTraceData));
   936     masm.PopRegsInMask(RegisterSet::Volatile());
   938     return true;
   939 }
   941 typedef bool (*InterruptCheckFn)(JSContext *);
   942 const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
   944 Label *
   945 CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
   946 {
   947     // If this is a loop backedge to a loop header with an implicit interrupt
   948     // check, use a patchable jump. Skip this search if compiling without a
   949     // script for asm.js, as there will be no interrupt check instruction.
   950     // Due to critical edge unsplitting there may no longer be unique loop
   951     // backedges, so just look for any edge going to an earlier block in RPO.
   952     if (!gen->compilingAsmJS() && mir->isLoopHeader() && mir->id() <= current->mir()->id()) {
   953         for (LInstructionIterator iter = mir->lir()->begin(); iter != mir->lir()->end(); iter++) {
   954             if (iter->isLabel() || iter->isMoveGroup()) {
   955                 // Continue searching for an interrupt check.
   956             } else if (iter->isInterruptCheckImplicit()) {
   957                 return iter->toInterruptCheckImplicit()->oolEntry();
   958             } else {
   959                 // The interrupt check should be the first instruction in the
   960                 // loop header other than the initial label and move groups.
   961                 JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar());
   962                 return nullptr;
   963             }
   964         }
   965     }
   967     return nullptr;
   968 }
   970 void
   971 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir)
   972 {
   973     // No jump necessary if we can fall through to the next block.
   974     if (isNextBlock(mir->lir()))
   975         return;
   977     if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
   978         // Note: the backedge is initially a jump to the next instruction.
   979         // It will be patched to the target block's label during link().
   980         RepatchLabel rejoin;
   981         CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin);
   982         masm.bind(&rejoin);
   984         masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
   985     } else {
   986         masm.jump(mir->lir()->label());
   987     }
   988 }
   990 // This function is not used for MIPS. MIPS has branchToBlock.
   991 #ifndef JS_CODEGEN_MIPS
   992 void
   993 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir, Assembler::Condition cond)
   994 {
   995     if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
   996         // Note: the backedge is initially a jump to the next instruction.
   997         // It will be patched to the target block's label during link().
   998         RepatchLabel rejoin;
   999         CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin, cond);
  1000         masm.bind(&rejoin);
  1002         masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
  1003     } else {
  1004         masm.j(cond, mir->lir()->label());
  1007 #endif
  1009 size_t
  1010 CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *numLocs)
  1012     size_t firstIndex = runtimeData_.length();
  1013     size_t numLocations = 0;
  1014     for (CacheLocationList::iterator iter = locs.begin(); iter != locs.end(); iter++) {
  1015         // allocateData() ensures that sizeof(CacheLocation) is word-aligned.
  1016         // If this changes, we will need to pad to ensure alignment.
  1017         size_t curIndex = allocateData(sizeof(CacheLocation));
  1018         new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script);
  1019         numLocations++;
  1021     JS_ASSERT(numLocations != 0);
  1022     *numLocs = numLocations;
  1023     return firstIndex;
  1026 ReciprocalMulConstants
  1027 CodeGeneratorShared::computeDivisionConstants(int d) {
  1028     // In what follows, d is positive and is not a power of 2.
  1029     JS_ASSERT(d > 0 && (d & (d - 1)) != 0);
  1031     // Speeding up division by non power-of-2 constants is possible by
  1032     // calculating, during compilation, a value M such that high-order
  1033     // bits of M*n correspond to the result of the division. Formally,
  1034     // we compute values 0 <= M < 2^32 and 0 <= s < 31 such that
  1035     //         (M * n) >> (32 + s) = floor(n/d)    if n >= 0
  1036     //         (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0.
  1037     // The original presentation of this technique appears in Hacker's
  1038     // Delight, a book by Henry S. Warren, Jr.. A proof of correctness
  1039     // for our version follows.
  1041     // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies
  1042     //                     M - 2^p/d <= 2^(s+1)/d.                 (1)
  1043     // (Observe that s = FloorLog32(d) satisfies this, because in this
  1044     // case d <= 2^(s+1) and so the RHS of (1) is at least one). Then,
  1045     //
  1046     // a) If s <= FloorLog32(d), then M <= 2^32 - 1.
  1047     // Proof: Indeed, M is monotone in s and, for s = FloorLog32(d),
  1048     // the inequalities 2^31 > d >= 2^s + 1 readily imply
  1049     //    2^p / d  = 2^p/(d - 1) * (d - 1)/d
  1050     //            <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2.
  1051     // The claim follows by applying the ceiling function.
  1052     //
  1053     // b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d).
  1054     // Proof: Put x = floor(Mn/2^p); it's the unique integer for which
  1055     //                    Mn/2^p - 1 < x <= Mn/2^p.                (2)
  1056     // Using M >= 2^p/d on the LHS and (1) on the RHS, we get
  1057     //           n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d.
  1058     // Since x is an integer, it's not in the interval (n/d, (n+1)/d),
  1059     // and so n/d - 1 < x <= n/d, which implies x = floor(n/d).
  1060     //
  1061     // c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d).
  1062     // Proof: The proof is similar. Equation (2) holds as above. Using
  1063     // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS,
  1064     //                 n/d + n/(2^31 d) - 1 < x < n/d.
  1065     // Using n >= -2^31 and summing 1,
  1066     //                  n/d - 1/d < x + 1 < n/d + 1.
  1067     // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1.
  1068     // In other words, x + 1 = ceil(n/d).
  1069     //
  1070     // Condition (1) isn't necessary for the existence of M and s with
  1071     // the properties above. Hacker's Delight provides a slightly less
  1072     // restrictive condition when d >= 196611, at the cost of a 3-page
  1073     // proof of correctness.
  1075     // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as
  1076     //                   2^(s+1) >= d - (2^p)%d.
  1077     // We now compute the least s with this property...
  1079     int32_t shift = 0;
  1080     while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d)
  1081         shift++;
  1083     // ...and the corresponding M. This may not fit in a signed 32-bit
  1084     // integer; we will compute (M - 2^32) * n + (2^32 * n) instead of
  1085     // M * n if this is the case (cf. item (a) above).
  1086     ReciprocalMulConstants rmc;
  1087     rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1);
  1088     rmc.shiftAmount = shift;
  1090     return rmc;
  1094 #ifdef JS_TRACE_LOGGING
  1096 bool
  1097 CodeGeneratorShared::emitTracelogScript(bool isStart)
  1099     RegisterSet regs = RegisterSet::Volatile();
  1100     Register logger = regs.takeGeneral();
  1101     Register script = regs.takeGeneral();
  1103     masm.Push(logger);
  1104     masm.Push(script);
  1106     CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
  1107     if (!patchableTraceLoggers_.append(patchLogger))
  1108         return false;
  1110     CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script);
  1111     if (!patchableTLScripts_.append(patchScript))
  1112         return false;
  1114     if (isStart)
  1115         masm.tracelogStart(logger, script);
  1116     else
  1117         masm.tracelogStop(logger, script);
  1119     masm.Pop(script);
  1120     masm.Pop(logger);
  1121     return true;
  1124 bool
  1125 CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
  1127     if (!TraceLogTextIdEnabled(textId))
  1128         return true;
  1130     RegisterSet regs = RegisterSet::Volatile();
  1131     Register logger = regs.takeGeneral();
  1133     masm.Push(logger);
  1135     CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
  1136     if (!patchableTraceLoggers_.append(patchLocation))
  1137         return false;
  1139     if (isStart) {
  1140         masm.tracelogStart(logger, textId);
  1141     } else {
  1142 #ifdef DEBUG
  1143         masm.tracelogStop(logger, textId);
  1144 #else
  1145         masm.tracelogStop(logger);
  1146 #endif
  1149     masm.Pop(logger);
  1150     return true;
  1152 #endif
  1154 } // namespace jit
  1155 } // namespace js

mercurial