Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ class InstCostVisitor : public InstVisitor<InstCostVisitor, Constant *> {
friend class InstVisitor<InstCostVisitor, Constant *>;

static bool canEliminateSuccessor(BasicBlock *BB, BasicBlock *Succ,
DenseSet<BasicBlock *> &DeadBlocks);
DenseSet<BasicBlock *> &DeadBlocks,
const SCCPSolver &Solver);

Cost getCodeSizeSavingsForUser(Instruction *User, Value *Use = nullptr,
Constant *C = nullptr);
Expand Down
42 changes: 24 additions & 18 deletions llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ static cl::opt<bool> SpecializeLiteralConstant(
"argument"));

bool InstCostVisitor::canEliminateSuccessor(BasicBlock *BB, BasicBlock *Succ,
DenseSet<BasicBlock *> &DeadBlocks) {
DenseSet<BasicBlock *> &DeadBlocks,
const SCCPSolver &Solver) {
unsigned I = 0;
return all_of(predecessors(Succ),
[&I, BB, Succ, &DeadBlocks] (BasicBlock *Pred) {
return all_of(predecessors(Succ), [&I, BB, Succ, &DeadBlocks,
&Solver](BasicBlock *Pred) {
return I++ < MaxBlockPredecessors &&
(Pred == BB || Pred == Succ || DeadBlocks.contains(Pred));
(Pred == BB || Pred == Succ || DeadBlocks.contains(Pred) ||
!Solver.isBlockExecutable(Pred));
});
}

Expand Down Expand Up @@ -135,15 +137,18 @@ Cost InstCostVisitor::estimateBasicBlocks(
// executable and only reachable from dead blocks.
for (BasicBlock *SuccBB : successors(BB))
if (isBlockExecutable(SuccBB) &&
canEliminateSuccessor(BB, SuccBB, DeadBlocks))
canEliminateSuccessor(BB, SuccBB, DeadBlocks, Solver))
WorkList.push_back(SuccBB);
}
return CodeSize;
}

static Constant *findConstantFor(Value *V, ConstMap &KnownConstants) {
static Constant *findConstantFor(Value *V, ConstMap &KnownConstants,
const SCCPSolver &Solver) {
if (auto *C = dyn_cast<Constant>(V))
return C;
if (auto *C = Solver.getConstantOrNull(V))
return C;
return KnownConstants.lookup(V);
}

Expand Down Expand Up @@ -266,7 +271,7 @@ Cost InstCostVisitor::estimateSwitchInst(SwitchInst &I) {
for (const auto &Case : I.cases()) {
BasicBlock *BB = Case.getCaseSuccessor();
if (BB != Succ && isBlockExecutable(BB) &&
canEliminateSuccessor(I.getParent(), BB, DeadBlocks))
canEliminateSuccessor(I.getParent(), BB, DeadBlocks, Solver))
WorkList.push_back(BB);
}

Expand All @@ -284,7 +289,7 @@ Cost InstCostVisitor::estimateBranchInst(BranchInst &I) {
// it is executable and has a unique predecessor.
SmallVector<BasicBlock *> WorkList;
if (isBlockExecutable(Succ) &&
canEliminateSuccessor(I.getParent(), Succ, DeadBlocks))
canEliminateSuccessor(I.getParent(), Succ, DeadBlocks, Solver))
WorkList.push_back(Succ);

return estimateBasicBlocks(WorkList);
Expand Down Expand Up @@ -312,10 +317,10 @@ bool InstCostVisitor::discoverTransitivelyIncomingValues(

// Disregard self-references and dead incoming values.
if (auto *Inst = dyn_cast<Instruction>(V))
if (Inst == PN || DeadBlocks.contains(PN->getIncomingBlock(I)))
if (Inst == PN || !isBlockExecutable(PN->getIncomingBlock(I)))
continue;

if (Constant *C = findConstantFor(V, KnownConstants)) {
if (Constant *C = findConstantFor(V, KnownConstants, Solver)) {
// Not all incoming values are the same constant. Bail immediately.
if (C != Const)
return false;
Expand Down Expand Up @@ -347,10 +352,10 @@ Constant *InstCostVisitor::visitPHINode(PHINode &I) {

// Disregard self-references and dead incoming values.
if (auto *Inst = dyn_cast<Instruction>(V))
if (Inst == &I || DeadBlocks.contains(I.getIncomingBlock(Idx)))
if (Inst == &I || !isBlockExecutable(I.getIncomingBlock(Idx)))
continue;

if (Constant *C = findConstantFor(V, KnownConstants)) {
if (Constant *C = findConstantFor(V, KnownConstants, Solver)) {
if (!Const)
Const = C;
// Not all incoming values are the same constant. Bail immediately.
Expand Down Expand Up @@ -415,7 +420,7 @@ Constant *InstCostVisitor::visitCallBase(CallBase &I) {

for (unsigned Idx = 0, E = I.getNumOperands() - 1; Idx != E; ++Idx) {
Value *V = I.getOperand(Idx);
Constant *C = findConstantFor(V, KnownConstants);
Constant *C = findConstantFor(V, KnownConstants, Solver);
if (!C)
return nullptr;
Operands.push_back(C);
Expand All @@ -439,7 +444,7 @@ Constant *InstCostVisitor::visitGetElementPtrInst(GetElementPtrInst &I) {

for (unsigned Idx = 0, E = I.getNumOperands(); Idx != E; ++Idx) {
Value *V = I.getOperand(Idx);
Constant *C = findConstantFor(V, KnownConstants);
Constant *C = findConstantFor(V, KnownConstants, Solver);
if (!C)
return nullptr;
Operands.push_back(C);
Expand All @@ -455,9 +460,10 @@ Constant *InstCostVisitor::visitSelectInst(SelectInst &I) {
if (I.getCondition() == LastVisited->first) {
Value *V = LastVisited->second->isZeroValue() ? I.getFalseValue()
: I.getTrueValue();
return findConstantFor(V, KnownConstants);
return findConstantFor(V, KnownConstants, Solver);
}
if (Constant *Condition = findConstantFor(I.getCondition(), KnownConstants))
if (Constant *Condition =
findConstantFor(I.getCondition(), KnownConstants, Solver))
if ((I.getTrueValue() == LastVisited->first && Condition->isOneValue()) ||
(I.getFalseValue() == LastVisited->first && Condition->isZeroValue()))
return LastVisited->second;
Expand All @@ -475,7 +481,7 @@ Constant *InstCostVisitor::visitCmpInst(CmpInst &I) {
Constant *Const = LastVisited->second;
bool ConstOnRHS = I.getOperand(1) == LastVisited->first;
Value *V = ConstOnRHS ? I.getOperand(0) : I.getOperand(1);
Constant *Other = findConstantFor(V, KnownConstants);
Constant *Other = findConstantFor(V, KnownConstants, Solver);

if (Other) {
if (ConstOnRHS)
Expand Down Expand Up @@ -503,7 +509,7 @@ Constant *InstCostVisitor::visitBinaryOperator(BinaryOperator &I) {

bool ConstOnRHS = I.getOperand(1) == LastVisited->first;
Value *V = ConstOnRHS ? I.getOperand(0) : I.getOperand(1);
Constant *Other = findConstantFor(V, KnownConstants);
Constant *Other = findConstantFor(V, KnownConstants, Solver);
Value *OtherVal = Other ? Other : V;
Value *ConstVal = LastVisited->second;

Expand Down
66 changes: 66 additions & 0 deletions llvm/test/Transforms/FunctionSpecialization/solver-constants.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 5
; RUN: opt -passes="ipsccp<func-spec>" -funcspec-min-function-size=1 \
; RUN: -funcspec-for-literal-constant=true \
; RUN: -funcspec-min-codesize-savings=50 \
; RUN: -funcspec-min-latency-savings=0 \
; RUN: -S < %s | FileCheck %s

; Verify that we are able to estimate the codesize savings arising from a branch
; based on a binary operator, where one operand is already found constant by
; IPSCCP.
define i32 @main(i1 %flag) {
%notspec = call i32 @test(i1 %flag, i1 false)
%spec = call i32 @test(i1 false, i1 false)
%sum = add i32 %notspec, %spec
ret i32 %sum
}

define internal i32 @test(i1 %argflag, i1 %constflag) {
entry:
%cond = or i1 %argflag, %constflag
br i1 %cond, label %if.then, label %if.end

if.then:
call void @do_something()
call void @do_something()
call void @do_something()
call void @do_something()
br label %if.end

if.end:
%res = phi i32 [ 0, %entry ], [ 1, %if.then]
ret i32 %res
}

declare void @do_something()
; CHECK-LABEL: define range(i32 0, 2) i32 @main(
; CHECK-SAME: i1 [[FLAG:%.*]]) {
; CHECK-NEXT: [[NOTSPEC:%.*]] = call i32 @test(i1 [[FLAG]], i1 false)
; CHECK-NEXT: [[SPEC:%.*]] = call i32 @test.specialized.1(i1 false, i1 false)
; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[NOTSPEC]], 0
; CHECK-NEXT: ret i32 [[SUM]]
;
;
; CHECK-LABEL: define internal range(i32 0, 2) i32 @test(
; CHECK-SAME: i1 [[ARGFLAG:%.*]], i1 [[CONSTFLAG:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[COND:%.*]] = or i1 [[ARGFLAG]], false
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
; CHECK: [[IF_THEN]]:
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: br label %[[IF_END]]
; CHECK: [[IF_END]]:
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ 1, %[[IF_THEN]] ]
; CHECK-NEXT: ret i32 [[RES]]
;
;
; CHECK-LABEL: define internal i32 @test.specialized.1(
; CHECK-SAME: i1 [[ARGFLAG:%.*]], i1 [[CONSTFLAG:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[IF_END:.*]]
; CHECK: [[IF_END]]:
; CHECK-NEXT: ret i32 poison
;
74 changes: 74 additions & 0 deletions llvm/test/Transforms/FunctionSpecialization/solver-dead-blocks.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 5
; RUN: opt -passes="ipsccp<func-spec>" -funcspec-min-function-size=1 \
; RUN: -funcspec-for-literal-constant=true \
; RUN: -funcspec-min-codesize-savings=50 \
; RUN: -funcspec-min-latency-savings=0 \
; RUN: -S < %s | FileCheck %s

; Verify that we are able to estimate the codesize savings arising from a block
; which is found dead, where the block has a predecessor that was found dead by
; IPSCCP.
define i32 @main(i1 %flag) {
%notspec = call i32 @test(i1 %flag, i1 true)
%spec = call i32 @test(i1 true, i1 true)
%sum = add i32 %notspec, %spec
ret i32 %sum
}

define internal i32 @test(i1 %argflag, i1 %constflag) {
entry:
br i1 %argflag, label %block1, label %block3

block1:
br i1 %constflag, label %end, label %block2

block2:
br label %block3

block3:
call void @do_something()
call void @do_something()
call void @do_something()
call void @do_something()
br label %end

end:
%res = phi i32 [ 0, %block1 ], [ 1, %block3]
ret i32 %res
}

declare void @do_something()
; CHECK-LABEL: define range(i32 0, 2) i32 @main(
; CHECK-SAME: i1 [[FLAG:%.*]]) {
; CHECK-NEXT: [[NOTSPEC:%.*]] = call i32 @test(i1 [[FLAG]], i1 true)
; CHECK-NEXT: [[SPEC:%.*]] = call i32 @test.specialized.1(i1 true, i1 true)
; CHECK-NEXT: [[SUM:%.*]] = add nuw nsw i32 [[NOTSPEC]], 0
; CHECK-NEXT: ret i32 [[SUM]]
;
;
; CHECK-LABEL: define internal range(i32 0, 2) i32 @test(
; CHECK-SAME: i1 [[ARGFLAG:%.*]], i1 [[CONSTFLAG:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[ARGFLAG]], label %[[BLOCK1:.*]], label %[[BLOCK3:.*]]
; CHECK: [[BLOCK1]]:
; CHECK-NEXT: br label %[[END:.*]]
; CHECK: [[BLOCK3]]:
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: call void @do_something()
; CHECK-NEXT: br label %[[END]]
; CHECK: [[END]]:
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, %[[BLOCK1]] ], [ 1, %[[BLOCK3]] ]
; CHECK-NEXT: ret i32 [[RES]]
;
;
; CHECK-LABEL: define internal i32 @test.specialized.1(
; CHECK-SAME: i1 [[ARGFLAG:%.*]], i1 [[CONSTFLAG:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[BLOCK1:.*]]
; CHECK: [[BLOCK1]]:
; CHECK-NEXT: br label %[[END:.*]]
; CHECK: [[END]]:
; CHECK-NEXT: ret i32 poison
;