From e6e7a6dd365d62e9baa04cc25e0dbaac6c5a0700 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Thu, 2 Mar 2023 08:22:07 -0400 Subject: [PATCH] Attributor: Add baseline tests for nofpclass --- llvm/test/Transforms/Attributor/nofpclass.ll | 616 +++++++++++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 llvm/test/Transforms/Attributor/nofpclass.ll diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll new file mode 100644 index 0000000..a21f60a --- /dev/null +++ b/llvm/test/Transforms/Attributor/nofpclass.ll @@ -0,0 +1,616 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --version 2 +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=3 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC + +declare nofpclass(nan) float @ret_nofpclass_nan() +declare [2 x [3 x float]] @ret_array() +declare float @extern() +declare void @extern.use(float) +declare void @extern.use.array([2 x [3 x float]]) +declare void @llvm.assume(i1 noundef) +declare void @unknown() +declare half @llvm.fabs.f16(half) +declare void @extern.use.f16(half) +declare i1 @llvm.is.fpclass.f32(float, i32 immarg) + +define float @returned_0() { +; CHECK-LABEL: define noundef float @returned_0() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret float 0.000000e+00 +; + call void @unknown() + ret float 0.0 +} + +define float @returned_neg0() { +; CHECK-LABEL: define noundef float @returned_neg0() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret float -0.000000e+00 +; + call void @unknown() + ret float -0.0 +} + +define float @returned_undef() { +; CHECK-LABEL: define float @returned_undef() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret float undef +; + call void @unknown() + ret float undef +} + +define float @returned_poison() { +; CHECK-LABEL: define float @returned_poison() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret float poison +; + call void @unknown() + ret float poison +} + +define double @returned_snan() { +; CHECK-LABEL: define noundef double @returned_snan() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret double 0x7FF0000000000001 +; + call void @unknown() + ret double 0x7FF0000000000001 +} + +define double @returned_qnan() { +; CHECK-LABEL: define noundef double @returned_qnan() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + call void @unknown() + ret double 0x7FF8000000000000 +} + +define <2 x double> @returned_zero_vector() { +; CHECK-LABEL: define noundef <2 x double> @returned_zero_vector() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret <2 x double> zeroinitializer +; + call void @unknown() + ret <2 x double> zeroinitializer +} + +define <2 x double> @returned_negzero_vector() { +; CHECK-LABEL: define noundef <2 x double> @returned_negzero_vector() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret <2 x double> +; + call void @unknown() + ret <2 x double> +} + +define <2 x double> @returned_qnan_zero_vector() { +; CHECK-LABEL: define noundef <2 x double> @returned_qnan_zero_vector() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret <2 x double> +; + call void @unknown() + ret <2 x double> +} + +; Return a float trivially nofpclass(nan) (call return attribute) +define float @return_nofpclass_nan_decl_return() { +; CHECK-LABEL: define float @return_nofpclass_nan_decl_return() { +; CHECK-NEXT: [[RET:%.*]] = call float @ret_nofpclass_nan() +; CHECK-NEXT: ret float [[RET]] +; + %ret = call float @ret_nofpclass_nan() + ret float %ret +} + +; Return a float trivially nofpclass(nan) (argument attribute) +define float @return_nofpclass_nan_arg(float returned nofpclass(nan) %p) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @return_nofpclass_nan_arg +; CHECK-SAME: (float returned nofpclass(nan) [[P:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: ret float [[P]] +; + ret float %p +} + +define [2 x [3 x float]] @return_nofpclass_inf_ret_array() { +; CHECK-LABEL: define [2 x [3 x float]] @return_nofpclass_inf_ret_array() { +; CHECK-NEXT: [[RET:%.*]] = call nofpclass(inf) [2 x [3 x float]] @ret_array() +; CHECK-NEXT: ret [2 x [3 x float]] [[RET]] +; + %ret = call nofpclass(inf) [2 x [3 x float]] @ret_array() + ret [2 x [3 x float]] %ret +} + +define float @returned_nnan_fadd(float %arg0, float %arg1) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define float @returned_nnan_fadd +; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[FADD:%.*]] = fadd nnan float [[ARG0]], [[ARG1]] +; CHECK-NEXT: ret float [[FADD]] +; + %fadd = fadd nnan float %arg0, %arg1 + ret float %fadd +} + +define float @return_nofpclass_nan_callsite() { +; CHECK-LABEL: define float @return_nofpclass_nan_callsite() { +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @extern() +; CHECK-NEXT: ret float [[CALL]] +; + %call = call nofpclass(nan) float @extern() + ret float %call +} + +; Can union the return classes +define nofpclass(inf) float @return_ninf_nofpclass_nan_callsite() { +; CHECK-LABEL: define nofpclass(inf) float @return_ninf_nofpclass_nan_callsite() { +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @extern() +; CHECK-NEXT: ret float [[CALL]] +; + %call = call nofpclass(nan) float @extern() + ret float %call +} + +define void @arg_used_by_nofpclass_nan_callsite(float %arg) { +; CHECK-LABEL: define void @arg_used_by_nofpclass_nan_callsite +; CHECK-SAME: (float [[ARG:%.*]]) { +; CHECK-NEXT: call void @extern.use(float nofpclass(nan) [[ARG]]) +; CHECK-NEXT: ret void +; + call void @extern.use(float nofpclass(nan) %arg) + ret void +} + +; Callsite can union the incoming and outgoing +define void @ninf_arg_used_by_nofpclass_nan_callsite(float nofpclass(inf) %arg) { +; CHECK-LABEL: define void @ninf_arg_used_by_nofpclass_nan_callsite +; CHECK-SAME: (float nofpclass(inf) [[ARG:%.*]]) { +; CHECK-NEXT: call void @extern.use(float nofpclass(nan) [[ARG]]) +; CHECK-NEXT: ret void +; + call void @extern.use(float nofpclass(nan) %arg) + ret void +} + +define void @ninf_arg_used_by_callsite_array([2 x [3 x float]] nofpclass(inf) %arg) { +; CHECK-LABEL: define void @ninf_arg_used_by_callsite_array +; CHECK-SAME: ([2 x [3 x float]] nofpclass(inf) [[ARG:%.*]]) { +; CHECK-NEXT: call void @extern.use.array([2 x [3 x float]] [[ARG]]) +; CHECK-NEXT: ret void +; + call void @extern.use.array([2 x [3 x float]] %arg) + ret void +} + +define float @mutually_recursive0(float %arg) { +; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @mutually_recursive0 +; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR3:[0-9]+]] { +; TUNIT-NEXT: ret float undef +; +; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @mutually_recursive0 +; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; CGSCC-NEXT: ret float undef +; + %call = call float @mutually_recursive1(float %arg) + ret float %call +} + +define float @mutually_recursive1(float %arg) { +; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @mutually_recursive1 +; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { +; TUNIT-NEXT: ret float undef +; +; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @mutually_recursive1 +; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; CGSCC-NEXT: ret float undef +; + %call = call float @mutually_recursive0(float %arg) + ret float %call +} + +define float @recursive_phi(ptr %ptr) { +; CHECK-LABEL: define float @recursive_phi +; CHECK-SAME: (ptr nofree [[PTR:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RET:%.*]] = call float @ret_nofpclass_nan() +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[PHI:%.*]] = phi float [ [[RET]], [[ENTRY:%.*]] ], [ [[RET]], [[LOOP]] ] +; CHECK-NEXT: [[COND:%.*]] = load volatile i1, ptr [[PTR]], align 1 +; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK: exit: +; CHECK-NEXT: ret float [[RET]] +; +entry: + %ret = call float @ret_nofpclass_nan() + br label %loop + +loop: + %phi = phi float [%ret, %entry], [%phi, %loop] + %cond = load volatile i1, ptr %ptr + br i1 %cond, label %loop, label %exit + +exit: + ret float %phi +} + +; Should be able to infer nofpclass(nan) return +define float @fcmp_uno_check(float %arg) local_unnamed_addr { +; CHECK-LABEL: define float @fcmp_uno_check +; CHECK-SAME: (float [[ARG:%.*]]) local_unnamed_addr { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ISNAN:%.*]] = fcmp uno float [[ARG]], 0.000000e+00 +; CHECK-NEXT: br i1 [[ISNAN]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[CALL:%.*]] = call float @ret_nofpclass_nan() +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[PHI:%.*]] = phi float [ [[CALL]], [[BB0]] ], [ [[ARG]], [[ENTRY:%.*]] ] +; CHECK-NEXT: ret float [[PHI]] +; +entry: + %isnan = fcmp uno float %arg, 0.0 + br i1 %isnan, label %bb0, label %bb1 + +bb0: + %call = call float @ret_nofpclass_nan() + br label %bb1 + +bb1: + %phi = phi float [ %call, %bb0 ], [ %arg, %entry ] + ret float %phi +} + +; Should be able to infer nofpclass(nan) on %arg use +define void @fcmp_ord_guard_callsite_arg(float %arg) { +; CHECK-LABEL: define void @fcmp_ord_guard_callsite_arg +; CHECK-SAME: (float [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IS_NOT_NAN:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 +; CHECK-NEXT: br i1 [[IS_NOT_NAN]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: call void @extern.use(float [[ARG]]) +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: ret void +; +entry: + %is.not.nan = fcmp ord float %arg, 0.0 + br i1 %is.not.nan, label %bb0, label %bb1 + +bb0: + call void @extern.use(float %arg) + br label %bb1 + +bb1: + ret void +} + +; Should be able to infer nofpclass on both %arg uses +define float @fcmp_ord_assume_callsite_arg_return(float %arg) { +; CHECK-LABEL: define float @fcmp_ord_assume_callsite_arg_return +; CHECK-SAME: (float returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IS_NOT_NAN:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_NAN]]) #[[ATTR4:[0-9]+]] +; CHECK-NEXT: call void @extern.use(float [[ARG]]) +; CHECK-NEXT: ret float [[ARG]] +; +entry: + %is.not.nan = fcmp ord float %arg, 0.0 + call void @llvm.assume(i1 %is.not.nan) + call void @extern.use(float %arg) + ret float %arg +} + +define internal float @returned_dead() { +; CHECK-LABEL: define internal float @returned_dead() { +; CHECK-NEXT: call void @unknown() +; CHECK-NEXT: ret float undef +; + call void @unknown() + ret float 0.0 +} + +define void @returned_dead_caller() { +; CHECK-LABEL: define void @returned_dead_caller() { +; CHECK-NEXT: [[TMP1:%.*]] = call float @returned_dead() +; CHECK-NEXT: ret void +; + call float @returned_dead() + ret void +} + +define internal float @only_nofpclass_inf_callers(float %arg) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define internal float @only_nofpclass_inf_callers +; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG]], [[ARG]] +; CHECK-NEXT: ret float [[ADD]] +; + %add = fadd float %arg, %arg + ret float %add +} + +define float @call_noinf_0(float nofpclass(inf) %arg) { +; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @call_noinf_0 +; TUNIT-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR2]] { +; TUNIT-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float [[ARG]]) #[[ATTR5:[0-9]+]] +; TUNIT-NEXT: ret float [[RESULT]] +; +; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @call_noinf_0 +; CGSCC-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] { +; CGSCC-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float [[ARG]]) #[[ATTR4]] +; CGSCC-NEXT: ret float [[RESULT]] +; + %result = call float @only_nofpclass_inf_callers(float %arg) + ret float %result +} + +define float @call_noinf_1(float %arg) { +; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @call_noinf_1 +; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; TUNIT-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float nofpclass(inf) [[ARG]]) #[[ATTR5]] +; TUNIT-NEXT: ret float [[RESULT]] +; +; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @call_noinf_1 +; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { +; CGSCC-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float nofpclass(inf) [[ARG]]) #[[ATTR4]] +; CGSCC-NEXT: ret float [[RESULT]] +; + %result = call float @only_nofpclass_inf_callers(float nofpclass(inf) %arg) + ret float %result +} + +; TODO: Should be able to infer nofpclass(inf) on return +define internal float @only_nofpclass_inf_return_users(float %arg) { +; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; CHECK-LABEL: define internal float @only_nofpclass_inf_return_users +; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG]], [[ARG]] +; CHECK-NEXT: ret float [[ADD]] +; + %add = fadd float %arg, %arg + ret float %add +} + +define float @call_noinf_return_0(float %arg) { +; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @call_noinf_return_0 +; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR5]] +; TUNIT-NEXT: ret float [[RESULT]] +; +; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @call_noinf_return_0 +; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { +; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR4]] +; CGSCC-NEXT: ret float [[RESULT]] +; + %result = call nofpclass(inf) float @only_nofpclass_inf_return_users(float %arg) + ret float %result +} + +define float @call_noinf_return_1(float %arg) { +; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) +; TUNIT-LABEL: define float @call_noinf_return_1 +; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR5]] +; TUNIT-NEXT: ret float [[RESULT]] +; +; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) +; CGSCC-LABEL: define float @call_noinf_return_1 +; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { +; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR4]] +; CGSCC-NEXT: ret float [[RESULT]] +; + %result = call nofpclass(inf) float @only_nofpclass_inf_return_users(float %arg) + ret float %result +} + +define float @fcmp_olt_assume_one_0_callsite_arg_return(float %arg) { +; CHECK-LABEL: define float @fcmp_olt_assume_one_0_callsite_arg_return +; CHECK-SAME: (float returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IS_NOT_ZERO_OR_NAN:%.*]] = fcmp one float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_ZERO_OR_NAN]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use(float [[ARG]]) +; CHECK-NEXT: ret float [[ARG]] +; +entry: + %is.not.zero.or.nan = fcmp one float %arg, 0.0 + call void @llvm.assume(i1 %is.not.zero.or.nan) + call void @extern.use(float %arg) + ret float %arg +} + +define float @fcmp_olt_assume_une_0_callsite_arg_return(float %arg) { +; CHECK-LABEL: define float @fcmp_olt_assume_une_0_callsite_arg_return +; CHECK-SAME: (float returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IS_NOT_ZERO_OR_NAN:%.*]] = fcmp une float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_ZERO_OR_NAN]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use(float [[ARG]]) +; CHECK-NEXT: ret float [[ARG]] +; +entry: + %is.not.zero.or.nan = fcmp une float %arg, 0.0 + call void @llvm.assume(i1 %is.not.zero.or.nan) + call void @extern.use(float %arg) + ret float %arg +} + +define half @fcmp_assume_issubnormal_callsite_arg_return(half %arg) { +; CHECK-LABEL: define half @fcmp_assume_issubnormal_callsite_arg_return +; CHECK-SAME: (half returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR4]] +; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use.f16(half [[ARG]]) +; CHECK-NEXT: ret half [[ARG]] +; +entry: + %fabs = call half @llvm.fabs.f16(half %arg) + %is.subnormal = fcmp olt half %fabs, 0xH0400 + call void @llvm.assume(i1 %is.subnormal) + call void @extern.use.f16(half %arg) + ret half %arg +} + +; Assume is after the call, shouldn't mark callsite. +define half @fcmp_assume_not_inf_after_call(half %arg) { +; CHECK-LABEL: define half @fcmp_assume_not_inf_after_call +; CHECK-SAME: (half returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @extern.use.f16(half [[ARG]]) +; CHECK-NEXT: [[NOT_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_INF]]) +; CHECK-NEXT: ret half [[ARG]] +; +entry: + call void @extern.use.f16(half %arg) + %not.inf = fcmp oeq half %arg, 0xH7C00 + call void @llvm.assume(i1 %not.inf) + ret half %arg +} + +; Assume not subnormal or zero, and not infinity +define half @fcmp_assume2_callsite_arg_return(half %arg) { +; CHECK-LABEL: define half @fcmp_assume2_callsite_arg_return +; CHECK-SAME: (half returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR4]] +; CHECK-NEXT: [[NOT_SUBNORMAL_OR_ZERO:%.*]] = fcmp oge half [[FABS]], 0xH0400 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_SUBNORMAL_OR_ZERO]]) #[[ATTR4]] +; CHECK-NEXT: [[NOT_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_INF]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use.f16(half [[ARG]]) +; CHECK-NEXT: ret half [[ARG]] +; +entry: + %fabs = call half @llvm.fabs.f16(half %arg) + %not.subnormal.or.zero = fcmp oge half %fabs, 0xH0400 + call void @llvm.assume(i1 %not.subnormal.or.zero) + + %not.inf = fcmp oeq half %arg, 0xH7C00 + call void @llvm.assume(i1 %not.inf) + + call void @extern.use.f16(half %arg) + ret half %arg +} + +define float @is_fpclass_assume_arg_return(float %arg) { +; CHECK-LABEL: define float @is_fpclass_assume_arg_return +; CHECK-SAME: (float returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CLASS_TEST:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG]], i32 noundef 292) #[[ATTR4]] +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CLASS_TEST]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use(float [[ARG]]) +; CHECK-NEXT: ret float [[ARG]] +; +entry: + %class.test = call i1 @llvm.is.fpclass.f32(float %arg, i32 292) + call void @llvm.assume(i1 %class.test) + call void @extern.use(float %arg) + ret float %arg +} + +; Make sure we don't get confused by looking at an unrelated assume +; based on the fabs of the value. +define half @assume_fcmp_fabs_with_other_fabs_assume(half %arg) { +; CHECK-LABEL: define half @assume_fcmp_fabs_with_other_fabs_assume +; CHECK-SAME: (half returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR4]] +; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp one half [[FABS]], 0xH0000 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR4]] +; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use.f16(half [[ARG]]) +; CHECK-NEXT: call void @extern.use.f16(half [[FABS]]) +; CHECK-NEXT: ret half [[ARG]] +; +entry: + + %fabs = call half @llvm.fabs.f16(half %arg) + %unrelated.fabs = fcmp one half %fabs, 0.0 + call void @llvm.assume(i1 %unrelated.fabs) + %is.subnormal = fcmp olt half %fabs, 0xH0400 + call void @llvm.assume(i1 %is.subnormal) + call void @extern.use.f16(half %arg) + call void @extern.use.f16(half %fabs) + ret half %arg +} + +; Make sure if looking through the fabs finds a different source +; value, we still identify a test mask by ignoring the fabs +define half @assume_fcmp_fabs_with_other_fabs_assume_fallback(half %arg) { +; CHECK-LABEL: define half @assume_fcmp_fabs_with_other_fabs_assume_fallback +; CHECK-SAME: (half returned [[ARG:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR4]] +; CHECK-NEXT: [[ONE_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ONE_INF]]) #[[ATTR4]] +; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp oeq half [[FABS]], 0xH0000 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR4]] +; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR4]] +; CHECK-NEXT: call void @extern.use.f16(half [[ARG]]) +; CHECK-NEXT: call void @extern.use.f16(half [[FABS]]) +; CHECK-NEXT: ret half [[ARG]] +; +entry: + + %fabs = call half @llvm.fabs.f16(half %arg) + + %one.inf = fcmp oeq half %arg, 0xH7C00 + call void @llvm.assume(i1 %one.inf) + + %unrelated.fabs = fcmp oeq half %fabs, 0.0 + call void @llvm.assume(i1 %unrelated.fabs) + + %is.subnormal = fcmp olt half %fabs, 0xH0400 + call void @llvm.assume(i1 %is.subnormal) + call void @extern.use.f16(half %arg) + call void @extern.use.f16(half %fabs) + ret half %arg +} + +define float @assume_bundles(i1 %c, float %ret) { +; CHECK-LABEL: define float @assume_bundles +; CHECK-SAME: (i1 noundef [[C:%.*]], float returned [[RET:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR4]] [ "nofpclass"(float [[RET]], i32 3) ] +; CHECK-NEXT: call void @extern.use(float [[RET]]) +; CHECK-NEXT: ret float [[RET]] +; CHECK: B: +; CHECK-NEXT: call void @llvm.assume(i1 noundef true) [ "nofpclass"(float [[RET]], i32 12) ] +; CHECK-NEXT: call void @extern.use(float [[RET]]) +; CHECK-NEXT: ret float [[RET]] +; +entry: + br i1 %c, label %A, label %B + +A: + call void @llvm.assume(i1 true) [ "nofpclass"(float %ret, i32 3) ] + call void @extern.use(float %ret) + ret float %ret + +B: + call void @llvm.assume(i1 true) [ "nofpclass"(float %ret, i32 12) ] + call void @extern.use(float %ret) + ret float %ret +} -- 2.7.4