[llvm] llvm-reduce: Add values to return reduction (PR #132686)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 24 00:37:27 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
In void functions, try to replace instruction uses
with a new non-void return. If the return type matches
the instruction, also try to directly return it.
This handles most of the cases, but doesn't try to handle
all of the weird exception related terminators.
Also doesn't try to replace argument uses, although it could. We
could also handle cases where we can insert a simple cast to an
original return value. I didn't think too hard about where to put this
in the default pass order. In many cases it obviates the need for most
of the CFG folds, but I've left it near the end initially.
I also think this is too aggressive about removing dead code, and
should leave existing dead code alone. I'm also not sure why we have
both "removeUnreachableBlocks" and EliminateUnreachableBlocks" in Utils.
Fixes #<!-- -->66039, fixes #<!-- -->107327
---
Patch is 43.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132686.diff
10 Files Affected:
- (modified) llvm/include/llvm/IR/Function.h (+8)
- (modified) llvm/lib/IR/Function.cpp (+82)
- (modified) llvm/test/tools/llvm-reduce/reduce-operands-fp.ll (+1-1)
- (modified) llvm/test/tools/llvm-reduce/reduce-operands-int.ll (+1-1)
- (modified) llvm/test/tools/llvm-reduce/reduce-operands-ptr.ll (+1-1)
- (added) llvm/test/tools/llvm-reduce/reduce-values-to-return.ll (+959)
- (modified) llvm/tools/llvm-reduce/CMakeLists.txt (+1)
- (modified) llvm/tools/llvm-reduce/DeltaManager.cpp (+2)
- (added) llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp (+245)
- (added) llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.h (+18)
``````````diff
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index f17b7140ca29c..6d4a53da7ff22 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -1048,6 +1048,14 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
void setValueSubclassDataBit(unsigned Bit, bool On);
};
+namespace CallingConv {
+
+// TODO: Need similar function for support of argument in position. General
+// version on FunctionType + Attributes + CallingConv::ID?
+LLVM_READNONE
+bool supportsNonVoidReturnType(CallingConv::ID CC);
+} // namespace CallingConv
+
/// Check whether null pointer dereferencing is considered undefined behavior
/// for a given function or an address space.
/// Null pointer access in non-zero address space is not considered undefined.
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 3644fab913b10..79308e4787b67 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -1177,3 +1177,85 @@ bool llvm::NullPointerIsDefined(const Function *F, unsigned AS) {
return false;
}
+
+bool llvm::CallingConv::supportsNonVoidReturnType(CallingConv::ID CC) {
+ switch (CC) {
+ case CallingConv::C:
+ case CallingConv::Fast:
+ case CallingConv::Cold:
+ case CallingConv::GHC:
+ case CallingConv::HiPE:
+ case CallingConv::AnyReg:
+ case CallingConv::PreserveMost:
+ case CallingConv::PreserveAll:
+ case CallingConv::Swift:
+ case CallingConv::CXX_FAST_TLS:
+ case CallingConv::Tail:
+ case CallingConv::CFGuard_Check:
+ case CallingConv::SwiftTail:
+ case CallingConv::PreserveNone:
+ case CallingConv::X86_StdCall:
+ case CallingConv::X86_FastCall:
+ case CallingConv::ARM_APCS:
+ case CallingConv::ARM_AAPCS:
+ case CallingConv::ARM_AAPCS_VFP:
+ case CallingConv::MSP430_INTR:
+ case CallingConv::X86_ThisCall:
+ case CallingConv::PTX_Device:
+ case CallingConv::SPIR_FUNC:
+ case CallingConv::Intel_OCL_BI:
+ case CallingConv::X86_64_SysV:
+ case CallingConv::Win64:
+ case CallingConv::X86_VectorCall:
+ case CallingConv::DUMMY_HHVM:
+ case CallingConv::DUMMY_HHVM_C:
+ case CallingConv::X86_INTR:
+ case CallingConv::AVR_INTR:
+ case CallingConv::AVR_SIGNAL:
+ case CallingConv::AVR_BUILTIN:
+ return true;
+ case CallingConv::AMDGPU_KERNEL:
+ case CallingConv::SPIR_KERNEL:
+ case CallingConv::AMDGPU_CS_Chain:
+ case CallingConv::AMDGPU_CS_ChainPreserve:
+ return false;
+ case CallingConv::AMDGPU_VS:
+ case CallingConv::AMDGPU_HS:
+ case CallingConv::AMDGPU_GS:
+ case CallingConv::AMDGPU_PS:
+ case CallingConv::AMDGPU_CS:
+ case CallingConv::AMDGPU_LS:
+ case CallingConv::AMDGPU_ES:
+ case CallingConv::MSP430_BUILTIN:
+ case CallingConv::AArch64_VectorCall:
+ case CallingConv::AArch64_SVE_VectorCall:
+ case CallingConv::WASM_EmscriptenInvoke:
+ case CallingConv::AMDGPU_Gfx:
+ case CallingConv::M68k_INTR:
+ case CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0:
+ case CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2:
+ case CallingConv::M68k_RTD:
+ case CallingConv::GRAAL:
+ case CallingConv::ARM64EC_Thunk_X64:
+ case CallingConv::ARM64EC_Thunk_Native:
+ case CallingConv::RISCV_VectorCall:
+ case CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X1:
+ case CallingConv::RISCV_VLSCall_32:
+ case CallingConv::RISCV_VLSCall_64:
+ case CallingConv::RISCV_VLSCall_128:
+ case CallingConv::RISCV_VLSCall_256:
+ case CallingConv::RISCV_VLSCall_512:
+ case CallingConv::RISCV_VLSCall_1024:
+ case CallingConv::RISCV_VLSCall_2048:
+ case CallingConv::RISCV_VLSCall_4096:
+ case CallingConv::RISCV_VLSCall_8192:
+ case CallingConv::RISCV_VLSCall_16384:
+ case CallingConv::RISCV_VLSCall_32768:
+ case CallingConv::RISCV_VLSCall_65536:
+ return true;
+ default:
+ return false;
+ }
+
+ llvm_unreachable("covered callingconv switch");
+}
diff --git a/llvm/test/tools/llvm-reduce/reduce-operands-fp.ll b/llvm/test/tools/llvm-reduce/reduce-operands-fp.ll
index b547c819bf0de..e10a3f8c010ce 100644
--- a/llvm/test/tools/llvm-reduce/reduce-operands-fp.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-operands-fp.ll
@@ -27,7 +27,7 @@
; CHECK-INTERESTINGNESS: = fadd <2 x float>
; CHECK-INTERESTINGNESS: = fadd <2 x float>
-; CHECK-LABEL: define void @foo(
+; CHECK-LABEL: define {{(void|<2 x float>)}} @foo(
; ONE: %fadd0 = fadd float %arg0, 1.000000e+00
diff --git a/llvm/test/tools/llvm-reduce/reduce-operands-int.ll b/llvm/test/tools/llvm-reduce/reduce-operands-int.ll
index 397a1595ca6b2..66ad30832aabc 100644
--- a/llvm/test/tools/llvm-reduce/reduce-operands-int.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-operands-int.ll
@@ -22,7 +22,7 @@
; CHECK-INTERESTINGNESS: = add <2 x i32>
; CHECK-INTERESTINGNESS: = add <2 x i32>
-; CHECK-LABEL: define void @foo(
+; CHECK-LABEL: define {{(void|<2 x i32>)}} @foo(
; ONE: %add0 = add i32 %arg0, 1
diff --git a/llvm/test/tools/llvm-reduce/reduce-operands-ptr.ll b/llvm/test/tools/llvm-reduce/reduce-operands-ptr.ll
index 3e163b30f6b38..4669cf76074d4 100644
--- a/llvm/test/tools/llvm-reduce/reduce-operands-ptr.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-operands-ptr.ll
@@ -8,7 +8,7 @@
; RUN: llvm-reduce --abort-on-invalid-reduction --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t
; RUN: FileCheck --check-prefixes=CHECK,ZERO %s < %t
-; CHECK-LABEL: define void @foo(
+; CHECK-LABEL: define {{(void|ptr)}} @foo(
; ONE: load i32, ptr %a0
; ONE: load i32, ptr @g
diff --git a/llvm/test/tools/llvm-reduce/reduce-values-to-return.ll b/llvm/test/tools/llvm-reduce/reduce-values-to-return.ll
new file mode 100644
index 0000000000000..0c36db8ebc278
--- /dev/null
+++ b/llvm/test/tools/llvm-reduce/reduce-values-to-return.ll
@@ -0,0 +1,959 @@
+; Test that llvm-reduce can move intermediate values by inserting
+; early returns
+;
+; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=values-to-return --test FileCheck --test-arg --check-prefixes=CHECK,INTERESTING --test-arg %s --test-arg --input-file %s -o %t
+; RUN: FileCheck --check-prefixes=CHECK,RESULT %s < %t
+
+ at gv = global i32 0, align 4
+ at gv_struct = global { i32, float } zeroinitializer, align 4
+ at gv_array = global [3 x i32] zeroinitializer, align 4
+ at gv_empty_struct = global { } zeroinitializer, align 4
+
+; CHECK: @global.func.user = global ptr @store_instruction_to_return_with_uses
+ at global.func.user = global ptr @store_instruction_to_return_with_uses
+
+; INTERESTING-LABEL: @store_instruction_to_return_with_uses(
+; INTERESTING-NEXT: = load
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_with_uses(ptr %arg) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_with_uses(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: define void @user(
+; INTERESTING: call
+
+; RESULT-LABEL: define void @user(
+; RESULT-NEXT: call i32 @store_instruction_to_return_with_uses(ptr %a, ptr %b)
+; RESULT-NEXT: ret void
+; RESULT-NEXT: }
+define void @user(ptr %a, ptr %b) {
+ call void @store_instruction_to_return_with_uses(ptr %a, ptr %b)
+ ret void
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_no_uses(
+; INTERESTING: = load i32
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_no_uses(
+; RESULT-NEXT: %load = load i32
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_no_uses(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_preserve_attrs(
+; INTERESTING: = load
+
+; RESULT: ; Function Attrs: nounwind
+; RESULT-NEXT: define weak i32 @store_instruction_to_return_preserve_attrs(ptr byref(i32) %arg) #0 {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define weak void @store_instruction_to_return_preserve_attrs(ptr byref(i32) %arg) nounwind "some-attr" {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_preserve_addrspace(
+; INTERESTING: = load
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_preserve_addrspace(ptr %arg) addrspace(1) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_preserve_addrspace(ptr %arg) addrspace(1) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_no_uses_unreachable(
+; INTERESTING: = load
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_no_uses_unreachable(ptr %arg) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_no_uses_unreachable(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ unreachable
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_with_non_callee_use(
+; INTERESTING: = load
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_with_non_callee_use(ptr %arg) {
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_with_non_callee_use(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+declare void @takes_fptr(ptr)
+
+; CHECK: @non_callee_user(
+; CHECK: ret void
+define void @non_callee_user(ptr %a, ptr %b) {
+ call void @takes_fptr(ptr @store_instruction_to_return_with_non_callee_use)
+ ret void
+}
+
+declare i32 @convergent_call() convergent
+
+; CHECK-LABEL: @no_return_token_def(
+; CHECK: call token
+; RESULT: ret void
+define void @no_return_token_def(ptr %arg) convergent {
+ %t = call token @llvm.experimental.convergence.entry()
+ ret void
+}
+
+; INTERESTING-LABEL: @no_return_token_def_other(
+; INTERESTING: call token
+
+; RESULT-LABEL: define i32 @no_return_token_def_other(
+; RESULT: call token
+; RESULT: call i32
+; RESULT: ret i32
+define void @no_return_token_def_other(ptr %arg) convergent {
+ %t = call token @llvm.experimental.convergence.entry()
+ %call = call i32 @convergent_call() [ "convergencectrl"(token %t) ]
+ store i32 %call, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @store_instruction_to_return_variadic_func(
+; INTERESTING: = load
+
+; RESULT-LABEL: define i32 @store_instruction_to_return_variadic_func(ptr %arg, ...)
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @store_instruction_to_return_variadic_func(ptr %arg, ...) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; Has a callsite use that is invoking the function with a non-void
+; return type, that does not match the new return type.
+
+; INTERESTING-LABEL: @inst_to_return_has_nonvoid_wrong_type_caller(
+
+; RESULT-LABEL: define void @inst_to_return_has_nonvoid_wrong_type_caller(
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: store i32 %load, ptr @gv
+; RESULT-NEXT: ret void
+define void @inst_to_return_has_nonvoid_wrong_type_caller(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @wrong_callsite_return_type(
+
+; RESULT-LABEL: define i64 @wrong_callsite_return_type(
+; RESULT-NEXT: %ret = call i64 @inst_to_return_has_nonvoid_wrong_type_caller(ptr %arg)
+; RESULT-NEXT: ret i64 %ret
+define i64 @wrong_callsite_return_type(ptr %arg) {
+ %ret = call i64 @inst_to_return_has_nonvoid_wrong_type_caller(ptr %arg)
+ ret i64 %ret
+}
+
+; INTERESTING-LABEL: @inst_to_return_already_has_new_type_caller(
+
+; RESULT-LABEL: define i32 @inst_to_return_already_has_new_type_caller(
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @inst_to_return_already_has_new_type_caller(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; Callsite has UB signature mismatch, but the return type happens to
+; match the new return type.
+;
+; INTERESTING-LABEL: @callsite_already_new_return_type(
+
+; RESULT-LABEL: define i32 @callsite_already_new_return_type(
+; RESULT-NEXT: %ret = call i32 @inst_to_return_already_has_new_type_caller(ptr %arg)
+; RESULT-NEXT: ret i32 %ret
+define i32 @callsite_already_new_return_type(ptr %arg) {
+ %ret = call i32 @inst_to_return_already_has_new_type_caller(ptr %arg)
+ ret i32 %ret
+}
+
+; INTERESTING-LABEL: @non_void_no_op(
+; INTERESTING: = load
+; INTERESTING: ret
+
+; RESULT-LABEL: define ptr @non_void_no_op(
+; RESULT: 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_same_type_use(
+; INTERESTING: = load
+; INTERESTING: ret
+
+; RESULT-LABEL: define i32 @non_void_same_type_use(
+; RESULT-NEXT: %load = load i32, ptr %arg
+; RESULT-NEXT: ret i32 %load
+define i32 @non_void_same_type_use(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret i32 0
+}
+
+; INTERESTING-LABEL: @non_void_bitcastable_type_use(
+; INTERESTING: = load
+; INTERESTING: ret
+
+; RESULT-LABEL: define i32 @non_void_bitcastable_type_use(
+; RESULT-NEXT: %load = load float, ptr %arg
+; RESULT-NEXT: store float %load,
+; RESULT-NEXT: ret i32 0
+define i32 @non_void_bitcastable_type_use(ptr %arg) {
+ %load = load float, ptr %arg
+ store float %load, ptr @gv
+ ret i32 0
+}
+
+; INTERESTING-LABEL: @form_return_struct(
+; INTERESTING: = load { i32, float }
+
+; RESULT-LABEL: define { i32, float } @form_return_struct(ptr %arg) {
+; RESULT-NEXT: %load = load { i32, float }, ptr %arg, align 4
+; RESULT-NEXT: ret { i32, float } %load
+define void @form_return_struct(ptr %arg) {
+ %load = load { i32, float }, ptr %arg
+ store { i32, float } %load, ptr @gv_struct
+ ret void
+}
+
+; INTERESTING-LABEL: define void @return_struct_user(
+; INTERESTING-NEXT: call
+; RESULT: call { i32, float } @form_return_struct(ptr %arg)
+define void @return_struct_user(ptr %arg) {
+ call void @form_return_struct(ptr %arg)
+ ret void
+}
+
+; INTERESTING-LABEL: @form_return_array(
+; INTERESTING: = load
+
+; RESULT-LABEL: define [3 x i32] @form_return_array(
+; RESULT-NEXT: %load = load [3 x i32]
+; RESULT-NEXT: ret [3 x i32] %load
+define void @form_return_array(ptr %arg) {
+ %load = load [3 x i32], ptr %arg
+ store [3 x i32] %load, ptr @gv_array
+ ret void
+}
+
+; CHECK-LABEL: @return_array_user(
+; RESULT: call [3 x i32] @form_return_array(ptr %arg)
+define void @return_array_user(ptr %arg) {
+ call void @form_return_array(ptr %arg)
+ ret void
+}
+
+; INTERESTING-LABEL: @form_return_empty_struct(
+; INTERESTING: = load
+
+; RESULT: define {} @form_return_empty_struct(
+; RESULT-NEXT: %load = load {}
+; RESULT-NEXT: ret {} %load
+define void @form_return_empty_struct(ptr %arg) {
+ %load = load { }, ptr %arg
+ store { } %load, ptr @gv_empty_struct
+ ret void
+}
+
+; CHECK-LABEL: define void @return_empty_struct_user(
+; RESULT: call {} @form_return_empty_struct(ptr %arg)
+define void @return_empty_struct_user(ptr %arg) {
+ call void @form_return_empty_struct(ptr %arg)
+ ret void
+}
+
+define target("sometarget.sometype") @target_type_func() {
+ ret target("sometarget.sometype") poison
+}
+
+define void @target_type_user(target("sometarget.sometype") %a) {
+ ret void
+}
+
+; INTERESTING-LABEL: @form_return_target_ty(
+; INTERESTING: call target("sometarget.sometype") @target_type_func()
+
+; RESULT: define target("sometarget.sometype") @form_return_target_ty(
+; RESULT-NEXT: %call = call target("sometarget.sometype") @target_type_func()
+; RESULT-NEXT: ret target("sometarget.sometype") %call
+define void @form_return_target_ty(ptr %arg) {
+ %call = call target("sometarget.sometype") @target_type_func()
+ call void @target_type_user(target("sometarget.sometype") %call)
+ ret void
+}
+
+; CHECK-LABEL: define void @return_target_ty_user(
+; RESULT-NEXT: %1 = call target("sometarget.sometype") @form_return_target_ty(ptr %arg)
+; RESULT-NEXT: ret void
+define void @return_target_ty_user(ptr %arg) {
+ call void @form_return_target_ty(ptr %arg)
+ ret void
+}
+
+; Make sure an invalid reduction isn't attempted for a function with
+; an sret argument
+
+; CHECK-LABEL: @no_sret_nonvoid_return
+define void @no_sret_nonvoid_return(ptr sret(i32) %out.sret, ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr %out.sret
+ ret void
+}
+
+; Test a calling convention where it's illegal to use a non-void
+; return. No invalid reduction should be introduced.
+
+; INTERESTING-LABEL: @no_void_return_callingconv(
+; INTERESTING: = load i32
+
+; RESULT-LABEL: define amdgpu_kernel void @no_void_return_callingconv(
+; RESULT-NEXT: %load = load i32
+; RESULT-NEXT: store i32 %load
+; RESULT-NEXT: ret void
+define amdgpu_kernel void @no_void_return_callingconv(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @keep_first_of_3(
+; INTERESTING: %load0 = load i32, ptr %arg0
+; INTERESTING: ret
+
+; RESULT-LABEL: define i32 @keep_first_of_3(
+; RESULT-NEXT: %load0 = load i32, ptr %arg0, align 4
+; RESULT-NEXT: ret i32 %load0
+define void @keep_first_of_3(ptr %arg0, ptr %arg1, ptr %arg2) {
+ %load0 = load i32, ptr %arg0
+ %load1 = load i32, ptr %arg1
+ %load2 = load i32, ptr %arg2
+ store i32 %load0, ptr @gv
+ store i32 %load1, ptr @gv
+ store i32 %load2, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @keep_second_of_3(
+; INTERESTING: %load1 = load i32, ptr %arg1
+
+; RESULT-LABEL: define i32 @keep_second_of_3(
+; RESULT-NEXT: %load0 = load i32, ptr %arg0
+; RESULT-NEXT: %load1 = load i32, ptr %arg1
+; RESULT-NEXT: ret i32 %load1
+define void @keep_second_of_3(ptr %arg0, ptr %arg1, ptr %arg2) {
+ %load0 = load i32, ptr %arg0
+ %load1 = load i32, ptr %arg1
+ %load2 = load i32, ptr %arg2
+ store i32 %load0, ptr @gv
+ store i32 %load1, ptr @gv
+ store i32 %load2, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @keep_third_of_3(
+; INTERESTING: %load2 = load i32, ptr %arg2
+
+; RESULT-LABEL: define i32 @keep_third_of_3(
+; RESULT-NEXT: %load0 = load i32, ptr %arg0, align 4
+; RESULT-NEXT: %load1 = load i32, ptr %arg1, align 4
+; RESULT-NEXT: %load2 = load i32, ptr %arg2, align 4
+; RESULT-NEXT: ret i32 %load2
+define void @keep_third_of_3(ptr %arg0, ptr %arg1, ptr %arg2) {
+ %load0 = load i32, ptr %arg0
+ %load1 = load i32, ptr %arg1
+ %load2 = load i32, ptr %arg2
+ store i32 %load0, ptr @gv
+ store i32 %load1, ptr @gv
+ store i32 %load2, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @keep_first_2_of_3(
+; INTERESTING: %load0 = load i32, ptr %arg0
+; INTERESTING: %load1 = load i32, ptr %arg1
+
+; RESULT-LABEL: define i32 @keep_first_2_of_3(
+; RESULT-NEXT: %load0 = load i32, ptr %arg0
+; RESULT-NEXT: %load1 = load i32, ptr %arg1
+; RESULT-NEXT: ret i32 %load1
+define void @keep_first_2_of_3(ptr %arg0, ptr %arg1, ptr %arg2) {
+ %load0 = load i32, ptr %arg0
+ %load1 = load i32, ptr %arg1
+ %load2 = load i32, ptr %arg2
+ store i32 %load0, ptr @gv
+ store i32 %load1, ptr @gv
+ store i32 %load2, ptr @gv
+ ret void
+}
+
+; INTERESTING-LABEL: @keep_second_of_3_already_ret_constexpr(
+; INTERESTING: %load1 = load i32, ptr %arg1
+; INTERESTING: ret
+
+; RESULT-LABEL: define i32 @keep_second_of_3_already_ret_constexpr(
+; RESULT-NEXT: %load0 = load i32, ptr %arg0, align 4
+; RESULT-NEXT: %load1 = load i32, ptr %arg1, align 4
+; RESULT-NEXT: ret i32 %load1
+define i32 @keep_second_of_3_already_ret_constexpr(ptr %arg0, ptr %arg1, ptr %arg2) {
+ %load0 = load i32, ptr %arg0
+ %load1 = load i32, ptr %arg1
+ %load2 = load i32, ptr %arg2
+ store i32 %load0, ptr @gv
+ store i32 %load1, ptr @gv
+ store i32 %load2, ptr @gv
+ ret i32 ptrtoint (ptr @gv to i32)
+}
+
+; INTERESTING-LABEL: @self_recursive(
+; INTERESTING: %load = load i32, ptr %arg
+
+; RESULT-LABEL: define i32 @self_recursive(
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @self_recursive(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @gv
+ call void @self_recursive(ptr %arg)
+ ret void
+}
+
+; INTERESTING-LABEL: @has_invoke_user(
+
+; RESULT-LABEL: define i32 @has_invoke_user(
+; RESULT-NEXT: %load = load i32, ptr %arg, align 4
+; RESULT-NEXT: ret i32 %load
+define void @has_invoke_user(ptr %arg) {
+ %load = load i32, ptr %arg
+ store i32 %load, ptr @g...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/132686
More information about the llvm-commits
mailing list