Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/IPO/SCCP.cpp
35269 views
//===-- SCCP.cpp ----------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements Interprocedural Sparse Conditional Constant Propagation.9//10//===----------------------------------------------------------------------===//1112#include "llvm/Transforms/IPO/SCCP.h"13#include "llvm/ADT/SetVector.h"14#include "llvm/Analysis/AssumptionCache.h"15#include "llvm/Analysis/BlockFrequencyInfo.h"16#include "llvm/Analysis/PostDominators.h"17#include "llvm/Analysis/TargetLibraryInfo.h"18#include "llvm/Analysis/TargetTransformInfo.h"19#include "llvm/Analysis/ValueLattice.h"20#include "llvm/Analysis/ValueLatticeUtils.h"21#include "llvm/Analysis/ValueTracking.h"22#include "llvm/IR/AttributeMask.h"23#include "llvm/IR/Constants.h"24#include "llvm/IR/DIBuilder.h"25#include "llvm/IR/IntrinsicInst.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/ModRef.h"28#include "llvm/Transforms/IPO.h"29#include "llvm/Transforms/IPO/FunctionSpecialization.h"30#include "llvm/Transforms/Scalar/SCCP.h"31#include "llvm/Transforms/Utils/Local.h"32#include "llvm/Transforms/Utils/SCCPSolver.h"3334using namespace llvm;3536#define DEBUG_TYPE "sccp"3738STATISTIC(NumInstRemoved, "Number of instructions removed");39STATISTIC(NumArgsElimed ,"Number of arguments constant propagated");40STATISTIC(NumGlobalConst, "Number of globals found to be constant");41STATISTIC(NumDeadBlocks , "Number of basic blocks unreachable");42STATISTIC(NumInstReplaced,43"Number of instructions replaced with (simpler) instruction");4445static cl::opt<unsigned> FuncSpecMaxIters(46"funcspec-max-iters", cl::init(10), cl::Hidden, cl::desc(47"The maximum number of iterations function specialization is run"));4849static void findReturnsToZap(Function &F,50SmallVector<ReturnInst *, 8> &ReturnsToZap,51SCCPSolver &Solver) {52// We can only do this if we know that nothing else can call the function.53if (!Solver.isArgumentTrackedFunction(&F))54return;5556if (Solver.mustPreserveReturn(&F)) {57LLVM_DEBUG(58dbgs()59<< "Can't zap returns of the function : " << F.getName()60<< " due to present musttail or \"clang.arc.attachedcall\" call of "61"it\n");62return;63}6465assert(66all_of(F.users(),67[&Solver](User *U) {68if (isa<Instruction>(U) &&69!Solver.isBlockExecutable(cast<Instruction>(U)->getParent()))70return true;71// Non-callsite uses are not impacted by zapping. Also, constant72// uses (like blockaddresses) could stuck around, without being73// used in the underlying IR, meaning we do not have lattice74// values for them.75if (!isa<CallBase>(U))76return true;77if (U->getType()->isStructTy()) {78return all_of(Solver.getStructLatticeValueFor(U),79[](const ValueLatticeElement &LV) {80return !SCCPSolver::isOverdefined(LV);81});82}8384// We don't consider assume-like intrinsics to be actual address85// captures.86if (auto *II = dyn_cast<IntrinsicInst>(U)) {87if (II->isAssumeLikeIntrinsic())88return true;89}9091return !SCCPSolver::isOverdefined(Solver.getLatticeValueFor(U));92}) &&93"We can only zap functions where all live users have a concrete value");9495for (BasicBlock &BB : F) {96if (CallInst *CI = BB.getTerminatingMustTailCall()) {97LLVM_DEBUG(dbgs() << "Can't zap return of the block due to present "98<< "musttail call : " << *CI << "\n");99(void)CI;100return;101}102103if (auto *RI = dyn_cast<ReturnInst>(BB.getTerminator()))104if (!isa<UndefValue>(RI->getOperand(0)))105ReturnsToZap.push_back(RI);106}107}108109static bool runIPSCCP(110Module &M, const DataLayout &DL, FunctionAnalysisManager *FAM,111std::function<const TargetLibraryInfo &(Function &)> GetTLI,112std::function<TargetTransformInfo &(Function &)> GetTTI,113std::function<AssumptionCache &(Function &)> GetAC,114std::function<DominatorTree &(Function &)> GetDT,115std::function<BlockFrequencyInfo &(Function &)> GetBFI,116bool IsFuncSpecEnabled) {117SCCPSolver Solver(DL, GetTLI, M.getContext());118FunctionSpecializer Specializer(Solver, M, FAM, GetBFI, GetTLI, GetTTI,119GetAC);120121// Loop over all functions, marking arguments to those with their addresses122// taken or that are external as overdefined.123for (Function &F : M) {124if (F.isDeclaration())125continue;126127DominatorTree &DT = GetDT(F);128AssumptionCache &AC = GetAC(F);129Solver.addPredicateInfo(F, DT, AC);130131// Determine if we can track the function's return values. If so, add the132// function to the solver's set of return-tracked functions.133if (canTrackReturnsInterprocedurally(&F))134Solver.addTrackedFunction(&F);135136// Determine if we can track the function's arguments. If so, add the137// function to the solver's set of argument-tracked functions.138if (canTrackArgumentsInterprocedurally(&F)) {139Solver.addArgumentTrackedFunction(&F);140continue;141}142143// Assume the function is called.144Solver.markBlockExecutable(&F.front());145146for (Argument &AI : F.args())147Solver.trackValueOfArgument(&AI);148}149150// Determine if we can track any of the module's global variables. If so, add151// the global variables we can track to the solver's set of tracked global152// variables.153for (GlobalVariable &G : M.globals()) {154G.removeDeadConstantUsers();155if (canTrackGlobalVariableInterprocedurally(&G))156Solver.trackValueOfGlobalVariable(&G);157}158159// Solve for constants.160Solver.solveWhileResolvedUndefsIn(M);161162if (IsFuncSpecEnabled) {163unsigned Iters = 0;164while (Iters++ < FuncSpecMaxIters && Specializer.run());165}166167// Iterate over all of the instructions in the module, replacing them with168// constants if we have found them to be of constant values.169bool MadeChanges = false;170for (Function &F : M) {171if (F.isDeclaration())172continue;173174SmallVector<BasicBlock *, 512> BlocksToErase;175176if (Solver.isBlockExecutable(&F.front())) {177bool ReplacedPointerArg = false;178for (Argument &Arg : F.args()) {179if (!Arg.use_empty() && Solver.tryToReplaceWithConstant(&Arg)) {180ReplacedPointerArg |= Arg.getType()->isPointerTy();181++NumArgsElimed;182}183}184185// If we replaced an argument, we may now also access a global (currently186// classified as "other" memory). Update memory attribute to reflect this.187if (ReplacedPointerArg) {188auto UpdateAttrs = [&](AttributeList AL) {189MemoryEffects ME = AL.getMemoryEffects();190if (ME == MemoryEffects::unknown())191return AL;192193ME |= MemoryEffects(IRMemLocation::Other,194ME.getModRef(IRMemLocation::ArgMem));195return AL.addFnAttribute(196F.getContext(),197Attribute::getWithMemoryEffects(F.getContext(), ME));198};199200F.setAttributes(UpdateAttrs(F.getAttributes()));201for (User *U : F.users()) {202auto *CB = dyn_cast<CallBase>(U);203if (!CB || CB->getCalledFunction() != &F)204continue;205206CB->setAttributes(UpdateAttrs(CB->getAttributes()));207}208}209MadeChanges |= ReplacedPointerArg;210}211212SmallPtrSet<Value *, 32> InsertedValues;213for (BasicBlock &BB : F) {214if (!Solver.isBlockExecutable(&BB)) {215LLVM_DEBUG(dbgs() << " BasicBlock Dead:" << BB);216++NumDeadBlocks;217218MadeChanges = true;219220if (&BB != &F.front())221BlocksToErase.push_back(&BB);222continue;223}224225MadeChanges |= Solver.simplifyInstsInBlock(226BB, InsertedValues, NumInstRemoved, NumInstReplaced);227}228229DominatorTree *DT = FAM->getCachedResult<DominatorTreeAnalysis>(F);230PostDominatorTree *PDT = FAM->getCachedResult<PostDominatorTreeAnalysis>(F);231DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);232// Change dead blocks to unreachable. We do it after replacing constants233// in all executable blocks, because changeToUnreachable may remove PHI234// nodes in executable blocks we found values for. The function's entry235// block is not part of BlocksToErase, so we have to handle it separately.236for (BasicBlock *BB : BlocksToErase) {237NumInstRemoved += changeToUnreachable(BB->getFirstNonPHIOrDbg(),238/*PreserveLCSSA=*/false, &DTU);239}240if (!Solver.isBlockExecutable(&F.front()))241NumInstRemoved += changeToUnreachable(F.front().getFirstNonPHIOrDbg(),242/*PreserveLCSSA=*/false, &DTU);243244BasicBlock *NewUnreachableBB = nullptr;245for (BasicBlock &BB : F)246MadeChanges |= Solver.removeNonFeasibleEdges(&BB, DTU, NewUnreachableBB);247248for (BasicBlock *DeadBB : BlocksToErase)249if (!DeadBB->hasAddressTaken())250DTU.deleteBB(DeadBB);251252for (BasicBlock &BB : F) {253for (Instruction &Inst : llvm::make_early_inc_range(BB)) {254if (Solver.getPredicateInfoFor(&Inst)) {255if (auto *II = dyn_cast<IntrinsicInst>(&Inst)) {256if (II->getIntrinsicID() == Intrinsic::ssa_copy) {257Value *Op = II->getOperand(0);258Inst.replaceAllUsesWith(Op);259Inst.eraseFromParent();260}261}262}263}264}265}266267// If we inferred constant or undef return values for a function, we replaced268// all call uses with the inferred value. This means we don't need to bother269// actually returning anything from the function. Replace all return270// instructions with return undef.271//272// Do this in two stages: first identify the functions we should process, then273// actually zap their returns. This is important because we can only do this274// if the address of the function isn't taken. In cases where a return is the275// last use of a function, the order of processing functions would affect276// whether other functions are optimizable.277SmallVector<ReturnInst*, 8> ReturnsToZap;278279for (const auto &I : Solver.getTrackedRetVals()) {280Function *F = I.first;281const ValueLatticeElement &ReturnValue = I.second;282283// If there is a known constant range for the return value, add range284// attribute to the return value.285if (ReturnValue.isConstantRange() &&286!ReturnValue.getConstantRange().isSingleElement()) {287// Do not add range metadata if the return value may include undef.288if (ReturnValue.isConstantRangeIncludingUndef())289continue;290291// Do not touch existing attribute for now.292// TODO: We should be able to take the intersection of the existing293// attribute and the inferred range.294if (F->hasRetAttribute(Attribute::Range))295continue;296auto &CR = ReturnValue.getConstantRange();297F->addRangeRetAttr(CR);298continue;299}300if (F->getReturnType()->isVoidTy())301continue;302if (SCCPSolver::isConstant(ReturnValue) || ReturnValue.isUnknownOrUndef())303findReturnsToZap(*F, ReturnsToZap, Solver);304}305306for (auto *F : Solver.getMRVFunctionsTracked()) {307assert(F->getReturnType()->isStructTy() &&308"The return type should be a struct");309StructType *STy = cast<StructType>(F->getReturnType());310if (Solver.isStructLatticeConstant(F, STy))311findReturnsToZap(*F, ReturnsToZap, Solver);312}313314// Zap all returns which we've identified as zap to change.315SmallSetVector<Function *, 8> FuncZappedReturn;316for (ReturnInst *RI : ReturnsToZap) {317Function *F = RI->getParent()->getParent();318RI->setOperand(0, PoisonValue::get(F->getReturnType()));319// Record all functions that are zapped.320FuncZappedReturn.insert(F);321}322323// Remove the returned attribute for zapped functions and the324// corresponding call sites.325// Also remove any attributes that convert an undef return value into326// immediate undefined behavior327AttributeMask UBImplyingAttributes =328AttributeFuncs::getUBImplyingAttributes();329for (Function *F : FuncZappedReturn) {330for (Argument &A : F->args())331F->removeParamAttr(A.getArgNo(), Attribute::Returned);332F->removeRetAttrs(UBImplyingAttributes);333for (Use &U : F->uses()) {334CallBase *CB = dyn_cast<CallBase>(U.getUser());335if (!CB) {336assert(isa<BlockAddress>(U.getUser()) ||337(isa<Constant>(U.getUser()) &&338all_of(U.getUser()->users(), [](const User *UserUser) {339return cast<IntrinsicInst>(UserUser)->isAssumeLikeIntrinsic();340})));341continue;342}343344for (Use &Arg : CB->args())345CB->removeParamAttr(CB->getArgOperandNo(&Arg), Attribute::Returned);346CB->removeRetAttrs(UBImplyingAttributes);347}348}349350// If we inferred constant or undef values for globals variables, we can351// delete the global and any stores that remain to it.352for (const auto &I : make_early_inc_range(Solver.getTrackedGlobals())) {353GlobalVariable *GV = I.first;354if (SCCPSolver::isOverdefined(I.second))355continue;356LLVM_DEBUG(dbgs() << "Found that GV '" << GV->getName()357<< "' is constant!\n");358while (!GV->use_empty()) {359StoreInst *SI = cast<StoreInst>(GV->user_back());360SI->eraseFromParent();361}362363// Try to create a debug constant expression for the global variable364// initializer value.365SmallVector<DIGlobalVariableExpression *, 1> GVEs;366GV->getDebugInfo(GVEs);367if (GVEs.size() == 1) {368DIBuilder DIB(M);369if (DIExpression *InitExpr = getExpressionForConstant(370DIB, *GV->getInitializer(), *GV->getValueType()))371GVEs[0]->replaceOperandWith(1, InitExpr);372}373374MadeChanges = true;375M.eraseGlobalVariable(GV);376++NumGlobalConst;377}378379return MadeChanges;380}381382PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) {383const DataLayout &DL = M.getDataLayout();384auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();385auto GetTLI = [&FAM](Function &F) -> const TargetLibraryInfo & {386return FAM.getResult<TargetLibraryAnalysis>(F);387};388auto GetTTI = [&FAM](Function &F) -> TargetTransformInfo & {389return FAM.getResult<TargetIRAnalysis>(F);390};391auto GetAC = [&FAM](Function &F) -> AssumptionCache & {392return FAM.getResult<AssumptionAnalysis>(F);393};394auto GetDT = [&FAM](Function &F) -> DominatorTree & {395return FAM.getResult<DominatorTreeAnalysis>(F);396};397auto GetBFI = [&FAM](Function &F) -> BlockFrequencyInfo & {398return FAM.getResult<BlockFrequencyAnalysis>(F);399};400401402if (!runIPSCCP(M, DL, &FAM, GetTLI, GetTTI, GetAC, GetDT, GetBFI,403isFuncSpecEnabled()))404return PreservedAnalyses::all();405406PreservedAnalyses PA;407PA.preserve<DominatorTreeAnalysis>();408PA.preserve<PostDominatorTreeAnalysis>();409PA.preserve<FunctionAnalysisManagerModuleProxy>();410return PA;411}412413414