https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/174494
>From 091ebf1b40e93b291ee5a0b2d92ce18983e11051 Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Mon, 5 Jan 2026 23:15:43 +0100 Subject: [PATCH] clang: Stop using llvm.convert.to.fp16/llvm.convert.from.fp16 There is no reason to use these over fpext/fptrunc and bitcast. Split out from #174484. The test coverage is also shockingly bad, so adds a new wasm test which shows different contexts the intrinsics are used. I've also reverted this to a more conservative version that leaves the useFP16ConversionIntrinsics configuration in place, and only replaces the exact intrinsic usage. This should be removed, but it seems to have turned into a buggy ABI option. Some contexts which probably meant to check NativeHalfType or NativeHalfArgsAndReturns were relying on this instead. Additionally, some of the SVE intrinsics appear to be using __fp16 but really expect _Float16 treatment. --- clang/include/clang/Basic/TargetInfo.h | 10 +- clang/lib/CodeGen/CGExprScalar.cpp | 76 +++++---- clang/test/CodeGen/builtin_float_strictfp.c | 3 +- clang/test/CodeGen/fp16-ops-strictfp.c | 4 +- clang/test/CodeGen/wasm-fp16.c | 161 ++++++++++++++++++++ 5 files changed, 206 insertions(+), 48 deletions(-) create mode 100644 clang/test/CodeGen/wasm-fp16.c diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 4ff77bb64cf1c..bf688f726e134 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1013,10 +1013,12 @@ class TargetInfo : public TransferrableTargetInfo, return ComplexLongDoubleUsesFP2Ret; } - /// Check whether llvm intrinsics such as llvm.convert.to.fp16 should be used - /// to convert to and from __fp16. - /// FIXME: This function should be removed once all targets stop using the - /// conversion intrinsics. + /// Check whether conversions to and from __fp16 should go through an integer + /// bitcast with i16. + /// + /// FIXME: This function should be removed. The intrinsics / no longer exist, + /// and are emulated with bitcast + fp cast. This only exists because of + /// misuse in ABI determining contexts. virtual bool useFP16ConversionIntrinsics() const { return true; } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 84421fef9f524..30a13c065a729 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1597,22 +1597,21 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, if (SrcType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { // Cast to FP using the intrinsic if the half type itself isn't supported. if (DstTy->isFloatingPointTy()) { - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) - return Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, DstTy), - Src); + if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { + Value *BitCast = Builder.CreateBitCast(Src, CGF.CGM.HalfTy); + return Builder.CreateFPExt(BitCast, DstTy, "conv"); + } } else { // Cast to other types through float, using either the intrinsic or FPExt, // depending on whether the half type itself is supported // (as opposed to operations on half, available with NativeHalfType). - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { - Src = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, - CGF.CGM.FloatTy), - Src); - } else { - Src = Builder.CreateFPExt(Src, CGF.CGM.FloatTy, "conv"); + + if (Src->getType() != CGF.CGM.HalfTy) { + assert(CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()); + Src = Builder.CreateBitCast(Src, CGF.CGM.HalfTy); } + + Src = Builder.CreateFPExt(Src, CGF.CGM.FloatTy, "conv"); SrcType = CGF.getContext().FloatTy; SrcTy = CGF.FloatTy; } @@ -1723,27 +1722,33 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, if (DstType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { // Make sure we cast in a single step if from another FP type. if (SrcTy->isFloatingPointTy()) { - // Use the intrinsic if the half type itself isn't supported - // (as opposed to operations on half, available with NativeHalfType). - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) - return Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, SrcTy), Src); + // Handle the case where the half type is represented as an integer (as + // opposed to operations on half, available with NativeHalfType). + // If the half type is supported, just use an fptrunc. - return Builder.CreateFPTrunc(Src, DstTy); + Value *Res = Builder.CreateFPTrunc(Src, CGF.CGM.HalfTy, "conv"); + if (DstTy == CGF.CGM.HalfTy) + return Res; + + assert(DstTy->isIntegerTy(16) && + CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics() && + "Only half FP requires extra conversion"); + return Builder.CreateBitCast(Res, DstTy); } + DstTy = CGF.FloatTy; } Res = EmitScalarCast(Src, SrcType, DstType, SrcTy, DstTy, Opts); if (DstTy != ResTy) { - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { - assert(ResTy->isIntegerTy(16) && "Only half FP requires extra conversion"); - Res = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, CGF.CGM.FloatTy), - Res); - } else { - Res = Builder.CreateFPTrunc(Res, ResTy, "conv"); + Res = Builder.CreateFPTrunc(Res, CGF.CGM.HalfTy, "conv"); + + if (ResTy != CGF.CGM.HalfTy) { + assert(ResTy->isIntegerTy(16) && + CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics() && + "Only half FP requires extra conversion"); + Res = Builder.CreateBitCast(Res, ResTy); } } @@ -3398,15 +3403,10 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E); if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { - // Another special case: half FP increment should be done via float - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { - value = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_from_fp16, - CGF.CGM.FloatTy), - input, "incdec.conv"); - } else { - value = Builder.CreateFPExt(input, CGF.CGM.FloatTy, "incdec.conv"); - } + // Another special case: half FP increment should be done via float. If + // the input isn't already half, it may be i16. + Value *bitcast = Builder.CreateBitCast(input, CGF.CGM.HalfTy); + value = Builder.CreateFPExt(bitcast, CGF.CGM.FloatTy, "incdec.conv"); } if (value->getType()->isFloatTy()) @@ -3439,14 +3439,8 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, value = Builder.CreateFAdd(value, amt, isInc ? "inc" : "dec"); if (type->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) { - if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { - value = Builder.CreateCall( - CGF.CGM.getIntrinsic(llvm::Intrinsic::convert_to_fp16, - CGF.CGM.FloatTy), - value, "incdec.conv"); - } else { - value = Builder.CreateFPTrunc(value, input->getType(), "incdec.conv"); - } + value = Builder.CreateFPTrunc(value, CGF.CGM.HalfTy, "incdec.conv"); + value = Builder.CreateBitCast(value, input->getType()); } // Fixed-point types. diff --git a/clang/test/CodeGen/builtin_float_strictfp.c b/clang/test/CodeGen/builtin_float_strictfp.c index b7cf567ccd66f..81bf89228f59c 100644 --- a/clang/test/CodeGen/builtin_float_strictfp.c +++ b/clang/test/CodeGen/builtin_float_strictfp.c @@ -18,7 +18,8 @@ void test_half(__fp16 *H, __fp16 *H2) { (void)__builtin_isinf(*H); // NOFP16: [[LDADDR:%.*]] = load ptr, ptr %{{.*}}, align 8 // NOFP16-NEXT: [[IHALF:%.*]] = load i16, ptr [[LDADDR]], align 2 - // NOFP16-NEXT: [[CONV:%.*]] = call float @llvm.convert.from.fp16.f32(i16 [[IHALF]]) + // NOFP16-NEXT: [[BITCAST:%.*]] = bitcast i16 [[IHALF]] to half + // NOFP16-NEXT: [[CONV:%.*]] = call float @llvm.experimental.constrained.fpext.f32.f16(half [[BITCAST]], metadata !"fpexcept.strict") // NOFP16-NEXT: [[RES1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CONV]], i32 516) // NOFP16-NEXT: zext i1 [[RES1]] to i32 // FP16: [[LDADDR:%.*]] = load ptr, ptr %{{.*}}, align 8 diff --git a/clang/test/CodeGen/fp16-ops-strictfp.c b/clang/test/CodeGen/fp16-ops-strictfp.c index 25753e5b98beb..830be6256456e 100644 --- a/clang/test/CodeGen/fp16-ops-strictfp.c +++ b/clang/test/CodeGen/fp16-ops-strictfp.c @@ -334,7 +334,7 @@ void foo(void) { // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %{{.*}}, metadata !"fpexcept.strict") // NOTNATIVE: call half @llvm.experimental.constrained.fptrunc.f16.f64(double 4.200000e+01, metadata !"round.tonearest", metadata !"fpexcept.strict") // NATIVE-HALF: call half @llvm.experimental.constrained.fptrunc.f16.f64(double 4.200000e+01, metadata !"round.tonearest", metadata !"fpexcept.strict") - // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %98, metadata !"fpexcept.strict") + // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %{{.*}}, metadata !"fpexcept.strict") // NOTNATIVE: call i1 @llvm.experimental.constrained.fcmps.f32(float %{{.*}}, float %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") // NATIVE-HALF: call i1 @llvm.experimental.constrained.fcmps.f16(half %{{.*}}, half %{{.*}}, metadata !"ole", metadata !"fpexcept.strict") // CHECK: store {{.*}} i32 {{.*}}, ptr @@ -418,7 +418,7 @@ void foo(void) { // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %{{.*}}, metadata !"fpexcept.strict") // NOTNATIVE: call half @llvm.experimental.constrained.fptrunc.f16.f64(double 1.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") // NATIVE-HALF: call half @llvm.experimental.constrained.fptrunc.f16.f64(double 1.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") - // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %122, metadata !"fpexcept.strict") + // NOTNATIVE: call float @llvm.experimental.constrained.fpext.f32.f16(half %{{.*}}, metadata !"fpexcept.strict") // NOTNATIVE: call i1 @llvm.experimental.constrained.fcmp.f32(float %{{.*}}, float %{{.*}}, metadata !"oeq", metadata !"fpexcept.strict") // NATIVE-HALF: call i1 @llvm.experimental.constrained.fcmp.f16(half %{{.*}}, half %{{.*}}, metadata !"oeq", metadata !"fpexcept.strict") // CHECK: store {{.*}} i32 {{.*}}, ptr diff --git a/clang/test/CodeGen/wasm-fp16.c b/clang/test/CodeGen/wasm-fp16.c new file mode 100644 index 0000000000000..e974b9f09d324 --- /dev/null +++ b/clang/test/CodeGen/wasm-fp16.c @@ -0,0 +1,161 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 6 +// RUN: %clang_cc1 -triple wasm64-- -fnative-half-arguments-and-returns -emit-llvm -O1 -disable-llvm-passes -o - %s | FileCheck %s + +__fp16 g = 2.0f; + +//. +// CHECK: @g = global i16 16384, align 2 +//. +// CHECK-LABEL: define float @test_memory_fp16_to_float( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP2]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_memory_fp16_to_float(__fp16 *ptr) { + return *ptr; +} + +// CHECK-LABEL: define void @test_memory_float_from_fp16( +// CHECK-SAME: ptr noundef [[PTR:%.*]], float noundef [[VAL:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4, !tbaa [[FLOAT_TBAA11:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4, !tbaa [[FLOAT_TBAA11]] +// CHECK-NEXT: [[CONV:%.*]] = fptrunc float [[TMP0]] to half +// CHECK-NEXT: [[TMP1:%.*]] = bitcast half [[CONV]] to i16 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: store i16 [[TMP1]], ptr [[TMP2]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: ret void +// +void test_memory_float_from_fp16(__fp16* ptr, float val) { + *ptr = val; +} + +// CHECK-LABEL: define float @test_memory_fp16_preinc( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[INCDEC_CONV:%.*]] = fpext half [[TMP2]] to float +// CHECK-NEXT: [[INC:%.*]] = fadd float [[INCDEC_CONV]], 1.000000e+00 +// CHECK-NEXT: [[INCDEC_CONV1:%.*]] = fptrunc float [[INC]] to half +// CHECK-NEXT: [[TMP3:%.*]] = bitcast half [[INCDEC_CONV1]] to i16 +// CHECK-NEXT: store i16 [[TMP3]], ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast i16 [[TMP3]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP4]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_memory_fp16_preinc(__fp16 *ptr) { + return ++(*ptr); +} + +// CHECK-LABEL: define float @test_memory_fp16_postinc( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[INCDEC_CONV:%.*]] = fpext half [[TMP2]] to float +// CHECK-NEXT: [[INC:%.*]] = fadd float [[INCDEC_CONV]], 1.000000e+00 +// CHECK-NEXT: [[INCDEC_CONV1:%.*]] = fptrunc float [[INC]] to half +// CHECK-NEXT: [[TMP3:%.*]] = bitcast half [[INCDEC_CONV1]] to i16 +// CHECK-NEXT: store i16 [[TMP3]], ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP4]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_memory_fp16_postinc(__fp16 *ptr) { + return (*ptr)++; +} + +// CHECK-LABEL: define float @test_memory_fp16_predec( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[INCDEC_CONV:%.*]] = fpext half [[TMP2]] to float +// CHECK-NEXT: [[DEC:%.*]] = fadd float [[INCDEC_CONV]], -1.000000e+00 +// CHECK-NEXT: [[INCDEC_CONV1:%.*]] = fptrunc float [[DEC]] to half +// CHECK-NEXT: [[TMP3:%.*]] = bitcast half [[INCDEC_CONV1]] to i16 +// CHECK-NEXT: store i16 [[TMP3]], ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast i16 [[TMP3]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP4]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_memory_fp16_predec(__fp16 *ptr) { + return --(*ptr); +} + +// CHECK-LABEL: define float @test_memory_fp16_postdec( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8, !tbaa [[__FP16PTR_TBAA6]] +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[INCDEC_CONV:%.*]] = fpext half [[TMP2]] to float +// CHECK-NEXT: [[DEC:%.*]] = fadd float [[INCDEC_CONV]], -1.000000e+00 +// CHECK-NEXT: [[INCDEC_CONV1:%.*]] = fptrunc float [[DEC]] to half +// CHECK-NEXT: [[TMP3:%.*]] = bitcast half [[INCDEC_CONV1]] to i16 +// CHECK-NEXT: store i16 [[TMP3]], ptr [[TMP0]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast i16 [[TMP1]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP4]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_memory_fp16_postdec(__fp16 *ptr) { + return (*ptr)--; +} + +// CHECK-LABEL: define i16 @test_arg_return( +// CHECK-SAME: i16 noundef [[X:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i16, align 2 +// CHECK-NEXT: store i16 [[X]], ptr [[X_ADDR]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[X_ADDR]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP1:%.*]] = bitcast i16 [[TMP0]] to half +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP1]] to float +// CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[X_ADDR]], align 2, !tbaa [[__FP16_TBAA9]] +// CHECK-NEXT: [[TMP3:%.*]] = bitcast i16 [[TMP2]] to half +// CHECK-NEXT: [[CONV1:%.*]] = fpext half [[TMP3]] to float +// CHECK-NEXT: [[ADD:%.*]] = fadd float [[CONV]], [[CONV1]] +// CHECK-NEXT: [[CONV2:%.*]] = fptrunc float [[ADD]] to half +// CHECK-NEXT: [[TMP4:%.*]] = bitcast half [[CONV2]] to i16 +// CHECK-NEXT: ret i16 [[TMP4]] +// +__fp16 test_arg_return(__fp16 x) { + return x + x; +} +//. +// CHECK: attributes #[[ATTR0]] = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +//. +// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} +// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +// CHECK: [[META2:![0-9]+]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[__FP16PTR_TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"p1 __fp16", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[__FP16_TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"__fp16", [[META4]], i64 0} +// CHECK: [[FLOAT_TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"float", [[META4]], i64 0} +//. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
