/************************************************************************************** * * 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 (GenTreePtr stmt = block->bbTreeList; stmt; stmt = stmt->gtNext) { for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext) { if (!tree->IsLocal()) { continue; } unsigned lclNum = tree->gtLclVarCommon.gtLclNum; if (fgExcludeFromSsa(lclNum)) { continue; } if (tree->gtFlags & GTF_VAR_DEF) { GenTreePtrStack* stack = nullptr; curSsaName->Lookup(lclNum, &stack); stack->Pop(); if (stack->Height() == 0) { curSsaName->Remove(lclNum); } } } } }
/************************************************************************************** * * Perform copy propagation on a given tree as we walk the graph and if it is a local * variable, then look up all currently live definitions and check if any of those * definitions share the same value number. If so, then we can make the replacement. * */ void Compiler::optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, LclNumToGenTreePtrStack* curSsaName) { // TODO-Review: EH successor/predecessor iteration seems broken. if (block->bbCatchTyp == BBCT_FINALLY || block->bbCatchTyp == BBCT_FAULT) { return; } // If not local nothing to do. if (!tree->IsLocal()) { return; } if (tree->OperGet() == GT_PHI_ARG || tree->OperGet() == GT_LCL_FLD) { return; } // Propagate only on uses. if (tree->gtFlags & GTF_VAR_DEF) { return; } unsigned lclNum = tree->AsLclVarCommon()->GetLclNum(); // Skip address exposed variables. if (fgExcludeFromSsa(lclNum)) { return; } assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN); for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter) { unsigned newLclNum = iter.Get(); GenTreePtr op = iter.GetValue()->Index(0); // Nothing to do if same. if (lclNum == newLclNum) { continue; } // Skip variables with assignments embedded in the statement (i.e., with a comma). Because we // are not currently updating their SSA names as live in the copy-prop pass of the stmt. if (VarSetOps::IsMember(this, optCopyPropKillSet, lvaTable[newLclNum].lvVarIndex)) { continue; } if (op->gtFlags & GTF_VAR_CAST) { continue; } if (gsShadowVarInfo != nullptr && lvaTable[newLclNum].lvIsParam && gsShadowVarInfo[newLclNum].shadowCopy == lclNum) { continue; } ValueNum opVN = GetUseAsgDefVNOrTreeVN(op); if (opVN == ValueNumStore::NoVN) { continue; } if (op->TypeGet() != tree->TypeGet()) { continue; } if (opVN != tree->gtVNPair.GetConservative()) { continue; } if (optCopyProp_LclVarScore(&lvaTable[lclNum], &lvaTable[newLclNum], true) <= 0) { continue; } // Check whether the newLclNum is live before being substituted. Otherwise, we could end // up in a situation where there must've been a phi node that got pruned because the variable // is not live anymore. For example, // if // x0 = 1 // else // x1 = 2 // print(c) <-- x is not live here. Let's say 'c' shares the value number with "x0." // // If we simply substituted 'c' with "x0", we would be wrong. Ideally, there would be a phi // node x2 = phi(x0, x1) which can then be used to substitute 'c' with. But because of pruning // there would be no such phi node. To solve this we'll check if 'x' is live, before replacing // 'c' with 'x.' if (!lvaTable[newLclNum].lvVerTypeInfo.IsThisPtr()) { if (lvaTable[newLclNum].lvAddrExposed) { continue; } // We compute liveness only on tracked variables. So skip untracked locals. if (!lvaTable[newLclNum].lvTracked) { continue; } // Because of this dependence on live variable analysis, CopyProp phase is immediately // after Liveness, SSA and VN. if (!VarSetOps::IsMember(this, compCurLife, lvaTable[newLclNum].lvVarIndex)) { continue; } } unsigned newSsaNum = SsaConfig::RESERVED_SSA_NUM; if (op->gtFlags & GTF_VAR_DEF) { newSsaNum = GetSsaNumForLocalVarDef(op); } else // parameters, this pointer etc. { newSsaNum = op->AsLclVarCommon()->GetSsaNum(); } if (newSsaNum == SsaConfig::RESERVED_SSA_NUM) { continue; } #ifdef DEBUG if (verbose) { JITDUMP("VN based copy assertion for "); printTreeID(tree); printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative)); printTreeID(op); printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative)); gtDispTree(tree, nullptr, nullptr, true); } #endif lvaTable[lclNum].decRefCnts(block->getBBWeight(this), this); lvaTable[newLclNum].incRefCnts(block->getBBWeight(this), this); tree->gtLclVarCommon.SetLclNum(newLclNum); tree->AsLclVarCommon()->SetSsaNum(newSsaNum); #ifdef DEBUG if (verbose) { printf("copy propagated to:\n"); gtDispTree(tree, nullptr, nullptr, true); } #endif break; } return; }
/************************************************************************************** * * Helper to check if tree is a local that participates in SSA numbering. */ bool Compiler::optIsSsaLocal(GenTreePtr tree) { return tree->IsLocal() && !fgExcludeFromSsa(tree->AsLclVarCommon()->GetLclNum()); }
bool RangeCheck::IsMonotonicallyIncreasing(GenTreePtr expr, SearchPath* path) { JITDUMP("[RangeCheck::IsMonotonicallyIncreasing] %p\n", dspPtr(expr)); if (path->Lookup(expr)) { return true; } // Add hashtable entry for expr. path->Set(expr, NULL); // Remove hashtable entry for expr when we exit the present scope. auto code = [&] { path->Remove(expr); }; jitstd::utility::scoped_code<decltype(code)> finally(code); // If the rhs expr is constant, then it is not part of the dependency // loop which has to increase monotonically. ValueNum vn = expr->gtVNPair.GetConservative(); if (m_pCompiler->vnStore->IsVNConstant(vn)) { return true; } // If the rhs expr is local, then try to find the def of the local. else if (expr->IsLocal()) { Location* loc = GetDef(expr); if (loc == nullptr) { return false; } GenTreePtr asg = loc->parent; assert(asg->OperKind() & GTK_ASGOP); switch (asg->OperGet()) { case GT_ASG: return IsMonotonicallyIncreasing(asg->gtGetOp2(), path); case GT_ASG_ADD: return IsBinOpMonotonicallyIncreasing(asg->gtGetOp1(), asg->gtGetOp2(), GT_ADD, path); } JITDUMP("Unknown local definition type\n"); return false; } else if (expr->OperGet() == GT_ADD) { return IsBinOpMonotonicallyIncreasing(expr->gtGetOp1(), expr->gtGetOp2(), GT_ADD, path); } else if (expr->OperGet() == GT_PHI) { for (GenTreeArgList* args = expr->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest()) { // If the arg is already in the path, skip. if (path->Lookup(args->Current())) { continue; } if (!IsMonotonicallyIncreasing(args->Current(), path)) { JITDUMP("Phi argument not monotonic\n"); return false; } } return true; } JITDUMP("Unknown tree type\n"); return false; }