Skip to content

[FuncSpec] Query SCCPSolver in more places #114964

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 6, 2024
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
;
Loading