[llvm] e3a81df - llvm-reduce: Change function return types if function is not called (#134035)
via llvm-commits
llvm-commits at lists.llvm.org
Fri May 2 07:20:01 PDT 2025
Author: Matt Arsenault
Date: 2025-05-02T16:19:58+02:00
New Revision: e3a81df38cb67d472d66db3e918a46bd534ea11c
URL: https://github.com/llvm/llvm-project/commit/e3a81df38cb67d472d66db3e918a46bd534ea11c
DIFF: https://github.com/llvm/llvm-project/commit/e3a81df38cb67d472d66db3e918a46bd534ea11c.diff
LOG: 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.
Added:
llvm/test/tools/llvm-reduce/reduce-values-to-return-new-return-type.ll
Modified:
llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll
llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll b/llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll
index 2af87aad05169..77501418f5283 100644
--- a/llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-instructions-to-return.ll
@@ -196,13 +196,25 @@ define i32 @callsite_already_new_return_type(ptr %arg) {
; INTERESTING: ret
; RESULT-LABEL: define ptr @non_void_no_op(
-; RESULT: ret ptr null
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: store i32 %load, ptr @gv
+; RESULT-NEXT: ret ptr null
define ptr @non_void_no_op(ptr %arg) {
%load = load i32, ptr %arg
store i32 %load, ptr @gv
ret ptr null
}
+; INTERESTING-LABEL: @non_void_no_op_caller(
+
+; RESULT-LABEL: define ptr @non_void_no_op_caller(ptr %arg) {
+; RESULT-NEXT: %call = call ptr @non_void_no_op(ptr %arg)
+; RESULT-NEXT: ret ptr %call
+define ptr @non_void_no_op_caller(ptr %arg) {
+ %call = call ptr @non_void_no_op(ptr %arg)
+ ret ptr %call
+}
+
; INTERESTING-LABEL: @non_void_same_type_use(
; INTERESTING: = load
; INTERESTING: ret
@@ -230,6 +242,12 @@ define i32 @non_void_bitcastable_type_use(ptr %arg) {
ret i32 0
}
+; INTERESTING-LABEL: @non_void_bitcastable_type_use_caller(
+define i32 @non_void_bitcastable_type_use_caller(ptr %arg) {
+ %ret = call i32 @non_void_bitcastable_type_use(ptr %arg)
+ ret i32 %ret
+}
+
; INTERESTING-LABEL: @form_return_struct(
; INTERESTING: = load { i32, float }
diff --git a/llvm/test/tools/llvm-reduce/reduce-values-to-return-new-return-type.ll b/llvm/test/tools/llvm-reduce/reduce-values-to-return-new-return-type.ll
new file mode 100644
index 0000000000000..76ae1d17f885c
--- /dev/null
+++ b/llvm/test/tools/llvm-reduce/reduce-values-to-return-new-return-type.ll
@@ -0,0 +1,188 @@
+; Test that llvm-reduce can move intermediate values by inserting
+; early returns when the function already has a
diff erent return type
+;
+; 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
+; RUN: FileCheck --check-prefix=RESULT %s < %t
+
+
+ at gv = global i32 0, align 4
+ at ptr_array = global [2 x ptr] [ptr @inst_to_return_has_
diff erent_type_but_no_func_call_use,
+ ptr @multiple_callsites_wrong_return_type]
+
+; Should rewrite this return from i64 to i32 since the function has no
+; uses.
+; INTERESTING-LABEL: @inst_to_return_has_
diff erent_type_but_no_func_call_use(
+; RESULT-LABEL: define i32 @inst_to_return_has_
diff erent_type_but_no_func_call_use(ptr %arg) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define i64 @inst_to_return_has_
diff erent_type_but_no_func_call_use(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret i64 0
+}
+
+; INTERESTING-LABEL: @multiple_returns_wrong_return_type_no_callers(
+; RESULT-LABEL: define i32 @multiple_returns_wrong_return_type_no_callers(
+
+; RESULT: bb0:
+; RESULT-NEXT: %load0 = load i32,
+; RESULT-NEXT: ret i32 %load0
+
+; RESULT: bb1:
+; RESULT-NEXT: store i32 8, ptr null
+; RESULT-NEXT: ret i32 0
+define i64 @multiple_returns_wrong_return_type_no_callers(ptr %arg, i1 %cond, i64 %arg2) {
+entry:
+ br i1 %cond, label %bb0, label %bb1
+
+bb0:
+ %load0 = load i32, ptr %arg
+ store i32 %load0, ptr @gv
+ ret i64 234
+
+bb1:
+ store i32 8, ptr null
+ ret i64 %arg2
+
+bb2:
+ ret i64 34
+}
+
+; INTERESTING-LABEL: define {{.+}} @callsite_
diff erent_type_unused_0(
+
+; RESULT-LABEL: define i64 @callsite_
diff erent_type_unused_0(ptr %arg) {
+; RESULT-NEXT: %unused0 = call range(i64 99, 1024) i64 @inst_to_return_has_
diff erent_type_but_call_result_unused(ptr %arg)
+; RESULT-NEXT: ret i64 %unused0
+define void @callsite_
diff erent_type_unused_0(ptr %arg) {
+ %unused0 = call range(i64 99, 1024) i64 @inst_to_return_has_
diff erent_type_but_call_result_unused(ptr %arg)
+ %unused1 = call i64 @inst_to_return_has_
diff erent_type_but_call_result_unused(ptr null)
+ ret void
+}
+
+; TODO: Could rewrite this return from i64 to i32 since the callsite is unused.
+; INTERESTING-LABEL: define {{.+}} @inst_to_return_has_
diff erent_type_but_call_result_unused(
+; RESULT-LABEL: define i64 @inst_to_return_has_
diff erent_type_but_call_result_unused(
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: store i32 %load, ptr @gv
+; RESULT: ret i64 0
+define i64 @inst_to_return_has_
diff erent_type_but_call_result_unused(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret i64 0
+}
+
+; INTERESTING-LABEL: @multiple_callsites_wrong_return_type(
+; RESULT-LABEL: define i64 @multiple_callsites_wrong_return_type(
+; RESULT: ret i64 0
+define i64 @multiple_callsites_wrong_return_type(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret i64 0
+}
+
+; INTERESTING-LABEL: @unused_with_wrong_return_types(
+; RESULT-LABEL: define i64 @unused_with_wrong_return_types(
+; RESULT-NEXT: %unused0 = call i64 @multiple_callsites_wrong_return_type(ptr %arg)
+; RESULT-NEXT: ret i64 %unused0
+define void @unused_with_wrong_return_types(ptr %arg) {
+ %unused0 = call i64 @multiple_callsites_wrong_return_type(ptr %arg)
+ %unused1 = call i32 @multiple_callsites_wrong_return_type(ptr %arg)
+ %unused2 = call ptr @multiple_callsites_wrong_return_type(ptr %arg)
+ ret void
+}
+
+; INTERESTING-LABEL: @multiple_returns_wrong_return_type(
+; INTERESTING: %load0 = load i32,
+
+; RESULT-LABEL: define i32 @multiple_returns_wrong_return_type(
+; RESULT: ret i32
+; RESULT: ret i32
+; RESULT: ret i32
+define i32 @multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2) {
+entry:
+ br i1 %cond, label %bb0, label %bb1
+
+bb0:
+ %load0 = load i32, ptr %arg
+ store i32 %load0, ptr @gv
+ ret i32 234
+
+bb1:
+ ret i32 %arg2
+
+bb2:
+ ret i32 34
+}
+
+; INTERESTING-LABEL: @call_multiple_returns_wrong_return_type(
+; RESULT-LABEL: define <2 x i32> @call_multiple_returns_wrong_return_type(
+; RESULT-NEXT: %unused = call <2 x i32> @multiple_returns_wrong_return_type(
+; RESULT-NEXT: ret <2 x i32> %unused
+define void @call_multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2) {
+ %unused = call <2 x i32> @multiple_returns_wrong_return_type(ptr %arg, i1 %cond, i32 %arg2)
+ ret void
+}
+
+; INTERESTING-LABEL: @drop_incompatible_type_return_attrs(
+
+; RESULT-LABEL: define i32 @drop_incompatible_type_return_attrs(ptr %arg, float %arg1) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define nofpclass(nan inf) float @drop_incompatible_type_return_attrs(ptr %arg, float %arg1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret float %arg1
+}
+
+; INTERESTING-LABEL: @drop_incompatible_return_range(
+; RESULT-LABEL: define i32 @drop_incompatible_return_range(ptr %arg, i64 %arg1) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define range(i64 8, 256) i64 @drop_incompatible_return_range(ptr %arg, i64 %arg1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret i64 %arg1
+}
+
+; INTERESTING-LABEL: @preserve_compatible_return_range(
+; RESULT-LABEL: define range(i32 8, 256) i32 @preserve_compatible_return_range(ptr %arg, <2 x i32> %arg1) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define range(i32 8, 256) <2 x i32> @preserve_compatible_return_range(ptr %arg, <2 x i32> %arg1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret <2 x i32> %arg1
+}
+
+; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_0(
+
+; RESULT-LABEL: define i32 @drop_incompatible_returned_param_attr_0(ptr %arg, ptr %arg1) {
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: ret i32 %load
+define ptr @drop_incompatible_returned_param_attr_0(ptr returned %arg, ptr %arg1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret ptr %arg
+}
+
+; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_1(
+
+; RESULT-LABEL: define i32 @drop_incompatible_returned_param_attr_1(ptr %arg, ptr %arg1) {
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: ret i32 %load
+define ptr @drop_incompatible_returned_param_attr_1(ptr %arg, ptr returned %arg1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret ptr %arg
+}
+
+; INTERESTING-LABEL: @drop_incompatible_returned_param_attr_2
+
+; RESULT-LABEL: define ptr @drop_incompatible_returned_param_attr_2(ptr %arg, ptr %arg1) {
+; RESULT-NEXT: %load = load ptr, ptr %arg
+; RESULT-NEXT: ret ptr %load
+define ptr @drop_incompatible_returned_param_attr_2(ptr %arg, ptr returned %arg1) {
+ %load = load ptr, ptr %arg
+ store ptr %load, ptr @gv
+ ret ptr %arg1
+}
diff --git a/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp b/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
index 6a0bca6f7f3a8..4ef2f50a68371 100644
--- a/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
+++ b/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
@@ -18,6 +18,8 @@
#include "Delta.h"
#include "Utils.h"
+#include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Debug.h"
@@ -55,8 +57,10 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
BasicBlock::iterator NewValIt =
NewRetI ? NewRetI->getIterator() : EntryBB.end();
- // Hack up any return values in other blocks, we can't leave them as ret void.
- if (OldFuncTy->getReturnType()->isVoidTy()) {
+ Type *OldRetTy = OldFuncTy->getReturnType();
+
+ // Hack up any return values in other blocks, we can't leave them as returning OldRetTy.
+ if (OldRetTy != NewRetTy) {
for (BasicBlock &OtherRetBB : OldF) {
if (&OtherRetBB != NewRetBlock) {
auto *OrigRI = dyn_cast<ReturnInst>(OtherRetBB.getTerminator());
@@ -92,6 +96,18 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
// result of our pruning here.
EliminateUnreachableBlocks(OldF);
+ // Drop the incompatible attributes before we copy over to the new function.
+ if (OldRetTy != NewRetTy) {
+ AttributeList AL = OldF.getAttributes();
+ AttributeMask IncompatibleAttrs =
+ AttributeFuncs::typeIncompatible(NewRetTy, AL.getRetAttrs());
+ OldF.removeRetAttrs(IncompatibleAttrs);
+ }
+
+ // Now we need to remove any returned attributes from parameters.
+ for (Argument &A : OldF.args())
+ OldF.removeParamAttr(A.getArgNo(), Attribute::Returned);
+
Function *NewF =
Function::Create(NewFuncTy, OldF.getLinkage(), OldF.getAddressSpace(), "",
OldF.getParent());
@@ -161,7 +177,9 @@ static bool canReplaceFuncUsers(const Function &F, Type *NewRetTy) {
if (CB->getType() == NewRetTy)
continue;
- LLVM_DEBUG(dbgs() << "Cannot replace callsite with wrong type: " << *CB
+ // TODO: If all callsites have no uses, we could mutate the type of all the
+ // callsites. This will complicate the visit and rewrite ordering though.
+ LLVM_DEBUG(dbgs() << "Cannot replace used callsite with wrong type: " << *CB
<< '\n');
return false;
}
@@ -197,8 +215,7 @@ static bool shouldForwardValueToReturn(const BasicBlock &BB, const Value *V,
if (!isReallyValidReturnType(V->getType()))
return false;
- return (RetTy->isVoidTy() ||
- (RetTy == V->getType() && shouldReplaceNonVoidReturnValue(BB, V))) &&
+ return (RetTy->isVoidTy() || shouldReplaceNonVoidReturnValue(BB, V)) &&
canReplaceFuncUsers(*BB.getParent(), V->getType());
}
More information about the llvm-commits
mailing list