//------------------------------------------------------------------------ // DecomposeNot: Decompose GT_NOT. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeNot(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_NOT); assert(m_compiler->compCurStmt != nullptr); GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); GenTree* tree = *ppTree; GenTree* op1 = tree->gtGetOp1(); noway_assert(op1->OperGet() == GT_LONG); GenTree* loOp1 = op1->gtGetOp1(); GenTree* hiOp1 = op1->gtGetOp2(); m_compiler->fgSnipNode(curStmt, op1); GenTree* loResult = tree; loResult->gtType = TYP_INT; loResult->gtOp.gtOp1 = loOp1; loOp1->gtNext = loResult; loResult->gtPrev = loOp1; GenTree* hiResult = new (m_compiler, GT_NOT) GenTreeOp(GT_NOT, TYP_INT, hiOp1, nullptr); hiOp1->gtNext = hiResult; hiResult->gtPrev = hiOp1; FinalizeDecomposition(ppTree, data, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeNeg: Decompose GT_NEG. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeNeg(LIR::Use& use) { assert(use.IsInitialized()); assert(use.Def()->OperGet() == GT_NEG); GenTree* tree = use.Def(); GenTree* gtLong = tree->gtGetOp1(); noway_assert(gtLong->OperGet() == GT_LONG); LIR::Use op1(Range(), >Long->gtOp.gtOp1, gtLong); op1.ReplaceWithLclVar(m_compiler, m_blockWeight); LIR::Use op2(Range(), >Long->gtOp.gtOp2, gtLong); op2.ReplaceWithLclVar(m_compiler, m_blockWeight); // Neither GT_NEG nor the introduced temporaries have side effects. tree->gtFlags &= ~GTF_ALL_EFFECT; GenTree* loOp1 = gtLong->gtGetOp1(); GenTree* hiOp1 = gtLong->gtGetOp2(); Range().Remove(gtLong); GenTree* loResult = tree; loResult->gtType = TYP_INT; loResult->gtOp.gtOp1 = loOp1; GenTree* zero = m_compiler->gtNewZeroConNode(TYP_INT); GenTree* hiAdjust = m_compiler->gtNewOperNode(GT_ADD_HI, TYP_INT, hiOp1, zero); GenTree* hiResult = m_compiler->gtNewOperNode(GT_NEG, TYP_INT, hiAdjust); hiResult->gtFlags = tree->gtFlags; Range().InsertAfter(loResult, zero, hiAdjust, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // 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(); Range().Remove(tree); hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, 0); Range().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); }
//------------------------------------------------------------------------ // 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(); // Now, remove op1 and op2 from the node list. Range().Remove(op1); Range().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(oper)); 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); Range().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); }
//------------------------------------------------------------------------ // 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); Range().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); Range().InsertAfter(loResult, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeLclFld: Decompose GT_LCL_FLD. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeLclFld(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_LCL_FLD); GenTree* tree = *ppTree; GenTreeLclFld* loResult = tree->AsLclFld(); loResult->gtType = TYP_INT; GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->gtLclNum, TYP_INT, loResult->gtLclOffs + 4); FinalizeDecomposition(ppTree, data, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeCnsLng: Decompose GT_CNS_LNG. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeCnsLng(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_CNS_LNG); GenTree* tree = *ppTree; 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); FinalizeDecomposition(ppTree, data, 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); }
//------------------------------------------------------------------------ // DecomposeLclVar: Decompose GT_LCL_VAR. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeLclVar(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_LCL_VAR); GenTree* tree = *ppTree; 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); 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); FinalizeDecomposition(ppTree, data, loResult, hiResult); }
//------------------------------------------------------------------------ // DecomposeNeg: Decompose GT_NEG. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeNeg(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_NEG); assert(m_compiler->compCurStmt != nullptr); GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); GenTree* tree = *ppTree; GenTree* op1 = tree->gtGetOp1(); noway_assert(op1->OperGet() == GT_LONG); CreateTemporary(&(op1->gtOp.gtOp1)); CreateTemporary(&(op1->gtOp.gtOp2)); // Neither GT_NEG nor the introduced temporaries have side effects. tree->gtFlags &= ~GTF_ALL_EFFECT; GenTree* loOp1 = op1->gtGetOp1(); GenTree* hiOp1 = op1->gtGetOp2(); Compiler::fgSnipNode(curStmt, op1); GenTree* loResult = tree; loResult->gtType = TYP_INT; loResult->gtOp.gtOp1 = loOp1; GenTree* zero = m_compiler->gtNewZeroConNode(TYP_INT); GenTree* hiAdjust = m_compiler->gtNewOperNode(GT_ADD_HI, TYP_INT, hiOp1, zero); GenTree* hiResult = m_compiler->gtNewOperNode(GT_NEG, TYP_INT, hiAdjust); hiResult->gtFlags = tree->gtFlags; Compiler::fgSnipNode(curStmt, hiOp1); // fgSnipNode doesn't clear gtNext/gtPrev... hiOp1->gtNext = nullptr; hiOp1->gtPrev = nullptr; SimpleLinkNodeAfter(hiOp1, zero); SimpleLinkNodeAfter(zero, hiAdjust); SimpleLinkNodeAfter(hiAdjust, hiResult); FinalizeDecomposition(ppTree, data, loResult, hiResult); }
//------------------------------------------------------------------------ // 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(); Range().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); Range().InsertAfter(loResult, hiResult); return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // DecomposeCast: Decompose GT_CAST. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeCast(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert((*ppTree)->OperGet() == GT_CAST); assert(m_compiler->compCurStmt != nullptr); GenTree* tree = *ppTree; GenTree* loResult = nullptr; GenTree* hiResult = nullptr; GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); 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(); hiResult = new (m_compiler, GT_CNS_INT) GenTreeIntCon(TYP_INT, 0); m_compiler->fgSnipNode(curStmt, tree); } else { NYI("Lowering of signed cast TYP_INT->TYP_LONG"); } break; default: NYI("Unimplemented type for Lowering of cast to TYP_LONG"); break; } FinalizeDecomposition(ppTree, data, 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* cast = use.Def()->AsCast(); GenTree* loResult = nullptr; GenTree* hiResult = nullptr; var_types srcType = cast->CastFromType(); var_types dstType = cast->CastToType(); if ((cast->gtFlags & GTF_UNSIGNED) != 0) { srcType = genUnsignedType(srcType); } if (varTypeIsLong(srcType)) { if (cast->gtOverflow() && (varTypeIsUnsigned(srcType) != varTypeIsUnsigned(dstType))) { GenTree* srcOp = cast->gtGetOp1(); noway_assert(srcOp->OperGet() == GT_LONG); GenTree* loSrcOp = srcOp->gtGetOp1(); GenTree* hiSrcOp = srcOp->gtGetOp2(); // // When casting between long types an overflow check is needed only if the types // have different signedness. In both cases (long->ulong and ulong->long) we only // need to check if the high part is negative or not. Use the existing cast node // to perform a int->uint cast of the high part to take advantage of the overflow // check provided by codegen. // loResult = loSrcOp; hiResult = cast; hiResult->gtType = TYP_INT; hiResult->AsCast()->gtCastType = TYP_UINT; hiResult->gtFlags &= ~GTF_UNSIGNED; hiResult->gtOp.gtOp1 = hiSrcOp; Range().Remove(cast); Range().Remove(srcOp); Range().InsertAfter(hiSrcOp, hiResult); } else { NYI("Unimplemented long->long no-op cast decomposition"); } } else if (varTypeIsIntegralOrI(srcType)) { if (cast->gtOverflow() && !varTypeIsUnsigned(srcType) && varTypeIsUnsigned(dstType)) { // // An overflow check is needed only when casting from a signed type to ulong. // Change the cast type to uint to take advantage of the overflow check provided // by codegen and then zero extend the resulting uint to ulong. // loResult = cast; loResult->AsCast()->gtCastType = TYP_UINT; loResult->gtType = TYP_INT; hiResult = m_compiler->gtNewZeroConNode(TYP_INT); Range().InsertAfter(loResult, hiResult); } else { if (varTypeIsUnsigned(srcType)) { loResult = cast->gtGetOp1(); hiResult = m_compiler->gtNewZeroConNode(TYP_INT); Range().Remove(cast); Range().InsertAfter(loResult, hiResult); } else { LIR::Use src(Range(), &(cast->gtOp.gtOp1), cast); unsigned lclNum = src.ReplaceWithLclVar(m_compiler, m_blockWeight); loResult = src.Def(); GenTree* loCopy = m_compiler->gtNewLclvNode(lclNum, TYP_INT); GenTree* shiftBy = m_compiler->gtNewIconNode(31, TYP_INT); hiResult = m_compiler->gtNewOperNode(GT_RSH, TYP_INT, loCopy, shiftBy); Range().Remove(cast); Range().InsertAfter(loResult, loCopy, shiftBy, hiResult); m_compiler->lvaIncRefCnts(loCopy); } } } else { NYI("Unimplemented cast decomposition"); } return FinalizeDecomposition(use, loResult, hiResult); }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // DecomposeArith: Decompose GT_ADD, GT_SUB, GT_OR, GT_XOR, GT_AND. // // Arguments: // ppTree - the tree to decompose // data - tree walk context // // Return Value: // None. // void DecomposeLongs::DecomposeArith(GenTree** ppTree, Compiler::fgWalkData* data) { assert(ppTree != nullptr); assert(*ppTree != nullptr); assert(data != nullptr); assert(m_compiler->compCurStmt != nullptr); GenTreeStmt* curStmt = m_compiler->compCurStmt->AsStmt(); GenTree* tree = *ppTree; genTreeOps oper = tree->OperGet(); assert((oper == GT_ADD) || (oper == GT_SUB) || (oper == GT_OR) || (oper == GT_XOR) || (oper == GT_AND)); NYI_IF((tree->gtFlags & GTF_REVERSE_OPS) != 0, "Binary operator with GTF_REVERSE_OPS"); 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. m_compiler->fgSnipNode(curStmt, op1); m_compiler->fgSnipNode(curStmt, 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; // The various halves will be correctly threaded internally. We simply need to // relink them into the proper order, i.e. loOp1 is followed by loOp2, and then // the loResult node. // (This rethreading, and that below, are where we need to address the reverse ops case). // The current order is (after snipping op1 and op2): // ... loOp1-> ... hiOp1->loOp2First ... loOp2->hiOp2First ... hiOp2 // The order we want is: // ... loOp1->loOp2First ... loOp2->loResult // ... hiOp1->hiOp2First ... hiOp2->hiResult // i.e. we swap hiOp1 and loOp2, and create (for now) separate loResult and hiResult trees GenTree* loOp2First = hiOp1->gtNext; GenTree* hiOp2First = loOp2->gtNext; // First, we will NYI if both hiOp1 and loOp2 have side effects. NYI_IF(((loOp2->gtFlags & GTF_ALL_EFFECT) != 0) && ((hiOp1->gtFlags & GTF_ALL_EFFECT) != 0), "Binary long operator with non-reorderable sub expressions"); // Now, we reorder the loOps and the loResult. loOp1->gtNext = loOp2First; loOp2First->gtPrev = loOp1; loOp2->gtNext = loResult; loResult->gtPrev = loOp2; // Next, reorder the hiOps and the hiResult. GenTree* hiResult = new (m_compiler, oper) GenTreeOp(GetHiOper(oper), TYP_INT, hiOp1, hiOp2); hiOp1->gtNext = hiOp2First; hiOp2First->gtPrev = hiOp1; hiOp2->gtNext = hiResult; hiResult->gtPrev = hiOp2; 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; } } FinalizeDecomposition(ppTree, data, loResult, hiResult); }