mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-11 21:45:16 +00:00
Recommit r333268: [IPSCCP] Use PredicateInfo to propagate facts from cmp instructions.
r335150 should resolve the issues with the clang-with-thin-lto-ubuntu and clang-with-lto-ubuntu builders. Original message: This patch updates IPSCCP to use PredicateInfo to propagate facts to true branches predicated by EQ and to false branches predicated by NE. As a follow up, we should be able to extend it to also propagate additional facts about nonnull. Reviewers: davide, mssimpso, dberlin, efriedma Reviewed By: davide, dberlin llvm-svn: 335206
This commit is contained in:
parent
bf2572cc9c
commit
8aea145f98
@ -21,11 +21,13 @@
|
||||
#ifndef LLVM_TRANSFORMS_SCALAR_SCCP_H
|
||||
#define LLVM_TRANSFORMS_SCALAR_SCCP_H
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Transforms/Utils/PredicateInfo.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@ -37,7 +39,9 @@ public:
|
||||
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
|
||||
};
|
||||
|
||||
bool runIPSCCP(Module &M, const DataLayout &DL, const TargetLibraryInfo *TLI);
|
||||
bool runIPSCCP(
|
||||
Module &M, const DataLayout &DL, const TargetLibraryInfo *TLI,
|
||||
function_ref<std::unique_ptr<PredicateInfo>(Function &)> getPredicateInfo);
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_SCALAR_SCCP_H
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "llvm/Transforms/IPO/SCCP.h"
|
||||
#include "llvm/Analysis/AssumptionCache.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include "llvm/Transforms/Scalar/SCCP.h"
|
||||
@ -8,7 +9,15 @@ using namespace llvm;
|
||||
PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) {
|
||||
const DataLayout &DL = M.getDataLayout();
|
||||
auto &TLI = AM.getResult<TargetLibraryAnalysis>(M);
|
||||
if (!runIPSCCP(M, DL, &TLI))
|
||||
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
|
||||
auto getPredicateInfo =
|
||||
[&FAM](Function &F) -> std::unique_ptr<PredicateInfo> {
|
||||
return make_unique<PredicateInfo>(F,
|
||||
FAM.getResult<DominatorTreeAnalysis>(F),
|
||||
FAM.getResult<AssumptionAnalysis>(F));
|
||||
};
|
||||
|
||||
if (!runIPSCCP(M, DL, &TLI, getPredicateInfo))
|
||||
return PreservedAnalyses::all();
|
||||
return PreservedAnalyses::none();
|
||||
}
|
||||
@ -34,10 +43,20 @@ public:
|
||||
const DataLayout &DL = M.getDataLayout();
|
||||
const TargetLibraryInfo *TLI =
|
||||
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
|
||||
return runIPSCCP(M, DL, TLI);
|
||||
|
||||
auto getPredicateInfo =
|
||||
[this](Function &F) -> std::unique_ptr<PredicateInfo> {
|
||||
return make_unique<PredicateInfo>(
|
||||
F, this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(),
|
||||
this->getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F));
|
||||
};
|
||||
|
||||
return runIPSCCP(M, DL, TLI, getPredicateInfo);
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<AssumptionCacheTracker>();
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.addRequired<TargetLibraryInfoWrapperPass>();
|
||||
}
|
||||
};
|
||||
@ -49,6 +68,7 @@ char IPSCCPLegacyPass::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(IPSCCPLegacyPass, "ipsccp",
|
||||
"Interprocedural Sparse Conditional Constant Propagation",
|
||||
false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(IPSCCPLegacyPass, "ipsccp",
|
||||
"Interprocedural Sparse Conditional Constant Propagation",
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Utils/PredicateInfo.h"
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -248,7 +249,21 @@ class SCCPSolver : public InstVisitor<SCCPSolver> {
|
||||
using Edge = std::pair<BasicBlock *, BasicBlock *>;
|
||||
DenseSet<Edge> KnownFeasibleEdges;
|
||||
|
||||
DenseMap<Function *, std::unique_ptr<PredicateInfo>> PredInfos;
|
||||
DenseMap<Value *, SmallPtrSet<User *, 2>> AdditionalUsers;
|
||||
|
||||
public:
|
||||
void addPredInfo(Function &F, std::unique_ptr<PredicateInfo> PI) {
|
||||
PredInfos[&F] = std::move(PI);
|
||||
}
|
||||
|
||||
const PredicateBase *getPredicateInfoFor(Instruction *I) {
|
||||
auto PI = PredInfos.find(I->getParent()->getParent());
|
||||
if (PI == PredInfos.end())
|
||||
return nullptr;
|
||||
return PI->second->getPredicateInfoFor(I);
|
||||
}
|
||||
|
||||
SCCPSolver(const DataLayout &DL, const TargetLibraryInfo *tli)
|
||||
: DL(DL), TLI(tli) {}
|
||||
|
||||
@ -563,6 +578,26 @@ private:
|
||||
visit(*I);
|
||||
}
|
||||
|
||||
// Add U as additional user of V.
|
||||
void addAdditionalUser(Value *V, User *U) {
|
||||
auto Iter = AdditionalUsers.insert({V, {}});
|
||||
Iter.first->second.insert(U);
|
||||
}
|
||||
|
||||
// Mark I's users as changed, including AdditionalUsers.
|
||||
void markUsersAsChanged(Value *I) {
|
||||
for (User *U : I->users())
|
||||
if (auto *UI = dyn_cast<Instruction>(U))
|
||||
OperandChangedState(UI);
|
||||
|
||||
auto Iter = AdditionalUsers.find(I);
|
||||
if (Iter != AdditionalUsers.end()) {
|
||||
for (User *U : Iter->second)
|
||||
if (auto *UI = dyn_cast<Instruction>(U))
|
||||
OperandChangedState(UI);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class InstVisitor<SCCPSolver>;
|
||||
|
||||
@ -1157,6 +1192,59 @@ void SCCPSolver::visitCallSite(CallSite CS) {
|
||||
Function *F = CS.getCalledFunction();
|
||||
Instruction *I = CS.getInstruction();
|
||||
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(I)) {
|
||||
if (II->getIntrinsicID() == Intrinsic::ssa_copy) {
|
||||
if (ValueState[I].isOverdefined())
|
||||
return;
|
||||
|
||||
auto *PI = getPredicateInfoFor(I);
|
||||
if (!PI)
|
||||
return;
|
||||
|
||||
auto *PBranch = dyn_cast<PredicateBranch>(getPredicateInfoFor(I));
|
||||
if (!PBranch)
|
||||
return mergeInValue(ValueState[I], I, getValueState(PI->OriginalOp));
|
||||
|
||||
Value *CopyOf = I->getOperand(0);
|
||||
Value *Cond = PBranch->Condition;
|
||||
|
||||
// Everything below relies on the condition being a comparison.
|
||||
auto *Cmp = dyn_cast<CmpInst>(Cond);
|
||||
if (!Cmp)
|
||||
return mergeInValue(ValueState[I], I, getValueState(PI->OriginalOp));
|
||||
|
||||
Value *CmpOp0 = Cmp->getOperand(0);
|
||||
Value *CmpOp1 = Cmp->getOperand(1);
|
||||
if (CopyOf != CmpOp0 && CopyOf != CmpOp1)
|
||||
return mergeInValue(ValueState[I], I, getValueState(PI->OriginalOp));
|
||||
|
||||
if (CmpOp0 != CopyOf)
|
||||
std::swap(CmpOp0, CmpOp1);
|
||||
|
||||
LatticeVal OriginalVal = getValueState(CopyOf);
|
||||
LatticeVal EqVal = getValueState(CmpOp1);
|
||||
LatticeVal &IV = ValueState[I];
|
||||
if (PBranch->TrueEdge && Cmp->getPredicate() == CmpInst::ICMP_EQ) {
|
||||
addAdditionalUser(CmpOp1, I);
|
||||
if (OriginalVal.isConstant())
|
||||
mergeInValue(IV, I, OriginalVal);
|
||||
else
|
||||
mergeInValue(IV, I, EqVal);
|
||||
return;
|
||||
}
|
||||
if (!PBranch->TrueEdge && Cmp->getPredicate() == CmpInst::ICMP_NE) {
|
||||
addAdditionalUser(CmpOp1, I);
|
||||
if (OriginalVal.isConstant())
|
||||
mergeInValue(IV, I, OriginalVal);
|
||||
else
|
||||
mergeInValue(IV, I, EqVal);
|
||||
return;
|
||||
}
|
||||
|
||||
return mergeInValue(IV, I, getValueState(PBranch->OriginalOp));
|
||||
}
|
||||
}
|
||||
|
||||
// The common case is that we aren't tracking the callee, either because we
|
||||
// are not doing interprocedural analysis or the callee is indirect, or is
|
||||
// external. Handle these cases first.
|
||||
@ -1268,9 +1356,7 @@ void SCCPSolver::Solve() {
|
||||
// since all of its users will have already been marked as overdefined
|
||||
// Update all of the users of this instruction's value.
|
||||
//
|
||||
for (User *U : I->users())
|
||||
if (auto *UI = dyn_cast<Instruction>(U))
|
||||
OperandChangedState(UI);
|
||||
markUsersAsChanged(I);
|
||||
}
|
||||
|
||||
// Process the instruction work list.
|
||||
@ -1287,9 +1373,7 @@ void SCCPSolver::Solve() {
|
||||
// Update all of the users of this instruction's value.
|
||||
//
|
||||
if (I->getType()->isStructTy() || !getValueState(I).isOverdefined())
|
||||
for (User *U : I->users())
|
||||
if (auto *UI = dyn_cast<Instruction>(U))
|
||||
OperandChangedState(UI);
|
||||
markUsersAsChanged(I);
|
||||
}
|
||||
|
||||
// Process the basic block work list.
|
||||
@ -1855,8 +1939,9 @@ static void findReturnsToZap(Function &F,
|
||||
}
|
||||
}
|
||||
|
||||
bool llvm::runIPSCCP(Module &M, const DataLayout &DL,
|
||||
const TargetLibraryInfo *TLI) {
|
||||
bool llvm::runIPSCCP(
|
||||
Module &M, const DataLayout &DL, const TargetLibraryInfo *TLI,
|
||||
function_ref<std::unique_ptr<PredicateInfo>(Function &)> getPredicateInfo) {
|
||||
SCCPSolver Solver(DL, TLI);
|
||||
|
||||
// Loop over all functions, marking arguments to those with their addresses
|
||||
@ -1865,6 +1950,7 @@ bool llvm::runIPSCCP(Module &M, const DataLayout &DL,
|
||||
if (F.isDeclaration())
|
||||
continue;
|
||||
|
||||
Solver.addPredInfo(F, getPredicateInfo(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))
|
||||
@ -1983,6 +2069,24 @@ bool llvm::runIPSCCP(Module &M, const DataLayout &DL,
|
||||
F.getBasicBlockList().erase(DeadBB);
|
||||
}
|
||||
BlocksToErase.clear();
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
for (BasicBlock::iterator BI = BB.begin(), E = BB.end(); BI != E;) {
|
||||
Instruction *Inst = &*BI++;
|
||||
if (const PredicateBase *PI = 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();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Inst->replaceAllUsesWith(PI->OriginalOp);
|
||||
Inst->eraseFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we inferred constant or undef return values for a function, we replaced
|
||||
|
@ -41,6 +41,8 @@
|
||||
; CHECK-O2-NEXT: Running analysis: ProfileSummaryAnalysis
|
||||
; CHECK-O2-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis
|
||||
; CHECK-O2-NEXT: Running pass: IPSCCPPass
|
||||
; CHECK-O2-DAG: Running analysis: AssumptionAnalysis on foo
|
||||
; CHECK-O2-DAG: Running analysis: DominatorTreeAnalysis on foo
|
||||
; CHECK-O2-NEXT: Running pass: CalledValuePropagationPass
|
||||
; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}PostOrderFunctionAttrsPass>
|
||||
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}SCC
|
||||
@ -56,8 +58,6 @@
|
||||
; CHECK-O-NEXT: Running pass: WholeProgramDevirtPass
|
||||
; CHECK-O2-NEXT: Running pass: GlobalOptPass
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PromotePass>
|
||||
; CHECK-O2-NEXT: Running analysis: DominatorTreeAnalysis
|
||||
; CHECK-O2-NEXT: Running analysis: AssumptionAnalysis
|
||||
; CHECK-O2-NEXT: Running pass: ConstantMergePass
|
||||
; CHECK-O2-NEXT: Running pass: DeadArgumentEliminationPass
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
|
||||
|
@ -28,6 +28,7 @@
|
||||
; CHECK-NEXT: Force set function attributes
|
||||
; CHECK-NEXT: Infer set function attributes
|
||||
; CHECK-NEXT: Interprocedural Sparse Conditional Constant Propagation
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
; CHECK-NEXT: Called Value Propagation
|
||||
; CHECK-NEXT: Global Variable Optimizer
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
@ -273,6 +274,9 @@
|
||||
; CHECK-NEXT: Module Verifier
|
||||
; CHECK-NEXT: Bitcode Writer
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: Target Library Information
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
|
@ -30,6 +30,7 @@
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Call-site splitting
|
||||
; CHECK-NEXT: Interprocedural Sparse Conditional Constant Propagation
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
; CHECK-NEXT: Called Value Propagation
|
||||
; CHECK-NEXT: Global Variable Optimizer
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
@ -277,6 +278,9 @@
|
||||
; CHECK-NEXT: Module Verifier
|
||||
; CHECK-NEXT: Bitcode Writer
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: Target Library Information
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
|
@ -28,6 +28,7 @@
|
||||
; CHECK-NEXT: Force set function attributes
|
||||
; CHECK-NEXT: Infer set function attributes
|
||||
; CHECK-NEXT: Interprocedural Sparse Conditional Constant Propagation
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
; CHECK-NEXT: Called Value Propagation
|
||||
; CHECK-NEXT: Global Variable Optimizer
|
||||
; CHECK-NEXT: Unnamed pass: implement Pass::getPassName()
|
||||
@ -259,6 +260,9 @@
|
||||
; CHECK-NEXT: Module Verifier
|
||||
; CHECK-NEXT: Bitcode Writer
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
; CHECK-NEXT: Pass Arguments:
|
||||
; CHECK-NEXT: Target Library Information
|
||||
; CHECK-NEXT: FunctionPass Manager
|
||||
; CHECK-NEXT: Dominator Tree Construction
|
||||
|
@ -9,7 +9,7 @@ define i8* @start(i8 %v) {
|
||||
%c1 = icmp eq i8 %v, 0
|
||||
br i1 %c1, label %true, label %false
|
||||
true:
|
||||
; CHECK: %ca = musttail call i8* @side_effects(i8 %v)
|
||||
; CHECK: %ca = musttail call i8* @side_effects(i8 0)
|
||||
; CHECK: ret i8* %ca
|
||||
%ca = musttail call i8* @side_effects(i8 %v)
|
||||
ret i8* %ca
|
||||
@ -34,7 +34,7 @@ define internal i8* @side_effects(i8 %v) {
|
||||
; is always `null`.
|
||||
; The call can't be removed due to `external` call above, though.
|
||||
|
||||
; CHECK: %ca = musttail call i8* @start(i8 %v)
|
||||
; CHECK: %ca = musttail call i8* @start(i8 0)
|
||||
%ca = musttail call i8* @start(i8 %v)
|
||||
|
||||
; Thus the result must be returned anyway
|
||||
|
68
test/Transforms/SCCP/ipsccp-predicated.ll
Normal file
68
test/Transforms/SCCP/ipsccp-predicated.ll
Normal file
@ -0,0 +1,68 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt < %s -ipsccp -S | FileCheck %s
|
||||
|
||||
define i32 @test1(i32 %v) {
|
||||
; CHECK-LABEL: @test1(
|
||||
; CHECK-NEXT: Entry:
|
||||
; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp eq i32 [[V:%.*]], 10
|
||||
; CHECK-NEXT: br i1 [[TOBOOL1]], label [[T:%.*]], label [[F:%.*]]
|
||||
; CHECK: T:
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @callee(i32 20)
|
||||
; CHECK-NEXT: ret i32 [[R]]
|
||||
; CHECK: F:
|
||||
; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i32 [[V]])
|
||||
; CHECK-NEXT: ret i32 [[X]]
|
||||
;
|
||||
Entry:
|
||||
%tobool1 = icmp eq i32 %v, 10
|
||||
br i1 %tobool1, label %T, label %F
|
||||
|
||||
T:
|
||||
%a = add i32 %v, 10
|
||||
%r = call i32 @callee(i32 %a)
|
||||
ret i32 %r
|
||||
|
||||
F:
|
||||
%x = call i32 @callee(i32 %v)
|
||||
ret i32 %x
|
||||
}
|
||||
|
||||
|
||||
define internal i32 @test2(i32 %v, i32 %c) {
|
||||
; CHECK-LABEL: @test2(
|
||||
; CHECK-NEXT: Entry:
|
||||
; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp eq i32 [[V:%.*]], 99
|
||||
; CHECK-NEXT: br i1 [[TOBOOL1]], label [[T:%.*]], label [[F:%.*]]
|
||||
; CHECK: T:
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @callee(i32 109)
|
||||
; CHECK-NEXT: ret i32 [[R]]
|
||||
; CHECK: F:
|
||||
; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i32 [[V]])
|
||||
; CHECK-NEXT: ret i32 [[X]]
|
||||
;
|
||||
Entry:
|
||||
%tobool1 = icmp eq i32 %v, %c
|
||||
br i1 %tobool1, label %T, label %F
|
||||
|
||||
T:
|
||||
%a = add i32 %v, 10
|
||||
%r = call i32 @callee(i32 %a)
|
||||
ret i32 %r
|
||||
|
||||
F:
|
||||
%x = call i32 @callee(i32 %v)
|
||||
ret i32 %x
|
||||
}
|
||||
|
||||
define i32 @caller_test2(i32 %v) {
|
||||
; CHECK-LABEL: @caller_test2(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @test2(i32 [[V:%.*]], i32 99)
|
||||
; CHECK-NEXT: ret i32 [[R]]
|
||||
;
|
||||
entry:
|
||||
%r = call i32 @test2(i32 %v, i32 99)
|
||||
ret i32 %r
|
||||
}
|
||||
|
||||
declare i32 @callee(i32)
|
Loading…
Reference in New Issue
Block a user