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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/shared/CodeGenerator-shared.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1155 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jit/shared/CodeGenerator-shared-inl.h"
    1.11 +
    1.12 +#include "mozilla/DebugOnly.h"
    1.13 +
    1.14 +#include "jit/IonCaches.h"
    1.15 +#include "jit/IonMacroAssembler.h"
    1.16 +#include "jit/IonSpewer.h"
    1.17 +#include "jit/MIR.h"
    1.18 +#include "jit/MIRGenerator.h"
    1.19 +#include "jit/ParallelFunctions.h"
    1.20 +#include "vm/TraceLogging.h"
    1.21 +
    1.22 +#include "jit/IonFrames-inl.h"
    1.23 +
    1.24 +using namespace js;
    1.25 +using namespace js::jit;
    1.26 +
    1.27 +using mozilla::DebugOnly;
    1.28 +
    1.29 +namespace js {
    1.30 +namespace jit {
    1.31 +
    1.32 +MacroAssembler &
    1.33 +CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg)
    1.34 +{
    1.35 +    if (masmArg)
    1.36 +        return *masmArg;
    1.37 +    maybeMasm_.construct();
    1.38 +    return maybeMasm_.ref();
    1.39 +}
    1.40 +
    1.41 +CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg)
    1.42 +  : oolIns(nullptr),
    1.43 +    maybeMasm_(),
    1.44 +    masm(ensureMasm(masmArg)),
    1.45 +    gen(gen),
    1.46 +    graph(*graph),
    1.47 +    current(nullptr),
    1.48 +    snapshots_(),
    1.49 +    recovers_(),
    1.50 +    deoptTable_(nullptr),
    1.51 +#ifdef DEBUG
    1.52 +    pushedArgs_(0),
    1.53 +#endif
    1.54 +    lastOsiPointOffset_(0),
    1.55 +    sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
    1.56 +    osrEntryOffset_(0),
    1.57 +    skipArgCheckEntryOffset_(0),
    1.58 +    frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize())
    1.59 +{
    1.60 +    if (!gen->compilingAsmJS())
    1.61 +        masm.setInstrumentation(&sps_);
    1.62 +
    1.63 +    // Since asm.js uses the system ABI which does not necessarily use a
    1.64 +    // regular array where all slots are sizeof(Value), it maintains the max
    1.65 +    // argument stack depth separately.
    1.66 +    if (gen->compilingAsmJS()) {
    1.67 +        JS_ASSERT(graph->argumentSlotCount() == 0);
    1.68 +        frameDepth_ += gen->maxAsmJSStackArgBytes();
    1.69 +
    1.70 +        // An MAsmJSCall does not align the stack pointer at calls sites but instead
    1.71 +        // relies on the a priori stack adjustment (in the prologue) on platforms
    1.72 +        // (like x64) which require the stack to be aligned.
    1.73 +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
    1.74 +        bool forceAlign = true;
    1.75 +#else
    1.76 +        bool forceAlign = false;
    1.77 +#endif
    1.78 +        if (gen->performsAsmJSCall() || forceAlign) {
    1.79 +            unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
    1.80 +            if (unsigned rem = alignmentAtCall % StackAlignment)
    1.81 +                frameDepth_ += StackAlignment - rem;
    1.82 +        }
    1.83 +
    1.84 +        // FrameSizeClass is only used for bailing, which cannot happen in
    1.85 +        // asm.js code.
    1.86 +        frameClass_ = FrameSizeClass::None();
    1.87 +    } else {
    1.88 +        frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
    1.89 +    }
    1.90 +}
    1.91 +
    1.92 +bool
    1.93 +CodeGeneratorShared::generateOutOfLineCode()
    1.94 +{
    1.95 +    for (size_t i = 0; i < outOfLineCode_.length(); i++) {
    1.96 +        if (!gen->alloc().ensureBallast())
    1.97 +            return false;
    1.98 +        masm.setFramePushed(outOfLineCode_[i]->framePushed());
    1.99 +        lastPC_ = outOfLineCode_[i]->pc();
   1.100 +        if (!sps_.prepareForOOL())
   1.101 +            return false;
   1.102 +        sps_.setPushed(outOfLineCode_[i]->script());
   1.103 +        outOfLineCode_[i]->bind(&masm);
   1.104 +
   1.105 +        oolIns = outOfLineCode_[i];
   1.106 +        if (!outOfLineCode_[i]->generate(this))
   1.107 +            return false;
   1.108 +        sps_.finishOOL();
   1.109 +    }
   1.110 +    oolIns = nullptr;
   1.111 +
   1.112 +    return true;
   1.113 +}
   1.114 +
   1.115 +bool
   1.116 +CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code)
   1.117 +{
   1.118 +    code->setFramePushed(masm.framePushed());
   1.119 +    // If an OOL instruction adds another OOL instruction, then use the original
   1.120 +    // instruction's script/pc instead of the basic block's that we're on
   1.121 +    // because they're probably not relevant any more.
   1.122 +    if (oolIns)
   1.123 +        code->setSource(oolIns->script(), oolIns->pc());
   1.124 +    else
   1.125 +        code->setSource(current ? current->mir()->info().script() : nullptr, lastPC_);
   1.126 +    JS_ASSERT_IF(code->script(), code->script()->containsPC(code->pc()));
   1.127 +    return outOfLineCode_.append(code);
   1.128 +}
   1.129 +
   1.130 +// see OffsetOfFrameSlot
   1.131 +static inline int32_t
   1.132 +ToStackIndex(LAllocation *a)
   1.133 +{
   1.134 +    if (a->isStackSlot()) {
   1.135 +        JS_ASSERT(a->toStackSlot()->slot() >= 1);
   1.136 +        return a->toStackSlot()->slot();
   1.137 +    }
   1.138 +    JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index());
   1.139 +    return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index());
   1.140 +}
   1.141 +
   1.142 +bool
   1.143 +CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint,
   1.144 +                                       uint32_t *startIndex)
   1.145 +{
   1.146 +    IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
   1.147 +            resumePoint->numOperands(), (void *) resumePoint, *startIndex);
   1.148 +    for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) {
   1.149 +        uint32_t i = allocno + *startIndex;
   1.150 +        MDefinition *mir = resumePoint->getOperand(allocno);
   1.151 +
   1.152 +        if (mir->isBox())
   1.153 +            mir = mir->toBox()->getOperand(0);
   1.154 +
   1.155 +        MIRType type = mir->isUnused()
   1.156 +                       ? MIRType_MagicOptimizedOut
   1.157 +                       : mir->type();
   1.158 +
   1.159 +        RValueAllocation alloc;
   1.160 +
   1.161 +        switch (type) {
   1.162 +          case MIRType_Undefined:
   1.163 +            alloc = RValueAllocation::Undefined();
   1.164 +            break;
   1.165 +          case MIRType_Null:
   1.166 +            alloc = RValueAllocation::Null();
   1.167 +            break;
   1.168 +          case MIRType_Int32:
   1.169 +          case MIRType_String:
   1.170 +          case MIRType_Object:
   1.171 +          case MIRType_Boolean:
   1.172 +          case MIRType_Double:
   1.173 +          case MIRType_Float32:
   1.174 +          {
   1.175 +            LAllocation *payload = snapshot->payloadOfSlot(i);
   1.176 +            JSValueType valueType = ValueTypeFromMIRType(type);
   1.177 +            if (payload->isMemory()) {
   1.178 +                if (type == MIRType_Float32)
   1.179 +                    alloc = RValueAllocation::Float32(ToStackIndex(payload));
   1.180 +                else
   1.181 +                    alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
   1.182 +            } else if (payload->isGeneralReg()) {
   1.183 +                alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
   1.184 +            } else if (payload->isFloatReg()) {
   1.185 +                FloatRegister reg = ToFloatRegister(payload);
   1.186 +                if (type == MIRType_Float32)
   1.187 +                    alloc = RValueAllocation::Float32(reg);
   1.188 +                else
   1.189 +                    alloc = RValueAllocation::Double(reg);
   1.190 +            } else {
   1.191 +                MConstant *constant = mir->toConstant();
   1.192 +                uint32_t index;
   1.193 +                if (!graph.addConstantToPool(constant->value(), &index))
   1.194 +                    return false;
   1.195 +                alloc = RValueAllocation::ConstantPool(index);
   1.196 +            }
   1.197 +            break;
   1.198 +          }
   1.199 +          case MIRType_MagicOptimizedArguments:
   1.200 +          case MIRType_MagicOptimizedOut:
   1.201 +          {
   1.202 +            uint32_t index;
   1.203 +            JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
   1.204 +                              ? JS_OPTIMIZED_ARGUMENTS
   1.205 +                              : JS_OPTIMIZED_OUT);
   1.206 +            Value v = MagicValue(why);
   1.207 +            if (!graph.addConstantToPool(v, &index))
   1.208 +                return false;
   1.209 +            alloc = RValueAllocation::ConstantPool(index);
   1.210 +            break;
   1.211 +          }
   1.212 +          default:
   1.213 +          {
   1.214 +            JS_ASSERT(mir->type() == MIRType_Value);
   1.215 +            LAllocation *payload = snapshot->payloadOfSlot(i);
   1.216 +#ifdef JS_NUNBOX32
   1.217 +            LAllocation *type = snapshot->typeOfSlot(i);
   1.218 +            if (type->isRegister()) {
   1.219 +                if (payload->isRegister())
   1.220 +                    alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
   1.221 +                else
   1.222 +                    alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload));
   1.223 +            } else {
   1.224 +                if (payload->isRegister())
   1.225 +                    alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload));
   1.226 +                else
   1.227 +                    alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload));
   1.228 +            }
   1.229 +#elif JS_PUNBOX64
   1.230 +            if (payload->isRegister())
   1.231 +                alloc = RValueAllocation::Untyped(ToRegister(payload));
   1.232 +            else
   1.233 +                alloc = RValueAllocation::Untyped(ToStackIndex(payload));
   1.234 +#endif
   1.235 +            break;
   1.236 +          }
   1.237 +        }
   1.238 +
   1.239 +        snapshots_.add(alloc);
   1.240 +    }
   1.241 +
   1.242 +    *startIndex += resumePoint->numOperands();
   1.243 +    return true;
   1.244 +}
   1.245 +
   1.246 +bool
   1.247 +CodeGeneratorShared::encode(LRecoverInfo *recover)
   1.248 +{
   1.249 +    if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
   1.250 +        return true;
   1.251 +
   1.252 +    uint32_t frameCount = recover->mir()->frameCount();
   1.253 +    IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)",
   1.254 +            (void *)recover, frameCount);
   1.255 +
   1.256 +    MResumePoint::Mode mode = recover->mir()->mode();
   1.257 +    JS_ASSERT(mode != MResumePoint::Outer);
   1.258 +    bool resumeAfter = (mode == MResumePoint::ResumeAfter);
   1.259 +
   1.260 +    RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter);
   1.261 +
   1.262 +    for (MResumePoint **it = recover->begin(), **end = recover->end();
   1.263 +         it != end;
   1.264 +         ++it)
   1.265 +    {
   1.266 +        if (!recovers_.writeFrame(*it))
   1.267 +            return false;
   1.268 +    }
   1.269 +
   1.270 +    recovers_.endRecover();
   1.271 +    recover->setRecoverOffset(offset);
   1.272 +    return !recovers_.oom();
   1.273 +}
   1.274 +
   1.275 +bool
   1.276 +CodeGeneratorShared::encode(LSnapshot *snapshot)
   1.277 +{
   1.278 +    if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET)
   1.279 +        return true;
   1.280 +
   1.281 +    LRecoverInfo *recoverInfo = snapshot->recoverInfo();
   1.282 +    if (!encode(recoverInfo))
   1.283 +        return false;
   1.284 +
   1.285 +    RecoverOffset recoverOffset = recoverInfo->recoverOffset();
   1.286 +    MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET);
   1.287 +
   1.288 +    IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)",
   1.289 +            (void *)snapshot, (void*) recoverInfo);
   1.290 +
   1.291 +    SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind());
   1.292 +
   1.293 +#ifdef TRACK_SNAPSHOTS
   1.294 +    uint32_t pcOpcode = 0;
   1.295 +    uint32_t lirOpcode = 0;
   1.296 +    uint32_t lirId = 0;
   1.297 +    uint32_t mirOpcode = 0;
   1.298 +    uint32_t mirId = 0;
   1.299 +
   1.300 +    if (LInstruction *ins = instruction()) {
   1.301 +        lirOpcode = ins->op();
   1.302 +        lirId = ins->id();
   1.303 +        if (ins->mirRaw()) {
   1.304 +            mirOpcode = ins->mirRaw()->op();
   1.305 +            mirId = ins->mirRaw()->id();
   1.306 +            if (ins->mirRaw()->trackedPc())
   1.307 +                pcOpcode = *ins->mirRaw()->trackedPc();
   1.308 +        }
   1.309 +    }
   1.310 +    snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
   1.311 +#endif
   1.312 +
   1.313 +    uint32_t startIndex = 0;
   1.314 +    for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
   1.315 +         it != end;
   1.316 +         ++it)
   1.317 +    {
   1.318 +        MResumePoint *mir = *it;
   1.319 +        if (!encodeAllocations(snapshot, mir, &startIndex))
   1.320 +            return false;
   1.321 +    }
   1.322 +
   1.323 +    MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots());
   1.324 +    snapshots_.endSnapshot();
   1.325 +    snapshot->setSnapshotOffset(offset);
   1.326 +    return !snapshots_.oom();
   1.327 +}
   1.328 +
   1.329 +bool
   1.330 +CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
   1.331 +{
   1.332 +    JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET);
   1.333 +
   1.334 +    // Can we not use bailout tables at all?
   1.335 +    if (!deoptTable_)
   1.336 +        return false;
   1.337 +
   1.338 +    JS_ASSERT(frameClass_ != FrameSizeClass::None());
   1.339 +
   1.340 +    if (snapshot->bailoutId() != INVALID_BAILOUT_ID)
   1.341 +        return true;
   1.342 +
   1.343 +    // Is the bailout table full?
   1.344 +    if (bailouts_.length() >= BAILOUT_TABLE_SIZE)
   1.345 +        return false;
   1.346 +
   1.347 +    unsigned bailoutId = bailouts_.length();
   1.348 +    snapshot->setBailoutId(bailoutId);
   1.349 +    IonSpew(IonSpew_Snapshots, "Assigned snapshot bailout id %u", bailoutId);
   1.350 +    return bailouts_.append(snapshot->snapshotOffset());
   1.351 +}
   1.352 +
   1.353 +void
   1.354 +CodeGeneratorShared::encodeSafepoints()
   1.355 +{
   1.356 +    for (SafepointIndex *it = safepointIndices_.begin(), *end = safepointIndices_.end();
   1.357 +         it != end;
   1.358 +         ++it)
   1.359 +    {
   1.360 +        LSafepoint *safepoint = it->safepoint();
   1.361 +
   1.362 +        if (!safepoint->encoded()) {
   1.363 +            safepoint->fixupOffset(&masm);
   1.364 +            safepoints_.encode(safepoint);
   1.365 +        }
   1.366 +
   1.367 +        it->resolve();
   1.368 +    }
   1.369 +}
   1.370 +
   1.371 +bool
   1.372 +CodeGeneratorShared::markSafepoint(LInstruction *ins)
   1.373 +{
   1.374 +    return markSafepointAt(masm.currentOffset(), ins);
   1.375 +}
   1.376 +
   1.377 +bool
   1.378 +CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction *ins)
   1.379 +{
   1.380 +    JS_ASSERT_IF(!safepointIndices_.empty(),
   1.381 +                 offset - safepointIndices_.back().displacement() >= sizeof(uint32_t));
   1.382 +    return safepointIndices_.append(SafepointIndex(offset, ins->safepoint()));
   1.383 +}
   1.384 +
   1.385 +void
   1.386 +CodeGeneratorShared::ensureOsiSpace()
   1.387 +{
   1.388 +    // For a refresher, an invalidation point is of the form:
   1.389 +    // 1: call <target>
   1.390 +    // 2: ...
   1.391 +    // 3: <osipoint>
   1.392 +    //
   1.393 +    // The four bytes *before* instruction 2 are overwritten with an offset.
   1.394 +    // Callers must ensure that the instruction itself has enough bytes to
   1.395 +    // support this.
   1.396 +    //
   1.397 +    // The bytes *at* instruction 3 are overwritten with an invalidation jump.
   1.398 +    // jump. These bytes may be in a completely different IR sequence, but
   1.399 +    // represent the join point of the call out of the function.
   1.400 +    //
   1.401 +    // At points where we want to ensure that invalidation won't corrupt an
   1.402 +    // important instruction, we make sure to pad with nops.
   1.403 +    if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::patchWrite_NearCallSize()) {
   1.404 +        int32_t paddingSize = Assembler::patchWrite_NearCallSize();
   1.405 +        paddingSize -= masm.currentOffset() - lastOsiPointOffset_;
   1.406 +        for (int32_t i = 0; i < paddingSize; ++i)
   1.407 +            masm.nop();
   1.408 +    }
   1.409 +    JS_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::patchWrite_NearCallSize());
   1.410 +    lastOsiPointOffset_ = masm.currentOffset();
   1.411 +}
   1.412 +
   1.413 +bool
   1.414 +CodeGeneratorShared::markOsiPoint(LOsiPoint *ins, uint32_t *callPointOffset)
   1.415 +{
   1.416 +    if (!encode(ins->snapshot()))
   1.417 +        return false;
   1.418 +
   1.419 +    ensureOsiSpace();
   1.420 +
   1.421 +    *callPointOffset = masm.currentOffset();
   1.422 +    SnapshotOffset so = ins->snapshot()->snapshotOffset();
   1.423 +    return osiIndices_.append(OsiIndex(*callPointOffset, so));
   1.424 +}
   1.425 +
   1.426 +#ifdef CHECK_OSIPOINT_REGISTERS
   1.427 +template <class Op>
   1.428 +static void
   1.429 +HandleRegisterDump(Op op, MacroAssembler &masm, RegisterSet liveRegs, Register activation,
   1.430 +                   Register scratch)
   1.431 +{
   1.432 +    const size_t baseOffset = JitActivation::offsetOfRegs();
   1.433 +
   1.434 +    // Handle live GPRs.
   1.435 +    for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); iter++) {
   1.436 +        Register reg = *iter;
   1.437 +        Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
   1.438 +
   1.439 +        if (reg == activation) {
   1.440 +            // To use the original value of the activation register (that's
   1.441 +            // now on top of the stack), we need the scratch register.
   1.442 +            masm.push(scratch);
   1.443 +            masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch);
   1.444 +            op(scratch, dump);
   1.445 +            masm.pop(scratch);
   1.446 +        } else {
   1.447 +            op(reg, dump);
   1.448 +        }
   1.449 +    }
   1.450 +
   1.451 +    // Handle live FPRs.
   1.452 +    for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); iter++) {
   1.453 +        FloatRegister reg = *iter;
   1.454 +        Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
   1.455 +        op(reg, dump);
   1.456 +    }
   1.457 +}
   1.458 +
   1.459 +class StoreOp
   1.460 +{
   1.461 +    MacroAssembler &masm;
   1.462 +
   1.463 +  public:
   1.464 +    StoreOp(MacroAssembler &masm)
   1.465 +      : masm(masm)
   1.466 +    {}
   1.467 +
   1.468 +    void operator()(Register reg, Address dump) {
   1.469 +        masm.storePtr(reg, dump);
   1.470 +    }
   1.471 +    void operator()(FloatRegister reg, Address dump) {
   1.472 +        masm.storeDouble(reg, dump);
   1.473 +    }
   1.474 +};
   1.475 +
   1.476 +static void
   1.477 +StoreAllLiveRegs(MacroAssembler &masm, RegisterSet liveRegs)
   1.478 +{
   1.479 +    // Store a copy of all live registers before performing the call.
   1.480 +    // When we reach the OsiPoint, we can use this to check nothing
   1.481 +    // modified them in the meantime.
   1.482 +
   1.483 +    // Load pointer to the JitActivation in a scratch register.
   1.484 +    GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   1.485 +    Register scratch = allRegs.takeAny();
   1.486 +    masm.push(scratch);
   1.487 +    masm.loadJitActivation(scratch);
   1.488 +
   1.489 +    Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   1.490 +    masm.add32(Imm32(1), checkRegs);
   1.491 +
   1.492 +    StoreOp op(masm);
   1.493 +    HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
   1.494 +
   1.495 +    masm.pop(scratch);
   1.496 +}
   1.497 +
   1.498 +class VerifyOp
   1.499 +{
   1.500 +    MacroAssembler &masm;
   1.501 +    Label *failure_;
   1.502 +
   1.503 +  public:
   1.504 +    VerifyOp(MacroAssembler &masm, Label *failure)
   1.505 +      : masm(masm), failure_(failure)
   1.506 +    {}
   1.507 +
   1.508 +    void operator()(Register reg, Address dump) {
   1.509 +        masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
   1.510 +    }
   1.511 +    void operator()(FloatRegister reg, Address dump) {
   1.512 +        masm.loadDouble(dump, ScratchFloatReg);
   1.513 +        masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_);
   1.514 +    }
   1.515 +};
   1.516 +
   1.517 +static void
   1.518 +OsiPointRegisterCheckFailed()
   1.519 +{
   1.520 +    // Any live register captured by a safepoint (other than temp registers)
   1.521 +    // must remain unchanged between the call and the OsiPoint instruction.
   1.522 +    MOZ_ASSUME_UNREACHABLE("Modified registers between VM call and OsiPoint");
   1.523 +}
   1.524 +
   1.525 +void
   1.526 +CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint)
   1.527 +{
   1.528 +    // Ensure the live registers stored by callVM did not change between
   1.529 +    // the call and this OsiPoint. Try-catch relies on this invariant.
   1.530 +
   1.531 +    // Load pointer to the JitActivation in a scratch register.
   1.532 +    GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   1.533 +    Register scratch = allRegs.takeAny();
   1.534 +    masm.push(scratch);
   1.535 +    masm.loadJitActivation(scratch);
   1.536 +
   1.537 +    // If we should not check registers (because the instruction did not call
   1.538 +    // into the VM, or a GC happened), we're done.
   1.539 +    Label failure, done;
   1.540 +    Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   1.541 +    masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
   1.542 +
   1.543 +    // Having more than one VM function call made in one visit function at
   1.544 +    // runtime is a sec-ciritcal error, because if we conservatively assume that
   1.545 +    // one of the function call can re-enter Ion, then the invalidation process
   1.546 +    // will potentially add a call at a random location, by patching the code
   1.547 +    // before the return address.
   1.548 +    masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
   1.549 +
   1.550 +    // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
   1.551 +    // temps after calling into the VM. This is fine because no other
   1.552 +    // instructions (including this OsiPoint) will depend on them. Also
   1.553 +    // backtracking can also use the same register for an input and an output.
   1.554 +    // These are marked as clobbered and shouldn't get checked.
   1.555 +    RegisterSet liveRegs = safepoint->liveRegs();
   1.556 +    liveRegs = RegisterSet::Intersect(liveRegs, RegisterSet::Not(safepoint->clobberedRegs()));
   1.557 +
   1.558 +    VerifyOp op(masm, &failure);
   1.559 +    HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
   1.560 +
   1.561 +    masm.jump(&done);
   1.562 +
   1.563 +    // Do not profile the callWithABI that occurs below.  This is to avoid a
   1.564 +    // rare corner case that occurs when profiling interacts with itself:
   1.565 +    //
   1.566 +    // When slow profiling assertions are turned on, FunctionBoundary ops
   1.567 +    // (which update the profiler pseudo-stack) may emit a callVM, which
   1.568 +    // forces them to have an osi point associated with them.  The
   1.569 +    // FunctionBoundary for inline function entry is added to the caller's
   1.570 +    // graph with a PC from the caller's code, but during codegen it modifies
   1.571 +    // SPS instrumentation to add the callee as the current top-most script.
   1.572 +    // When codegen gets to the OSIPoint, and the callWithABI below is
   1.573 +    // emitted, the codegen thinks that the current frame is the callee, but
   1.574 +    // the PC it's using from the OSIPoint refers to the caller.  This causes
   1.575 +    // the profiler instrumentation of the callWithABI below to ASSERT, since
   1.576 +    // the script and pc are mismatched.  To avoid this, we simply omit
   1.577 +    // instrumentation for these callWithABIs.
   1.578 +    masm.bind(&failure);
   1.579 +    masm.setupUnalignedABICall(0, scratch);
   1.580 +    masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, OsiPointRegisterCheckFailed));
   1.581 +    masm.breakpoint();
   1.582 +
   1.583 +    masm.bind(&done);
   1.584 +    masm.pop(scratch);
   1.585 +}
   1.586 +
   1.587 +bool
   1.588 +CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint)
   1.589 +{
   1.590 +    if (!js_JitOptions.checkOsiPointRegisters)
   1.591 +        return false;
   1.592 +
   1.593 +    if (gen->info().executionMode() != SequentialExecution)
   1.594 +        return false;
   1.595 +
   1.596 +    if (safepoint->liveRegs().empty(true) && safepoint->liveRegs().empty(false))
   1.597 +        return false; // No registers to check.
   1.598 +
   1.599 +    return true;
   1.600 +}
   1.601 +
   1.602 +void
   1.603 +CodeGeneratorShared::resetOsiPointRegs(LSafepoint *safepoint)
   1.604 +{
   1.605 +    if (!shouldVerifyOsiPointRegs(safepoint))
   1.606 +        return;
   1.607 +
   1.608 +    // Set checkRegs to 0. If we perform a VM call, the instruction
   1.609 +    // will set it to 1.
   1.610 +    GeneralRegisterSet allRegs(GeneralRegisterSet::All());
   1.611 +    Register scratch = allRegs.takeAny();
   1.612 +    masm.push(scratch);
   1.613 +    masm.loadJitActivation(scratch);
   1.614 +    Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
   1.615 +    masm.store32(Imm32(0), checkRegs);
   1.616 +    masm.pop(scratch);
   1.617 +}
   1.618 +#endif
   1.619 +
   1.620 +// Before doing any call to Cpp, you should ensure that volatile
   1.621 +// registers are evicted by the register allocator.
   1.622 +bool
   1.623 +CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack)
   1.624 +{
   1.625 +    // Different execution modes have different sets of VM functions.
   1.626 +    JS_ASSERT(fun.executionMode == gen->info().executionMode());
   1.627 +
   1.628 +    // If we're calling a function with an out parameter type of double, make
   1.629 +    // sure we have an FPU.
   1.630 +    JS_ASSERT_IF(fun.outParam == Type_Double, GetIonContext()->runtime->jitSupportsFloatingPoint());
   1.631 +
   1.632 +#ifdef DEBUG
   1.633 +    if (ins->mirRaw()) {
   1.634 +        JS_ASSERT(ins->mirRaw()->isInstruction());
   1.635 +        MInstruction *mir = ins->mirRaw()->toInstruction();
   1.636 +        JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint());
   1.637 +    }
   1.638 +#endif
   1.639 +
   1.640 +#ifdef JS_TRACE_LOGGING
   1.641 +    if (!emitTracelogStartEvent(TraceLogger::VM))
   1.642 +        return false;
   1.643 +#endif
   1.644 +
   1.645 +    // Stack is:
   1.646 +    //    ... frame ...
   1.647 +    //    [args]
   1.648 +#ifdef DEBUG
   1.649 +    JS_ASSERT(pushedArgs_ == fun.explicitArgs);
   1.650 +    pushedArgs_ = 0;
   1.651 +#endif
   1.652 +
   1.653 +    // Get the wrapper of the VM function.
   1.654 +    JitCode *wrapper = gen->jitRuntime()->getVMWrapper(fun);
   1.655 +    if (!wrapper)
   1.656 +        return false;
   1.657 +
   1.658 +#ifdef CHECK_OSIPOINT_REGISTERS
   1.659 +    if (shouldVerifyOsiPointRegs(ins->safepoint()))
   1.660 +        StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
   1.661 +#endif
   1.662 +
   1.663 +    // Call the wrapper function.  The wrapper is in charge to unwind the stack
   1.664 +    // when returning from the call.  Failures are handled with exceptions based
   1.665 +    // on the return value of the C functions.  To guard the outcome of the
   1.666 +    // returned value, use another LIR instruction.
   1.667 +    uint32_t callOffset;
   1.668 +    if (dynStack)
   1.669 +        callOffset = masm.callWithExitFrame(wrapper, *dynStack);
   1.670 +    else
   1.671 +        callOffset = masm.callWithExitFrame(wrapper);
   1.672 +
   1.673 +    if (!markSafepointAt(callOffset, ins))
   1.674 +        return false;
   1.675 +
   1.676 +    // Remove rest of the frame left on the stack. We remove the return address
   1.677 +    // which is implicitly poped when returning.
   1.678 +    int framePop = sizeof(IonExitFrameLayout) - sizeof(void*);
   1.679 +
   1.680 +    // Pop arguments from framePushed.
   1.681 +    masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
   1.682 +    // Stack is:
   1.683 +    //    ... frame ...
   1.684 +
   1.685 +#ifdef JS_TRACE_LOGGING
   1.686 +    if (!emitTracelogStopEvent(TraceLogger::VM))
   1.687 +        return false;
   1.688 +#endif
   1.689 +
   1.690 +    return true;
   1.691 +}
   1.692 +
   1.693 +class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
   1.694 +{
   1.695 +    FloatRegister src_;
   1.696 +    Register dest_;
   1.697 +    bool needFloat32Conversion_;
   1.698 +
   1.699 +  public:
   1.700 +    OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false)
   1.701 +      : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion)
   1.702 +    { }
   1.703 +
   1.704 +    bool accept(CodeGeneratorShared *codegen) {
   1.705 +        return codegen->visitOutOfLineTruncateSlow(this);
   1.706 +    }
   1.707 +    FloatRegister src() const {
   1.708 +        return src_;
   1.709 +    }
   1.710 +    Register dest() const {
   1.711 +        return dest_;
   1.712 +    }
   1.713 +    bool needFloat32Conversion() const {
   1.714 +        return needFloat32Conversion_;
   1.715 +    }
   1.716 +
   1.717 +};
   1.718 +
   1.719 +OutOfLineCode *
   1.720 +CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest)
   1.721 +{
   1.722 +    OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest);
   1.723 +    if (!addOutOfLineCode(ool))
   1.724 +        return nullptr;
   1.725 +    return ool;
   1.726 +}
   1.727 +
   1.728 +bool
   1.729 +CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register &dest)
   1.730 +{
   1.731 +    OutOfLineCode *ool = oolTruncateDouble(src, dest);
   1.732 +    if (!ool)
   1.733 +        return false;
   1.734 +
   1.735 +    masm.branchTruncateDouble(src, dest, ool->entry());
   1.736 +    masm.bind(ool->rejoin());
   1.737 +    return true;
   1.738 +}
   1.739 +
   1.740 +bool
   1.741 +CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest)
   1.742 +{
   1.743 +    OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true);
   1.744 +    if (!addOutOfLineCode(ool))
   1.745 +        return false;
   1.746 +
   1.747 +    masm.branchTruncateFloat32(src, dest, ool->entry());
   1.748 +    masm.bind(ool->rejoin());
   1.749 +    return true;
   1.750 +}
   1.751 +
   1.752 +bool
   1.753 +CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool)
   1.754 +{
   1.755 +    FloatRegister src = ool->src();
   1.756 +    Register dest = ool->dest();
   1.757 +
   1.758 +    saveVolatile(dest);
   1.759 +
   1.760 +    if (ool->needFloat32Conversion()) {
   1.761 +        masm.push(src);
   1.762 +        masm.convertFloat32ToDouble(src, src);
   1.763 +    }
   1.764 +
   1.765 +    masm.setupUnalignedABICall(1, dest);
   1.766 +    masm.passABIArg(src, MoveOp::DOUBLE);
   1.767 +    if (gen->compilingAsmJS())
   1.768 +        masm.callWithABI(AsmJSImm_ToInt32);
   1.769 +    else
   1.770 +        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
   1.771 +    masm.storeCallResult(dest);
   1.772 +
   1.773 +    if (ool->needFloat32Conversion())
   1.774 +        masm.pop(src);
   1.775 +
   1.776 +    restoreVolatile(dest);
   1.777 +
   1.778 +    masm.jump(ool->rejoin());
   1.779 +    return true;
   1.780 +}
   1.781 +
   1.782 +bool
   1.783 +CodeGeneratorShared::omitOverRecursedCheck() const
   1.784 +{
   1.785 +    // If the current function makes no calls (which means it isn't recursive)
   1.786 +    // and it uses only a small amount of stack space, it doesn't need a
   1.787 +    // stack overflow check. Note that the actual number here is somewhat
   1.788 +    // arbitrary, and codegen actually uses small bounded amounts of
   1.789 +    // additional stack space in some cases too.
   1.790 +    return frameSize() < 64 && !gen->performsCall();
   1.791 +}
   1.792 +
   1.793 +void
   1.794 +CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type)
   1.795 +{
   1.796 +    if (index->isConstant()) {
   1.797 +        Address address(base, ToInt32(index) * sizeof(Value));
   1.798 +        masm.patchableCallPreBarrier(address, type);
   1.799 +    } else {
   1.800 +        BaseIndex address(base, ToRegister(index), TimesEight);
   1.801 +        masm.patchableCallPreBarrier(address, type);
   1.802 +    }
   1.803 +}
   1.804 +
   1.805 +void
   1.806 +CodeGeneratorShared::emitPreBarrier(Address address, MIRType type)
   1.807 +{
   1.808 +    masm.patchableCallPreBarrier(address, type);
   1.809 +}
   1.810 +
   1.811 +void
   1.812 +CodeGeneratorShared::dropArguments(unsigned argc)
   1.813 +{
   1.814 +    pushedArgumentSlots_.shrinkBy(argc);
   1.815 +}
   1.816 +
   1.817 +bool
   1.818 +CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
   1.819 +{
   1.820 +    for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) {
   1.821 +        if (!safepoint->addValueSlot(pushedArgumentSlots_[i]))
   1.822 +            return false;
   1.823 +    }
   1.824 +    return true;
   1.825 +}
   1.826 +
   1.827 +OutOfLineAbortPar *
   1.828 +CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
   1.829 +                                 jsbytecode *bytecode)
   1.830 +{
   1.831 +    OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode);
   1.832 +    if (!ool || !addOutOfLineCode(ool))
   1.833 +        return nullptr;
   1.834 +    return ool;
   1.835 +}
   1.836 +
   1.837 +OutOfLineAbortPar *
   1.838 +CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir)
   1.839 +{
   1.840 +    MDefinition *mir = lir->mirRaw();
   1.841 +    MBasicBlock *block = mir->block();
   1.842 +    jsbytecode *pc = mir->trackedPc();
   1.843 +    if (!pc) {
   1.844 +        if (lir->snapshot())
   1.845 +            pc = lir->snapshot()->mir()->pc();
   1.846 +        else
   1.847 +            pc = block->pc();
   1.848 +    }
   1.849 +    return oolAbortPar(cause, block, pc);
   1.850 +}
   1.851 +
   1.852 +OutOfLinePropagateAbortPar *
   1.853 +CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir)
   1.854 +{
   1.855 +    OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir);
   1.856 +    if (!ool || !addOutOfLineCode(ool))
   1.857 +        return nullptr;
   1.858 +    return ool;
   1.859 +}
   1.860 +
   1.861 +bool
   1.862 +OutOfLineAbortPar::generate(CodeGeneratorShared *codegen)
   1.863 +{
   1.864 +    codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
   1.865 +    return codegen->visitOutOfLineAbortPar(this);
   1.866 +}
   1.867 +
   1.868 +bool
   1.869 +OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen)
   1.870 +{
   1.871 +    codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
   1.872 +    return codegen->visitOutOfLinePropagateAbortPar(this);
   1.873 +}
   1.874 +
   1.875 +bool
   1.876 +CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
   1.877 +                                  const char *bailoutName)
   1.878 +{
   1.879 +    JS_ASSERT_IF(!lir, bailoutName);
   1.880 +
   1.881 +    if (!IonSpewEnabled(IonSpew_Trace))
   1.882 +        return true;
   1.883 +
   1.884 +    uint32_t execMode = (uint32_t) gen->info().executionMode();
   1.885 +    uint32_t lirIndex;
   1.886 +    const char *lirOpName;
   1.887 +    const char *mirOpName;
   1.888 +    JSScript *script;
   1.889 +    jsbytecode *pc;
   1.890 +
   1.891 +    masm.PushRegsInMask(RegisterSet::Volatile());
   1.892 +    masm.reserveStack(sizeof(IonLIRTraceData));
   1.893 +
   1.894 +    // This first move is here so that when you scan the disassembly,
   1.895 +    // you can easily pick out where each instruction begins.  The
   1.896 +    // next few items indicate to you the Basic Block / LIR.
   1.897 +    masm.move32(Imm32(0xDEADBEEF), CallTempReg0);
   1.898 +
   1.899 +    if (lir) {
   1.900 +        lirIndex = lir->id();
   1.901 +        lirOpName = lir->opName();
   1.902 +        if (MDefinition *mir = lir->mirRaw()) {
   1.903 +            mirOpName = mir->opName();
   1.904 +            script = mir->block()->info().script();
   1.905 +            pc = mir->trackedPc();
   1.906 +        } else {
   1.907 +            mirOpName = nullptr;
   1.908 +            script = nullptr;
   1.909 +            pc = nullptr;
   1.910 +        }
   1.911 +    } else {
   1.912 +        blockIndex = lirIndex = 0xDEADBEEF;
   1.913 +        lirOpName = mirOpName = bailoutName;
   1.914 +        script = nullptr;
   1.915 +        pc = nullptr;
   1.916 +    }
   1.917 +
   1.918 +    masm.store32(Imm32(blockIndex),
   1.919 +                 Address(StackPointer, offsetof(IonLIRTraceData, blockIndex)));
   1.920 +    masm.store32(Imm32(lirIndex),
   1.921 +                 Address(StackPointer, offsetof(IonLIRTraceData, lirIndex)));
   1.922 +    masm.store32(Imm32(execMode),
   1.923 +                 Address(StackPointer, offsetof(IonLIRTraceData, execModeInt)));
   1.924 +    masm.storePtr(ImmPtr(lirOpName),
   1.925 +                  Address(StackPointer, offsetof(IonLIRTraceData, lirOpName)));
   1.926 +    masm.storePtr(ImmPtr(mirOpName),
   1.927 +                  Address(StackPointer, offsetof(IonLIRTraceData, mirOpName)));
   1.928 +    masm.storePtr(ImmGCPtr(script),
   1.929 +                  Address(StackPointer, offsetof(IonLIRTraceData, script)));
   1.930 +    masm.storePtr(ImmPtr(pc),
   1.931 +                  Address(StackPointer, offsetof(IonLIRTraceData, pc)));
   1.932 +
   1.933 +    masm.movePtr(StackPointer, CallTempReg0);
   1.934 +    masm.setupUnalignedABICall(1, CallTempReg1);
   1.935 +    masm.passABIArg(CallTempReg0);
   1.936 +    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR));
   1.937 +
   1.938 +    masm.freeStack(sizeof(IonLIRTraceData));
   1.939 +    masm.PopRegsInMask(RegisterSet::Volatile());
   1.940 +
   1.941 +    return true;
   1.942 +}
   1.943 +
   1.944 +typedef bool (*InterruptCheckFn)(JSContext *);
   1.945 +const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
   1.946 +
   1.947 +Label *
   1.948 +CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
   1.949 +{
   1.950 +    // If this is a loop backedge to a loop header with an implicit interrupt
   1.951 +    // check, use a patchable jump. Skip this search if compiling without a
   1.952 +    // script for asm.js, as there will be no interrupt check instruction.
   1.953 +    // Due to critical edge unsplitting there may no longer be unique loop
   1.954 +    // backedges, so just look for any edge going to an earlier block in RPO.
   1.955 +    if (!gen->compilingAsmJS() && mir->isLoopHeader() && mir->id() <= current->mir()->id()) {
   1.956 +        for (LInstructionIterator iter = mir->lir()->begin(); iter != mir->lir()->end(); iter++) {
   1.957 +            if (iter->isLabel() || iter->isMoveGroup()) {
   1.958 +                // Continue searching for an interrupt check.
   1.959 +            } else if (iter->isInterruptCheckImplicit()) {
   1.960 +                return iter->toInterruptCheckImplicit()->oolEntry();
   1.961 +            } else {
   1.962 +                // The interrupt check should be the first instruction in the
   1.963 +                // loop header other than the initial label and move groups.
   1.964 +                JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar());
   1.965 +                return nullptr;
   1.966 +            }
   1.967 +        }
   1.968 +    }
   1.969 +
   1.970 +    return nullptr;
   1.971 +}
   1.972 +
   1.973 +void
   1.974 +CodeGeneratorShared::jumpToBlock(MBasicBlock *mir)
   1.975 +{
   1.976 +    // No jump necessary if we can fall through to the next block.
   1.977 +    if (isNextBlock(mir->lir()))
   1.978 +        return;
   1.979 +
   1.980 +    if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
   1.981 +        // Note: the backedge is initially a jump to the next instruction.
   1.982 +        // It will be patched to the target block's label during link().
   1.983 +        RepatchLabel rejoin;
   1.984 +        CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin);
   1.985 +        masm.bind(&rejoin);
   1.986 +
   1.987 +        masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
   1.988 +    } else {
   1.989 +        masm.jump(mir->lir()->label());
   1.990 +    }
   1.991 +}
   1.992 +
   1.993 +// This function is not used for MIPS. MIPS has branchToBlock.
   1.994 +#ifndef JS_CODEGEN_MIPS
   1.995 +void
   1.996 +CodeGeneratorShared::jumpToBlock(MBasicBlock *mir, Assembler::Condition cond)
   1.997 +{
   1.998 +    if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
   1.999 +        // Note: the backedge is initially a jump to the next instruction.
  1.1000 +        // It will be patched to the target block's label during link().
  1.1001 +        RepatchLabel rejoin;
  1.1002 +        CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin, cond);
  1.1003 +        masm.bind(&rejoin);
  1.1004 +
  1.1005 +        masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
  1.1006 +    } else {
  1.1007 +        masm.j(cond, mir->lir()->label());
  1.1008 +    }
  1.1009 +}
  1.1010 +#endif
  1.1011 +
  1.1012 +size_t
  1.1013 +CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *numLocs)
  1.1014 +{
  1.1015 +    size_t firstIndex = runtimeData_.length();
  1.1016 +    size_t numLocations = 0;
  1.1017 +    for (CacheLocationList::iterator iter = locs.begin(); iter != locs.end(); iter++) {
  1.1018 +        // allocateData() ensures that sizeof(CacheLocation) is word-aligned.
  1.1019 +        // If this changes, we will need to pad to ensure alignment.
  1.1020 +        size_t curIndex = allocateData(sizeof(CacheLocation));
  1.1021 +        new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script);
  1.1022 +        numLocations++;
  1.1023 +    }
  1.1024 +    JS_ASSERT(numLocations != 0);
  1.1025 +    *numLocs = numLocations;
  1.1026 +    return firstIndex;
  1.1027 +}
  1.1028 +
  1.1029 +ReciprocalMulConstants
  1.1030 +CodeGeneratorShared::computeDivisionConstants(int d) {
  1.1031 +    // In what follows, d is positive and is not a power of 2.
  1.1032 +    JS_ASSERT(d > 0 && (d & (d - 1)) != 0);
  1.1033 +
  1.1034 +    // Speeding up division by non power-of-2 constants is possible by
  1.1035 +    // calculating, during compilation, a value M such that high-order
  1.1036 +    // bits of M*n correspond to the result of the division. Formally,
  1.1037 +    // we compute values 0 <= M < 2^32 and 0 <= s < 31 such that
  1.1038 +    //         (M * n) >> (32 + s) = floor(n/d)    if n >= 0
  1.1039 +    //         (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0.
  1.1040 +    // The original presentation of this technique appears in Hacker's
  1.1041 +    // Delight, a book by Henry S. Warren, Jr.. A proof of correctness
  1.1042 +    // for our version follows.
  1.1043 +
  1.1044 +    // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies
  1.1045 +    //                     M - 2^p/d <= 2^(s+1)/d.                 (1)
  1.1046 +    // (Observe that s = FloorLog32(d) satisfies this, because in this
  1.1047 +    // case d <= 2^(s+1) and so the RHS of (1) is at least one). Then,
  1.1048 +    //
  1.1049 +    // a) If s <= FloorLog32(d), then M <= 2^32 - 1.
  1.1050 +    // Proof: Indeed, M is monotone in s and, for s = FloorLog32(d),
  1.1051 +    // the inequalities 2^31 > d >= 2^s + 1 readily imply
  1.1052 +    //    2^p / d  = 2^p/(d - 1) * (d - 1)/d
  1.1053 +    //            <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2.
  1.1054 +    // The claim follows by applying the ceiling function.
  1.1055 +    //
  1.1056 +    // b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d).
  1.1057 +    // Proof: Put x = floor(Mn/2^p); it's the unique integer for which
  1.1058 +    //                    Mn/2^p - 1 < x <= Mn/2^p.                (2)
  1.1059 +    // Using M >= 2^p/d on the LHS and (1) on the RHS, we get
  1.1060 +    //           n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d.
  1.1061 +    // Since x is an integer, it's not in the interval (n/d, (n+1)/d),
  1.1062 +    // and so n/d - 1 < x <= n/d, which implies x = floor(n/d).
  1.1063 +    //
  1.1064 +    // c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d).
  1.1065 +    // Proof: The proof is similar. Equation (2) holds as above. Using
  1.1066 +    // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS,
  1.1067 +    //                 n/d + n/(2^31 d) - 1 < x < n/d.
  1.1068 +    // Using n >= -2^31 and summing 1,
  1.1069 +    //                  n/d - 1/d < x + 1 < n/d + 1.
  1.1070 +    // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1.
  1.1071 +    // In other words, x + 1 = ceil(n/d).
  1.1072 +    //
  1.1073 +    // Condition (1) isn't necessary for the existence of M and s with
  1.1074 +    // the properties above. Hacker's Delight provides a slightly less
  1.1075 +    // restrictive condition when d >= 196611, at the cost of a 3-page
  1.1076 +    // proof of correctness.
  1.1077 +
  1.1078 +    // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as
  1.1079 +    //                   2^(s+1) >= d - (2^p)%d.
  1.1080 +    // We now compute the least s with this property...
  1.1081 +
  1.1082 +    int32_t shift = 0;
  1.1083 +    while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d)
  1.1084 +        shift++;
  1.1085 +
  1.1086 +    // ...and the corresponding M. This may not fit in a signed 32-bit
  1.1087 +    // integer; we will compute (M - 2^32) * n + (2^32 * n) instead of
  1.1088 +    // M * n if this is the case (cf. item (a) above).
  1.1089 +    ReciprocalMulConstants rmc;
  1.1090 +    rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1);
  1.1091 +    rmc.shiftAmount = shift;
  1.1092 +
  1.1093 +    return rmc;
  1.1094 +}
  1.1095 +
  1.1096 +
  1.1097 +#ifdef JS_TRACE_LOGGING
  1.1098 +
  1.1099 +bool
  1.1100 +CodeGeneratorShared::emitTracelogScript(bool isStart)
  1.1101 +{
  1.1102 +    RegisterSet regs = RegisterSet::Volatile();
  1.1103 +    Register logger = regs.takeGeneral();
  1.1104 +    Register script = regs.takeGeneral();
  1.1105 +
  1.1106 +    masm.Push(logger);
  1.1107 +    masm.Push(script);
  1.1108 +
  1.1109 +    CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
  1.1110 +    if (!patchableTraceLoggers_.append(patchLogger))
  1.1111 +        return false;
  1.1112 +
  1.1113 +    CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script);
  1.1114 +    if (!patchableTLScripts_.append(patchScript))
  1.1115 +        return false;
  1.1116 +
  1.1117 +    if (isStart)
  1.1118 +        masm.tracelogStart(logger, script);
  1.1119 +    else
  1.1120 +        masm.tracelogStop(logger, script);
  1.1121 +
  1.1122 +    masm.Pop(script);
  1.1123 +    masm.Pop(logger);
  1.1124 +    return true;
  1.1125 +}
  1.1126 +
  1.1127 +bool
  1.1128 +CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
  1.1129 +{
  1.1130 +    if (!TraceLogTextIdEnabled(textId))
  1.1131 +        return true;
  1.1132 +
  1.1133 +    RegisterSet regs = RegisterSet::Volatile();
  1.1134 +    Register logger = regs.takeGeneral();
  1.1135 +
  1.1136 +    masm.Push(logger);
  1.1137 +
  1.1138 +    CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
  1.1139 +    if (!patchableTraceLoggers_.append(patchLocation))
  1.1140 +        return false;
  1.1141 +
  1.1142 +    if (isStart) {
  1.1143 +        masm.tracelogStart(logger, textId);
  1.1144 +    } else {
  1.1145 +#ifdef DEBUG
  1.1146 +        masm.tracelogStop(logger, textId);
  1.1147 +#else
  1.1148 +        masm.tracelogStop(logger);
  1.1149 +#endif
  1.1150 +    }
  1.1151 +
  1.1152 +    masm.Pop(logger);
  1.1153 +    return true;
  1.1154 +}
  1.1155 +#endif
  1.1156 +
  1.1157 +} // namespace jit
  1.1158 +} // namespace js

mercurial