Skip to content

Commit e3a81df

Browse files
authored
llvm-reduce: Change function return types if function is not called (#134035)
Extend the early return on value reduction to mutate the function return type if the function has no call uses. This could be generalized to rewrite cases where all callsites are used, but it turns out that complicates the visitation order given we try to compute all opportunities up front. This is enough to cleanup the common case where we end up with one function with a return of an uninteresting constant.
1 parent 26bc8b0 commit e3a81df

File tree

3 files changed

+229
-6
lines changed

3 files changed

+229
-6
lines changed

llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,25 @@ define i32 @callsite_already_new_return_type(ptr %arg) {
196196
; INTERESTING: ret
197197

198198
; RESULT-LABEL: define ptr @non_void_no_op(
199-
; RESULT: ret ptr null
199+
; RESULT-NEXT: %load = load i32, ptr %arg
200+
; RESULT-NEXT: store i32 %load, ptr @gv
201+
; RESULT-NEXT: ret ptr null
200202
define ptr @non_void_no_op(ptr %arg) {
201203
%load = load i32, ptr %arg
202204
store i32 %load, ptr @gv
203205
ret ptr null
204206
}
205207

208+
; INTERESTING-LABEL: @non_void_no_op_caller(
209+
210+
; RESULT-LABEL: define ptr @non_void_no_op_caller(ptr %arg) {
211+
; RESULT-NEXT: %call = call ptr @non_void_no_op(ptr %arg)
212+
; RESULT-NEXT: ret ptr %call
213+
define ptr @non_void_no_op_caller(ptr %arg) {
214+
%call = call ptr @non_void_no_op(ptr %arg)
215+
ret ptr %call
216+
}
217+
206218
; INTERESTING-LABEL: @non_void_same_type_use(
207219
; INTERESTING: = load
208220
; INTERESTING: ret
@@ -230,6 +242,12 @@ define i32 @non_void_bitcastable_type_use(ptr %arg) {
230242
ret i32 0
231243
}
232244

245+
; INTERESTING-LABEL: @non_void_bitcastable_type_use_caller(
246+
define i32 @non_void_bitcastable_type_use_caller(ptr %arg) {
247+
%ret = call i32 @non_void_bitcastable_type_use(ptr %arg)
248+
ret i32 %ret
249+
}
250+
233251
; INTERESTING-LABEL: @form_return_struct(
234252
; INTERESTING: = load { i32, float }
235253

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
; Test that llvm-reduce can move intermediate values by inserting
2+
; early returns when the function already has a different return type
3+
;
4+
; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=instructions-to-return --test FileCheck --test-arg --check-prefix=INTERESTING --test-arg %s --test-arg --input-file %s -o %t
5+
; RUN: FileCheck --check-prefix=RESULT %s < %t
6+
7+
8+
@gv = global i32 0, align 4
9+
@ptr_array = global [2 x ptr] [ptr @inst_to_return_has_different_type_but_no_func_call_use,
10+
ptr @multiple_callsites_wrong_return_type]
11+
12+
; Should rewrite this return from i64 to i32 since the function has no
13+
; uses.
14+
; INTERESTING-LABEL: @inst_to_return_has_different_type_but_no_func_call_use(
15+
; RESULT-LABEL: define i32 @inst_to_return_has_different_type_but_no_func_call_use(ptr %arg) {
16+
; RESULT-NEXT: %load = load i32, ptr %arg, align 4
17+
; RESULT-NEXT: ret i32 %load
18+
define i64 @inst_to_return_has_different_type_but_no_func_call_use(ptr %arg) {
19+
%load = load i32, ptr %arg
20+
store i32 %load, ptr @gv
21+
ret i64 0
22+
}
23+
24+
; INTERESTING-LABEL: @multiple_returns_wrong_return_type_no_callers(
25+
; RESULT-LABEL: define i32 @multiple_returns_wrong_return_type_no_callers(
26+
27+
; RESULT: bb0:
28+
; RESULT-NEXT: %load0 = load i32,
29+
; RESULT-NEXT: ret i32 %load0
30+
31+
; RESULT: bb1:
32+
; RESULT-NEXT: store i32 8, ptr null
33+
; RESULT-NEXT: ret i32 0
34+
define i64 @multiple_returns_wrong_return_type_no_callers(ptr %arg, i1 %cond, i64 %arg2) {
35+
entry:
36+
br i1 %cond, label %bb0, label %bb1
37+
38+
bb0:
39+
%load0 = load i32, ptr %arg
40+
store i32 %load0, ptr @gv
41+
ret i64 234
42+
43+
bb1:
44+
store i32 8, ptr null
45+
ret i64 %arg2
46+
47+
bb2:
48+
ret i64 34
49+
}
50+
51+
; INTERESTING-LABEL: define {{.+}} @callsite_different_type_unused_0(
52+
53+
; RESULT-LABEL: define i64 @callsite_different_type_unused_0(ptr %arg) {
54+
; RESULT-NEXT: %unused0 = call range(i64 99, 1024) i64 @inst_to_return_has_different_type_but_call_result_unused(ptr %arg)
55+
; RESULT-NEXT: ret i64 %unused0
56+
define void @callsite_different_type_unused_0(ptr %arg) {
57+
%unused0 = call range(i64 99, 1024) i64 @inst_to_return_has_different_type_but_call_result_unused(ptr %arg)
58+
%unused1 = call i64 @inst_to_return_has_different_type_but_call_result_unused(ptr null)
59+
ret void
60+
}
61+
62+
; TODO: Could rewrite this return from i64 to i32 since the callsite is unused.
63+
; INTERESTING-LABEL: define {{.+}} @inst_to_return_has_different_type_but_call_result_unused(
64+
; RESULT-LABEL: define i64 @inst_to_return_has_different_type_but_call_result_unused(
65+
; RESULT-NEXT: %load = load i32, ptr %arg
66+
; RESULT-NEXT: store i32 %load, ptr @gv
67+
; RESULT: ret i64 0
68+
define i64 @inst_to_return_has_different_type_but_call_result_unused(ptr %arg) {
69+
%load = load i32, ptr %arg
70+
store i32 %load, ptr @gv
71+
ret i64 0
72+
}
73+
74+
; INTERESTING-LABEL: @multiple_callsites_wrong_return_type(
75+
; RESULT-LABEL: define i64 @multiple_callsites_wrong_return_type(
76+
; RESULT: ret i64 0
77+
define i64 @multiple_callsites_wrong_return_type(ptr %arg) {
78+
%load = load i32, ptr %arg
79+
store i32 %load, ptr @gv
80+
ret i64 0
81+
}
82+
83+
; INTERESTING-LABEL: @unused_with_wrong_return_types(
84+
; RESULT-LABEL: define i64 @unused_with_wrong_return_types(
85+
; RESULT-NEXT: %unused0 = call i64 @multiple_callsites_wrong_return_type(ptr %arg)
86+
; RESULT-NEXT: ret i64 %unused0
87+
define void @unused_with_wrong_return_types(ptr %arg) {
88+
%unused0 = call i64 @multiple_callsites_wrong_return_type(ptr %arg)
89+
%unused1 = call i32 @multiple_callsites_wrong_return_type(ptr %arg)
90+
%unused2 = call ptr @multiple_callsites_wrong_return_type(ptr %arg)
91+
ret void
92+
}
93+
94+
; INTERESTING-LABEL: @multiple_returns_wrong_return_type(
95+
; INTERESTING: %load0 = load i32,
96+
97+
; RESULT-LABEL: define i32 @multiple_returns_wrong_return_type(
98+
; RESULT: ret i32
99+
; RESULT: ret i32
100+
; RESULT: ret i32
101+
define i32 @multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2) {
102+
entry:
103+
br i1 %cond, label %bb0, label %bb1
104+
105+
bb0:
106+
%load0 = load i32, ptr %arg
107+
store i32 %load0, ptr @gv
108+
ret i32 234
109+
110+
bb1:
111+
ret i32 %arg2
112+
113+
bb2:
114+
ret i32 34
115+
}
116+
117+
; INTERESTING-LABEL: @call_multiple_returns_wrong_return_type(
118+
; RESULT-LABEL: define <2 x i32> @call_multiple_returns_wrong_return_type(
119+
; RESULT-NEXT: %unused = call <2 x i32> @multiple_returns_wrong_return_type(
120+
; RESULT-NEXT: ret <2 x i32> %unused
121+
define void @call_multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2) {
122+
%unused = call <2 x i32> @multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2)
123+
ret void
124+
}
125+
126+
; INTERESTING-LABEL: @drop_incompatible_type_return_attrs(
127+
128+
; RESULT-LABEL: define i32 @drop_incompatible_type_return_attrs(ptr %arg, float %arg1) {
129+
; RESULT-NEXT: %load = load i32, ptr %arg, align 4
130+
; RESULT-NEXT: ret i32 %load
131+
define nofpclass(nan inf) float @drop_incompatible_type_return_attrs(ptr %arg, float %arg1) {
132+
%load = load i32, ptr %arg
133+
store i32 %load, ptr @gv
134+
ret float %arg1
135+
}
136+
137+
; INTERESTING-LABEL: @drop_incompatible_return_range(
138+
; RESULT-LABEL: define i32 @drop_incompatible_return_range(ptr %arg, i64 %arg1) {
139+
; RESULT-NEXT: %load = load i32, ptr %arg, align 4
140+
; RESULT-NEXT: ret i32 %load
141+
define range(i64 8, 256) i64 @drop_incompatible_return_range(ptr %arg, i64 %arg1) {
142+
%load = load i32, ptr %arg
143+
store i32 %load, ptr @gv
144+
ret i64 %arg1
145+
}
146+
147+
; INTERESTING-LABEL: @preserve_compatible_return_range(
148+
; RESULT-LABEL: define range(i32 8, 256) i32 @preserve_compatible_return_range(ptr %arg, <2 x i32> %arg1) {
149+
; RESULT-NEXT: %load = load i32, ptr %arg, align 4
150+
; RESULT-NEXT: ret i32 %load
151+
define range(i32 8, 256) <2 x i32> @preserve_compatible_return_range(ptr %arg, <2 x i32> %arg1) {
152+
%load = load i32, ptr %arg
153+
store i32 %load, ptr @gv
154+
ret <2 x i32> %arg1
155+
}
156+
157+
; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_0(
158+
159+
; RESULT-LABEL: define i32 @drop_incompatible_returned_param_attr_0(ptr %arg, ptr %arg1) {
160+
; RESULT-NEXT: %load = load i32, ptr %arg
161+
; RESULT-NEXT: ret i32 %load
162+
define ptr @drop_incompatible_returned_param_attr_0(ptr returned %arg, ptr %arg1) {
163+
%load = load i32, ptr %arg
164+
store i32 %load, ptr @gv
165+
ret ptr %arg
166+
}
167+
168+
; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_1(
169+
170+
; RESULT-LABEL: define i32 @drop_incompatible_returned_param_attr_1(ptr %arg, ptr %arg1) {
171+
; RESULT-NEXT: %load = load i32, ptr %arg
172+
; RESULT-NEXT: ret i32 %load
173+
define ptr @drop_incompatible_returned_param_attr_1(ptr %arg, ptr returned %arg1) {
174+
%load = load i32, ptr %arg
175+
store i32 %load, ptr @gv
176+
ret ptr %arg
177+
}
178+
179+
; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_2
180+
181+
; RESULT-LABEL: define ptr @drop_incompatible_returned_param_attr_2(ptr %arg, ptr %arg1) {
182+
; RESULT-NEXT: %load = load ptr, ptr %arg
183+
; RESULT-NEXT: ret ptr %load
184+
define ptr @drop_incompatible_returned_param_attr_2(ptr %arg, ptr returned %arg1) {
185+
%load = load ptr, ptr %arg
186+
store ptr %load, ptr @gv
187+
ret ptr %arg1
188+
}

llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include "Delta.h"
2020
#include "Utils.h"
21+
#include "llvm/IR/AttributeMask.h"
22+
#include "llvm/IR/Attributes.h"
2123
#include "llvm/IR/CFG.h"
2224
#include "llvm/IR/Instructions.h"
2325
#include "llvm/Support/Debug.h"
@@ -55,8 +57,10 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
5557
BasicBlock::iterator NewValIt =
5658
NewRetI ? NewRetI->getIterator() : EntryBB.end();
5759

58-
// Hack up any return values in other blocks, we can't leave them as ret void.
59-
if (OldFuncTy->getReturnType()->isVoidTy()) {
60+
Type *OldRetTy = OldFuncTy->getReturnType();
61+
62+
// Hack up any return values in other blocks, we can't leave them as returning OldRetTy.
63+
if (OldRetTy != NewRetTy) {
6064
for (BasicBlock &OtherRetBB : OldF) {
6165
if (&OtherRetBB != NewRetBlock) {
6266
auto *OrigRI = dyn_cast<ReturnInst>(OtherRetBB.getTerminator());
@@ -92,6 +96,18 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
9296
// result of our pruning here.
9397
EliminateUnreachableBlocks(OldF);
9498

99+
// Drop the incompatible attributes before we copy over to the new function.
100+
if (OldRetTy != NewRetTy) {
101+
AttributeList AL = OldF.getAttributes();
102+
AttributeMask IncompatibleAttrs =
103+
AttributeFuncs::typeIncompatible(NewRetTy, AL.getRetAttrs());
104+
OldF.removeRetAttrs(IncompatibleAttrs);
105+
}
106+
107+
// Now we need to remove any returned attributes from parameters.
108+
for (Argument &A : OldF.args())
109+
OldF.removeParamAttr(A.getArgNo(), Attribute::Returned);
110+
95111
Function *NewF =
96112
Function::Create(NewFuncTy, OldF.getLinkage(), OldF.getAddressSpace(), "",
97113
OldF.getParent());
@@ -161,7 +177,9 @@ static bool canReplaceFuncUsers(const Function &F, Type *NewRetTy) {
161177
if (CB->getType() == NewRetTy)
162178
continue;
163179

164-
LLVM_DEBUG(dbgs() << "Cannot replace callsite with wrong type: " << *CB
180+
// TODO: If all callsites have no uses, we could mutate the type of all the
181+
// callsites. This will complicate the visit and rewrite ordering though.
182+
LLVM_DEBUG(dbgs() << "Cannot replace used callsite with wrong type: " << *CB
165183
<< '\n');
166184
return false;
167185
}
@@ -197,8 +215,7 @@ static bool shouldForwardValueToReturn(const BasicBlock &BB, const Value *V,
197215
if (!isReallyValidReturnType(V->getType()))
198216
return false;
199217

200-
return (RetTy->isVoidTy() ||
201-
(RetTy == V->getType() && shouldReplaceNonVoidReturnValue(BB, V))) &&
218+
return (RetTy->isVoidTy() || shouldReplaceNonVoidReturnValue(BB, V)) &&
202219
canReplaceFuncUsers(*BB.getParent(), V->getType());
203220
}
204221

0 commit comments

Comments
 (0)