[llvm] FunctionAttrs: Basic propagation of nofpclass (PR #182444)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 21 00:22:28 PST 2026
================
@@ -0,0 +1,317 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt -S -passes='cgscc(function-attrs),rpo-function-attrs' < %s | FileCheck %s
+
+define float @return_f32_extern(ptr %ptr) {
+; CHECK-LABEL: define float @return_f32_extern(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+ %val = load volatile float, ptr %ptr
+ ret float %val
+}
+
+; Deduce nofpclass(inf) on return
+define internal float @only_noinf_ret_uses(ptr %ptr) {
+; CHECK-LABEL: define internal nofpclass(inf) float @only_noinf_ret_uses(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+ %val = load volatile float, ptr %ptr
+ ret float %val
+}
+
+define float @calls_no_inf_return(ptr %ptr) {
+; CHECK-LABEL: define float @calls_no_inf_return(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CALL0:%.*]] = call float @return_f32_extern(ptr [[PTR]])
+; CHECK-NEXT: [[CALL1:%.*]] = call nofpclass(inf) float @only_noinf_ret_uses(ptr [[PTR]])
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[CALL0]], [[CALL1]]
+; CHECK-NEXT: ret float [[ADD]]
+;
+ %call0 = call float @return_f32_extern(ptr %ptr)
+ %call1 = call nofpclass(inf) float @only_noinf_ret_uses(ptr %ptr)
+ %add = fadd float %call0, %call1
+ ret float %add
+}
+
+; Deduce nofpclass(nan) on return, not inf or zero
+define internal float @merged_ret_uses(ptr %ptr) {
+; CHECK-LABEL: define internal nofpclass(nan) float @merged_ret_uses(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+ %val = load volatile float, ptr %ptr
+ ret float %val
+}
+
+define float @calls_merge_rets(ptr %ptr) {
+; CHECK-LABEL: define float @calls_merge_rets(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CALL0:%.*]] = call nofpclass(nan inf) float @merged_ret_uses(ptr [[PTR]])
+; CHECK-NEXT: [[CALL1:%.*]] = call nofpclass(nan zero) float @merged_ret_uses(ptr [[PTR]])
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[CALL0]], [[CALL1]]
+; CHECK-NEXT: ret float [[ADD]]
+;
+ %call0 = call nofpclass(nan inf) float @merged_ret_uses(ptr %ptr)
+ %call1 = call nofpclass(nan zero) float @merged_ret_uses(ptr %ptr)
+ %add = fadd float %call0, %call1
+ ret float %add
+}
+
+; Do not infer nofpclass on return
+define internal float @called_with_wrong_ret_type(ptr %ptr) {
+; CHECK-LABEL: define internal float @called_with_wrong_ret_type(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+ %val = load volatile float, ptr %ptr
+ ret float %val
+}
+
+define <2 x half> @wrong_callee_ret_type(ptr %ptr) {
+; CHECK-LABEL: define <2 x half> @wrong_callee_ret_type(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) <2 x half> @called_with_wrong_ret_type(ptr [[PTR]])
+; CHECK-NEXT: ret <2 x half> [[RET]]
+;
+ %ret = call nofpclass(nan) <2 x half> @called_with_wrong_ret_type(ptr %ptr)
+ ret <2 x half> %ret
+}
+
+; Do not infer nofpclass on return
+define internal float @non_callee_use_ret(ptr %ptr) {
+; CHECK-LABEL: define internal float @non_callee_use_ret(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+ %val = load volatile float, ptr %ptr
+ ret float %val
+}
+
+declare float @uses_func_ptr(ptr)
+
+define float @caller_non_callee_use(ptr %ptr) {
+; CHECK-LABEL: define float @caller_non_callee_use(
+; CHECK-SAME: ptr readnone captures(none) [[PTR:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @uses_func_ptr(ptr @non_callee_use_ret)
+; CHECK-NEXT: ret float [[RET]]
+;
+ %ret = call nofpclass(nan) float @uses_func_ptr(ptr @non_callee_use_ret)
+ ret float %ret
+}
+
+define internal { double, double } @mixed_ret_and_args(float %scalar.arg, i32 %not.fp, <2 x double> %vec, { double, double } %struct) {
+; CHECK-LABEL: define internal nofpclass(sub) { double, double } @mixed_ret_and_args(
+; CHECK-SAME: float nofpclass(nan) [[SCALAR_ARG:%.*]], i32 [[NOT_FP:%.*]], <2 x double> nofpclass(inf) [[VEC:%.*]], { double, double } nofpclass(zero) [[STRUCT:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[FPEXT:%.*]] = fpext float [[SCALAR_ARG]] to double
+; CHECK-NEXT: [[VEC_0:%.*]] = extractelement <2 x double> [[VEC]], i32 0
+; CHECK-NEXT: [[INSERT_0:%.*]] = insertvalue { double, double } poison, double [[FPEXT]], 0
+; CHECK-NEXT: [[INSERT_1:%.*]] = insertvalue { double, double } poison, double [[VEC_0]], 0
+; CHECK-NEXT: ret { double, double } [[INSERT_1]]
+;
+ %fpext = fpext float %scalar.arg to double
+ %vec.0 = extractelement <2 x double> %vec, i32 0
+ %insert.0 = insertvalue { double, double } poison, double %fpext, 0
+ %insert.1 = insertvalue { double, double } poison, double %vec.0, 0
+ ret { double, double } %insert.1
+}
+
+define void @call_mixed_ret_and_args_0(float %a, i32 %b, <2 x double> %c, { double, double } %d) {
+; CHECK-LABEL: define void @call_mixed_ret_and_args_0(
+; CHECK-SAME: float [[A:%.*]], i32 [[B:%.*]], <2 x double> [[C:%.*]], { double, double } [[D:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[RESULT:%.*]] = call nofpclass(sub) { double, double } @mixed_ret_and_args(float nofpclass(nan) [[A]], i32 [[B]], <2 x double> nofpclass(inf) [[C]], { double, double } nofpclass(zero) [[D]])
+; CHECK-NEXT: ret void
+;
+ %result = call nofpclass(sub) { double, double } @mixed_ret_and_args(float nofpclass(nan) %a, i32 %b, <2 x double> nofpclass(inf) %c, { double, double } nofpclass(zero) %d)
+ ret void
+}
+
+define internal float @merged_arg_uses(float %arg0, half %arg1, half %arg2) {
+; CHECK-LABEL: define internal float @merged_arg_uses(
+; CHECK-SAME: float nofpclass(nan) [[ARG0:%.*]], half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[FPEXT0:%.*]] = fpext half [[ARG1]] to float
+; CHECK-NEXT: [[FPEXT1:%.*]] = fpext half [[ARG1]] to float
+; CHECK-NEXT: [[ADD0:%.*]] = fadd float [[ARG0]], [[FPEXT0]]
+; CHECK-NEXT: [[ADD1:%.*]] = fadd float [[ADD0]], [[FPEXT1]]
+; CHECK-NEXT: ret float [[ADD1]]
+;
+ %fpext0 = fpext half %arg1 to float
+ %fpext1 = fpext half %arg1 to float
+ %add0 = fadd float %arg0, %fpext0
+ %add1 = fadd float %add0, %fpext1
+ ret float %add1
+}
+
+define float @calls_merged_arg_uses(float %arg0, half %arg1, half %arg2) {
+; CHECK-LABEL: define float @calls_merged_arg_uses(
+; CHECK-SAME: float [[ARG0:%.*]], half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[CALL0:%.*]] = call float @merged_arg_uses(float nofpclass(nan inf) [[ARG0]], half nofpclass(zero) [[ARG1]], half nofpclass(zero) [[ARG2]])
+; CHECK-NEXT: [[CALL1:%.*]] = call float @merged_arg_uses(float nofpclass(nan) [[ARG0]], half nofpclass(sub) [[ARG1]], half [[ARG2]])
+; CHECK-NEXT: [[RET:%.*]] = fadd float [[CALL0]], [[CALL1]]
+; CHECK-NEXT: ret float [[RET]]
+;
+ %call0 = call float @merged_arg_uses(float nofpclass(inf nan) %arg0, half nofpclass(zero) %arg1, half nofpclass(zero) %arg2)
+ %call1 = call float @merged_arg_uses(float nofpclass(nan) %arg0, half nofpclass(sub) %arg1, half %arg2)
+ %ret = fadd float %call0, %call1
+ ret float %ret
+}
+
+define internal float @self_recursive_callsite_attrs(float %x) {
+; CHECK-LABEL: define internal float @self_recursive_callsite_attrs(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = call nofpclass(inf) float @self_recursive_callsite_attrs(float nofpclass(nan) [[X]])
+; CHECK-NEXT: ret float [[RET]]
+;
+ %ret = call nofpclass(inf) float @self_recursive_callsite_attrs(float nofpclass(nan) %x)
+ ret float %ret
+}
+
+define internal float @mutually_recursive0(float %arg) {
+; CHECK-LABEL: define internal float @mutually_recursive0(
+; CHECK-SAME: float [[ARG:%.*]]) {
+; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @mutually_recursive1(float [[ARG]])
+; CHECK-NEXT: ret float [[CALL]]
+;
+ %call = call nofpclass(nan) float @mutually_recursive1(float %arg)
+ ret float %call
+}
+
+define internal float @mutually_recursive1(float %arg) {
+; CHECK-LABEL: define internal float @mutually_recursive1(
+; CHECK-SAME: float [[ARG:%.*]]) {
+; CHECK-NEXT: [[CALL:%.*]] = call float @mutually_recursive0(float [[ARG]])
+; CHECK-NEXT: ret float [[CALL]]
+;
+ %call = call float @mutually_recursive0(float %arg)
+ ret float %call
+}
+
+define internal void @infer_arg_from_constants(float %a, <2 x half> %b, float %c, float %d) {
----------------
dtcxzyw wrote:
Are the following tests (`infer_arg_from_constants` and `call_infer_arg_from_load`) left as todo?
https://github.com/llvm/llvm-project/pull/182444
More information about the llvm-commits
mailing list