Exemple #1
0
void RegSet::SetLockedRegFloat(GenTree * tree, bool bValue)
{
    regNumber  reg     = tree->gtRegNum;
    var_types  type    = tree->TypeGet();    assert(varTypeIsFloating(type));
    regMaskTP  regMask = genRegMaskFloat(reg, tree->TypeGet());

    if (bValue)
    {
        JITDUMP("locking register %s\n", getRegNameFloat(reg, type));

        assert((rsGetMaskUsed() & regMask) == regMask);
        assert((rsGetMaskLock() & regMask) == 0);

        rsSetMaskLock( (rsGetMaskLock() | regMask) );
    }
    else
    {
        JITDUMP("unlocking register %s\n", getRegNameFloat(reg, type));

        assert((rsGetMaskUsed()   & regMask) == regMask);
        assert((rsGetMaskLock() & regMask) == regMask);

        rsSetMaskLock( (rsGetMaskLock() & ~regMask) );
    }
}
//------------------------------------------------------------------------
// LowerCast: Lower GT_CAST(srcType, DstType) nodes.
//
// Arguments:
//    tree - GT_CAST node to be lowered
//
// Return Value:
//    None.
//
// Notes:
//    Casts from float/double to a smaller int type are transformed as follows:
//    GT_CAST(float/double, byte)     =   GT_CAST(GT_CAST(float/double, int32), byte)
//    GT_CAST(float/double, sbyte)    =   GT_CAST(GT_CAST(float/double, int32), sbyte)
//    GT_CAST(float/double, int16)    =   GT_CAST(GT_CAST(double/double, int32), int16)
//    GT_CAST(float/double, uint16)   =   GT_CAST(GT_CAST(double/double, int32), uint16)
//
//    Note that for the overflow conversions we still depend on helper calls and
//    don't expect to see them here.
//    i) GT_CAST(float/double, int type with overflow detection)
//
void Lowering::LowerCast(GenTree* tree)
{
    assert(tree->OperGet() == GT_CAST);

    JITDUMP("LowerCast for: ");
    DISPNODE(tree);
    JITDUMP("\n");

    GenTree*  op1     = tree->gtOp.gtOp1;
    var_types dstType = tree->CastToType();
    var_types srcType = genActualType(op1->TypeGet());
    var_types tmpType = TYP_UNDEF;

    if (varTypeIsFloating(srcType))
    {
        noway_assert(!tree->gtOverflow());
        assert(!varTypeIsSmall(dstType)); // fgMorphCast creates intermediate casts when converting from float to small
                                          // int.
    }

    assert(!varTypeIsSmall(srcType));

    if (tmpType != TYP_UNDEF)
    {
        GenTree* tmp = comp->gtNewCastNode(tmpType, op1, tree->IsUnsigned(), tmpType);
        tmp->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));

        tree->gtFlags &= ~GTF_UNSIGNED;
        tree->gtOp.gtOp1 = tmp;
        BlockRange().InsertAfter(op1, tmp);
    }

    // Now determine if we have operands that should be contained.
    ContainCheckCast(tree->AsCast());
}
Exemple #3
0
void CodeGen::siEndScope(unsigned varNum)
{
    for (siScope* scope = siOpenScopeList.scNext; scope; scope = scope->scNext)
    {
        if (scope->scVarNum == varNum)
        {
            siEndScope(scope);
            return;
        }
    }

    JITDUMP("siEndScope: Failed to end scope for V%02u\n", varNum);

    // At this point, we probably have a bad LocalVarTab
    if (compiler->opts.compDbgCode)
    {
        JITDUMP("...checking var tab validity\n");

        // Note the following assert is saying that we expect
        // the VM supplied info to be invalid...
        assert(!siVerifyLocalVarTab());

        compiler->opts.compScopeInfo = false;
    }
}
Exemple #4
0
bool RangeCheck::IsBinOpMonotonicallyIncreasing(GenTreeOp* binop)
{
    assert(binop->OperIs(GT_ADD));

    GenTree* op1 = binop->gtGetOp1();
    GenTree* op2 = binop->gtGetOp2();

    JITDUMP("[RangeCheck::IsBinOpMonotonicallyIncreasing] [%06d], [%06d]\n", Compiler::dspTreeID(op1),
            Compiler::dspTreeID(op2));
    // Check if we have a var + const.
    if (op2->OperGet() == GT_LCL_VAR)
    {
        jitstd::swap(op1, op2);
    }
    if (op1->OperGet() != GT_LCL_VAR)
    {
        JITDUMP("Not monotonic because op1 is not lclVar.\n");
        return false;
    }
    switch (op2->OperGet())
    {
        case GT_LCL_VAR:
            // When adding two local variables, we also must ensure that any constant is non-negative.
            return IsMonotonicallyIncreasing(op1, true) && IsMonotonicallyIncreasing(op2, true);

        case GT_CNS_INT:
            return (op2->AsIntConCommon()->IconValue() >= 0) && IsMonotonicallyIncreasing(op1, false);

        default:
            JITDUMP("Not monotonic because expression is not recognized.\n");
            return false;
    }
}
Exemple #5
0
bool RangeCheck::IsBinOpMonotonicallyIncreasing(GenTreePtr op1, GenTreePtr op2, genTreeOps oper, SearchPath* path)
{
    JITDUMP("[RangeCheck::IsBinOpMonotonicallyIncreasing] %p, %p\n", dspPtr(op1), dspPtr(op2));
    // Check if we have a var + const.
    if (op2->OperGet() == GT_LCL_VAR)
    {
        jitstd::swap(op1, op2);
    }
    if (op1->OperGet() != GT_LCL_VAR)
    {
        JITDUMP("Not monotonic because op1 is not lclVar.\n");
        return false;
    }
    switch (op2->OperGet())
    {
    case GT_LCL_VAR:
        return IsMonotonicallyIncreasing(op1, path) && 
            IsMonotonicallyIncreasing(op2, path);

    case GT_CNS_INT:
        return oper == GT_ADD && op2->AsIntConCommon()->IconValue() >= 0 &&
            IsMonotonicallyIncreasing(op1, path);

    default:
        JITDUMP("Not monotonic because expression is not recognized.\n");
        return false;
    }
}
Exemple #6
0
void        CodeGen::siEndBlock(BasicBlock* block)
{
    assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));

#if FEATURE_EH_FUNCLETS
    if (siInFuncletRegion)
        return;
#endif // FEATURE_EH_FUNCLETS

#ifdef DEBUG
    if (verbose)
    {
        printf("\nScope info: end block BB%02u, IL range ", block->bbNum);
        block->dspBlockILRange();
        printf("\n");
    }
#endif // DEBUG

    unsigned endOffs = block->bbCodeOffsEnd;

    if (endOffs == BAD_IL_OFFSET)
    {
        JITDUMP("Scope info: ignoring block end\n");
        return;
    }

    // If non-debuggable code, find all scopes which end over this block
    // and close them. For debuggable code, scopes will only end on block
    // boundaries.

    VarScopeDsc* varScope;
    while ((varScope = compiler->compGetNextExitScope(endOffs, !compiler->opts.compDbgCode)) != NULL)
    {
        // brace-matching editor workaround for following line: (
        JITDUMP("Scope info: ending scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg, varScope->vsdLifeEnd);

        unsigned    varNum     = varScope->vsdVarNum;
        LclVarDsc * lclVarDsc1 = &compiler->lvaTable[varNum];

        assert(lclVarDsc1);

        if (lclVarDsc1->lvTracked)
        {
            siEndTrackedScope(lclVarDsc1->lvVarIndex);
        }
        else
        {
            siEndScope(varNum);
        }
    }

    siLastEndOffs = endOffs;

#ifdef DEBUG
    if (verbose)
        siDispOpenScopes();
#endif

}
Exemple #7
0
void Rationalizer::RewriteAddress(LIR::Use& use)
{
    assert(use.IsInitialized());

    GenTreeUnOp* address = use.Def()->AsUnOp();
    assert(address->OperGet() == GT_ADDR);

    GenTree*   location   = address->gtGetOp1();
    genTreeOps locationOp = location->OperGet();

    if (location->IsLocal())
    {
// We are changing the child from GT_LCL_VAR TO GT_LCL_VAR_ADDR.
// Therefore gtType of the child needs to be changed to a TYP_BYREF
#ifdef DEBUG
        if (locationOp == GT_LCL_VAR)
        {
            JITDUMP("Rewriting GT_ADDR(GT_LCL_VAR) to GT_LCL_VAR_ADDR:\n");
        }
        else
        {
            assert(locationOp == GT_LCL_FLD);
            JITDUMP("Rewriting GT_ADDR(GT_LCL_FLD) to GT_LCL_FLD_ADDR:\n");
        }
#endif // DEBUG

        location->SetOper(addrForm(locationOp));
        location->gtType = TYP_BYREF;
        copyFlags(location, address, GTF_ALL_EFFECT);

        use.ReplaceWith(comp, location);
        BlockRange().Remove(address);
    }
    else if (locationOp == GT_CLS_VAR)
    {
        location->SetOper(GT_CLS_VAR_ADDR);
        location->gtType = TYP_BYREF;
        copyFlags(location, address, GTF_ALL_EFFECT);

        use.ReplaceWith(comp, location);
        BlockRange().Remove(address);

        JITDUMP("Rewriting GT_ADDR(GT_CLS_VAR) to GT_CLS_VAR_ADDR:\n");
    }
    else if (location->OperIsIndir())
    {
        use.ReplaceWith(comp, location->gtGetOp1());
        BlockRange().Remove(location);
        BlockRange().Remove(address);

        JITDUMP("Rewriting GT_ADDR(GT_IND(X)) to X:\n");
    }

    DISPTREERANGE(BlockRange(), use.Def());
    JITDUMP("\n");
}
Exemple #8
0
void Compiler::optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName)
{
    JITDUMP("{ ");
    for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
    {
        GenTree* node = iter.GetValue()->Index(0);
        JITDUMP("%d-[%06d]:V%02u ", iter.Get(), dspTreeID(node), node->AsLclVarCommon()->gtLclNum);
    }
    JITDUMP("}\n\n");
}
Exemple #9
0
//------------------------------------------------------------------------
// DecomposeInd: Decompose GT_IND.
//
// Arguments:
//    use - the LIR::Use object for the def that needs to be decomposed.
//
// Return Value:
//    The next node to process.
//
GenTree* DecomposeLongs::DecomposeInd(LIR::Use& use)
{
    GenTree* indLow = use.Def();

    LIR::Use address(Range(), &indLow->gtOp.gtOp1, indLow);
    address.ReplaceWithLclVar(m_compiler, m_blockWeight);
    JITDUMP("[DecomposeInd]: Saving addr tree to a temp var:\n");
    DISPTREERANGE(Range(), address.Def());

    // Change the type of lower ind.
    indLow->gtType = TYP_INT;

    // Create tree of ind(addr+4)
    GenTreePtr addrBase     = indLow->gtGetOp1();
    GenTreePtr addrBaseHigh = new (m_compiler, GT_LCL_VAR)
    GenTreeLclVar(GT_LCL_VAR, addrBase->TypeGet(), addrBase->AsLclVarCommon()->GetLclNum(), BAD_IL_OFFSET);
    GenTreePtr addrHigh =
        new (m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT));
    GenTreePtr indHigh = new (m_compiler, GT_IND) GenTreeIndir(GT_IND, TYP_INT, addrHigh, nullptr);

    m_compiler->lvaIncRefCnts(addrBaseHigh);

    Range().InsertAfter(indLow, addrBaseHigh, addrHigh, indHigh);

    return FinalizeDecomposition(use, indLow, indHigh);
}
Exemple #10
0
void RangeCheck::Widen(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, SearchPath* path, Range* pRange)
{
#ifdef DEBUG
    if (m_pCompiler->verbose)
    {
        printf("[RangeCheck::Widen] BB%02d, \n", block->bbNum);
        Compiler::printTreeID(tree);
        printf("\n");
    }
#endif // DEBUG

    Range& range = *pRange;

    // Try to deduce the lower bound, if it is not known already.
    if (range.LowerLimit().IsDependent() || range.LowerLimit().IsUnknown())
    {
        // To determine the lower bound, ask if the loop increases monotonically.
        bool increasing = IsMonotonicallyIncreasing(tree, path);
        JITDUMP("IsMonotonicallyIncreasing %d", increasing);
        if (increasing)
        {
            GetRangeMap()->RemoveAll();
            *pRange = GetRange(block, stmt, tree, path, true DEBUGARG(0));
        }
    }
}
Exemple #11
0
//--------------------------------------------------------------------------------------------------
// CancelLoopOptInfo - Cancel loop cloning optimization for this loop.
//
// Arguments:
//      loopNum     the loop index.
//
// Return Values:
//      None.
//
void LoopCloneContext::CancelLoopOptInfo(unsigned loopNum)
{
    JITDUMP("Cancelling loop cloning for loop L_%02u\n", loopNum);
    optInfo[loopNum] = nullptr;
    if (conditions[loopNum] != nullptr)
    {
        conditions[loopNum]->Reset();
        conditions[loopNum] = nullptr;
    }
}
Exemple #12
0
void CodeGen::UnspillFloat(RegSet::SpillDsc *spillDsc)
{
    JITDUMP("UnspillFloat() for SpillDsc [%08p]\n", dspPtr(spillDsc));

    RemoveSpillDsc(spillDsc);   
    UnspillFloatMachineDep(spillDsc);

    RegSet::SpillDsc::freeDsc(&regSet, spillDsc);
    compiler->tmpRlsTemp(spillDsc->spillTemp);
}
Exemple #13
0
void CodeGen::UnspillFloat(LclVarDsc * varDsc)
{
    JITDUMP("UnspillFloat() for var [%08p]\n", dspPtr(varDsc));

    RegSet::SpillDsc*  cur = regSet.rsSpillFloat;
    assert(cur);    

    while (cur->spillVarDsc != varDsc)
        cur = cur->spillNext;

    UnspillFloat(cur);
}
Exemple #14
0
//------------------------------------------------------------------------
// LowerCast: Lower GT_CAST(srcType, DstType) nodes.
//
// Arguments:
//    tree - GT_CAST node to be lowered
//
// Return Value:
//    None.
//
// Notes:
//    Casts from float/double to a smaller int type are transformed as follows:
//    GT_CAST(float/double, byte)     =   GT_CAST(GT_CAST(float/double, int32), byte)
//    GT_CAST(float/double, sbyte)    =   GT_CAST(GT_CAST(float/double, int32), sbyte)
//    GT_CAST(float/double, int16)    =   GT_CAST(GT_CAST(double/double, int32), int16)
//    GT_CAST(float/double, uint16)   =   GT_CAST(GT_CAST(double/double, int32), uint16)
//
//    Note that for the overflow conversions we still depend on helper calls and
//    don't expect to see them here.
//    i) GT_CAST(float/double, int type with overflow detection)
//
void Lowering::LowerCast(GenTree* tree)
{
    assert(tree->OperGet() == GT_CAST);

    JITDUMP("LowerCast for: ");
    DISPNODE(tree);
    JITDUMP("\n");

    GenTreePtr op1     = tree->gtOp.gtOp1;
    var_types  dstType = tree->CastToType();
    var_types  srcType = genActualType(op1->TypeGet());
    var_types  tmpType = TYP_UNDEF;

    if (varTypeIsFloating(srcType))
    {
        noway_assert(!tree->gtOverflow());
    }

    assert(!varTypeIsSmall(srcType));

    // case of src is a floating point type and dst is a small type.
    if (varTypeIsFloating(srcType) && varTypeIsSmall(dstType))
    {
        NYI_ARM("Lowering for cast from float to small type"); // Not tested yet.
        tmpType = TYP_INT;
    }

    if (tmpType != TYP_UNDEF)
    {
        GenTreePtr tmp = comp->gtNewCastNode(tmpType, op1, tmpType);
        tmp->gtFlags |= (tree->gtFlags & (GTF_UNSIGNED | GTF_OVERFLOW | GTF_EXCEPT));

        tree->gtFlags &= ~GTF_UNSIGNED;
        tree->gtOp.gtOp1 = tmp;
        BlockRange().InsertAfter(op1, tmp);
    }

    // Now determine if we have operands that should be contained.
    ContainCheckCast(tree->AsCast());
}
Exemple #15
0
static void RewriteAssignmentIntoStoreLclCore(GenTreeOp* assignment,
                                              GenTree*   location,
                                              GenTree*   value,
                                              genTreeOps locationOp)
{
    assert(assignment != nullptr);
    assert(assignment->OperGet() == GT_ASG);
    assert(location != nullptr);
    assert(value != nullptr);

    genTreeOps storeOp = storeForm(locationOp);

#ifdef DEBUG
    JITDUMP("rewriting asg(%s, X) to %s(X)\n", GenTree::NodeName(locationOp), GenTree::NodeName(storeOp));
#endif // DEBUG

    assignment->SetOper(storeOp);
    GenTreeLclVarCommon* store = assignment->AsLclVarCommon();

    GenTreeLclVarCommon* var = location->AsLclVarCommon();
    store->SetLclNum(var->gtLclNum);
    store->SetSsaNum(var->gtSsaNum);

    if (locationOp == GT_LCL_FLD)
    {
        store->gtLclFld.gtLclOffs  = var->gtLclFld.gtLclOffs;
        store->gtLclFld.gtFieldSeq = var->gtLclFld.gtFieldSeq;
    }

    copyFlags(store, var, GTF_LIVENESS_MASK);
    store->gtFlags &= ~GTF_REVERSE_OPS;

    store->gtType = var->TypeGet();
    store->gtOp1  = value;

    DISPNODE(store);
    JITDUMP("\n");
}
Exemple #16
0
void LoopCloneContext::PrintBlockConditions(unsigned loopNum)
{
    JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* levelCond = blockConditions[loopNum];
    if (levelCond == nullptr || levelCond->Size() == 0)
    {
        JITDUMP("No block conditions\n");
        return;
    }

    for (unsigned i = 0; i < levelCond->Size(); ++i)
    {
        JITDUMP("%d = {", i);
        for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j)
        {
            if (j != 0)
            {
                JITDUMP(" & ");
            }
            (*((*levelCond)[i]))[j].Print();
        }
        JITDUMP("}\n");
    }
}
Exemple #17
0
void Compiler::fgFixupArgTabEntryPtr(GenTreePtr parentCall, GenTreePtr oldArg, GenTreePtr newArg)
{
    assert(parentCall != nullptr);
    assert(oldArg != nullptr);
    assert(newArg != nullptr);

    JITDUMP("parent call was :\n");
    DISPNODE(parentCall);

    JITDUMP("old child was :\n");
    DISPNODE(oldArg);

    if (oldArg->gtFlags & GTF_LATE_ARG)
    {
        newArg->gtFlags |= GTF_LATE_ARG;
    }
    else
    {
        fgArgTabEntryPtr fp = Compiler::gtArgEntryByNode(parentCall, oldArg);
        assert(fp->node == oldArg);
        fp->node = newArg;
    }
}
Exemple #18
0
// Add the def location to the hash table.
void RangeCheck::SetDef(UINT64 hash, Location* loc)
{
    if (m_pDefTable == nullptr)
    {
        m_pDefTable = new (m_pCompiler->getAllocator()) VarToLocMap(m_pCompiler->getAllocator());
    }
#ifdef DEBUG
    Location* loc2;
    if (m_pDefTable->Lookup(hash, &loc2))
    {
        JITDUMP("Already have BB%02d, %08X, %08X for hash => %0I64X", loc2->block->bbNum, dspPtr(loc2->stmt), dspPtr(loc2->tree), hash);
        assert(false);
    }
#endif
    m_pDefTable->Set(hash, loc);
}
Exemple #19
0
// Add the def location to the hash table.
void RangeCheck::SetDef(UINT64 hash, Location* loc)
{
    if (m_pDefTable == nullptr)
    {
        m_pDefTable = new (m_alloc) VarToLocMap(m_alloc);
    }
#ifdef DEBUG
    Location* loc2;
    if (m_pDefTable->Lookup(hash, &loc2))
    {
        JITDUMP("Already have " FMT_BB ", [%06d], [%06d] for hash => %0I64X", loc2->block->bbNum,
                Compiler::dspTreeID(loc2->stmt), Compiler::dspTreeID(loc2->tree), hash);
        assert(false);
    }
#endif
    m_pDefTable->Set(hash, loc);
}
void InlineResult::report()
{
    // User may have suppressed reporting via setReported(). If so, do nothing.
    if (inlReported)
    {
        return;
    }

    inlReported = true;

#ifdef DEBUG

    // Optionally dump the result
    if (VERBOSE)
    {
        const char* format = "INLINER: during '%s' result '%s' reason '%s' for '%s' calling '%s'\n";
        const char* caller = (inlCaller == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlCaller);
        const char* callee = (inlCallee == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlCallee);

        JITDUMP(format, inlContext, resultString(), reasonString(), caller, callee);
    }

    // If the inline failed, leave information on the call so we can
    // later recover what observation lead to the failure.
    if (isFailure() && (inlCall != nullptr))
    {
        // compiler should have revoked candidacy on the call by now
        assert((inlCall->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);

        inlCall->gtInlineObservation = static_cast<unsigned>(inlObservation);
    }

#endif // DEBUG

    if (isDecided())
    {
        const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
        JITLOG_THIS(inlCompiler, (LL_INFO100000, format, inlContext, resultString(), reasonString()));
        COMP_HANDLE comp = inlCompiler->info.compCompHnd;
        comp->reportInliningDecision(inlCaller, inlCallee, result(), reasonString());
    }
}
Exemple #21
0
//------------------------------------------------------------------------
// shouldDoubleAlign: Determine whether to double-align the frame
//
// Arguments:
//    refCntStk       - sum of     ref counts for all stack based variables
//    refCntEBP       - sum of     ref counts for EBP enregistered variables
//    refCntWtdEBP    - sum of wtd ref counts for EBP enregistered variables
//    refCntStkParam  - sum of     ref counts for all stack based parameters
//    refCntWtdStkDbl - sum of wtd ref counts for stack based doubles (including structs
//                      with double fields).
//
// Return Value:
//    Returns true if this method estimates that a double-aligned frame would be beneficial
//
// Notes:
//    The impact of a double-aligned frame is computed as follows:
//    - We save a byte of code for each parameter reference (they are frame-pointer relative)
//    - We pay a byte of code for each non-parameter stack reference.
//    - We save the misalignment penalty and possible cache-line crossing penalty.
//      This is estimated as 0 for SMALL_CODE, 16 for FAST_CODE and 4 otherwise.
//    - We pay 7 extra bytes for:
//        MOV EBP,ESP,
//        LEA ESP,[EBP-offset]
//        AND ESP,-8 to double align ESP
//    - We pay one extra memory reference for each variable that could have been enregistered in EBP (refCntWtdEBP).
//
//    If the misalignment penalty is estimated to be less than the bytes used, we don't double align.
//    Otherwise, we compare the weighted ref count of ebp-enregistered variables against double the
//    ref count for double-aligned values.
//
bool Compiler::shouldDoubleAlign(
    unsigned refCntStk, unsigned refCntEBP, unsigned refCntWtdEBP, unsigned refCntStkParam, unsigned refCntWtdStkDbl)
{
    bool           doDoubleAlign        = false;
    const unsigned DBL_ALIGN_SETUP_SIZE = 7;

    unsigned bytesUsed         = refCntStk + refCntEBP - refCntStkParam + DBL_ALIGN_SETUP_SIZE;
    unsigned misaligned_weight = 4;

    if (compCodeOpt() == Compiler::SMALL_CODE)
        misaligned_weight = 0;

    if (compCodeOpt() == Compiler::FAST_CODE)
        misaligned_weight *= 4;

    JITDUMP("\nDouble alignment:\n");
    JITDUMP("  Bytes that could be saved by not using EBP frame: %i\n", bytesUsed);
    JITDUMP("  Sum of weighted ref counts for EBP enregistered variables: %i\n", refCntWtdEBP);
    JITDUMP("  Sum of weighted ref counts for weighted stack based doubles: %i\n", refCntWtdStkDbl);

    if (bytesUsed > ((refCntWtdStkDbl * misaligned_weight) / BB_UNITY_WEIGHT))
    {
        JITDUMP("    Predicting not to double-align ESP to save %d bytes of code.\n", bytesUsed);
    }
    else if (refCntWtdEBP > refCntWtdStkDbl * 2)
    {
        // TODO-CQ: On P4 2 Proc XEON's, SciMark.FFT degrades if SciMark.FFT.transform_internal is
        // not double aligned.
        // Here are the numbers that make this not double-aligned.
        //     refCntWtdStkDbl = 0x164
        //     refCntWtdEBP    = 0x1a4
        // We think we do need to change the heuristic to be in favor of double-align.

        JITDUMP("    Predicting not to double-align ESP to allow EBP to be used to enregister variables.\n");
    }
    else
    {
        // OK we passed all of the benefit tests, so we'll predict a double aligned frame.
        JITDUMP("    Predicting to create a double-aligned frame\n");
        doDoubleAlign = true;
    }
    return doDoubleAlign;
}
Exemple #22
0
//------------------------------------------------------------------------
// DecomposeInd: Decompose GT_IND.
//
// Arguments:
//    tree - the tree to decompose
//
// Return Value:
//    None.
//
void DecomposeLongs::DecomposeInd(GenTree** ppTree, Compiler::fgWalkData* data)
{
    GenTreePtr indLow = *ppTree;
    GenTreeStmt* addrStmt = CreateTemporary(&indLow->gtOp.gtOp1);
    JITDUMP("[DecomposeInd]: Saving addr tree to a temp var:\n");
    DISPTREE(addrStmt);

    // Change the type of lower ind.
    indLow->gtType = TYP_INT;

    // Create tree of ind(addr+4)
    GenTreePtr addrBase = indLow->gtGetOp1();
    GenTreePtr addrBaseHigh = new(m_compiler, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR,
        addrBase->TypeGet(), addrBase->AsLclVarCommon()->GetLclNum(), BAD_IL_OFFSET);
    GenTreePtr addrHigh = new(m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT));
    GenTreePtr indHigh = new (m_compiler, GT_IND) GenTreeIndir(GT_IND, TYP_INT, addrHigh, nullptr);
    
    // Connect linear links
    SimpleLinkNodeAfter(addrBaseHigh, addrHigh);
    SimpleLinkNodeAfter(addrHigh, indHigh);

    FinalizeDecomposition(ppTree, data, indLow, indHigh);
}
Exemple #23
0
//------------------------------------------------------------------------
// DecomposeNode: Decompose long-type trees into lower and upper halves.
//
// Arguments:
//    *ppTree - A node that may or may not require decomposition.
//    data    - The tree-walk data that provides the context.
//
// Return Value:
//    None. It the tree at *ppTree is of TYP_LONG, it will generally be replaced.
//
void DecomposeLongs::DecomposeNode(GenTree** ppTree, Compiler::fgWalkData* data)
{
    GenTree* tree = *ppTree;

    // Handle the case where we are implicitly using the lower half of a long lclVar.
    if ((tree->TypeGet() == TYP_INT) && tree->OperIsLocal())
    {
        LclVarDsc* varDsc = m_compiler->lvaTable + tree->AsLclVarCommon()->gtLclNum;
        if (varTypeIsLong(varDsc) && varDsc->lvPromoted)
        {
#ifdef DEBUG
            if (m_compiler->verbose)
            {
                printf("Changing implicit reference to lo half of long lclVar to an explicit reference of its promoted half:\n");
                m_compiler->gtDispTree(tree);
            }
#endif // DEBUG
            m_compiler->lvaDecRefCnts(tree);
            unsigned loVarNum = varDsc->lvFieldLclStart;
            tree->AsLclVarCommon()->SetLclNum(loVarNum);
            m_compiler->lvaIncRefCnts(tree);
            return;
        }
    }

    if (tree->TypeGet() != TYP_LONG)
    {
        return;
    }

#ifdef DEBUG
    if (m_compiler->verbose)
    {
        printf("Decomposing TYP_LONG tree.  BEFORE:\n");
        m_compiler->gtDispTree(tree);
    }
#endif // DEBUG

    switch (tree->OperGet())
    {
    case GT_PHI:
    case GT_PHI_ARG:
        break;

    case GT_LCL_VAR:
        DecomposeLclVar(ppTree, data);
        break;

    case GT_LCL_FLD:
        DecomposeLclFld(ppTree, data);
        break;

    case GT_STORE_LCL_VAR:
        DecomposeStoreLclVar(ppTree, data);
        break;

    case GT_CAST:
        DecomposeCast(ppTree, data);
        break;

    case GT_CNS_LNG:
        DecomposeCnsLng(ppTree, data);
        break;

    case GT_CALL:
        DecomposeCall(ppTree, data);
        break;

    case GT_RETURN:
        assert(tree->gtOp.gtOp1->OperGet() == GT_LONG);
        break;

    case GT_STOREIND:
        DecomposeStoreInd(ppTree, data);
        break;

    case GT_STORE_LCL_FLD:
        assert(tree->gtOp.gtOp1->OperGet() == GT_LONG);
        NYI("st.lclFld of of TYP_LONG");
        break;

    case GT_IND:
        DecomposeInd(ppTree, data);
        break;

    case GT_NOT:
        DecomposeNot(ppTree, data);
        break;

    case GT_NEG:
        DecomposeNeg(ppTree, data);
        break;

    // Binary operators. Those that require different computation for upper and lower half are
    // handled by the use of GetHiOper().
    case GT_ADD:
    case GT_SUB:
    case GT_OR:
    case GT_XOR:
    case GT_AND:
        DecomposeArith(ppTree, data);
        break;

    case GT_MUL:
        NYI("Arithmetic binary operators on TYP_LONG - GT_MUL");
        break;

    case GT_DIV:
        NYI("Arithmetic binary operators on TYP_LONG - GT_DIV");
        break;

    case GT_MOD:
        NYI("Arithmetic binary operators on TYP_LONG - GT_MOD");
        break;

    case GT_UDIV:
        NYI("Arithmetic binary operators on TYP_LONG - GT_UDIV");
        break;

    case GT_UMOD:
        NYI("Arithmetic binary operators on TYP_LONG - GT_UMOD");
        break;

    case GT_LSH:
    case GT_RSH:
    case GT_RSZ:
        NYI("Arithmetic binary operators on TYP_LONG - SHIFT");
        break;

    case GT_ROL:
    case GT_ROR:
        NYI("Arithmetic binary operators on TYP_LONG - ROTATE");
        break;

    case GT_MULHI:
        NYI("Arithmetic binary operators on TYP_LONG - MULHI");
        break;

    case GT_LOCKADD:
    case GT_XADD:
    case GT_XCHG:
    case GT_CMPXCHG:
        NYI("Interlocked operations on TYP_LONG");
        break;

    default:
        {
            JITDUMP("Illegal TYP_LONG node %s in Decomposition.", GenTree::NodeName(tree->OperGet()));
            noway_assert(!"Illegal TYP_LONG node in Decomposition.");
            break;
        }
    }

#ifdef DEBUG
    if (m_compiler->verbose)
    {
        printf("  AFTER:\n");
        m_compiler->gtDispTree(*ppTree);
    }
#endif
}
Exemple #24
0
void        CodeGen::siBeginBlock(BasicBlock* block)
{
    assert(block != nullptr);

    if (!compiler->opts.compScopeInfo)
        return;
    
    if (compiler->info.compVarScopesCount == 0)
        return;

#if FEATURE_EH_FUNCLETS
    if (siInFuncletRegion)
        return;

    if (block->bbFlags & BBF_FUNCLET_BEG)
    {
        // For now, don't report any scopes in funclets. JIT64 doesn't.
        siInFuncletRegion = true;

        JITDUMP("Scope info: found beginning of funclet region at block BB%02u; ignoring following blocks\n",
            block->bbNum);

        return;
    }
#endif // FEATURE_EH_FUNCLETS

#ifdef DEBUG
    if  (verbose)
    {
        printf("\nScope info: begin block BB%02u, IL range ", block->bbNum);
        block->dspBlockILRange();
        printf("\n");
    }
#endif // DEBUG

    unsigned beginOffs = block->bbCodeOffs;

    if (beginOffs == BAD_IL_OFFSET)
    {
        JITDUMP("Scope info: ignoring block beginning\n");
        return;
    }

    if (!compiler->opts.compDbgCode)
    {
        /* For non-debuggable code */
        
        // End scope of variables which are not live for this block

        siUpdate();

        // Check that vars which are live on entry have an open scope

        VARSET_ITER_INIT(compiler, iter, block->bbLiveIn, i);
        while (iter.NextElem(compiler, &i))
        {
            unsigned varNum = compiler->lvaTrackedToVarNum[i];
            // lvRefCnt may go down to 0 after liveness-analysis.
            // So we need to check if this tracked variable is actually used.
            if (!compiler->lvaTable[varNum].lvIsInReg() && !compiler->lvaTable[varNum].lvOnFrame)
            {
                assert(compiler->lvaTable[varNum].lvRefCnt == 0);
                continue;
            }

            siCheckVarScope(varNum, beginOffs);
        }
    }
    else
    {
        // For debuggable code, scopes can begin only on block boundaries.
        // Check if there are any scopes on the current block's start boundary.

        VarScopeDsc* varScope;

#if FEATURE_EH_FUNCLETS

        // If we find a spot where the code offset isn't what we expect, because
        // there is a gap, it might be because we've moved the funclets out of
        // line. Catch up with the enter and exit scopes of the current block.
        // Ignore the enter/exit scope changes of the missing scopes, which for
        // funclets must be matched.

        if  (siLastEndOffs != beginOffs)
        {
            assert(beginOffs > 0);
            assert(siLastEndOffs < beginOffs);

            JITDUMP("Scope info: found offset hole. lastOffs=%u, currOffs=%u\n",
                siLastEndOffs, beginOffs);

            // Skip enter scopes
            while ((varScope = compiler->compGetNextEnterScope(beginOffs - 1, true)) != NULL)
            {
                /* do nothing */
                JITDUMP("Scope info: skipping enter scope, LVnum=%u\n", varScope->vsdLVnum);
            }

            // Skip exit scopes
            while ((varScope = compiler->compGetNextExitScope(beginOffs - 1, true)) != NULL)
            {
                /* do nothing */
                JITDUMP("Scope info: skipping exit scope, LVnum=%u\n", varScope->vsdLVnum);
            }
        }

#else // FEATURE_EH_FUNCLETS

        if  (siLastEndOffs != beginOffs)
        {
            assert(siLastEndOffs < beginOffs);
            return;
        }

#endif // FEATURE_EH_FUNCLETS

        while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != NULL)
        {
            // brace-matching editor workaround for following line: (
            JITDUMP("Scope info: opening scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg, varScope->vsdLifeEnd);

            siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);

#ifdef DEBUG
            LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
            if (VERBOSE)
            {
                printf("Scope info: >> new scope, VarNum=%u, tracked? %s, VarIndex=%u, bbLiveIn=%s ",
                       varScope->vsdVarNum,
                       lclVarDsc1->lvTracked ? "yes" : "no",
                       lclVarDsc1->lvVarIndex,
                       VarSetOps::ToString(compiler, block->bbLiveIn));
                dumpConvertedVarSet(compiler, block->bbLiveIn);
                printf("\n");
            }
            assert(!lclVarDsc1->lvTracked || VarSetOps::IsMember(compiler, block->bbLiveIn, lclVarDsc1->lvVarIndex));
#endif // DEBUG
        }
    }

#ifdef DEBUG
    if (verbose)
        siDispOpenScopes();
#endif

}
Exemple #25
0
// Merge assertions on the edge flowing into the block about a variable.
void RangeCheck::MergeEdgeAssertions(GenTreePtr tree, EXPSET_TP assertions, Range* pRange)
{
    if (assertions == 0)
    {
        return;
    }

    GenTreeLclVarCommon* lcl = (GenTreeLclVarCommon*) tree;
    if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM)
    {
        return;
    }
    // Walk through the "assertions" to check if the apply.
    unsigned index = 1;
    for (EXPSET_TP mask = 1; index <= m_pCompiler->GetAssertionCount(); index++, mask <<= 1)
    {
        if ((assertions & mask) == 0)
        {
            continue;
        }

        Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion(index);

        // Current assertion is about array length.
        if (!curAssertion->IsArrLenArithBound() &&
            !curAssertion->IsArrLenBound())
        {
            continue;
        }

#ifdef DEBUG
        if (m_pCompiler->verbose)
        {
            m_pCompiler->optPrintAssertion(curAssertion, index);
        }
#endif

        assert(m_pCompiler->vnStore->IsVNArrLenArithBound(curAssertion->op1.vn) ||
               m_pCompiler->vnStore->IsVNArrLenBound(curAssertion->op1.vn));
        
        ValueNumStore::ArrLenArithBoundInfo info;
        Limit limit(Limit::keUndef);
        // Current assertion is of the form (i < a.len - cns) != 0
        if (curAssertion->IsArrLenArithBound())
        {
            // Get i, a.len, cns and < as "info."
            m_pCompiler->vnStore->GetArrLenArithBoundInfo(curAssertion->op1.vn, &info);
                       
            if (m_pCompiler->lvaTable[lcl->gtLclNum].GetPerSsaData(lcl->gtSsaNum)->m_vnPair.GetConservative()
                    != info.cmpOp)
            {
                continue;
            }

            switch (info.arrOper)
            {
            case GT_SUB:
            case GT_ADD:
                {
                    // If the operand that operates on the array is not constant, then done.
                    if (!m_pCompiler->vnStore->IsVNConstant(info.arrOp) || m_pCompiler->vnStore->TypeOfVN(info.arrOp) != TYP_INT)
                    {
                        break;
                    }
                    int cons = m_pCompiler->vnStore->ConstantValue<int>(info.arrOp);
                    limit = Limit(Limit::keBinOpArray, info.vnArray, info.arrOper == GT_SUB ? -cons : cons);
                }
            }
        }
        // Current assertion is of the form (i < a.len) != 0
        else if (curAssertion->IsArrLenBound())
        {
            // Get the info as "i", "<" and "a.len"
            m_pCompiler->vnStore->GetArrLenBoundInfo(curAssertion->op1.vn, &info);

            ValueNum lclVn = m_pCompiler->lvaTable[lcl->gtLclNum].GetPerSsaData(lcl->gtSsaNum)->m_vnPair.GetConservative();
            // If we don't have the same variable we are comparing against, bail.
            if (lclVn != info.cmpOp)
            {
                continue;
            }
            limit.type = Limit::keArray;
            limit.vn = info.vnArray;
        }
        else
        {
            noway_assert(false);
        }

        if (limit.IsUndef())
        {
            continue;
        }

        // Make sure the assertion is of the form != 0 or == 0.
        if (curAssertion->op2.vn != m_pCompiler->vnStore->VNZeroForType(TYP_INT))
        {
            continue;
        }
#ifdef DEBUG
        if (m_pCompiler->verbose) m_pCompiler->optPrintAssertion(curAssertion, index);
#endif

        noway_assert(limit.IsBinOpArray() || limit.IsArray());

        ValueNum arrLenVN = m_pCurBndsChk->gtArrLen->gtVNPair.GetConservative();
        ValueNum arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(arrLenVN);

        // During assertion prop we add assertions of the form:
        // 
        //      (i < a.Length) == 0
        //      (i < a.Length) != 0
        //
        // At this point, we have detected that op1.vn is (i < a.Length) or (i < a.Length + cns),
        // and the op2.vn is 0.
        //
        // Now, let us check if we are == 0 (i.e., op1 assertion is false) or != 0 (op1 assertion
        // is true.),
        //
        // If we have an assertion of the form == 0 (i.e., equals false), then reverse relop.
        // The relop has to be reversed because we have: (i < a.Length) is false which is the same
        // as (i >= a.Length).
        genTreeOps cmpOper = (genTreeOps) info.cmpOper;
        if (curAssertion->assertionKind == Compiler::OAK_EQUAL)
        {
            cmpOper = GenTree::ReverseRelop(cmpOper);
        }

        // Bounds are inclusive, so add -1 for upper bound when "<". But make sure we won't overflow.
        if (cmpOper == GT_LT && !limit.AddConstant(-1))
        {
            continue;
        }
        // Bounds are inclusive, so add +1 for lower bound when ">". But make sure we won't overflow.
        if (cmpOper == GT_GT && !limit.AddConstant(1))
        {
            continue;
        }

        // Doesn't tighten the current bound. So skip.
        if (pRange->uLimit.IsConstant() && limit.vn != arrRefVN)
        {
            continue;
        }

        // Check if the incoming limit from assertions tightens the existing upper limit.
        if ((pRange->uLimit.IsArray() || pRange->uLimit.IsBinOpArray()) && pRange->uLimit.vn == arrRefVN)
        {
            // We have checked the current range's (pRange's) upper limit is either of the form:
            //      a.Length
            //      a.Length + cns
            //      and a == the bndsChkCandidate's arrRef
            //
            // We want to check if the incoming limit tightens the bound, and for that the
            // we need to make sure that incoming limit is also on a.Length or a.Length + cns
            // and not b.Length or some c.Length.

            if (limit.vn != arrRefVN)
            {
                JITDUMP("Array ref did not match cur=$%x, assert=$%x\n", arrRefVN, limit.vn);
                continue;
            }

            int curCns = (pRange->uLimit.IsBinOpArray()) ? pRange->uLimit.cns : 0;
            int limCns = (limit.IsBinOpArray()) ? limit.cns : 0;
            
            // Incoming limit doesn't tighten the existing upper limit.
            if (limCns >= curCns)
            {
                JITDUMP("Bound limit %d doesn't tighten current bound %d\n", limCns, curCns);
                continue;
            }
        }
        else
        {
            // Current range's upper bound is not "a.Length or a.Length + cns" and the
            // incoming limit is not on the same arrRef as the bounds check candidate.
            // So we could skip this assertion. But in cases, of Dependent or Unknown
            // type of upper limit, the incoming assertion still tightens the upper
            // bound to a saner value. So do not skip the assertion.
        }

        // cmpOp (loop index i) cmpOper a.len +/- cns
        switch (cmpOper)
        {
        case GT_LT:
            pRange->uLimit = limit;
            break;

        case GT_GT:
            pRange->lLimit = limit;
            break;

        case GT_GE:
            pRange->lLimit = limit;
            break;

        case GT_LE:
            pRange->uLimit = limit;
            break;
        }
        JITDUMP("The range after edge merging:");
        JITDUMP(pRange->ToString(m_pCompiler->getAllocatorDebugOnly()));
        JITDUMP("\n");
    }
}
Exemple #26
0
// Check if the computed range is within bounds.
bool RangeCheck::BetweenBounds(Range& range, int lower, GenTreePtr upper)
{
#ifdef DEBUG
    if (m_pCompiler->verbose)
    {
        printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler->getAllocatorDebugOnly()), lower);
        Compiler::printTreeID(upper);
        printf(">\n");
    }
#endif // DEBUG

    // Get the VN for the upper limit.
    ValueNum uLimitVN = upper->gtVNPair.GetConservative();

#ifdef DEBUG
    JITDUMP("VN%04X upper bound is: ", uLimitVN);
    if (m_pCompiler->verbose)
    {
        m_pCompiler->vnStore->vnDump(m_pCompiler, uLimitVN);
    }
    JITDUMP("\n");
#endif
    // If the upper limit is not length, then bail.
    if (!m_pCompiler->vnStore->IsVNArrLen(uLimitVN))
    {
        return false;
    }

    // Get the array reference from the length.
    ValueNum arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(uLimitVN);

#ifdef DEBUG
    JITDUMP("Array ref VN");
    if (m_pCompiler->verbose)
    {
        m_pCompiler->vnStore->vnDump(m_pCompiler, arrRefVN);
    }
    JITDUMP("\n");
#endif

    // Check if array size can be obtained.
    int arrSize = m_pCompiler->vnStore->GetNewArrSize(arrRefVN);
    JITDUMP("Array size is: %d\n", arrSize);

    // Upper limit: a.len + ucns (upper limit constant).
    if (range.UpperLimit().IsBinOpArray())
    {
        if (range.UpperLimit().vn != arrRefVN)
        {
            return false;
        }

        int ucns = range.UpperLimit().GetConstant();
        
        // Upper limit: a.Len + [0..n]
        if (ucns >= 0)
        {
            return false;
        }

        // If lower limit is a.len return false.
        if (range.LowerLimit().IsArray())
        {
            return false;
        }
 
        // Since upper limit is bounded by the array, return true if lower bound is good.
        // TODO-CQ: I am retaining previous behavior to minimize ASM diffs, but we could
        // return "true" here if GetConstant() >= 0 instead of "== 0".
        if (range.LowerLimit().IsConstant() && range.LowerLimit().GetConstant() == 0)
        {
            return true;
        }
        
        // Check if we have the array size allocated by new.
        if (arrSize <= 0)
        {
            return false;
        }

        // At this point,
        // upper limit = a.len + ucns. ucns < 0
        // lower limit = a.len + lcns.
        if (range.LowerLimit().IsBinOpArray())
        {
            int lcns = range.LowerLimit().GetConstant();
            if (lcns >= 0 || -lcns > arrSize)
            {
                return false;
            }
            return (range.LowerLimit().vn == arrRefVN && lcns <= ucns); 
        }
    }
    // If upper limit is constant
    else if (range.UpperLimit().IsConstant())
    {
        if (arrSize <= 0)
        {
            return false;
        }
        int ucns = range.UpperLimit().GetConstant();
        if (ucns >= arrSize)
        {
            return false;
        }
        if (range.LowerLimit().IsConstant())
        {
            int lcns = range.LowerLimit().GetConstant();
            // Make sure lcns < ucns which is already less than arrSize.
            return (lcns >= 0 && lcns <= ucns);
        }
        if (range.LowerLimit().IsBinOpArray())
        {
            int lcns = range.LowerLimit().GetConstant();
            // a.len + lcns, make sure we don't subtract too much from a.len.
            if (lcns >= 0 || -lcns > arrSize)
            {
                return false;
            }
            // Make sure a.len + lcns <= ucns.
            return (range.LowerLimit().vn == arrRefVN && (arrSize + lcns) <= ucns);
        }
    }

    return false;
}
Exemple #27
0
/**************************************************************************************
 *
 * Perform copy propagation on a given tree as we walk the graph and if it is a local
 * variable, then look up all currently live definitions and check if any of those
 * definitions share the same value number. If so, then we can make the replacement.
 *
 */
void Compiler::optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, LclNumToGenTreePtrStack* curSsaName)
{
    // TODO-Review: EH successor/predecessor iteration seems broken.
    if (block->bbCatchTyp == BBCT_FINALLY || block->bbCatchTyp == BBCT_FAULT)
    {
        return;
    }

    // If not local nothing to do.
    if (!tree->IsLocal())
    {
        return;
    }
    if (tree->OperGet() == GT_PHI_ARG || tree->OperGet() == GT_LCL_FLD)
    {
        return;
    }

    // Propagate only on uses.
    if (tree->gtFlags & GTF_VAR_DEF)
    {
        return;
    }
    unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();

    // Skip address exposed variables.
    if (fgExcludeFromSsa(lclNum))
    {
        return;
    }

    assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN);

    for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter)
    {
        unsigned newLclNum = iter.Get();

        GenTreePtr op = iter.GetValue()->Index(0);

        // Nothing to do if same.
        if (lclNum == newLclNum)
        {
            continue;
        }

        // Skip variables with assignments embedded in the statement (i.e., with a comma). Because we
        // are not currently updating their SSA names as live in the copy-prop pass of the stmt.
        if (VarSetOps::IsMember(this, optCopyPropKillSet, lvaTable[newLclNum].lvVarIndex))
        {
            continue;
        }

        if (op->gtFlags & GTF_VAR_CAST)
        {
            continue;
        }
        if (gsShadowVarInfo != nullptr && lvaTable[newLclNum].lvIsParam &&
            gsShadowVarInfo[newLclNum].shadowCopy == lclNum)
        {
            continue;
        }
        ValueNum opVN = GetUseAsgDefVNOrTreeVN(op);
        if (opVN == ValueNumStore::NoVN)
        {
            continue;
        }
        if (op->TypeGet() != tree->TypeGet())
        {
            continue;
        }
        if (opVN != tree->gtVNPair.GetConservative())
        {
            continue;
        }
        if (optCopyProp_LclVarScore(&lvaTable[lclNum], &lvaTable[newLclNum], true) <= 0)
        {
            continue;
        }
        // Check whether the newLclNum is live before being substituted. Otherwise, we could end
        // up in a situation where there must've been a phi node that got pruned because the variable
        // is not live anymore. For example,
        //  if
        //     x0 = 1
        //  else
        //     x1 = 2
        //  print(c) <-- x is not live here. Let's say 'c' shares the value number with "x0."
        //
        // If we simply substituted 'c' with "x0", we would be wrong. Ideally, there would be a phi
        // node x2 = phi(x0, x1) which can then be used to substitute 'c' with. But because of pruning
        // there would be no such phi node. To solve this we'll check if 'x' is live, before replacing
        // 'c' with 'x.'
        if (!lvaTable[newLclNum].lvVerTypeInfo.IsThisPtr())
        {
            if (lvaTable[newLclNum].lvAddrExposed)
            {
                continue;
            }

            // We compute liveness only on tracked variables. So skip untracked locals.
            if (!lvaTable[newLclNum].lvTracked)
            {
                continue;
            }

            // Because of this dependence on live variable analysis, CopyProp phase is immediately
            // after Liveness, SSA and VN.
            if (!VarSetOps::IsMember(this, compCurLife, lvaTable[newLclNum].lvVarIndex))
            {
                continue;
            }
        }
        unsigned newSsaNum = SsaConfig::RESERVED_SSA_NUM;
        if (op->gtFlags & GTF_VAR_DEF)
        {
            newSsaNum = GetSsaNumForLocalVarDef(op);
        }
        else // parameters, this pointer etc.
        {
            newSsaNum = op->AsLclVarCommon()->GetSsaNum();
        }

        if (newSsaNum == SsaConfig::RESERVED_SSA_NUM)
        {
            continue;
        }

#ifdef DEBUG
        if (verbose)
        {
            JITDUMP("VN based copy assertion for ");
            printTreeID(tree);
            printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative));
            printTreeID(op);
            printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative));
            gtDispTree(tree, nullptr, nullptr, true);
        }
#endif

        lvaTable[lclNum].decRefCnts(block->getBBWeight(this), this);
        lvaTable[newLclNum].incRefCnts(block->getBBWeight(this), this);
        tree->gtLclVarCommon.SetLclNum(newLclNum);
        tree->AsLclVarCommon()->SetSsaNum(newSsaNum);
#ifdef DEBUG
        if (verbose)
        {
            printf("copy propagated to:\n");
            gtDispTree(tree, nullptr, nullptr, true);
        }
#endif
        break;
    }
    return;
}
Exemple #28
0
void InlineResult::Report()
{
    // If we weren't actually inlining, user may have suppressed
    // reporting via setReported(). If so, do nothing.
    if (m_Reported)
    {
        return;
    }

    m_Reported = true;

#ifdef DEBUG
    const char* callee = nullptr;

    // Optionally dump the result
    if (VERBOSE)
    {
        const char* format = "INLINER: during '%s' result '%s' reason '%s' for '%s' calling '%s'\n";
        const char* caller = (m_Caller == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Caller);

        callee = (m_Callee == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Callee);

        JITDUMP(format, m_Context, ResultString(), ReasonString(), caller, callee);
    }

    // If the inline failed, leave information on the call so we can
    // later recover what observation lead to the failure.
    if (IsFailure() && (m_Call != nullptr))
    {
        // compiler should have revoked candidacy on the call by now
        assert((m_Call->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);

        m_Call->gtInlineObservation = m_Policy->GetObservation();
    }

#endif // DEBUG

    // Was the result NEVER? If so we might want to propagate this to
    // the runtime.

    if (IsNever() && m_Policy->PropagateNeverToRuntime())
    {
        // If we know the callee, and if the observation that got us
        // to this Never inline state is something *other* than
        // IS_NOINLINE, then we've uncovered a reason why this method
        // can't ever be inlined. Update the callee method attributes
        // so that future inline attempts for this callee fail faster.

        InlineObservation obs = m_Policy->GetObservation();

        if ((m_Callee != nullptr) && (obs != InlineObservation::CALLEE_IS_NOINLINE))
        {

#ifdef DEBUG

            if (VERBOSE)
            {
                const char* obsString = InlGetObservationString(obs);
                JITDUMP("\nINLINER: Marking %s as NOINLINE because of %s\n", callee, obsString);
            }

#endif  // DEBUG

            COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
            comp->setMethodAttribs(m_Callee, CORINFO_FLG_BAD_INLINEE);
        }
    }

    if (IsDecided())
    {
        const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
        JITLOG_THIS(m_RootCompiler, (LL_INFO100000, format, m_Context, ResultString(), ReasonString()));
        COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
        comp->reportInliningDecision(m_Caller, m_Callee, Result(), ReasonString());
    }
}
Exemple #29
0
/**************************************************************************************
 *
 * Perform copy propagation using currently live definitions on the current block's
 * variables. Also as new definitions are encountered update the "curSsaName" which
 * tracks the currently live definitions.
 *
 */
void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName)
{
    JITDUMP("Copy Assertion for BB%02u\n", block->bbNum);

    // There are no definitions at the start of the block. So clear it.
    compCurLifeTree = nullptr;
    VarSetOps::Assign(this, compCurLife, block->bbLiveIn);
    for (GenTreePtr stmt = block->bbTreeList; stmt; stmt = stmt->gtNext)
    {
        VarSetOps::OldStyleClearD(this, optCopyPropKillSet);

        // Walk the tree to find if any local variable can be replaced with current live definitions.
        for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
        {
            compUpdateLife</*ForCodeGen*/ false>(tree);
            optCopyProp(block, stmt, tree, curSsaName);

            // TODO-Review: Merge this loop with the following loop to correctly update the
            // live SSA num while also propagating copies.
            //
            // 1. This loop performs copy prop with currently live (on-top-of-stack) SSA num.
            // 2. The subsequent loop maintains a stack for each lclNum with
            //    currently active SSA numbers when definitions are encountered.
            //
            // If there is an embedded definition using a "comma" in a stmt, then the currently
            // live SSA number will get updated only in the next loop (2). However, this new
            // definition is now supposed to be live (on tos). If we did not update the stacks
            // using (2), copy prop (1) will use a SSA num defined outside the stmt ignoring the
            // embedded update. Killing the variable is a simplification to produce 0 ASM diffs
            // for an update release.
            //
            if (optIsSsaLocal(tree) && (tree->gtFlags & GTF_VAR_DEF))
            {
                VarSetOps::AddElemD(this, optCopyPropKillSet, lvaTable[tree->gtLclVarCommon.gtLclNum].lvVarIndex);
            }
        }

        // This logic must be in sync with SSA renaming process.
        for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
        {
            if (!optIsSsaLocal(tree))
            {
                continue;
            }

            unsigned lclNum = tree->gtLclVarCommon.gtLclNum;

            // As we encounter a definition add it to the stack as a live definition.
            if (tree->gtFlags & GTF_VAR_DEF)
            {
                GenTreePtrStack* stack;
                if (!curSsaName->Lookup(lclNum, &stack))
                {
                    stack = new (getAllocator()) GenTreePtrStack(this);
                }
                stack->Push(tree);
                curSsaName->Set(lclNum, stack);
            }
            // If we encounter first use of a param or this pointer add it as a live definition.
            // Since they are always live, do it only once.
            else if ((tree->gtOper == GT_LCL_VAR) && !(tree->gtFlags & GTF_VAR_USEASG) &&
                     (lvaTable[lclNum].lvIsParam || lvaTable[lclNum].lvVerTypeInfo.IsThisPtr()))
            {
                GenTreePtrStack* stack;
                if (!curSsaName->Lookup(lclNum, &stack))
                {
                    stack = new (getAllocator()) GenTreePtrStack(this);
                    stack->Push(tree);
                    curSsaName->Set(lclNum, stack);
                }
            }
        }
    }
}
Exemple #30
0
//------------------------------------------------------------------------
// DecomposeStoreInd: Decompose GT_STOREIND.
//
// Arguments:
//    tree - the tree to decompose
//
// Return Value:
//    None.
//
void DecomposeLongs::DecomposeStoreInd(GenTree** ppTree, Compiler::fgWalkData* data)
{
    assert(ppTree != nullptr);
    assert(*ppTree != nullptr);
    assert(data != nullptr);
    assert((*ppTree)->OperGet() == GT_STOREIND);
    assert(m_compiler->compCurStmt != nullptr);

    GenTree* tree = *ppTree;

    assert(tree->gtOp.gtOp2->OperGet() == GT_LONG);

    GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt();
    bool isEmbeddedStmt = !curStmt->gtStmtIsTopLevel();

    // Example input trees (a nested embedded statement case)
    //
    //   <linkBegin Node>
    //   *  stmtExpr  void  (top level) (IL   ???...  ???)
    //   |  /--*  argPlace  ref    $280
    //   |  +--*  argPlace  int    $4a
    //   |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //   |  |  {  |     /--*  lclVar    ref    V11 tmp9         u:3 $21c
    //   |  |  {  |     +--*  const     int    4 $44
    //   |  |  {  |  /--*  +         byref  $2c8
    //   |  |  {  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //   |  |  {  |  |     {  |  /--*  lclFld    long   V01 arg1         u:2[+8] Fseq[i] $380
    //   |  |  {  |  |     {  \--*  st.lclVar long  (P) V21 cse8
    //   |  |  {  |  |     {  \--*    int    V21.hi (offs=0x00) -> V22 rat0    
    //   |  |  {  |  |     {  \--*    int    V21.hi (offs=0x04) -> V23 rat1    
    //   |  |  {  |  |  /--*  lclVar    int    V22 rat0          $380
    //   |  |  {  |  |  +--*  lclVar    int    V23 rat1
    //   |  |  {  |  +--*  gt_long   long
    //   |  |  {  \--*  storeIndir long
    //   |  +--*  lclVar    ref    V11 tmp9         u:3 (last use) $21c
    //   |  +--*  lclVar    ref    V02 tmp0         u:3 $280
    //   |  +--*  const     int    8 $4a
    //   \--*  call help void   HELPER.CORINFO_HELP_ARRADDR_ST $205
    //  <linkEndNode>
    //
    // (editor brace matching compensation: }}}}}}}}}}}}}}}}}})

    GenTree* linkBegin = m_compiler->fgGetFirstNode(tree)->gtPrev;
    GenTree* linkEnd = tree->gtNext;
    GenTree* gtLong = tree->gtOp.gtOp2;

    // Save address to a temp. It is used in storeIndLow and storeIndHigh trees.
    GenTreeStmt* addrStmt = CreateTemporary(&tree->gtOp.gtOp1);
    JITDUMP("[DecomposeStoreInd]: Saving address tree to a temp var:\n");
    DISPTREE(addrStmt);

    if (!gtLong->gtOp.gtOp1->OperIsLeaf())
    {
        GenTreeStmt* dataLowStmt = CreateTemporary(&gtLong->gtOp.gtOp1);
        JITDUMP("[DecomposeStoreInd]: Saving low data tree to a temp var:\n");
        DISPTREE(dataLowStmt);
    }

    if (!gtLong->gtOp.gtOp2->OperIsLeaf())
    {
        GenTreeStmt* dataHighStmt = CreateTemporary(&gtLong->gtOp.gtOp2);
        JITDUMP("[DecomposeStoreInd]: Saving high data tree to a temp var:\n");
        DISPTREE(dataHighStmt);
    }

    // Example trees after embedded statements for address and data are added.
    // This example saves all address and data trees into temp variables 
    // to show how those embedded statements are created.
    //
    //  *  stmtExpr  void  (top level) (IL   ???...  ???)
    //  |  /--*  argPlace  ref    $280
    //  |  +--*  argPlace  int    $4a
    //  |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |     /--*  lclVar    ref    V11 tmp9         u:3 $21c
    //  |  |  {  |     +--*  const     int    4 $44
    //  |  |  {  |  /--*  +         byref  $2c8
    //  |  |  {  \--*  st.lclVar byref  V24 rat2
    //  |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |  /--*  lclVar    byref  V24 rat2
    //  |  |  {  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |  |     {  |  /--*  lclFld    long   V01 arg1         u:2[+8] Fseq[i] $380380
    //  |  |  {  |  |     {  \--*  st.lclVar long  (P) V21 cse8
    //  |  |  {  |  |     {  \--*    int    V21.hi (offs=0x00) -> V22 rat0
    //  |  |  {  |  |     {  \--*    int    V21.hi (offs=0x04) -> V23 rat1
    //  |  |  {  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |  |     {  |  /--*  lclVar    int    V22 rat0          $380
    //  |  |  {  |  |     {  \--*  st.lclVar int    V25 rat3
    //  |  |  {  |  |  /--*  lclVar    int    V25 rat3
    //  |  |  {  |  |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |  |  |  {  |  /--*  lclVar    int    V23 rat1
    //  |  |  {  |  |  |  {  \--*  st.lclVar int    V26 rat4
    //  |  |  {  |  |  +--*  lclVar    int    V26 rat4
    //  |  |  {  |  +--*  gt_long   long
    //  |  |  {  \--*  storeIndir long
    //  |  +--*  lclVar    ref    V11 tmp9         u:3 (last use) $21c
    //  |  +--*  lclVar    ref    V02 tmp0         u:3 $280
    //  |  +--*  const     int    8 $4a
    //  \--*  call help void   HELPER.CORINFO_HELP_ARRADDR_ST $205
    //
    // (editor brace matching compensation: }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})

    GenTree* addrBase = tree->gtOp.gtOp1;
    GenTree* dataHigh = gtLong->gtOp.gtOp2;
    GenTree* dataLow = gtLong->gtOp.gtOp1;
    GenTree* storeIndLow = tree;

    // Rewrite storeIndLow tree to save only lower 32-bit data.
    // 
    //  |  |  {  |  /--*  lclVar    byref  V24 rat2   (address)
    //  ...
    //  |  |  {  |  +--*  lclVar    int    V25 rat3   (lower 32-bit data)
    //  |  |  {  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |  {  |  {  |  /--*  lclVar    int    V23 rat1
    //  |  |  {  |  {  \--*  st.lclVar int    V26 rat4
    //  |  |  {  \--*  storeIndir int
    //
    // (editor brace matching compensation: }}}}}}}}})

    m_compiler->fgSnipNode(curStmt, gtLong);
    m_compiler->fgSnipNode(curStmt, dataHigh);
    storeIndLow->gtOp.gtOp2 = dataLow;
    storeIndLow->gtType = TYP_INT;

    // Construct storeIndHigh tree
    //
    // | | {  *stmtExpr  void  (embedded)(IL ? ? ? ... ? ? ? )
    // | | { | / --*  lclVar    int    V26 rat4
    // | | { | | / --*  lclVar    byref  V24 rat2
    // | | { | +--*  lea(b + 4)  ref
    // | | {  \--*  storeIndir int
    //
    // (editor brace matching compensation: }}}}})

    GenTree* addrBaseHigh = new(m_compiler, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, 
        addrBase->TypeGet(), addrBase->AsLclVarCommon()->GetLclNum(), BAD_IL_OFFSET);
    GenTree* addrHigh = new(m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT));
    GenTree* storeIndHigh = new(m_compiler, GT_STOREIND) GenTreeStoreInd(TYP_INT, addrHigh, dataHigh);
    storeIndHigh->gtFlags = (storeIndLow->gtFlags & (GTF_ALL_EFFECT | GTF_LIVENESS_MASK));
    storeIndHigh->gtFlags |= GTF_REVERSE_OPS;
    storeIndHigh->CopyCosts(storeIndLow);

    // Internal links of storeIndHigh tree
    dataHigh->gtPrev = nullptr;
    dataHigh->gtNext = nullptr;
    SimpleLinkNodeAfter(dataHigh, addrBaseHigh);
    SimpleLinkNodeAfter(addrBaseHigh, addrHigh);
    SimpleLinkNodeAfter(addrHigh, storeIndHigh);
    
    // External links of storeIndHigh tree
    //dataHigh->gtPrev = nullptr;
    if (isEmbeddedStmt)
    {
        // If storeIndTree is an embedded statement, connect storeIndLow
        // and dataHigh
        storeIndLow->gtNext = dataHigh;
        dataHigh->gtPrev = storeIndLow;
    }
    storeIndHigh->gtNext = linkEnd;
    if (linkEnd != nullptr)
    {
        linkEnd->gtPrev = storeIndHigh;
    }

    InsertNodeAsStmt(storeIndHigh);

    // Example final output 
    //
    //  *  stmtExpr  void  (top level) (IL   ???...  ???)
    //  |  /--*  argPlace  ref    $280
    //  |  +--*  argPlace  int    $4a
    //  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |     /--*  lclVar    ref    V11 tmp9         u:3 $21c
    //  |  |     {  |     +--*  const     int    4 $44
    //  |  |     {  |  /--*  +         byref  $2c8
    //  |  |     {  \--*  st.lclVar byref  V24 rat2
    //  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |  /--*  lclVar    byref  V24 rat2
    //  |  |     {  |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |  |  {  |     /--*  lclFld    int    V01 arg1         u:2[+8] Fseq[i] $380
    //  |  |     {  |  |  {  |     +--*  lclFld    int    V01 arg1         [+12]
    //  |  |     {  |  |  {  |  /--*  gt_long   long
    //  |  |     {  |  |  {  \--*  st.lclVar long  (P) V21 cse8
    //  |  |     {  |  |  {  \--*    int    V21.hi (offs=0x00) -> V22 rat0    
    //  |  |     {  |  |  {  \--*    int    V21.hi (offs=0x04) -> V23 rat1    
    //  |  |     {  |  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |  |  {  |  /--*  lclVar    int    V22 rat0          $380
    //  |  |     {  |  |  {  \--*  st.lclVar int    V25 rat3
    //  |  |     {  |  +--*  lclVar    int    V25 rat3
    //  |  |     {  |  {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |  {  |  /--*  lclVar    int    V23 rat1
    //  |  |     {  |  {  \--*  st.lclVar int    V26 rat4
    //  |  |     {  \--*  storeIndir int
    //  |  |     {  *  stmtExpr  void  (embedded) (IL   ???...  ???)
    //  |  |     {  |  /--*  lclVar    int    V26 rat4
    //  |  |     {  |  |  /--*  lclVar    byref  V24 rat2
    //  |  |     {  |  +--*  lea(b+4)  ref
    //  |  |     {  \--*  storeIndir int
    //  |  |  /--*  lclVar    ref    V11 tmp9         u:3 (last use) $21c
    //  |  +--*  putarg_stk [+0x00] ref
    //  |  |  /--*  lclVar    ref    V02 tmp0         u:3 $280
    //  |  +--*  putarg_reg ref
    //  |  |  /--*  const     int    8 $4a
    //  |  +--*  putarg_reg int
    //  \--*  call help void   HELPER.CORINFO_HELP_ARRADDR_ST $205
    //
    // (editor brace matching compensation: }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})
}