//------------------------------------------------------------------------ // StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't, // store the node to a var. Then decompose the new LclVar. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::StoreNodeToVar(LIR::Use& use) { if (use.IsDummyUse()) return use.Def()->gtNext; GenTree* tree = use.Def(); GenTree* user = use.User(); if (user->OperGet() == GT_STORE_LCL_VAR) { // If parent is already a STORE_LCL_VAR, we can skip it if // it is already marked as lvIsMultiRegRet. unsigned varNum = user->AsLclVarCommon()->gtLclNum; if (m_compiler->lvaTable[varNum].lvIsMultiRegRet) { return tree->gtNext; } else if (!m_compiler->lvaTable[varNum].lvPromoted) { // If var wasn't promoted, we can just set lvIsMultiRegRet. m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; return tree->gtNext; } } // Otherwise, we need to force var = call() unsigned varNum = use.ReplaceWithLclVar(m_compiler, m_blockWeight); m_compiler->lvaTable[varNum].lvIsMultiRegRet = true; // Decompose the new LclVar use return DecomposeLclVar(use); }
void Rationalizer::RewriteAssignment(LIR::Use& use) { assert(use.IsInitialized()); GenTreeOp* assignment = use.Def()->AsOp(); assert(assignment->OperGet() == GT_ASG); GenTree* location = assignment->gtGetOp1(); GenTree* value = assignment->gtGetOp2(); genTreeOps locationOp = location->OperGet(); switch (locationOp) { case GT_LCL_VAR: case GT_LCL_FLD: case GT_REG_VAR: case GT_PHI_ARG: RewriteAssignmentIntoStoreLclCore(assignment, location, value, locationOp); BlockRange().Remove(location); break; case GT_IND: { GenTreeStoreInd* store = new (comp, GT_STOREIND) GenTreeStoreInd(location->TypeGet(), location->gtGetOp1(), value); copyFlags(store, assignment, GTF_ALL_EFFECT); copyFlags(store, location, GTF_IND_FLAGS); if (assignment->IsReverseOp()) { store->gtFlags |= GTF_REVERSE_OPS; } // TODO: JIT dump // Remove the GT_IND node and replace the assignment node with the store BlockRange().Remove(location); BlockRange().InsertBefore(assignment, store); use.ReplaceWith(comp, store); BlockRange().Remove(assignment); } break; case GT_CLS_VAR: { location->SetOper(GT_CLS_VAR_ADDR); location->gtType = TYP_BYREF; assignment->SetOper(GT_STOREIND); // TODO: JIT dump } break; default: unreached(); break; } }
InlineContext* InlineContext::NewSuccess(Compiler* compiler, InlineInfo* inlineInfo) { InlineContext* calleeContext = new (compiler, CMK_Inlining) InlineContext; GenTree* stmt = inlineInfo->iciStmt; BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode; InlineContext* parentContext = stmt->gtStmt.gtInlineContext; noway_assert(parentContext != nullptr); calleeContext->m_Code = calleeIL; calleeContext->m_Parent = parentContext; // Push on front here will put siblings in reverse lexical // order which we undo in the dumper calleeContext->m_Sibling = parentContext->m_Child; parentContext->m_Child = calleeContext; calleeContext->m_Child = nullptr; calleeContext->m_Offset = stmt->AsStmt()->gtStmtILoffsx; calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); #ifdef DEBUG calleeContext->m_Callee = inlineInfo->fncHandle; calleeContext->m_TreeID = inlineInfo->inlineResult->GetCall()->gtTreeID; #endif return calleeContext; }
GenTree* isNodeCallArg(ArrayStack<GenTree*>* parentStack) { for (int i = 1; // 0 is current node, so start at 1 i < parentStack->Height(); i++) { GenTree* node = parentStack->Index(i); switch (node->OperGet()) { case GT_LIST: case GT_ARGPLACE: break; case GT_NOP: // Currently there's an issue when the rationalizer performs // the fixup of a call argument: the case is when we remove an // inserted NOP as a parent of a call introduced by fgMorph; // when then the rationalizer removes it, the tree stack in the // walk is not consistent with the node it was just deleted, so the // solution is just to go 1 level deeper. // TODO-Cleanup: This has to be fixed in a proper way: make the rationalizer // correctly modify the evaluation stack when removing treenodes. if (node->gtOp.gtOp1->gtOper == GT_CALL) { return node->gtOp.gtOp1; } break; case GT_CALL: return node; default: return nullptr; } } return nullptr; }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // LowerRotate: Lower GT_ROL and GT_ROR nodes. // // Arguments: // tree - the node to lower // // Return Value: // None. // void Lowering::LowerRotate(GenTree* tree) { if (tree->OperGet() == GT_ROL) { // There is no ROL instruction on ARM. Convert ROL into ROR. GenTree* rotatedValue = tree->gtOp.gtOp1; unsigned rotatedValueBitSize = genTypeSize(rotatedValue->gtType) * 8; GenTree* rotateLeftIndexNode = tree->gtOp.gtOp2; if (rotateLeftIndexNode->IsCnsIntOrI()) { ssize_t rotateLeftIndex = rotateLeftIndexNode->gtIntCon.gtIconVal; ssize_t rotateRightIndex = rotatedValueBitSize - rotateLeftIndex; rotateLeftIndexNode->gtIntCon.gtIconVal = rotateRightIndex; } else { GenTree* tmp = comp->gtNewOperNode(GT_NEG, genActualType(rotateLeftIndexNode->gtType), rotateLeftIndexNode); BlockRange().InsertAfter(rotateLeftIndexNode, tmp); tree->gtOp.gtOp2 = tmp; } tree->ChangeOper(GT_ROR); } ContainCheckShiftRotate(tree->AsOp()); }
/************************************************************************************** * * Corresponding to the live definition pushes, pop the stack as we finish a sub-paths * of the graph originating from the block. Refer SSA renaming for any additional info. * "curSsaName" tracks the currently live definitions. */ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName) { for (GenTree* stmt = block->bbTreeList; stmt; stmt = stmt->gtNext) { for (GenTree* tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext) { if (!tree->IsLocal()) { continue; } unsigned lclNum = tree->gtLclVarCommon.gtLclNum; if (!lvaInSsa(lclNum)) { continue; } if (tree->gtFlags & GTF_VAR_DEF) { GenTreePtrStack* stack = nullptr; curSsaName->Lookup(lclNum, &stack); stack->Pop(); if (stack->Height() == 0) { curSsaName->Remove(lclNum); } } } } }
//------------------------------------------------------------------------ // DecomposeMul: Decompose GT_MUL. The only GT_MULs that make it to decompose are // those with the GTF_MUL_64RSLT flag set. These muls result in a mul instruction that // returns its result in two registers like GT_CALLs do. Additionally, these muls are // guaranteed to be in the form long = (long)int * (long)int. Therefore, to decompose // these nodes, we convert them into GT_MUL_LONGs, undo the cast from int to long by // stripping out the lo ops, and force them into the form var = mul, as we do for // GT_CALLs. In codegen, we then produce a mul instruction that produces the result // in edx:eax, and store those registers on the stack in genStoreLongLclVar. // // All other GT_MULs have been converted to helper calls in morph.cpp // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeMul(LIR::Use& use) { assert(use.IsInitialized()); GenTree* tree = use.Def(); genTreeOps oper = tree->OperGet(); assert(oper == GT_MUL); assert((tree->gtFlags & GTF_MUL_64RSLT) != 0); GenTree* op1 = tree->gtGetOp1(); GenTree* op2 = tree->gtGetOp2(); GenTree* loOp1 = op1->gtGetOp1(); GenTree* hiOp1 = op1->gtGetOp2(); GenTree* loOp2 = op2->gtGetOp1(); GenTree* hiOp2 = op2->gtGetOp2(); Range().Remove(hiOp1); Range().Remove(hiOp2); Range().Remove(op1); Range().Remove(op2); // Get rid of the hi ops. We don't need them. tree->gtOp.gtOp1 = loOp1; tree->gtOp.gtOp2 = loOp2; tree->gtOper = GT_MUL_LONG; return StoreNodeToVar(use); }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // ContainCheckIndir: Determine whether operands of an indir should be contained. // // Arguments: // indirNode - The indirection node of interest // // Notes: // This is called for both store and load indirections. // // Return Value: // None. // void Lowering::ContainCheckIndir(GenTreeIndir* indirNode) { // If this is the rhs of a block copy it will be handled when we handle the store. if (indirNode->TypeGet() == TYP_STRUCT) { return; } #ifdef FEATURE_SIMD // If indirTree is of TYP_SIMD12, don't mark addr as contained // so that it always get computed to a register. This would // mean codegen side logic doesn't need to handle all possible // addr expressions that could be contained. // // TODO-ARM64-CQ: handle other addr mode expressions that could be marked // as contained. if (indirNode->TypeGet() == TYP_SIMD12) { return; } #endif // FEATURE_SIMD GenTree* addr = indirNode->Addr(); bool makeContained = true; if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirNode, addr)) { GenTreeAddrMode* lea = addr->AsAddrMode(); GenTree* base = lea->Base(); GenTree* index = lea->Index(); int cns = lea->Offset(); #ifdef _TARGET_ARM_ // ARM floating-point load/store doesn't support a form similar to integer // ldr Rdst, [Rbase + Roffset] with offset in a register. The only supported // form is vldr Rdst, [Rbase + imm] with a more limited constraint on the imm. if (lea->HasIndex() || !emitter::emitIns_valid_imm_for_vldst_offset(cns)) { if (indirNode->OperGet() == GT_STOREIND) { if (varTypeIsFloating(indirNode->AsStoreInd()->Data())) { makeContained = false; } } else if (indirNode->OperGet() == GT_IND) { if (varTypeIsFloating(indirNode)) { makeContained = false; } } } #endif if (makeContained) { MakeSrcContained(indirNode, addr); } } }
//------------------------------------------------------------------------ // AssertWhenAllocObjFoundVisitor: Look for a GT_ALLOCOBJ node and assert // when found one. Compiler::fgWalkResult ObjectAllocator::AssertWhenAllocObjFoundVisitor(GenTree** pTree, Compiler::fgWalkData* data) { GenTree* tree = *pTree; assert(tree != nullptr); assert(tree->OperGet() != GT_ALLOCOBJ); return Compiler::fgWalkResult::WALK_CONTINUE; }
GenTreeStmt* BasicBlock::lastStmt() { if (bbTreeList == nullptr) return nullptr; GenTree* result = bbTreeList->gtPrev; assert(result && result->gtNext == nullptr); return result->AsStmt(); }
void Compiler::optDumpCopyPropStack(LclNumToGenTreePtrStack* curSsaName) { JITDUMP("{ "); for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter) { GenTree* node = iter.GetValue()->Index(0); JITDUMP("%d-[%06d]:V%02u ", iter.Get(), dspTreeID(node), node->AsLclVarCommon()->gtLclNum); } JITDUMP("}\n\n"); }
void Rationalizer::RewriteAssignmentIntoStoreLcl(GenTreeOp* assignment) { assert(assignment != nullptr); assert(assignment->OperGet() == GT_ASG); GenTree* location = assignment->gtGetOp1(); GenTree* value = assignment->gtGetOp2(); RewriteAssignmentIntoStoreLclCore(assignment, location, value, location->OperGet()); }
//------------------------------------------------------------------------ // ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained. // // Arguments: // node - pointer to the node // void Lowering::ContainCheckStoreIndir(GenTreeIndir* node) { #ifdef _TARGET_ARM64_ GenTree* src = node->gtOp.gtOp2; if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0)) { // an integer zero for 'src' can be contained. MakeSrcContained(node, src); } #endif // _TARGET_ARM64_ ContainCheckIndir(node); }
//------------------------------------------------------------------------ // ContainCheckCast: determine whether the source of a CAST node should be contained. // // Arguments: // node - pointer to the node // void Lowering::ContainCheckCast(GenTreeCast* node) { #ifdef _TARGET_ARM_ GenTree* castOp = node->CastOp(); var_types castToType = node->CastToType(); var_types srcType = castOp->TypeGet(); if (varTypeIsLong(castOp)) { assert(castOp->OperGet() == GT_LONG); MakeSrcContained(node, castOp); } #endif // _TARGET_ARM_ }
// Driver for testing the general tree implementation int main() { GenTree<int> tree; GTNode<int>* ptr; GenTree<int> tree2; GTNode<int>* ptr2; tree.newroot(1, NULL, NULL); ptr = tree.root(); cout << "Print the tree with one node\n"; tree.print(); ptr->insertFirst(new GTNode<int>(2)); cout << "Print the tree with two nodes\n"; tree.print(); ptr = ptr->leftmostChild(); cout << "ptr now at node " << ptr->value() << "\n"; ptr->insertNext(new GTNode<int>(3)); cout << "Print the tree with three nodes\n"; tree.print(); ptr->insertNext(new GTNode<int>(4)); cout << "Print the tree with four nodes\n"; tree.print(); ptr = ptr->rightSibling(); cout << "ptr now at node " << ptr->value() << "\n"; ptr->insertFirst(new GTNode<int>(5)); cout << "Print the tree with 5 nodes\n"; tree.print(); tree2.newroot(11, NULL, NULL); ptr2 = tree2.root(); ptr2->insertFirst(new GTNode<int>(12)); ptr2 = ptr2->leftmostChild(); ptr2->insertNext(new GTNode<int>(13)); return 0; }
Compiler::fgWalkResult CodeGen::genRegVarDiesInSubTreeWorker(GenTree** pTree, Compiler::fgWalkData* data) { GenTree* tree = *pTree; genRegVarDiesInSubTreeData* pData = (genRegVarDiesInSubTreeData*)data->pCallbackData; // if it's dying, just rename the register, else load it normally if (tree->IsRegVar() && tree->IsRegVarDeath() && tree->gtRegVar.gtRegNum == pData->reg) { pData->result = true; return Compiler::WALK_ABORT; } return Compiler::WALK_CONTINUE; }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // DecomposeBlock: Do LONG decomposition to all the statements in the given block. // This must be done before lowering the block, as decomposition can insert // additional statements. // // Decomposition is done as a post-order tree walk. Lower levels of the tree can // create new nodes that need to be further decomposed at higher levels. That is, // the decomposition "bubbles up" the tree. // // Arguments: // block - the block to process // // Return Value: // None. // void DecomposeLongs::DecomposeBlock(BasicBlock* block) { assert(block == m_compiler->compCurBB); // compCurBB must already be set. for (GenTree* stmt = block->bbTreeList; stmt != nullptr; stmt = stmt->gtNext) { #ifdef DEBUG if (m_compiler->verbose) { printf("Decomposing BB%02u, stmt id %u\n", block->bbNum, stmt->gtTreeID); } #endif // DEBUG DecomposeStmt(stmt->AsStmt()); } }
InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) { InlineContext* calleeContext = new (m_Compiler, CMK_Inlining) InlineContext(this); GenTree* stmt = inlineInfo->iciStmt; BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode; unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize; InlineContext* parentContext = stmt->gtStmt.gtInlineContext; noway_assert(parentContext != nullptr); calleeContext->m_Code = calleeIL; calleeContext->m_ILSize = calleeILSize; calleeContext->m_Parent = parentContext; // Push on front here will put siblings in reverse lexical // order which we undo in the dumper calleeContext->m_Sibling = parentContext->m_Child; parentContext->m_Child = calleeContext; calleeContext->m_Child = nullptr; calleeContext->m_Offset = stmt->AsStmt()->gtStmtILoffsx; calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); calleeContext->m_Success = true; #if defined(DEBUG) || defined(INLINE_DATA) InlinePolicy* policy = inlineInfo->inlineResult->GetPolicy(); calleeContext->m_Policy = policy; calleeContext->m_CodeSizeEstimate = policy->CodeSizeEstimate(); calleeContext->m_Callee = inlineInfo->fncHandle; // +1 here since we set this before calling NoteOutcome. calleeContext->m_Ordinal = m_InlineCount + 1; // Update offset with more accurate info calleeContext->m_Offset = inlineInfo->inlineResult->GetCall()->gtRawILOffset; #endif // defined(DEBUG) || defined(INLINE_DATA) #if defined(DEBUG) calleeContext->m_TreeID = inlineInfo->inlineResult->GetCall()->gtTreeID; #endif // defined(DEBUG) NoteOutcome(calleeContext); return calleeContext; }
//------------------------------------------------------------------------ // 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); }
//------------------------------------------------------------------------ // ContainCheckIndir: Determine whether operands of an indir should be contained. // // Arguments: // indirNode - The indirection node of interest // // Notes: // This is called for both store and load indirections. // // Return Value: // None. // void Lowering::ContainCheckIndir(GenTreeIndir* indirNode) { // If this is the rhs of a block copy it will be handled when we handle the store. if (indirNode->TypeGet() == TYP_STRUCT) { return; } GenTree* addr = indirNode->Addr(); bool makeContained = true; if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirNode, addr)) { GenTreeAddrMode* lea = addr->AsAddrMode(); GenTree* base = lea->Base(); GenTree* index = lea->Index(); int cns = lea->Offset(); #ifdef _TARGET_ARM_ // ARM floating-point load/store doesn't support a form similar to integer // ldr Rdst, [Rbase + Roffset] with offset in a register. The only supported // form is vldr Rdst, [Rbase + imm] with a more limited constraint on the imm. if (lea->HasIndex() || !emitter::emitIns_valid_imm_for_vldst_offset(cns)) { if (indirNode->OperGet() == GT_STOREIND) { if (varTypeIsFloating(indirNode->AsStoreInd()->Data())) { makeContained = false; } } else if (indirNode->OperGet() == GT_IND) { if (varTypeIsFloating(indirNode)) { makeContained = false; } } } #endif if (makeContained) { MakeSrcContained(indirNode, addr); } } }
// sanity checks that apply to all kinds of IR void Rationalizer::SanityCheck() { // TODO: assert(!IsLIR()); BasicBlock* block; foreach_block(comp, block) { for (GenTree* statement = block->bbTreeList; statement != nullptr; statement = statement->gtNext) { ValidateStatement(statement, block); for (GenTree* tree = statement->gtStmt.gtStmtList; tree; tree = tree->gtNext) { // QMARK nodes should have been removed before this phase. assert(tree->OperGet() != GT_QMARK); if (tree->OperGet() == GT_ASG) { if (tree->gtGetOp1()->OperGet() == GT_LCL_VAR) { assert(tree->gtGetOp1()->gtFlags & GTF_VAR_DEF); } else if (tree->gtGetOp2()->OperGet() == GT_LCL_VAR) { assert(!(tree->gtGetOp2()->gtFlags & GTF_VAR_DEF)); } } } } } }
//------------------------------------------------------------------------ // 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); }
// Rewrite GT_OBJ of SIMD Vector as GT_IND(GT_LEA(obj.op1)) of a SIMD type. // // Arguments: // ppTree - A pointer-to-a-pointer for the GT_OBJ // fgWalkData - A pointer to tree walk data providing the context // // Return Value: // None. // // TODO-Cleanup: Once SIMD types are plumbed through the frontend, this will no longer // be required. // void Rationalizer::RewriteObj(LIR::Use& use) { #ifdef FEATURE_SIMD GenTreeObj* obj = use.Def()->AsObj(); // For UNIX struct passing, we can have Obj nodes for arguments. // For other cases, we should never see a non-SIMD type here. #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING if (!varTypeIsSIMD(obj)) { return; } #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING // Should come here only if featureSIMD is enabled noway_assert(comp->featureSIMD); // We should only call this with a SIMD type. noway_assert(varTypeIsSIMD(obj)); var_types simdType = obj->TypeGet(); // If the operand of obj is a GT_ADDR(GT_LCL_VAR) and LclVar is known to be a SIMD type, // replace obj by GT_LCL_VAR. GenTree* srcAddr = obj->gtGetOp1(); if (srcAddr->OperIsLocalAddr() && comp->isAddrOfSIMDType(srcAddr)) { BlockRange().Remove(obj); srcAddr->SetOper(loadForm(srcAddr->OperGet())); srcAddr->gtType = simdType; use.ReplaceWith(comp, srcAddr); } else { obj->SetOper(GT_IND); obj->gtType = simdType; } #else // we should never reach without feature SIMD assert(!"Unexpected obj during rationalization\n"); unreached(); #endif }
//------------------------------------------------------------------------ // ContainCheckShiftRotate: Determine whether a mul op's operands should be contained. // // Arguments: // node - the node we care about // void Lowering::ContainCheckShiftRotate(GenTreeOp* node) { GenTree* shiftBy = node->gtOp2; assert(node->OperIsShiftOrRotate()); #ifdef _TARGET_ARM_ GenTree* source = node->gtOp1; if (node->OperIs(GT_LSH_HI, GT_RSH_LO)) { assert(source->OperGet() == GT_LONG); MakeSrcContained(node, source); } #endif // _TARGET_ARM_ if (shiftBy->IsCnsIntOrI()) { MakeSrcContained(node, shiftBy); } }
//------------------------------------------------------------------------ // 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); }