Example #1
0
void Rationalizer::DoPhase()
{
    DBEXEC(TRUE, SanityCheck());

    comp->compCurBB = nullptr;
    comp->fgOrder   = Compiler::FGOrderLinear;

    BasicBlock* firstBlock = comp->fgFirstBB;

    for (BasicBlock* block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
    {
        comp->compCurBB = block;
        m_block         = block;

        // Establish the first and last nodes for the block. This is necessary in order for the LIR
        // utilities that hang off the BasicBlock type to work correctly.
        GenTreeStmt* firstStatement = block->firstStmt();
        if (firstStatement == nullptr)
        {
            // No statements in this block; skip it.
            block->MakeLIR(nullptr, nullptr);
            continue;
        }

        GenTreeStmt* lastStatement = block->lastStmt();

        // Rewrite intrinsics that are not supported by the target back into user calls.
        // This needs to be done before the transition to LIR because it relies on the use
        // of fgMorphArgs, which is designed to operate on HIR. Once this is done for a
        // particular statement, link that statement's nodes into the current basic block.
        //
        // This walk also clears the GTF_VAR_USEDEF bit on locals, which is not necessary
        // in the backend.
        GenTree* lastNodeInPreviousStatement = nullptr;
        for (GenTreeStmt* statement = firstStatement; statement != nullptr; statement = statement->getNextStmt())
        {
            assert(statement->gtStmtList != nullptr);
            assert(statement->gtStmtList->gtPrev == nullptr);
            assert(statement->gtStmtExpr != nullptr);
            assert(statement->gtStmtExpr->gtNext == nullptr);

            SplitData splitData;
            splitData.root      = statement;
            splitData.block     = block;
            splitData.thisPhase = this;

            comp->fgWalkTreePost(&statement->gtStmtExpr,
                                 [](GenTree** use, Compiler::fgWalkData* walkData) -> Compiler::fgWalkResult {
                                     GenTree* node = *use;
                                     if (node->OperGet() == GT_INTRINSIC &&
                                         Compiler::IsIntrinsicImplementedByUserCall(node->gtIntrinsic.gtIntrinsicId))
                                     {
                                         RewriteIntrinsicAsUserCall(use, walkData);
                                     }
                                     else if (node->OperIsLocal())
                                     {
                                         node->gtFlags &= ~GTF_VAR_USEDEF;
                                     }

                                     return Compiler::WALK_CONTINUE;
                                 },
                                 &splitData, true);

            GenTree* firstNodeInStatement = statement->gtStmtList;
            if (lastNodeInPreviousStatement != nullptr)
            {
                lastNodeInPreviousStatement->gtNext = firstNodeInStatement;
            }

            firstNodeInStatement->gtPrev = lastNodeInPreviousStatement;
            lastNodeInPreviousStatement  = statement->gtStmtExpr;
        }

        block->MakeLIR(firstStatement->gtStmtList, lastStatement->gtStmtExpr);

        // Rewrite HIR nodes into LIR nodes.
        for (GenTreeStmt *statement = firstStatement, *nextStatement; statement != nullptr; statement = nextStatement)
        {
            nextStatement = statement->getNextStmt();

            // If this statement has correct offset information, change it into an IL offset
            // node and insert it into the LIR.
            if (statement->gtStmtILoffsx != BAD_IL_OFFSET)
            {
                assert(!statement->IsPhiDefnStmt());
                statement->SetOper(GT_IL_OFFSET);
                statement->gtNext = nullptr;
                statement->gtPrev = nullptr;

                BlockRange().InsertBefore(statement->gtStmtList, statement);
            }

            m_statement = statement;
            comp->fgWalkTreePost(&statement->gtStmtExpr,
                                 [](GenTree** use, Compiler::fgWalkData* walkData) -> Compiler::fgWalkResult {
                                     return reinterpret_cast<Rationalizer*>(walkData->pCallbackData)
                                         ->RewriteNode(use, *walkData->parentStack);
                                 },
                                 this, true);
        }

        assert(BlockRange().CheckLIR(comp));
    }

    comp->compRationalIRForm = true;
}
Example #2
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
}
Example #3
0
Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<GenTree*>& parentStack)
{
    assert(useEdge != nullptr);

    GenTree* node = *useEdge;
    assert(node != nullptr);

#ifdef DEBUG
    const bool isLateArg = (node->gtFlags & GTF_LATE_ARG) != 0;
#endif

    // First, remove any preceeding GT_LIST nodes, which are not otherwise visited by the tree walk.
    //
    // NOTE: GT_LIST nodes that are used by block ops and phi nodes will in fact be visited.
    for (GenTree* prev = node->gtPrev; prev != nullptr && prev->OperGet() == GT_LIST; prev = node->gtPrev)
    {
        BlockRange().Remove(prev);
    }

    // In addition, remove the current node if it is a GT_LIST node.
    if ((*useEdge)->OperGet() == GT_LIST)
    {
        BlockRange().Remove(*useEdge);
        return Compiler::WALK_CONTINUE;
    }

    LIR::Use use;
    if (parentStack.Height() < 2)
    {
        use = LIR::Use::GetDummyUse(BlockRange(), *useEdge);
    }
    else
    {
        use = LIR::Use(BlockRange(), useEdge, parentStack.Index(1));
    }

    assert(node == use.Def());
    switch (node->OperGet())
    {
        case GT_ASG:
            RewriteAssignment(use);
            break;

        case GT_BOX:
            // GT_BOX at this level just passes through so get rid of it
            use.ReplaceWith(comp, node->gtGetOp1());
            BlockRange().Remove(node);
            break;

        case GT_ADDR:
            RewriteAddress(use);
            break;

        case GT_NOP:
            // fgMorph sometimes inserts NOP nodes between defs and uses
            // supposedly 'to prevent constant folding'. In this case, remove the
            // NOP.
            if (node->gtGetOp1() != nullptr)
            {
                use.ReplaceWith(comp, node->gtGetOp1());
                BlockRange().Remove(node);
            }
            break;

        case GT_COMMA:
        {
            GenTree* op1 = node->gtGetOp1();
            if ((op1->gtFlags & GTF_ALL_EFFECT) == 0)
            {
                // The LHS has no side effects. Remove it.
                bool               isClosed    = false;
                unsigned           sideEffects = 0;
                LIR::ReadOnlyRange lhsRange    = BlockRange().GetTreeRange(op1, &isClosed, &sideEffects);

                // None of the transforms performed herein violate tree order, so these
                // should always be true.
                assert(isClosed);
                assert((sideEffects & GTF_ALL_EFFECT) == 0);

                BlockRange().Delete(comp, m_block, std::move(lhsRange));
            }

            GenTree* replacement = node->gtGetOp2();
            if (!use.IsDummyUse())
            {
                use.ReplaceWith(comp, replacement);
            }
            else
            {
                // This is a top-level comma. If the RHS has no side effects we can remove
                // it as well.
                if ((replacement->gtFlags & GTF_ALL_EFFECT) == 0)
                {
                    bool               isClosed    = false;
                    unsigned           sideEffects = 0;
                    LIR::ReadOnlyRange rhsRange    = BlockRange().GetTreeRange(replacement, &isClosed, &sideEffects);

                    // None of the transforms performed herein violate tree order, so these
                    // should always be true.
                    assert(isClosed);
                    assert((sideEffects & GTF_ALL_EFFECT) == 0);

                    BlockRange().Delete(comp, m_block, std::move(rhsRange));
                }
            }

            BlockRange().Remove(node);
        }
        break;

        case GT_ARGPLACE:
            // Remove argplace and list nodes from the execution order.
            //
            // TODO: remove phi args and phi nodes as well?
            BlockRange().Remove(node);
            break;

#ifdef _TARGET_XARCH_
        case GT_CLS_VAR:
        {
            // Class vars that are the target of an assignment will get rewritten into
            // GT_STOREIND(GT_CLS_VAR_ADDR, val) by RewriteAssignment. This check is
            // not strictly necessary--the GT_IND(GT_CLS_VAR_ADDR) pattern that would
            // otherwise be generated would also be picked up by RewriteAssignment--but
            // skipping the rewrite here saves an allocation and a bit of extra work.
            const bool isLHSOfAssignment = (use.User()->OperGet() == GT_ASG) && (use.User()->gtGetOp1() == node);
            if (!isLHSOfAssignment)
            {
                GenTree* ind = comp->gtNewOperNode(GT_IND, node->TypeGet(), node);

                node->SetOper(GT_CLS_VAR_ADDR);
                node->gtType = TYP_BYREF;

                BlockRange().InsertAfter(node, ind);
                use.ReplaceWith(comp, ind);

                // TODO: JIT dump
            }
        }
        break;
#endif // _TARGET_XARCH_

        case GT_INTRINSIC:
            // Non-target intrinsics should have already been rewritten back into user calls.
            assert(Compiler::IsTargetIntrinsic(node->gtIntrinsic.gtIntrinsicId));
            break;

#ifdef FEATURE_SIMD
        case GT_INITBLK:
            RewriteInitBlk(use);
            break;

        case GT_COPYBLK:
            RewriteCopyBlk(use);
            break;

        case GT_OBJ:
            RewriteObj(use);
            break;

        case GT_LCL_FLD:
        case GT_STORE_LCL_FLD:
            // TODO-1stClassStructs: Eliminate this.
            FixupIfSIMDLocal(node->AsLclVarCommon());
            break;

        case GT_STOREIND:
        case GT_IND:
            if (node->gtType == TYP_STRUCT)
            {
                GenTree* addr = node->AsIndir()->Addr();
                assert(addr->TypeGet() == TYP_BYREF);

                if (addr->OperIsLocal())
                {
                    LclVarDsc* varDsc = &(comp->lvaTable[addr->AsLclVarCommon()->gtLclNum]);
                    assert(varDsc->lvSIMDType);
                    unsigned simdSize = (unsigned int)roundUp(varDsc->lvExactSize, TARGET_POINTER_SIZE);
                    node->gtType      = comp->getSIMDTypeForSize(simdSize);
                }
#if DEBUG
                else
                {
                    // If the address is not a local var, assert that the user of this IND is an ADDR node.
                    assert((use.User()->OperGet() == GT_ADDR) || use.User()->OperIsLocalAddr());
                }
#endif
            }
            break;

        case GT_SIMD:
        {
            noway_assert(comp->featureSIMD);
            GenTreeSIMD* simdNode = node->AsSIMD();
            unsigned     simdSize = simdNode->gtSIMDSize;
            var_types    simdType = comp->getSIMDTypeForSize(simdSize);

            // TODO-1stClassStructs: This should be handled more generally for enregistered or promoted
            // structs that are passed or returned in a different register type than their enregistered
            // type(s).
            if (simdNode->gtType == TYP_I_IMPL && simdNode->gtSIMDSize == TARGET_POINTER_SIZE)
            {
                // This happens when it is consumed by a GT_RET_EXPR.
                // It can only be a Vector2f or Vector2i.
                assert(genTypeSize(simdNode->gtSIMDBaseType) == 4);
                simdNode->gtType = TYP_SIMD8;
            }
            else if (simdNode->gtType == TYP_STRUCT || varTypeIsSIMD(simdNode))
            {
                node->gtType = simdType;
            }

            // Certain SIMD trees require rationalizing.
            if (simdNode->gtSIMD.gtSIMDIntrinsicID == SIMDIntrinsicInitArray)
            {
                // Rewrite this as an explicit load.
                JITDUMP("Rewriting GT_SIMD array init as an explicit load:\n");
                unsigned int baseTypeSize = genTypeSize(simdNode->gtSIMDBaseType);
                GenTree*     address = new (comp, GT_LEA) GenTreeAddrMode(TYP_BYREF, simdNode->gtOp1, simdNode->gtOp2,
                                                                      baseTypeSize, offsetof(CORINFO_Array, u1Elems));
                GenTree* ind = comp->gtNewOperNode(GT_IND, simdType, address);

                BlockRange().InsertBefore(simdNode, address, ind);
                use.ReplaceWith(comp, ind);
                BlockRange().Remove(simdNode);

                DISPTREERANGE(BlockRange(), use.Def());
                JITDUMP("\n");
            }
            else
            {
                // This code depends on the fact that NONE of the SIMD intrinsics take vector operands
                // of a different width.  If that assumption changes, we will EITHER have to make these type
                // transformations during importation, and plumb the types all the way through the JIT,
                // OR add a lot of special handling here.
                GenTree* op1 = simdNode->gtGetOp1();
                if (op1 != nullptr && op1->gtType == TYP_STRUCT)
                {
                    op1->gtType = simdType;
                }

                GenTree* op2 = simdNode->gtGetOp2();
                if (op2 != nullptr && op2->gtType == TYP_STRUCT)
                {
                    op2->gtType = simdType;
                }
            }
        }
        break;
#endif // FEATURE_SIMD

        default:
            break;
    }

    // Do some extra processing on top-level nodes to remove unused local reads.
    if (use.IsDummyUse() && node->OperIsLocalRead())
    {
        assert((node->gtFlags & GTF_ALL_EFFECT) == 0);

        comp->lvaDecRefCnts(node);
        BlockRange().Remove(node);
    }

    assert(isLateArg == ((node->gtFlags & GTF_LATE_ARG) != 0));

    return Compiler::WALK_CONTINUE;
}