| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --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 nofpclass(nan inf nzero sub norm) 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 nofpclass(nan inf pzero sub norm) 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 nofpclass(all) 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 nofpclass(all) 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 nofpclass(qnan inf zero sub norm) 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 nofpclass(snan inf zero sub norm) 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 nofpclass(nan inf nzero sub norm) <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 nofpclass(nan inf pzero sub norm) <2 x double> @returned_negzero_vector() { |
| ; CHECK-NEXT: call void @unknown() |
| ; CHECK-NEXT: ret <2 x double> <double -0.000000e+00, double -0.000000e+00> |
| ; |
| call void @unknown() |
| ret <2 x double> <double -0.0, double -0.0> |
| } |
| |
| ; Test a vector element that's a constant but not ConstantFP. |
| define <2 x double> @returned_strange_constant_vector_elt() { |
| ; CHECK-LABEL: define <2 x double> @returned_strange_constant_vector_elt() { |
| ; CHECK-NEXT: call void @unknown() |
| ; CHECK-NEXT: ret <2 x double> <double -0.000000e+00, double bitcast (i64 ptrtoint (ptr @unknown to i64) to double)> |
| ; |
| call void @unknown() |
| ret <2 x double> <double -0.0, double bitcast (i64 ptrtoint (ptr @unknown to i64) to double)> |
| } |
| |
| ; Test a vector element that's an undef/poison |
| define <3 x double> @returned_undef_constant_vector_elt() { |
| ; CHECK-LABEL: define nofpclass(nan inf pzero sub norm) <3 x double> @returned_undef_constant_vector_elt() { |
| ; CHECK-NEXT: call void @unknown() |
| ; CHECK-NEXT: ret <3 x double> <double -0.000000e+00, double poison, double undef> |
| ; |
| call void @unknown() |
| ret <3 x double> <double -0.0, double poison, double undef> |
| } |
| |
| define <2 x double> @returned_qnan_zero_vector() { |
| ; CHECK-LABEL: define noundef nofpclass(snan inf nzero sub norm) <2 x double> @returned_qnan_zero_vector() { |
| ; CHECK-NEXT: call void @unknown() |
| ; CHECK-NEXT: ret <2 x double> <double 0x7FF8000000000000, double 0.000000e+00> |
| ; |
| call void @unknown() |
| ret <2 x double> <double 0x7FF8000000000000, double 0.0> |
| } |
| |
| ; Return a float trivially nofpclass(nan) (call return attribute) |
| define float @return_nofpclass_nan_decl_return() { |
| ; CHECK-LABEL: define nofpclass(nan) float @return_nofpclass_nan_decl_return() { |
| ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) 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 nofpclass(nan) 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 nofpclass(inf) [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 nofpclass(nan) 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 nofpclass(nan) 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(nan 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 inf) [[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]] nofpclass(inf) [[ARG]]) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @extern.use.array([2 x [3 x float]] %arg) |
| ret void |
| } |
| |
| define void @nofpclass_call_use_after_unannotated_use(float %arg) { |
| ; CHECK-LABEL: define void @nofpclass_call_use_after_unannotated_use |
| ; CHECK-SAME: (float [[ARG:%.*]]) { |
| ; CHECK-NEXT: call void @extern(float [[ARG]]) #[[ATTR5:[0-9]+]] |
| ; CHECK-NEXT: call void @extern(float nofpclass(nan inf) [[ARG]]) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @extern(float %arg) willreturn nounwind ; < annotate this use |
| call void @extern(float nofpclass(nan inf) %arg) |
| ret void |
| } |
| |
| define float @mutually_recursive0(float %arg) { |
| ; TUNIT: Function Attrs: nofree nosync nounwind willreturn memory(none) |
| ; TUNIT-LABEL: define nofpclass(all) 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 nofpclass(all) 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 nofpclass(all) 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 nofpclass(all) 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 nofpclass(nan) float @recursive_phi |
| ; CHECK-SAME: (ptr nofree [[PTR:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) 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 nofpclass(nan) 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 nofpclass(inf zero sub norm) float @fcmp_ord_assume_callsite_arg_return |
| ; CHECK-SAME: (float returned nofpclass(inf zero sub norm) [[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]]) #[[ATTR6:[0-9]+]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(inf zero sub norm) [[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 nofpclass(nan inf nzero sub norm) 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 nofpclass(inf) [[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 nofpclass(inf) [[ARG]]) #[[ATTR7:[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 nofpclass(inf) [[ARG]]) #[[ATTR6]] |
| ; 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]]) #[[ATTR7]] |
| ; 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]]) #[[ATTR6]] |
| ; 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 nofpclass(inf) float @call_noinf_return_0 |
| ; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { |
| ; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR7]] |
| ; TUNIT-NEXT: ret float [[RESULT]] |
| ; |
| ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) |
| ; CGSCC-LABEL: define nofpclass(inf) float @call_noinf_return_0 |
| ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { |
| ; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR6]] |
| ; 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 nofpclass(inf) float @call_noinf_return_1 |
| ; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { |
| ; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR7]] |
| ; TUNIT-NEXT: ret float [[RESULT]] |
| ; |
| ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) |
| ; CGSCC-LABEL: define nofpclass(inf) float @call_noinf_return_1 |
| ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { |
| ; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR6]] |
| ; 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 nofpclass(inf sub norm) float @fcmp_olt_assume_one_0_callsite_arg_return |
| ; CHECK-SAME: (float returned nofpclass(inf sub norm) [[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]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(inf sub norm) [[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 nofpclass(nan inf sub norm) float @fcmp_olt_assume_une_0_callsite_arg_return |
| ; CHECK-SAME: (float returned nofpclass(nan inf sub norm) [[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]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(nan inf sub norm) [[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 nofpclass(zero sub) half @fcmp_assume_issubnormal_callsite_arg_return |
| ; CHECK-SAME: (half returned nofpclass(zero sub) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half nofpclass(zero sub) [[ARG]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(zero sub) [[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 nofpclass(inf norm) half @fcmp_assume2_callsite_arg_return |
| ; CHECK-SAME: (half returned nofpclass(inf norm) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf) half @llvm.fabs.f16(half nofpclass(inf norm) [[ARG]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[NOT_SUBNORMAL_OR_ZERO:%.*]] = fcmp oge half [[FABS]], 0xH0400 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_SUBNORMAL_OR_ZERO]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[NOT_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_INF]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf norm) [[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 nofpclass(ninf nzero pnorm) float @is_fpclass_assume_arg_return |
| ; CHECK-SAME: (float returned nofpclass(ninf nzero pnorm) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[CLASS_TEST:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(ninf nzero pnorm) [[ARG]], i32 noundef 292) #[[ATTR6]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CLASS_TEST]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(ninf nzero pnorm) [[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 nofpclass(zero sub) half @assume_fcmp_fabs_with_other_fabs_assume |
| ; CHECK-SAME: (half returned nofpclass(zero sub) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf zero sub norm) half @llvm.fabs.f16(half nofpclass(zero sub) [[ARG]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp one half [[FABS]], 0xH0000 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(zero sub) [[ARG]]) |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf zero sub norm) [[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 nofpclass(pinf zero sub) half @assume_fcmp_fabs_with_other_fabs_assume_fallback |
| ; CHECK-SAME: (half returned nofpclass(pinf zero sub) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf zero sub nnorm) half @llvm.fabs.f16(half nofpclass(pinf zero sub) [[ARG]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[ONE_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ONE_INF]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp oeq half [[FABS]], 0xH0000 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(pinf zero sub) [[ARG]]) |
| ; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf zero sub nnorm) [[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) #[[ATTR6]] [ "nofpclass"(float [[RET]], i32 3) ] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(nan) [[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 nofpclass(ninf nnorm) [[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 |
| } |
| |
| define float @returned_load(ptr %ptr) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read) |
| ; CHECK-LABEL: define float @returned_load |
| ; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[PTR:%.*]]) #[[ATTR4:[0-9]+]] { |
| ; CHECK-NEXT: [[LOAD:%.*]] = load float, ptr [[PTR]], align 4 |
| ; CHECK-NEXT: ret float [[LOAD]] |
| ; |
| %load = load float, ptr %ptr |
| ret float %load |
| } |
| |
| define float @pass_nofpclass_inf_through_memory(float nofpclass(inf) %arg) { |
| ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; TUNIT-LABEL: define float @pass_nofpclass_inf_through_memory |
| ; TUNIT-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR2]] { |
| ; TUNIT-NEXT: [[ALLOCA:%.*]] = alloca float, align 4 |
| ; TUNIT-NEXT: store float [[ARG]], ptr [[ALLOCA]], align 4 |
| ; TUNIT-NEXT: [[RET:%.*]] = call float @returned_load(ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ALLOCA]]) #[[ATTR7]] |
| ; TUNIT-NEXT: ret float [[RET]] |
| ; |
| ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) |
| ; CGSCC-LABEL: define float @pass_nofpclass_inf_through_memory |
| ; CGSCC-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR3]] { |
| ; CGSCC-NEXT: [[ALLOCA:%.*]] = alloca float, align 4 |
| ; CGSCC-NEXT: store float [[ARG]], ptr [[ALLOCA]], align 4 |
| ; CGSCC-NEXT: [[RET:%.*]] = call float @returned_load(ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ALLOCA]]) #[[ATTR6]] |
| ; CGSCC-NEXT: ret float [[RET]] |
| ; |
| %alloca = alloca float |
| store float %arg, ptr %alloca |
| %ret = call float @returned_load(ptr %alloca) |
| ret float %ret |
| } |
| |
| define float @uitofp_i32_to_f32(i32 %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @uitofp_i32_to_f32 |
| ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = uitofp i32 [[ARG]] to float |
| ; CHECK-NEXT: ret float [[CVT]] |
| ; |
| %cvt = uitofp i32 %arg to float |
| ret float %cvt |
| } |
| |
| define float @sitofp_i32_to_f32(i32 %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan inf) float @sitofp_i32_to_f32 |
| ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = sitofp i32 [[ARG]] to float |
| ; CHECK-NEXT: ret float [[CVT]] |
| ; |
| %cvt = sitofp i32 %arg to float |
| ret float %cvt |
| } |
| |
| define <2 x float> @uitofp_v2i32_to_v2f32(<2 x i32> %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) <2 x float> @uitofp_v2i32_to_v2f32 |
| ; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = uitofp <2 x i32> [[ARG]] to <2 x float> |
| ; CHECK-NEXT: ret <2 x float> [[CVT]] |
| ; |
| %cvt = uitofp <2 x i32> %arg to <2 x float> |
| ret <2 x float> %cvt |
| } |
| |
| define <2 x float> @sitofp_v2i32_to_v2i32(<2 x i32> %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan inf) <2 x float> @sitofp_v2i32_to_v2i32 |
| ; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = sitofp <2 x i32> [[ARG]] to <2 x float> |
| ; CHECK-NEXT: ret <2 x float> [[CVT]] |
| ; |
| %cvt = sitofp <2 x i32> %arg to <2 x float> |
| ret <2 x float> %cvt |
| } |
| |
| define half @uitofp_i17_to_f16(i17 %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) half @uitofp_i17_to_f16 |
| ; CHECK-SAME: (i17 [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = uitofp i17 [[ARG]] to half |
| ; CHECK-NEXT: ret half [[CVT]] |
| ; |
| %cvt = uitofp i17 %arg to half |
| ret half %cvt |
| } |
| |
| define half @sitofp_i17_to_f16(i17 %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) half @sitofp_i17_to_f16 |
| ; CHECK-SAME: (i17 [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = sitofp i17 [[ARG]] to half |
| ; CHECK-NEXT: ret half [[CVT]] |
| ; |
| %cvt = sitofp i17 %arg to half |
| ret half %cvt |
| } |
| |
| define <2 x half> @uitofp_v2i17_to_v2f16(<2 x i17> %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) <2 x half> @uitofp_v2i17_to_v2f16 |
| ; CHECK-SAME: (<2 x i17> [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = uitofp <2 x i17> [[ARG]] to <2 x half> |
| ; CHECK-NEXT: ret <2 x half> [[CVT]] |
| ; |
| %cvt = uitofp <2 x i17> %arg to <2 x half> |
| ret <2 x half> %cvt |
| } |
| |
| define <2 x half> @sitofp_v2i17_to_v2i17(<2 x i17> %arg) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) <2 x half> @sitofp_v2i17_to_v2i17 |
| ; CHECK-SAME: (<2 x i17> [[ARG:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[CVT:%.*]] = sitofp <2 x i17> [[ARG]] to <2 x half> |
| ; CHECK-NEXT: ret <2 x half> [[CVT]] |
| ; |
| %cvt = sitofp <2 x i17> %arg to <2 x half> |
| ret <2 x half> %cvt |
| } |
| |
| define float @assume_intersection_not_zero_and_not_nan(float %arg) { |
| ; CHECK-LABEL: define nofpclass(all) float @assume_intersection_not_zero_and_not_nan |
| ; CHECK-SAME: (float returned nofpclass(all) [[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]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[IS_ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_ORD]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(all) [[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) |
| %is.ord = fcmp ord float %arg, 0.0 |
| call void @llvm.assume(i1 %is.ord) |
| call void @extern.use(float %arg) |
| ret float %arg |
| } |
| |
| define float @assume_intersection_class(float %arg) { |
| ; CHECK-LABEL: define nofpclass(psub norm) float @assume_intersection_class |
| ; CHECK-SAME: (float returned nofpclass(psub norm) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[POS_NORMAL_OR_POS_SUBNORMAL:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(psub norm) [[ARG]], i32 noundef 384) #[[ATTR6]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[POS_NORMAL_OR_POS_SUBNORMAL]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[IS_NORMAL:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(psub norm) [[ARG]], i32 noundef 264) #[[ATTR6]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NORMAL]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(psub norm) [[ARG]]) |
| ; CHECK-NEXT: ret float [[ARG]] |
| ; |
| entry: |
| %pos.normal.or.pos.subnormal = call i1 @llvm.is.fpclass.f32(float %arg, i32 384) |
| call void @llvm.assume(i1 %pos.normal.or.pos.subnormal) |
| %is.normal = call i1 @llvm.is.fpclass.f32(float %arg, i32 264) |
| call void @llvm.assume(i1 %is.normal) |
| |
| call void @extern.use(float %arg) |
| ret float %arg |
| } |
| |
| define float @assume_intersection_none(float %arg) { |
| ; CHECK-LABEL: define nofpclass(all) float @assume_intersection_none |
| ; CHECK-SAME: (float returned nofpclass(all) [[ARG:%.*]]) { |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[CLASS1:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(all) [[ARG]], i32 noundef 682) #[[ATTR6]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CLASS1]]) #[[ATTR6]] |
| ; CHECK-NEXT: [[CLASS2:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(all) [[ARG]], i32 noundef 341) #[[ATTR6]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CLASS2]]) #[[ATTR6]] |
| ; CHECK-NEXT: call void @extern.use(float nofpclass(all) [[ARG]]) |
| ; CHECK-NEXT: ret float [[ARG]] |
| ; |
| entry: |
| %class1 = call i1 @llvm.is.fpclass.f32(float %arg, i32 682) |
| call void @llvm.assume(i1 %class1) |
| %class2 = call i1 @llvm.is.fpclass.f32(float %arg, i32 341) |
| call void @llvm.assume(i1 %class2) |
| call void @extern.use(float %arg) |
| ret float %arg |
| } |
| |
| define float @returned_extractelement_dynamic_index(<4 x float> nofpclass(nan) %vec, i32 %idx) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) float @returned_extractelement_dynamic_index |
| ; CHECK-SAME: (<4 x float> nofpclass(nan) [[VEC:%.*]], i32 [[IDX:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[EXTRACT:%.*]] = extractelement <4 x float> [[VEC]], i32 [[IDX]] |
| ; CHECK-NEXT: ret float [[EXTRACT]] |
| ; |
| %extract = extractelement <4 x float> %vec, i32 %idx |
| ret float %extract |
| } |
| |
| define float @returned_extractelement_index0(<4 x float> nofpclass(nan) %vec) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) float @returned_extractelement_index0 |
| ; CHECK-SAME: (<4 x float> nofpclass(nan) [[VEC:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[EXTRACT:%.*]] = extractelement <4 x float> [[VEC]], i32 0 |
| ; CHECK-NEXT: ret float [[EXTRACT]] |
| ; |
| %extract = extractelement <4 x float> %vec, i32 0 |
| ret float %extract |
| } |
| |
| define float @returned_extractelement_index_oob(<4 x float> nofpclass(nan) %vec) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) float @returned_extractelement_index_oob |
| ; CHECK-SAME: (<4 x float> nofpclass(nan) [[VEC:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[EXTRACT:%.*]] = extractelement <4 x float> [[VEC]], i32 5 |
| ; CHECK-NEXT: ret float [[EXTRACT]] |
| ; |
| %extract = extractelement <4 x float> %vec, i32 5 |
| ret float %extract |
| } |
| |
| define float @returned_extractelement_scalable(<vscale x 4 x float> nofpclass(nan) %vec) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define float @returned_extractelement_scalable |
| ; CHECK-SAME: (<vscale x 4 x float> nofpclass(nan) [[VEC:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[EXTRACT:%.*]] = extractelement <vscale x 4 x float> [[VEC]], i32 0 |
| ; CHECK-NEXT: ret float [[EXTRACT]] |
| ; |
| %extract = extractelement <vscale x 4 x float> %vec, i32 0 |
| ret float %extract |
| } |
| |
| define float @returned_extractvalue([4 x float] nofpclass(nan) %array) { |
| ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) |
| ; CHECK-LABEL: define nofpclass(nan) float @returned_extractvalue |
| ; CHECK-SAME: ([4 x float] nofpclass(nan) [[ARRAY:%.*]]) #[[ATTR2]] { |
| ; CHECK-NEXT: [[EXTRACT:%.*]] = extractvalue [4 x float] [[ARRAY]], 0 |
| ; CHECK-NEXT: ret float [[EXTRACT]] |
| ; |
| %extract = extractvalue [4 x float] %array, 0 |
| ret float %extract |
| } |