1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
|
//===-- SCCP.cpp ----------------------------------------------------------===//
//
// 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 Interprocedural Sparse Conditional Constant Propagation.
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/IPO/SCCP.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueLattice.h"
#include "llvm/Analysis/ValueLatticeUtils.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ModRef.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/FunctionSpecialization.h"
#include "llvm/Transforms/Scalar/SCCP.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/SCCPSolver.h"
using namespace llvm;
#define DEBUG_TYPE "sccp"
STATISTIC(NumInstRemoved, "Number of instructions removed");
STATISTIC(NumArgsElimed ,"Number of arguments constant propagated");
STATISTIC(NumGlobalConst, "Number of globals found to be constant");
STATISTIC(NumDeadBlocks , "Number of basic blocks unreachable");
STATISTIC(NumInstReplaced,
"Number of instructions replaced with (simpler) instruction");
static cl::opt<unsigned> FuncSpecMaxIters(
"funcspec-max-iters", cl::init(1), cl::Hidden, cl::desc(
"The maximum number of iterations function specialization is run"));
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 !SCCPSolver::isOverdefined(LV);
});
}
// We don't consider assume-like intrinsics to be actual address
// captures.
if (auto *II = dyn_cast<IntrinsicInst>(U)) {
if (II->isAssumeLikeIntrinsic())
return true;
}
return !SCCPSolver::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 runIPSCCP(
Module &M, const DataLayout &DL, FunctionAnalysisManager *FAM,
std::function<const TargetLibraryInfo &(Function &)> GetTLI,
std::function<TargetTransformInfo &(Function &)> GetTTI,
std::function<AssumptionCache &(Function &)> GetAC,
std::function<DominatorTree &(Function &)> GetDT,
std::function<BlockFrequencyInfo &(Function &)> GetBFI,
bool IsFuncSpecEnabled) {
SCCPSolver Solver(DL, GetTLI, M.getContext());
FunctionSpecializer Specializer(Solver, M, FAM, GetBFI, GetTLI, GetTTI,
GetAC);
// 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;
DominatorTree &DT = GetDT(F);
AssumptionCache &AC = GetAC(F);
Solver.addPredicateInfo(F, DT, AC);
// 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.
Solver.solveWhileResolvedUndefsIn(M);
if (IsFuncSpecEnabled) {
unsigned Iters = 0;
while (Iters++ < FuncSpecMaxIters && Specializer.run());
}
// Iterate over all of the instructions in the module, replacing them with
// constants if we have found them to be of constant values.
bool MadeChanges = false;
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() && Solver.tryToReplaceWithConstant(&Arg)) {
ReplacedPointerArg |= Arg.getType()->isPointerTy();
++NumArgsElimed;
}
}
// If we replaced an argument, we may now also access a global (currently
// classified as "other" memory). Update memory attribute to reflect this.
if (ReplacedPointerArg) {
auto UpdateAttrs = [&](AttributeList AL) {
MemoryEffects ME = AL.getMemoryEffects();
if (ME == MemoryEffects::unknown())
return AL;
ME |= MemoryEffects(IRMemLocation::Other,
ME.getModRef(IRMemLocation::ArgMem));
return AL.addFnAttribute(
F.getContext(),
Attribute::getWithMemoryEffects(F.getContext(), ME));
};
F.setAttributes(UpdateAttrs(F.getAttributes()));
for (User *U : F.users()) {
auto *CB = dyn_cast<CallBase>(U);
if (!CB || CB->getCalledFunction() != &F)
continue;
CB->setAttributes(UpdateAttrs(CB->getAttributes()));
}
}
MadeChanges |= ReplacedPointerArg;
}
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 |= Solver.simplifyInstsInBlock(
BB, InsertedValues, NumInstRemoved, NumInstReplaced);
}
DominatorTree *DT = FAM->getCachedResult<DominatorTreeAnalysis>(F);
PostDominatorTree *PDT = FAM->getCachedResult<PostDominatorTreeAnalysis>(F);
DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
// 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(),
/*PreserveLCSSA=*/false, &DTU);
}
if (!Solver.isBlockExecutable(&F.front()))
NumInstRemoved += changeToUnreachable(F.front().getFirstNonPHI(),
/*PreserveLCSSA=*/false, &DTU);
BasicBlock *NewUnreachableBB = nullptr;
for (BasicBlock &BB : F)
MadeChanges |= Solver.removeNonFeasibleEdges(&BB, DTU, NewUnreachableBB);
for (BasicBlock *DeadBB : BlocksToErase)
if (!DeadBB->hasAddressTaken())
DTU.deleteBB(DeadBB);
for (BasicBlock &BB : F) {
for (Instruction &Inst : llvm::make_early_inc_range(BB)) {
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;
// 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 (SCCPSolver::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 (ReturnInst *RI : ReturnsToZap) {
Function *F = RI->getParent()->getParent();
RI->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.
// Also remove any attributes that convert an undef return value into
// immediate undefined behavior
AttributeMask UBImplyingAttributes =
AttributeFuncs::getUBImplyingAttributes();
for (Function *F : FuncZappedReturn) {
for (Argument &A : F->args())
F->removeParamAttr(A.getArgNo(), Attribute::Returned);
F->removeRetAttrs(UBImplyingAttributes);
for (Use &U : F->uses()) {
CallBase *CB = dyn_cast<CallBase>(U.getUser());
if (!CB) {
assert(isa<BlockAddress>(U.getUser()) ||
(isa<Constant>(U.getUser()) &&
all_of(U.getUser()->users(), [](const User *UserUser) {
return cast<IntrinsicInst>(UserUser)->isAssumeLikeIntrinsic();
})));
continue;
}
for (Use &Arg : CB->args())
CB->removeParamAttr(CB->getArgOperandNo(&Arg), Attribute::Returned);
CB->removeRetAttrs(UBImplyingAttributes);
}
}
// If we inferred constant or undef values for globals variables, we can
// delete the global and any stores that remain to it.
for (const auto &I : make_early_inc_range(Solver.getTrackedGlobals())) {
GlobalVariable *GV = I.first;
if (SCCPSolver::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.eraseGlobalVariable(GV);
++NumGlobalConst;
}
return MadeChanges;
}
PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) {
const DataLayout &DL = M.getDataLayout();
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto GetTLI = [&FAM](Function &F) -> const TargetLibraryInfo & {
return FAM.getResult<TargetLibraryAnalysis>(F);
};
auto GetTTI = [&FAM](Function &F) -> TargetTransformInfo & {
return FAM.getResult<TargetIRAnalysis>(F);
};
auto GetAC = [&FAM](Function &F) -> AssumptionCache & {
return FAM.getResult<AssumptionAnalysis>(F);
};
auto GetDT = [&FAM](Function &F) -> DominatorTree & {
return FAM.getResult<DominatorTreeAnalysis>(F);
};
auto GetBFI = [&FAM](Function &F) -> BlockFrequencyInfo & {
return FAM.getResult<BlockFrequencyAnalysis>(F);
};
if (!runIPSCCP(M, DL, &FAM, GetTLI, GetTTI, GetAC, GetDT, GetBFI,
isFuncSpecEnabled()))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<PostDominatorTreeAnalysis>();
PA.preserve<FunctionAnalysisManagerModuleProxy>();
return PA;
}
|