//------------------------------------------------------------------------ // TreeNodeInfoInitStoreLoc: Set register requirements for a store of a lclVar // // Arguments: // storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR) // // Notes: // This involves: // - Setting the appropriate candidates for a store of a multi-reg call return value. // - Handling of contained immediates. // void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc) { TreeNodeInfo* info = &(storeLoc->gtLsraInfo); // Is this the case of var = call where call is returning // a value in multiple return registers? GenTree* op1 = storeLoc->gtGetOp1(); if (op1->IsMultiRegCall()) { // backend expects to see this case only for store lclvar. assert(storeLoc->OperGet() == GT_STORE_LCL_VAR); // srcCount = number of registers in which the value is returned by call GenTreeCall* call = op1->AsCall(); ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); info->srcCount = retTypeDesc->GetReturnRegCount(); // Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1 regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call); op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates); } #if defined(_TARGET_ARM_) else if (op1->OperGet() == GT_LONG) { op1->SetContained(); } #endif // _TARGET_ARM_ else { CheckImmedAndMakeContained(storeLoc, op1); } }
//------------------------------------------------------------------------ // TreeNodeInfoInitStoreLoc: Set register requirements for a store of a lclVar // // Arguments: // storeLoc - the local store (GT_STORE_LCL_FLD or GT_STORE_LCL_VAR) // // Notes: // This involves: // - Setting the appropriate candidates for a store of a multi-reg call return value. // - Handling of contained immediates. // void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc) { ContainCheckStoreLoc(storeLoc); TreeNodeInfo* info = &(storeLoc->gtLsraInfo); GenTree* op1 = storeLoc->gtGetOp1(); info->dstCount = 0; #ifdef _TARGET_ARM_ if (varTypeIsLong(op1)) { info->srcCount = 2; assert(!op1->OperIs(GT_LONG) || op1->isContained()); } else #endif // _TARGET_ARM_ if (op1->isContained()) { info->srcCount = 0; } else if (op1->IsMultiRegCall()) { // This is the case of var = call where call is returning // a value in multiple return registers. // Must be a store lclvar. assert(storeLoc->OperGet() == GT_STORE_LCL_VAR); // srcCount = number of registers in which the value is returned by call GenTreeCall* call = op1->AsCall(); ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); info->srcCount = retTypeDesc->GetReturnRegCount(); // Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1 regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call); op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates); } else { info->srcCount = 1; } }
//------------------------------------------------------------------------ // DecomposeShift: Decompose GT_LSH, GT_RSH, GT_RSZ. For shift nodes, we need to use // the shift helper functions, so we here convert the shift into a helper call by // pulling its arguments out of linear order and making them the args to a call, then // replacing the original node with the new call. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) { assert(use.IsInitialized()); GenTree* tree = use.Def(); GenTree* gtLong = tree->gtGetOp1(); genTreeOps oper = tree->OperGet(); assert((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)); LIR::Use loOp1Use(Range(), >Long->gtOp.gtOp1, gtLong); loOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); LIR::Use hiOp1Use(Range(), >Long->gtOp.gtOp2, gtLong); hiOp1Use.ReplaceWithLclVar(m_compiler, m_blockWeight); LIR::Use shiftWidthUse(Range(), &tree->gtOp.gtOp2, tree); shiftWidthUse.ReplaceWithLclVar(m_compiler, m_blockWeight); GenTree* loOp1 = gtLong->gtGetOp1(); GenTree* hiOp1 = gtLong->gtGetOp2(); GenTree* shiftWidthOp = tree->gtGetOp2(); Range().Remove(gtLong); Range().Remove(loOp1); Range().Remove(hiOp1); Range().Remove(shiftWidthOp); // TODO-X86-CQ: If the shift operand is a GT_CNS_INT, we should pipe the instructions through to codegen // and generate the shift instructions ourselves there, rather than replacing it with a helper call. unsigned helper; switch (oper) { case GT_LSH: helper = CORINFO_HELP_LLSH; break; case GT_RSH: helper = CORINFO_HELP_LRSH; break; case GT_RSZ: helper = CORINFO_HELP_LRSZ; break; default: unreached(); } GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftWidthOp); GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, 0, argList); GenTreeCall* callNode = call->AsCall(); ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc(); retTypeDesc->InitializeLongReturnType(m_compiler); call = m_compiler->fgMorphArgs(callNode); Range().InsertAfter(tree, LIR::SeqTree(m_compiler, call)); Range().Remove(tree); use.ReplaceWith(m_compiler, call); return call; }