1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00
llvm-mirror/lib/Transforms/Scalar/SCCP.cpp
Sjoerd Meijer 6a49dbd1a3 Function Specialization Pass
This adds a function specialization pass to LLVM. Constant parameters
like function pointers and constant globals are propagated to the callee by
specializing the function.

This is a first version with a number of limitations:
- The pass is off by default, so needs to be enabled on the command line,
- It does not handle specialization of recursive functions,
- It does not yet handle constants and constant ranges,
- Only 1 argument per function is specialised,
- The cost-model could be further looked into, and perhaps related,
- We are not yet caching analysis results.

This is based on earlier work by Matthew Simpson (D36432) and Vinay Madhusudan.
More recently this was also discussed on the list, see:

https://lists.llvm.org/pipermail/llvm-dev/2021-March/149380.html.

The motivation for this work is that function specialisation often comes up as
a reason for performance differences of generated code between LLVM and GCC,
which has this enabled by default from optimisation level -O3 and up. And while
this certainly helps a few cpu benchmark cases, this also triggers in real
world codes and is thus a generally useful transformation to have in LLVM.

Function specialisation has great potential to increase compile-times and
code-size.  The summary from some investigations with this patch is:
- Compile-time increases for short compile jobs is high relatively, but the
  increase in absolute numbers still low.
- For longer compile-jobs, the extra compile time is around 1%, and very much
  in line with GCC.
- It is difficult to blame one thing for compile-time increases: it looks like
  everywhere a little bit more time is spent processing more functions and
  instructions.
- But the function specialisation pass itself is not very expensive; it doesn't
  show up very high in the profile of the optimisation passes.

The goal of this work is to reach parity with GCC which means that eventually
we would like to get this enabled by default. But first we would like to address
some of the limitations before that.

Differential Revision: https://reviews.llvm.org/D93838
2021-06-11 09:11:29 +01:00

668 lines
24 KiB
C++

//===- SCCP.cpp - Sparse Conditional Constant Propagation -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements sparse conditional constant propagation and merging:
//
// Specifically, this:
// * Assumes values are constant unless proven otherwise
// * Assumes BasicBlocks are dead unless proven otherwise
// * Proves values to be constant, and replaces them with constants
// * Proves conditional branches to be unconditional
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Scalar/SCCP.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueLattice.h"
#include "llvm/Analysis/ValueLatticeUtils.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PredicateInfo.h"
#include <cassert>
#include <utility>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "sccp"
STATISTIC(NumInstRemoved, "Number of instructions removed");
STATISTIC(NumDeadBlocks , "Number of basic blocks unreachable");
STATISTIC(NumInstReplaced,
"Number of instructions replaced with (simpler) instruction");
STATISTIC(IPNumInstRemoved, "Number of instructions removed by IPSCCP");
STATISTIC(IPNumArgsElimed ,"Number of arguments constant propagated by IPSCCP");
STATISTIC(IPNumGlobalConst, "Number of globals found to be constant by IPSCCP");
STATISTIC(
IPNumInstReplaced,
"Number of instructions replaced with (simpler) instruction by IPSCCP");
// Helper to check if \p LV is either a constant or a constant
// range with a single element. This should cover exactly the same cases as the
// old ValueLatticeElement::isConstant() and is intended to be used in the
// transition to ValueLatticeElement.
static bool isConstant(const ValueLatticeElement &LV) {
return LV.isConstant() ||
(LV.isConstantRange() && LV.getConstantRange().isSingleElement());
}
// Helper to check if \p LV is either overdefined or a constant range with more
// than a single element. This should cover exactly the same cases as the old
// ValueLatticeElement::isOverdefined() and is intended to be used in the
// transition to ValueLatticeElement.
static bool isOverdefined(const ValueLatticeElement &LV) {
return !LV.isUnknownOrUndef() && !isConstant(LV);
}
static bool tryToReplaceWithConstant(SCCPSolver &Solver, Value *V) {
Constant *Const = nullptr;
if (V->getType()->isStructTy()) {
std::vector<ValueLatticeElement> IVs = Solver.getStructLatticeValueFor(V);
if (any_of(IVs,
[](const ValueLatticeElement &LV) { return isOverdefined(LV); }))
return false;
std::vector<Constant *> ConstVals;
auto *ST = cast<StructType>(V->getType());
for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) {
ValueLatticeElement V = IVs[i];
ConstVals.push_back(isConstant(V)
? Solver.getConstant(V)
: UndefValue::get(ST->getElementType(i)));
}
Const = ConstantStruct::get(ST, ConstVals);
} else {
const ValueLatticeElement &IV = Solver.getLatticeValueFor(V);
if (isOverdefined(IV))
return false;
Const =
isConstant(IV) ? Solver.getConstant(IV) : UndefValue::get(V->getType());
}
assert(Const && "Constant is nullptr here!");
// Replacing `musttail` instructions with constant breaks `musttail` invariant
// unless the call itself can be removed.
// Calls with "clang.arc.attachedcall" implicitly use the return value and
// those uses cannot be updated with a constant.
CallBase *CB = dyn_cast<CallBase>(V);
if (CB && ((CB->isMustTailCall() && !CB->isSafeToRemove()) ||
CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall))) {
Function *F = CB->getCalledFunction();
// Don't zap returns of the callee
if (F)
Solver.addToMustPreserveReturnsInFunctions(F);
LLVM_DEBUG(dbgs() << " Can\'t treat the result of call " << *CB
<< " as a constant\n");
return false;
}
LLVM_DEBUG(dbgs() << " Constant: " << *Const << " = " << *V << '\n');
// Replaces all of the uses of a variable with uses of the constant.
V->replaceAllUsesWith(Const);
return true;
}
static bool simplifyInstsInBlock(SCCPSolver &Solver, BasicBlock &BB,
SmallPtrSetImpl<Value *> &InsertedValues,
Statistic &InstRemovedStat,
Statistic &InstReplacedStat) {
bool MadeChanges = false;
for (Instruction &Inst : make_early_inc_range(BB)) {
if (Inst.getType()->isVoidTy())
continue;
if (tryToReplaceWithConstant(Solver, &Inst)) {
if (Inst.isSafeToRemove())
Inst.eraseFromParent();
MadeChanges = true;
++InstRemovedStat;
} else if (isa<SExtInst>(&Inst)) {
Value *ExtOp = Inst.getOperand(0);
if (isa<Constant>(ExtOp) || InsertedValues.count(ExtOp))
continue;
const ValueLatticeElement &IV = Solver.getLatticeValueFor(ExtOp);
if (!IV.isConstantRange(/*UndefAllowed=*/false))
continue;
if (IV.getConstantRange().isAllNonNegative()) {
auto *ZExt = new ZExtInst(ExtOp, Inst.getType(), "", &Inst);
InsertedValues.insert(ZExt);
Inst.replaceAllUsesWith(ZExt);
Solver.removeLatticeValueFor(&Inst);
Inst.eraseFromParent();
InstReplacedStat++;
MadeChanges = true;
}
}
}
return MadeChanges;
}
// runSCCP() - Run the Sparse Conditional Constant Propagation algorithm,
// and return true if the function was modified.
static bool runSCCP(Function &F, const DataLayout &DL,
const TargetLibraryInfo *TLI) {
LLVM_DEBUG(dbgs() << "SCCP on function '" << F.getName() << "'\n");
SCCPSolver Solver(
DL, [TLI](Function &F) -> const TargetLibraryInfo & { return *TLI; },
F.getContext());
// Mark the first block of the function as being executable.
Solver.markBlockExecutable(&F.front());
// Mark all arguments to the function as being overdefined.
for (Argument &AI : F.args())
Solver.markOverdefined(&AI);
// Solve for constants.
bool ResolvedUndefs = true;
while (ResolvedUndefs) {
Solver.solve();
LLVM_DEBUG(dbgs() << "RESOLVING UNDEFs\n");
ResolvedUndefs = Solver.resolvedUndefsIn(F);
}
bool MadeChanges = false;
// If we decided that there are basic blocks that are dead in this function,
// delete their contents now. Note that we cannot actually delete the blocks,
// as we cannot modify the CFG of the function.
SmallPtrSet<Value *, 32> InsertedValues;
for (BasicBlock &BB : F) {
if (!Solver.isBlockExecutable(&BB)) {
LLVM_DEBUG(dbgs() << " BasicBlock Dead:" << BB);
++NumDeadBlocks;
NumInstRemoved += removeAllNonTerminatorAndEHPadInstructions(&BB).first;
MadeChanges = true;
continue;
}
MadeChanges |= simplifyInstsInBlock(Solver, BB, InsertedValues,
NumInstRemoved, NumInstReplaced);
}
return MadeChanges;
}
PreservedAnalyses SCCPPass::run(Function &F, FunctionAnalysisManager &AM) {
const DataLayout &DL = F.getParent()->getDataLayout();
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
if (!runSCCP(F, DL, &TLI))
return PreservedAnalyses::all();
auto PA = PreservedAnalyses();
PA.preserveSet<CFGAnalyses>();
return PA;
}
namespace {
//===--------------------------------------------------------------------===//
//
/// SCCP Class - This class uses the SCCPSolver to implement a per-function
/// Sparse Conditional Constant Propagator.
///
class SCCPLegacyPass : public FunctionPass {
public:
// Pass identification, replacement for typeid
static char ID;
SCCPLegacyPass() : FunctionPass(ID) {
initializeSCCPLegacyPassPass(*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
AU.setPreservesCFG();
}
// runOnFunction - Run the Sparse Conditional Constant Propagation
// algorithm, and return true if the function was modified.
bool runOnFunction(Function &F) override {
if (skipFunction(F))
return false;
const DataLayout &DL = F.getParent()->getDataLayout();
const TargetLibraryInfo *TLI =
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
return runSCCP(F, DL, TLI);
}
};
} // end anonymous namespace
char SCCPLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(SCCPLegacyPass, "sccp",
"Sparse Conditional Constant Propagation", false, false)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_END(SCCPLegacyPass, "sccp",
"Sparse Conditional Constant Propagation", false, false)
// createSCCPPass - This is the public interface to this file.
FunctionPass *llvm::createSCCPPass() { return new SCCPLegacyPass(); }
static void findReturnsToZap(Function &F,
SmallVector<ReturnInst *, 8> &ReturnsToZap,
SCCPSolver &Solver) {
// We can only do this if we know that nothing else can call the function.
if (!Solver.isArgumentTrackedFunction(&F))
return;
if (Solver.mustPreserveReturn(&F)) {
LLVM_DEBUG(
dbgs()
<< "Can't zap returns of the function : " << F.getName()
<< " due to present musttail or \"clang.arc.attachedcall\" call of "
"it\n");
return;
}
assert(
all_of(F.users(),
[&Solver](User *U) {
if (isa<Instruction>(U) &&
!Solver.isBlockExecutable(cast<Instruction>(U)->getParent()))
return true;
// Non-callsite uses are not impacted by zapping. Also, constant
// uses (like blockaddresses) could stuck around, without being
// used in the underlying IR, meaning we do not have lattice
// values for them.
if (!isa<CallBase>(U))
return true;
if (U->getType()->isStructTy()) {
return all_of(Solver.getStructLatticeValueFor(U),
[](const ValueLatticeElement &LV) {
return !isOverdefined(LV);
});
}
return !isOverdefined(Solver.getLatticeValueFor(U));
}) &&
"We can only zap functions where all live users have a concrete value");
for (BasicBlock &BB : F) {
if (CallInst *CI = BB.getTerminatingMustTailCall()) {
LLVM_DEBUG(dbgs() << "Can't zap return of the block due to present "
<< "musttail call : " << *CI << "\n");
(void)CI;
return;
}
if (auto *RI = dyn_cast<ReturnInst>(BB.getTerminator()))
if (!isa<UndefValue>(RI->getOperand(0)))
ReturnsToZap.push_back(RI);
}
}
static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB,
DomTreeUpdater &DTU) {
SmallPtrSet<BasicBlock *, 8> FeasibleSuccessors;
bool HasNonFeasibleEdges = false;
for (BasicBlock *Succ : successors(BB)) {
if (Solver.isEdgeFeasible(BB, Succ))
FeasibleSuccessors.insert(Succ);
else
HasNonFeasibleEdges = true;
}
// All edges feasible, nothing to do.
if (!HasNonFeasibleEdges)
return false;
// SCCP can only determine non-feasible edges for br, switch and indirectbr.
Instruction *TI = BB->getTerminator();
assert((isa<BranchInst>(TI) || isa<SwitchInst>(TI) ||
isa<IndirectBrInst>(TI)) &&
"Terminator must be a br, switch or indirectbr");
if (FeasibleSuccessors.size() == 1) {
// Replace with an unconditional branch to the only feasible successor.
BasicBlock *OnlyFeasibleSuccessor = *FeasibleSuccessors.begin();
SmallVector<DominatorTree::UpdateType, 8> Updates;
bool HaveSeenOnlyFeasibleSuccessor = false;
for (BasicBlock *Succ : successors(BB)) {
if (Succ == OnlyFeasibleSuccessor && !HaveSeenOnlyFeasibleSuccessor) {
// Don't remove the edge to the only feasible successor the first time
// we see it. We still do need to remove any multi-edges to it though.
HaveSeenOnlyFeasibleSuccessor = true;
continue;
}
Succ->removePredecessor(BB);
Updates.push_back({DominatorTree::Delete, BB, Succ});
}
BranchInst::Create(OnlyFeasibleSuccessor, BB);
TI->eraseFromParent();
DTU.applyUpdatesPermissive(Updates);
} else if (FeasibleSuccessors.size() > 1) {
SwitchInstProfUpdateWrapper SI(*cast<SwitchInst>(TI));
SmallVector<DominatorTree::UpdateType, 8> Updates;
for (auto CI = SI->case_begin(); CI != SI->case_end();) {
if (FeasibleSuccessors.contains(CI->getCaseSuccessor())) {
++CI;
continue;
}
BasicBlock *Succ = CI->getCaseSuccessor();
Succ->removePredecessor(BB);
Updates.push_back({DominatorTree::Delete, BB, Succ});
SI.removeCase(CI);
// Don't increment CI, as we removed a case.
}
DTU.applyUpdatesPermissive(Updates);
} else {
llvm_unreachable("Must have at least one feasible successor");
}
return true;
}
bool llvm::runIPSCCP(
Module &M, const DataLayout &DL,
std::function<const TargetLibraryInfo &(Function &)> GetTLI,
function_ref<AnalysisResultsForFn(Function &)> getAnalysis) {
SCCPSolver Solver(DL, GetTLI, M.getContext());
// Loop over all functions, marking arguments to those with their addresses
// taken or that are external as overdefined.
for (Function &F : M) {
if (F.isDeclaration())
continue;
Solver.addAnalysis(F, getAnalysis(F));
// Determine if we can track the function's return values. If so, add the
// function to the solver's set of return-tracked functions.
if (canTrackReturnsInterprocedurally(&F))
Solver.addTrackedFunction(&F);
// Determine if we can track the function's arguments. If so, add the
// function to the solver's set of argument-tracked functions.
if (canTrackArgumentsInterprocedurally(&F)) {
Solver.addArgumentTrackedFunction(&F);
continue;
}
// Assume the function is called.
Solver.markBlockExecutable(&F.front());
// Assume nothing about the incoming arguments.
for (Argument &AI : F.args())
Solver.markOverdefined(&AI);
}
// Determine if we can track any of the module's global variables. If so, add
// the global variables we can track to the solver's set of tracked global
// variables.
for (GlobalVariable &G : M.globals()) {
G.removeDeadConstantUsers();
if (canTrackGlobalVariableInterprocedurally(&G))
Solver.trackValueOfGlobalVariable(&G);
}
// Solve for constants.
bool ResolvedUndefs = true;
Solver.solve();
while (ResolvedUndefs) {
LLVM_DEBUG(dbgs() << "RESOLVING UNDEFS\n");
ResolvedUndefs = false;
for (Function &F : M) {
if (Solver.resolvedUndefsIn(F))
ResolvedUndefs = true;
}
if (ResolvedUndefs)
Solver.solve();
}
bool MadeChanges = false;
// Iterate over all of the instructions in the module, replacing them with
// constants if we have found them to be of constant values.
for (Function &F : M) {
if (F.isDeclaration())
continue;
SmallVector<BasicBlock *, 512> BlocksToErase;
if (Solver.isBlockExecutable(&F.front())) {
bool ReplacedPointerArg = false;
for (Argument &Arg : F.args()) {
if (!Arg.use_empty() && tryToReplaceWithConstant(Solver, &Arg)) {
ReplacedPointerArg |= Arg.getType()->isPointerTy();
++IPNumArgsElimed;
}
}
// If we replaced an argument, the argmemonly and
// inaccessiblemem_or_argmemonly attributes do not hold any longer. Remove
// them from both the function and callsites.
if (ReplacedPointerArg) {
AttrBuilder AttributesToRemove;
AttributesToRemove.addAttribute(Attribute::ArgMemOnly);
AttributesToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly);
F.removeAttributes(AttributeList::FunctionIndex, AttributesToRemove);
for (User *U : F.users()) {
auto *CB = dyn_cast<CallBase>(U);
if (!CB || CB->getCalledFunction() != &F)
continue;
CB->removeAttributes(AttributeList::FunctionIndex,
AttributesToRemove);
}
}
}
SmallPtrSet<Value *, 32> InsertedValues;
for (BasicBlock &BB : F) {
if (!Solver.isBlockExecutable(&BB)) {
LLVM_DEBUG(dbgs() << " BasicBlock Dead:" << BB);
++NumDeadBlocks;
MadeChanges = true;
if (&BB != &F.front())
BlocksToErase.push_back(&BB);
continue;
}
MadeChanges |= simplifyInstsInBlock(Solver, BB, InsertedValues,
IPNumInstRemoved, IPNumInstReplaced);
}
DomTreeUpdater DTU = Solver.getDTU(F);
// Change dead blocks to unreachable. We do it after replacing constants
// in all executable blocks, because changeToUnreachable may remove PHI
// nodes in executable blocks we found values for. The function's entry
// block is not part of BlocksToErase, so we have to handle it separately.
for (BasicBlock *BB : BlocksToErase) {
NumInstRemoved +=
changeToUnreachable(BB->getFirstNonPHI(), /*UseLLVMTrap=*/false,
/*PreserveLCSSA=*/false, &DTU);
}
if (!Solver.isBlockExecutable(&F.front()))
NumInstRemoved += changeToUnreachable(F.front().getFirstNonPHI(),
/*UseLLVMTrap=*/false,
/*PreserveLCSSA=*/false, &DTU);
for (BasicBlock &BB : F)
MadeChanges |= removeNonFeasibleEdges(Solver, &BB, DTU);
for (BasicBlock *DeadBB : BlocksToErase)
DTU.deleteBB(DeadBB);
for (BasicBlock &BB : F) {
for (BasicBlock::iterator BI = BB.begin(), E = BB.end(); BI != E;) {
Instruction *Inst = &*BI++;
if (Solver.getPredicateInfoFor(Inst)) {
if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
if (II->getIntrinsicID() == Intrinsic::ssa_copy) {
Value *Op = II->getOperand(0);
Inst->replaceAllUsesWith(Op);
Inst->eraseFromParent();
}
}
}
}
}
}
// If we inferred constant or undef return values for a function, we replaced
// all call uses with the inferred value. This means we don't need to bother
// actually returning anything from the function. Replace all return
// instructions with return undef.
//
// Do this in two stages: first identify the functions we should process, then
// actually zap their returns. This is important because we can only do this
// if the address of the function isn't taken. In cases where a return is the
// last use of a function, the order of processing functions would affect
// whether other functions are optimizable.
SmallVector<ReturnInst*, 8> ReturnsToZap;
for (const auto &I : Solver.getTrackedRetVals()) {
Function *F = I.first;
const ValueLatticeElement &ReturnValue = I.second;
// If there is a known constant range for the return value, add !range
// metadata to the function's call sites.
if (ReturnValue.isConstantRange() &&
!ReturnValue.getConstantRange().isSingleElement()) {
// Do not add range metadata if the return value may include undef.
if (ReturnValue.isConstantRangeIncludingUndef())
continue;
auto &CR = ReturnValue.getConstantRange();
for (User *User : F->users()) {
auto *CB = dyn_cast<CallBase>(User);
if (!CB || CB->getCalledFunction() != F)
continue;
// Limit to cases where the return value is guaranteed to be neither
// poison nor undef. Poison will be outside any range and currently
// values outside of the specified range cause immediate undefined
// behavior.
if (!isGuaranteedNotToBeUndefOrPoison(CB, nullptr, CB))
continue;
// Do not touch existing metadata for now.
// TODO: We should be able to take the intersection of the existing
// metadata and the inferred range.
if (CB->getMetadata(LLVMContext::MD_range))
continue;
LLVMContext &Context = CB->getParent()->getContext();
Metadata *RangeMD[] = {
ConstantAsMetadata::get(ConstantInt::get(Context, CR.getLower())),
ConstantAsMetadata::get(ConstantInt::get(Context, CR.getUpper()))};
CB->setMetadata(LLVMContext::MD_range, MDNode::get(Context, RangeMD));
}
continue;
}
if (F->getReturnType()->isVoidTy())
continue;
if (isConstant(ReturnValue) || ReturnValue.isUnknownOrUndef())
findReturnsToZap(*F, ReturnsToZap, Solver);
}
for (auto F : Solver.getMRVFunctionsTracked()) {
assert(F->getReturnType()->isStructTy() &&
"The return type should be a struct");
StructType *STy = cast<StructType>(F->getReturnType());
if (Solver.isStructLatticeConstant(F, STy))
findReturnsToZap(*F, ReturnsToZap, Solver);
}
// Zap all returns which we've identified as zap to change.
SmallSetVector<Function *, 8> FuncZappedReturn;
for (unsigned i = 0, e = ReturnsToZap.size(); i != e; ++i) {
Function *F = ReturnsToZap[i]->getParent()->getParent();
ReturnsToZap[i]->setOperand(0, UndefValue::get(F->getReturnType()));
// Record all functions that are zapped.
FuncZappedReturn.insert(F);
}
// Remove the returned attribute for zapped functions and the
// corresponding call sites.
for (Function *F : FuncZappedReturn) {
for (Argument &A : F->args())
F->removeParamAttr(A.getArgNo(), Attribute::Returned);
for (Use &U : F->uses()) {
// Skip over blockaddr users.
if (isa<BlockAddress>(U.getUser()))
continue;
CallBase *CB = cast<CallBase>(U.getUser());
for (Use &Arg : CB->args())
CB->removeParamAttr(CB->getArgOperandNo(&Arg), Attribute::Returned);
}
}
// If we inferred constant or undef values for globals variables, we can
// delete the global and any stores that remain to it.
for (auto &I : make_early_inc_range(Solver.getTrackedGlobals())) {
GlobalVariable *GV = I.first;
if (isOverdefined(I.second))
continue;
LLVM_DEBUG(dbgs() << "Found that GV '" << GV->getName()
<< "' is constant!\n");
while (!GV->use_empty()) {
StoreInst *SI = cast<StoreInst>(GV->user_back());
SI->eraseFromParent();
MadeChanges = true;
}
M.getGlobalList().erase(GV);
++IPNumGlobalConst;
}
return MadeChanges;
}