//------------------------------------------------------------------------ // DecomposeLclFld: Decompose GT_LCL_FLD. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeLclFld(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_LCL_FLD); GenTree* tree = use.Def(); GenTreeLclFld* loResult = tree->AsLclFld(); loResult->gtType = TYP_INT; GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->gtLclNum, TYP_INT, loResult->gtLclOffs + 4); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeCnsLng: Decompose GT_CNS_LNG. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeCnsLng(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_CNS_LNG); GenTree* tree = use.Def(); INT32 hiVal = tree->AsLngCon()->HiVal(); GenTree* loResult = tree; loResult->ChangeOperConst(GT_CNS_INT); loResult->gtType = TYP_INT; GenTree* hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, hiVal); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeLclVar: Decompose GT_LCL_VAR. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeLclVar(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_LCL_VAR); GenTree* tree = use.Def(); unsigned varNum = tree->AsLclVarCommon()->gtLclNum; LclVarDsc* varDsc = m_compiler->lvaTable + varNum; m_compiler->lvaDecRefCnts(tree); GenTree* loResult = tree; loResult->gtType = TYP_INT; GenTree* hiResult = m_compiler->gtNewLclLNode(varNum, TYP_INT); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); if (varDsc->lvPromoted) { assert(varDsc->lvFieldCnt == 2); unsigned loVarNum = varDsc->lvFieldLclStart; unsigned hiVarNum = loVarNum + 1; loResult->AsLclVarCommon()->SetLclNum(loVarNum); hiResult->AsLclVarCommon()->SetLclNum(hiVarNum); } else { noway_assert(varDsc->lvLRACandidate == false); loResult->SetOper(GT_LCL_FLD); loResult->AsLclFld()->gtLclOffs = 0; loResult->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); hiResult->SetOper(GT_LCL_FLD); hiResult->AsLclFld()->gtLclOffs = 4; hiResult->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField(); } m_compiler->lvaIncRefCnts(loResult); m_compiler->lvaIncRefCnts(hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // FinalizeDecomposition: A helper function to finalize LONG decomposition by // taking the resulting two halves of the decomposition, and tie them together // with a new GT_LONG node that will replace the original node. // // Arguments: // ppTree - the original tree node // data - tree walk context // loResult - the decomposed low part // hiResult - the decomposed high part // // Return Value: // None. // void DecomposeLongs::FinalizeDecomposition(GenTree** ppTree, Compiler::fgWalkData* data, GenTree* loResult, GenTree* hiResult) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert(loResult != nullptr); assert(hiResult != nullptr); assert(m_compiler->compCurStmt != nullptr); GenTree* tree = *ppTree; m_compiler->fgInsertTreeInListAfter(hiResult, loResult, m_compiler->compCurStmt->AsStmt()); hiResult->CopyCosts(tree); GenTree* newTree = new (m_compiler, GT_LONG) GenTreeOp(GT_LONG, TYP_LONG, loResult, hiResult); SimpleLinkNodeAfter(hiResult, newTree); m_compiler->fgFixupIfCallArg(data->parentStack, tree, newTree); newTree->CopyCosts(tree); *ppTree = newTree; }
//------------------------------------------------------------------------ // DecomposeNot: Decompose GT_NOT. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeNot(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_NOT); GenTree* tree = use.Def(); GenTree* gtLong = tree->gtGetOp1(); noway_assert(gtLong->OperGet() == GT_LONG); GenTree* loOp1 = gtLong->gtGetOp1(); GenTree* hiOp1 = gtLong->gtGetOp2(); BlockRange().Remove(gtLong); GenTree* loResult = tree; loResult->gtType = TYP_INT; loResult->gtOp.gtOp1 = loOp1; GenTree* hiResult = new (m_compiler, GT_NOT) GenTreeOp(GT_NOT, TYP_INT, hiOp1, nullptr); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeCast: Decompose GT_CAST. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_CAST); GenTree* tree = use.Def(); GenTree* loResult = nullptr; GenTree* hiResult = nullptr; assert(tree->gtPrev == tree->gtGetOp1()); NYI_IF(tree->gtOverflow(), "TYP_LONG cast with overflow"); switch (tree->AsCast()->CastFromType()) { case TYP_INT: if (tree->gtFlags & GTF_UNSIGNED) { loResult = tree->gtGetOp1(); BlockRange().Remove(tree); hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, 0); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); } else { NYI("Lowering of signed cast TYP_INT->TYP_LONG"); } break; default: NYI("Unimplemented type for Lowering of cast to TYP_LONG"); break; } return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // DecomposeArith: Decompose GT_ADD, GT_SUB, GT_OR, GT_XOR, GT_AND. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeArith(LIR::Use& use) { assert(use.IsInitialized()); GenTree* tree = use.Def(); genTreeOps oper = tree->OperGet(); assert((oper == GT_ADD) || (oper == GT_SUB) || (oper == GT_OR) || (oper == GT_XOR) || (oper == GT_AND)); GenTree* op1 = tree->gtGetOp1(); GenTree* op2 = tree->gtGetOp2(); // Both operands must have already been decomposed into GT_LONG operators. noway_assert((op1->OperGet() == GT_LONG) && (op2->OperGet() == GT_LONG)); // Capture the lo and hi halves of op1 and op2. GenTree* loOp1 = op1->gtGetOp1(); GenTree* hiOp1 = op1->gtGetOp2(); GenTree* loOp2 = op2->gtGetOp1(); GenTree* hiOp2 = op2->gtGetOp2(); // We don't have support to decompose a TYP_LONG node that already has a child that has // been decomposed into parts, where the high part depends on the value generated by the // low part (via the flags register). For example, if we have: // +(gt_long(+(lo3, lo4), +Hi(hi3, hi4)), gt_long(lo2, hi2)) // We would decompose it here to: // gt_long(+(+(lo3, lo4), lo2), +Hi(+Hi(hi3, hi4), hi2)) // But this would generate incorrect code, because the "+Hi(hi3, hi4)" code generation // needs to immediately follow the "+(lo3, lo4)" part. Also, if this node is one that // requires a unique high operator, and the child nodes are not simple locals (e.g., // they are decomposed nodes), then we also can't decompose the node, as we aren't // guaranteed the high and low parts will be executed immediately after each other. NYI_IF(hiOp1->OperIsHigh() || hiOp2->OperIsHigh() || (GenTree::OperIsHigh(GetHiOper(oper)) && (!loOp1->OperIsLeaf() || !hiOp1->OperIsLeaf() || !loOp1->OperIsLeaf() || !hiOp2->OperIsLeaf())), "Can't decompose expression tree TYP_LONG node"); // Now, remove op1 and op2 from the node list. BlockRange().Remove(op1); BlockRange().Remove(op2); // We will reuse "tree" for the loResult, which will now be of TYP_INT, and its operands // will be the lo halves of op1 from above. GenTree* loResult = tree; loResult->SetOper(GetLoOper(loResult->OperGet())); loResult->gtType = TYP_INT; loResult->gtOp.gtOp1 = loOp1; loResult->gtOp.gtOp2 = loOp2; GenTree* hiResult = new (m_compiler, oper) GenTreeOp(GetHiOper(oper), TYP_INT, hiOp1, hiOp2); hiResult->CopyCosts(loResult); BlockRange().InsertAfter(loResult, hiResult); if ((oper == GT_ADD) || (oper == GT_SUB)) { if (loResult->gtOverflow()) { hiResult->gtFlags |= GTF_OVERFLOW; loResult->gtFlags &= ~GTF_OVERFLOW; } if (loResult->gtFlags & GTF_UNSIGNED) { hiResult->gtFlags |= GTF_UNSIGNED; } } return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeStoreLclVar: Decompose GT_STORE_LCL_VAR. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeStoreLclVar(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_STORE_LCL_VAR); GenTree* tree = use.Def(); 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 tree->gtNext; } 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' is going to steal the loRhs node for itself, so we need to remove the // GT_LONG node from the threading. BlockRange().Remove(rhs); tree->gtOp.gtOp1 = loRhs; tree->gtType = TYP_INT; hiStore->gtOp.gtOp1 = hiRhs; hiStore->gtFlags |= GTF_VAR_DEF; m_compiler->lvaIncRefCnts(tree); m_compiler->lvaIncRefCnts(hiStore); hiStore->CopyCosts(tree); BlockRange().InsertAfter(tree, hiStore); return hiStore->gtNext; }