//------------------------------------------------------------------------ // CreateTemporary: call fgInsertEmbeddedFormTemp to replace *ppTree with // a new temp that is assigned to the value previously at *ppTree by inserting // an embedded statement. In addition, if the resulting statement actually ends // up being top-level, it might pull along some embedded statements that have // not yet been decomposed. So recursively decompose those before returning. // // Arguments: // *ppTree - tree to replace with a temp. // // Return Value: // The new statement that was created to create the temp. // GenTreeStmt* DecomposeLongs::CreateTemporary(GenTree** ppTree) { GenTreeStmt* newStmt = m_compiler->fgInsertEmbeddedFormTemp(ppTree); if (newStmt->gtStmtIsTopLevel()) { for (GenTreeStmt* nextEmbeddedStmt = newStmt->gtStmtNextIfEmbedded(); nextEmbeddedStmt != nullptr; nextEmbeddedStmt = nextEmbeddedStmt->gtStmt.gtStmtNextIfEmbedded()) { DecomposeStmt(nextEmbeddedStmt); } } return newStmt; }
//------------------------------------------------------------------------ // InsertNodeAsStmt: Insert a node as the root node of a new statement. // If the current statement is embedded, the new statement will also be // embedded. Otherwise, the new statement will be top level. // // Arguments: // node - node to insert // // Return Value: // None // // Notes: // compCurStmt and compCurBB must be correctly set. // void DecomposeLongs::InsertNodeAsStmt(GenTree* node) { assert(node != nullptr); assert(m_compiler->compCurBB != nullptr); assert(m_compiler->compCurStmt != nullptr); GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); GenTreeStmt* newStmt; if (curStmt->gtStmtIsTopLevel()) { newStmt = m_compiler->fgNewStmtFromTree(node); // Find an insert point. Skip all embedded statements. GenTree* insertPt = curStmt; while ((insertPt->gtNext != nullptr) && (!insertPt->gtNext->AsStmt()->gtStmtIsTopLevel())) { insertPt = insertPt->gtNext; } m_compiler->fgInsertStmtAfter(m_compiler->compCurBB, insertPt, newStmt); } else { // The current statement is an embedded statement. Create a new embedded statement to // contain the node. First, find the parent non-embedded statement containing the // current statement. GenTree* parentStmt = curStmt; while ((parentStmt != nullptr) && (!parentStmt->AsStmt()->gtStmtIsTopLevel())) { parentStmt = parentStmt->gtPrev; } assert(parentStmt != nullptr); newStmt = m_compiler->fgMakeEmbeddedStmt(m_compiler->compCurBB, node, parentStmt); } newStmt->gtStmtILoffsx = curStmt->gtStmtILoffsx; #ifdef DEBUG newStmt->gtStmtLastILoffs = curStmt->gtStmtLastILoffs; #endif // DEBUG }
//------------------------------------------------------------------------ // 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(>Long->gtOp.gtOp1); JITDUMP("[DecomposeStoreInd]: Saving low data tree to a temp var:\n"); DISPTREE(dataLowStmt); } if (!gtLong->gtOp.gtOp2->OperIsLeaf()) { GenTreeStmt* dataHighStmt = CreateTemporary(>Long->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: }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}) }
//------------------------------------------------------------------------ // DecomposeStoreLclVar: Decompose GT_STORE_LCL_VAR. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeStoreLclVar(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_STORE_LCL_VAR); assert(m_compiler->compCurStmt != nullptr); GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); GenTree* tree = *ppTree; GenTree* nextTree = tree->gtNext; GenTree* rhs = tree->gtGetOp1(); if ((rhs->OperGet() == GT_PHI) || (rhs->OperGet() == GT_CALL)) { // GT_CALLs are not decomposed, so will not be converted to GT_LONG // GT_STORE_LCL_VAR = GT_CALL are handled in genMultiRegCallStoreToLocal return; } noway_assert(rhs->OperGet() == GT_LONG); unsigned varNum = tree->AsLclVarCommon()->gtLclNum; LclVarDsc* varDsc = m_compiler->lvaTable + varNum; m_compiler->lvaDecRefCnts(tree); GenTree* loRhs = rhs->gtGetOp1(); GenTree* hiRhs = rhs->gtGetOp2(); GenTree* hiStore = m_compiler->gtNewLclLNode(varNum, TYP_INT); if (varDsc->lvPromoted) { assert(varDsc->lvFieldCnt == 2); unsigned loVarNum = varDsc->lvFieldLclStart; unsigned hiVarNum = loVarNum + 1; tree->AsLclVarCommon()->SetLclNum(loVarNum); hiStore->SetOper(GT_STORE_LCL_VAR); hiStore->AsLclVarCommon()->SetLclNum(hiVarNum); } else { noway_assert(varDsc->lvLRACandidate == false); tree->SetOper(GT_STORE_LCL_FLD); tree->AsLclFld()->gtLclOffs = 0; tree->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); hiStore->SetOper(GT_STORE_LCL_FLD); hiStore->AsLclFld()->gtLclOffs = 4; hiStore->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); } tree->gtOp.gtOp1 = loRhs; tree->gtType = TYP_INT; loRhs->gtNext = tree; tree->gtPrev = loRhs; hiStore->gtOp.gtOp1 = hiRhs; hiStore->CopyCosts(tree); hiStore->gtFlags |= GTF_VAR_DEF; m_compiler->lvaIncRefCnts(tree); m_compiler->lvaIncRefCnts(hiStore); tree->gtNext = hiRhs; hiRhs->gtPrev = tree; hiRhs->gtNext = hiStore; hiStore->gtPrev = hiRhs; hiStore->gtNext = nextTree; if (nextTree != nullptr) { nextTree->gtPrev = hiStore; } nextTree = hiRhs; bool isEmbeddedStmt = !curStmt->gtStmtIsTopLevel(); if (!isEmbeddedStmt) { tree->gtNext = nullptr; hiRhs->gtPrev = nullptr; } InsertNodeAsStmt(hiStore); }
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; }
void Rationalizer::DoPhase() { class RationalizeVisitor final : public GenTreeVisitor<RationalizeVisitor> { Rationalizer& m_rationalizer; public: enum { ComputeStack = true, DoPreOrder = true, DoPostOrder = true, UseExecutionOrder = true, }; RationalizeVisitor(Rationalizer& rationalizer) : GenTreeVisitor<RationalizeVisitor>(rationalizer.comp), m_rationalizer(rationalizer) { } // 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. fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) { GenTree* const node = *use; if (node->OperGet() == GT_INTRINSIC && Compiler::IsIntrinsicImplementedByUserCall(node->gtIntrinsic.gtIntrinsicId)) { m_rationalizer.RewriteIntrinsicAsUserCall(use, this->m_ancestors); } return Compiler::WALK_CONTINUE; } // Rewrite HIR nodes into LIR nodes. fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) { return m_rationalizer.RewriteNode(use, this->m_ancestors); } }; DBEXEC(TRUE, SanityCheck()); comp->compCurBB = nullptr; comp->fgOrder = Compiler::FGOrderLinear; RationalizeVisitor visitor(*this); for (BasicBlock* block = comp->fgFirstBB; block != nullptr; block = block->bbNext) { comp->compCurBB = block; m_block = block; GenTreeStmt* firstStatement = block->firstStmt(); block->MakeLIR(nullptr, nullptr); // 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. if (firstStatement == nullptr) { // No statements in this block; skip it. continue; } for (GenTreeStmt *statement = firstStatement, *nextStatement; statement != nullptr; statement = nextStatement) { assert(statement->gtStmtList != nullptr); assert(statement->gtStmtList->gtPrev == nullptr); assert(statement->gtStmtExpr != nullptr); assert(statement->gtStmtExpr->gtNext == nullptr); BlockRange().InsertAtEnd(LIR::Range(statement->gtStmtList, statement->gtStmtExpr)); nextStatement = statement->getNextStmt(); statement->gtNext = nullptr; statement->gtPrev = nullptr; // 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); BlockRange().InsertBefore(statement->gtStmtList, statement); } m_block = block; visitor.WalkTree(&statement->gtStmtExpr, nullptr); } assert(BlockRange().CheckLIR(comp, true)); } comp->compRationalIRForm = true; }