https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/233391
Fixes #170434 `__builtin_fmaxf16` takes `__fp16`, so calling it with a `__bf16` argument converts between the two 16-bit types. `EmitScalarConversion` lowers a conversion into or out of a non-native `__fp16` with a single `fptrunc` or `fpext`, which is invalid between types of the same width, and `CastInst::Create` asserts. `__builtin_convertvector`, matrix casts, `__fp16 += __bf16` and the start value of `__builtin_reduce_*fadd` picked the instruction by type order and failed the same way. Add `EmitFloatingCast`, which emits one `fpext` or `fptrunc` when the widths differ and otherwise goes through `float`, which holds both `half` and `bfloat` exactly, so the value rounds once. Every floating-point conversion site in CGExprScalar uses it, and the reduction start value goes through `EmitScalarConversion`. LLM tools were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub. >From 8f46407909a3f0704fca88dbcc032f659189d010 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 11:46:54 +0530 Subject: [PATCH] [clang][CodeGen] Convert between bfloat and half through float `__builtin_fmaxf16` takes `__fp16`, so calling it with a `__bf16` argument converts between the two 16-bit types. `EmitScalarConversion` lowers a conversion into or out of a non-native `__fp16` with a single `fptrunc` or `fpext`, which is invalid between types of the same width, and `CastInst::Create` asserts. `__builtin_convertvector`, matrix casts, `__fp16 += __bf16` and the start value of `__builtin_reduce_*fadd` picked the instruction by type order and failed the same way. Add `EmitFloatingCast`, which emits one `fpext` or `fptrunc` when the widths differ and otherwise goes through `float`, which holds both `half` and `bfloat` exactly, so the value rounds once. Every floating-point conversion site in CGExprScalar uses it, and the reduction start value goes through `EmitScalarConversion`. Fixes #170434 --- clang/docs/ReleaseNotes.md | 1 + clang/lib/CodeGen/CGBuiltin.cpp | 7 +- clang/lib/CodeGen/CGExprScalar.cpp | 49 +-- .../test/CodeGen/X86/bfloat16-convert-half.c | 290 +++++++++++++++++- clang/test/CodeGen/X86/bfloat16.cpp | 89 ++++++ clang/test/CodeGen/arm-bf16-params-returns.c | 38 ++- 6 files changed, 448 insertions(+), 26 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 3f0c48cf36644d..a7e3116f02ef46 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -719,6 +719,7 @@ features cannot lower the translation-unit ABI level; - Fixed an assertion when `#pragma omp declare simd` or `#pragma omp declare variant` is followed by another OpenMP declarative directive containing a qualified identifier. (#GH217204) - Fixed crashes on an OpenMP `target` region inside a lambda or block at namespace scope, including when the region used a global reference. (#GH223397) - Fixed a crash when an `asm` label names the register for a global variable of incomplete type. (#GH219746) +- Fixed a crash when converting between `__bf16` and `__fp16`, such as when passing a `__bf16` value to `__builtin_fmaxf16`, and when converting vectors or matrices of `__bf16` and `_Float16`. (#GH170434) - Fixed a crash when a local register variable bound to a pseudo or status register by an `asm` label, such as `register long double t __asm("17");` (the flags register), was used as an inline asm operand. Such declarations are now rejected, matching GCC. (#GH225033) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index c93375ff3a18ef..bebf7b4c65e772 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4505,8 +4505,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm::Value *Vector = EmitScalarExpr(E->getArg(0)); llvm::Type *ScalarTy = Vector->getType()->getScalarType(); llvm::Value *StartValue = nullptr; - if (E->getNumArgs() == 2) - StartValue = Builder.CreateFPCast(EmitScalarExpr(E->getArg(1)), ScalarTy); + if (E->getNumArgs() == 2) { + const Expr *Start = E->getArg(1); + StartValue = EmitScalarConversion(EmitScalarExpr(Start), Start->getType(), + E->getType(), Start->getExprLoc()); + } llvm::Value *Args[] = {/*start_value=*/StartValue ? StartValue : llvm::ConstantFP::get(ScalarTy, -0.0F), diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index db2b7a4ad1fce2..e4143786b6b529 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -437,6 +437,9 @@ class ScalarExprEmitter Value *EmitScalarCast(Value *Src, QualType SrcType, QualType DstType, llvm::Type *SrcTy, llvm::Type *DstTy, ScalarConversionOpts Opts); + /// Emit fpext or fptrunc, going through float for half <-> bfloat. + Value *EmitFloatingCast(Value *Src, llvm::Type *DstTy, + const Twine &Name = "conv"); Value * EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc, @@ -1617,13 +1620,29 @@ Value *ScalarExprEmitter::EmitScalarCast(Value *Src, QualType SrcType, return Builder.CreateFPToUI(Src, DstTy, "conv"); } - if ((DstElementTy->is16bitFPTy() && SrcElementTy->is16bitFPTy())) { - Value *FloatVal = Builder.CreateFPExt(Src, Builder.getFloatTy(), "fpext"); - return Builder.CreateFPTrunc(FloatVal, DstTy, "fptrunc"); - } - if (DstElementTy->getTypeID() < SrcElementTy->getTypeID()) - return Builder.CreateFPTrunc(Src, DstTy, "conv"); - return Builder.CreateFPExt(Src, DstTy, "conv"); + return EmitFloatingCast(Src, DstTy); +} + +Value *ScalarExprEmitter::EmitFloatingCast(Value *Src, llvm::Type *DstTy, + const Twine &Name) { + llvm::Type *SrcTy = Src->getType(); + if (SrcTy == DstTy) + return Src; + + unsigned SrcBits = SrcTy->getScalarSizeInBits(); + unsigned DstBits = DstTy->getScalarSizeInBits(); + if (SrcBits < DstBits) + return Builder.CreateFPExt(Src, DstTy, Name); + if (SrcBits > DstBits) + return Builder.CreateFPTrunc(Src, DstTy, Name); + + // half and bfloat are the same width; only float holds both exactly. + assert(SrcTy->getScalarType()->is16bitFPTy() && + DstTy->getScalarType()->is16bitFPTy() && + "unexpected conversion between floating-point types of equal width"); + Value *FloatVal = + Builder.CreateFPExt(Src, SrcTy->getWithNewType(CGF.FloatTy), "fpext"); + return Builder.CreateFPTrunc(FloatVal, DstTy, "fptrunc"); } /// Emit a conversion from the specified type to the specified destination type, @@ -1699,7 +1718,7 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, if (SrcType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType && SrcTy == CGF.CGM.HalfTy && DstTy != CGF.CGM.HalfTy) { if (DstTy->isFloatingPointTy()) - return Builder.CreateFPExt(Src, DstTy, "conv"); + return EmitFloatingCast(Src, DstTy); // Cast to other types through float (as opposed to operations on half, // available with NativeHalfType). @@ -1816,7 +1835,7 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, DstTy == CGF.CGM.HalfTy) { // Make sure we cast in a single step if from another FP type. if (SrcTy->isFloatingPointTy()) - return Builder.CreateFPTrunc(Src, CGF.CGM.HalfTy, "conv"); + return EmitFloatingCast(Src, DstTy); DstTy = CGF.FloatTy; } @@ -2134,10 +2153,7 @@ Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) { assert(SrcEltTy->isFloatingPointTy() && DstEltTy->isFloatingPointTy() && "Unknown real conversion"); CodeGenFunction::CGFPOptionsRAII FPOptions(CGF, E); - if (DstEltTy->getTypeID() < SrcEltTy->getTypeID()) - Res = Builder.CreateFPTrunc(Src, DstTy, "conv"); - else - Res = Builder.CreateFPExt(Src, DstTy, "conv"); + Res = EmitFloatingCast(Src, DstTy); } return Res; @@ -3099,12 +3115,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { case CK_FloatingCast: { if (E->getType()->isVectorType() && DestTy->isVectorType()) { // TODO: Support constrained FP intrinsics. - QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType(); - QualType DstElTy = DestTy->castAs<VectorType>()->getElementType(); - if (DstElTy->castAs<BuiltinType>()->getKind() < - SrcElTy->castAs<BuiltinType>()->getKind()) - return Builder.CreateFPTrunc(Visit(E), ConvertType(DestTy), "conv"); - return Builder.CreateFPExt(Visit(E), ConvertType(DestTy), "conv"); + return EmitFloatingCast(Visit(E), ConvertType(DestTy)); } CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE); return EmitScalarConversion(Visit(E), E->getType(), DestTy, diff --git a/clang/test/CodeGen/X86/bfloat16-convert-half.c b/clang/test/CodeGen/X86/bfloat16-convert-half.c index 55451dc6f092cd..8d18467477746e 100644 --- a/clang/test/CodeGen/X86/bfloat16-convert-half.c +++ b/clang/test/CodeGen/X86/bfloat16-convert-half.c @@ -1,5 +1,16 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-O0-optnone -emit-llvm \ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-O0-optnone -fenable-matrix -emit-llvm \ // RUN: %s -o - | opt -S -passes=mem2reg | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-O0-optnone -fenable-matrix -emit-llvm \ +// RUN: -target-feature +avx512fp16 %s -o - | opt -S -passes=mem2reg | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-O0-optnone -fenable-matrix -emit-llvm \ +// RUN: -fnative-half-type %s -o - | opt -S -passes=mem2reg | FileCheck %s + +typedef __bf16 bf16x8 __attribute__((ext_vector_type(8))); +typedef _Float16 f16x8 __attribute__((ext_vector_type(8))); +typedef __fp16 fp16x8 __attribute__((ext_vector_type(8))); +typedef float f32x8 __attribute__((ext_vector_type(8))); +typedef __bf16 bf16x2x2 __attribute__((matrix_type(2, 2))); +typedef _Float16 f16x2x2 __attribute__((matrix_type(2, 2))); // CHECK-LABEL: define dso_local half @test_convert_from_bf16_to_fp16( // CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { @@ -23,3 +34,280 @@ __bf16 test_convert_from_fp16_to_bf16(_Float16 a) { return (__bf16)a; } +// GH170434 +// CHECK-LABEL: define dso_local bfloat @test_fmaxf16_bf16( +// CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext nsz bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc nsz float [[FPEXT]] to half +// CHECK-NEXT: [[FPEXT1:%.*]] = fpext nsz bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC2:%.*]] = fptrunc nsz float [[FPEXT1]] to half +// CHECK-NEXT: [[TMP0:%.*]] = call nsz half @llvm.maxnum.f16(half [[FPTRUNC]], half [[FPTRUNC2]]) +// CHECK-NEXT: [[FPEXT3:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[FPTRUNC4:%.*]] = fptrunc float [[FPEXT3]] to bfloat +// CHECK-NEXT: ret bfloat [[FPTRUNC4]] +// +__bf16 test_fmaxf16_bf16(__bf16 a) { + return __builtin_fmaxf16(a, a); +} + +// CHECK-LABEL: define dso_local half @test_return_bf16_as_f16( +// CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: ret half [[FPTRUNC]] +// +_Float16 test_return_bf16_as_f16(__bf16 a) { + return a; +} + +void take_f16(_Float16); +// CHECK-LABEL: define dso_local void @test_pass_bf16_as_f16( +// CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: call void @take_f16(half noundef [[FPTRUNC]]) +// CHECK-NEXT: ret void +// +void test_pass_bf16_as_f16(__bf16 a) { + take_f16(a); +} + +// CHECK-LABEL: define dso_local void @test_assign_f16_to_bf16( +// CHECK-SAME: ptr noundef [[P:%.*]], half noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext half [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to bfloat +// CHECK-NEXT: store bfloat [[FPTRUNC]], ptr [[P]], align 2 +// CHECK-NEXT: ret void +// +void test_assign_f16_to_bf16(__bf16 *p, _Float16 a) { + *p = a; +} + +// CHECK-LABEL: define dso_local void @test_assign_bf16_to_fp16( +// CHECK-SAME: ptr noundef [[P:%.*]], bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: store half [[FPTRUNC]], ptr [[P]], align 2 +// CHECK-NEXT: ret void +// +void test_assign_bf16_to_fp16(__fp16 *p, __bf16 a) { + *p = a; +} + +// CHECK-LABEL: define dso_local bfloat @test_return_fp16_as_bf16( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[P]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to bfloat +// CHECK-NEXT: ret bfloat [[FPTRUNC]] +// +__bf16 test_return_fp16_as_bf16(__fp16 *p) { + return *p; +} + +// CHECK-LABEL: define dso_local void @test_cast_bf16_to_fp16( +// CHECK-SAME: ptr noundef [[P:%.*]], bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: store half [[FPTRUNC]], ptr [[P]], align 2 +// CHECK-NEXT: ret void +// +void test_cast_bf16_to_fp16(__fp16 *p, __bf16 a) { + *p = (__fp16)a; +} + +// CHECK-LABEL: define dso_local bfloat @test_cast_fp16_to_bf16( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[P]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to bfloat +// CHECK-NEXT: ret bfloat [[FPTRUNC]] +// +__bf16 test_cast_fp16_to_bf16(__fp16 *p) { + return (__bf16)*p; +} + +// CHECK-LABEL: define dso_local void @test_compound_assign_fp16_bf16( +// CHECK-SAME: ptr noundef [[P:%.*]], bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[P]], align 2 +// CHECK-NEXT: [[ADD:%.*]] = fadd half [[TMP0]], [[FPTRUNC]] +// CHECK-NEXT: store half [[ADD]], ptr [[P]], align 2 +// CHECK-NEXT: ret void +// +void test_compound_assign_fp16_bf16(__fp16 *p, __bf16 a) { + *p += a; +} + +// CHECK-LABEL: define dso_local <8 x half> @test_convertvector_bf16_to_f16( +// CHECK-SAME: <8 x bfloat> noundef [[V:%.*]]) #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <8 x bfloat> [[V]] to <8 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <8 x float> [[FPEXT]] to <8 x half> +// CHECK-NEXT: ret <8 x half> [[FPTRUNC]] +// +f16x8 test_convertvector_bf16_to_f16(bf16x8 v) { + return __builtin_convertvector(v, f16x8); +} + +// CHECK-LABEL: define dso_local <8 x bfloat> @test_convertvector_f16_to_bf16( +// CHECK-SAME: <8 x half> noundef [[V:%.*]]) #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <8 x half> [[V]] to <8 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <8 x float> [[FPEXT]] to <8 x bfloat> +// CHECK-NEXT: ret <8 x bfloat> [[FPTRUNC]] +// +bf16x8 test_convertvector_f16_to_bf16(f16x8 v) { + return __builtin_convertvector(v, bf16x8); +} + +// CHECK-LABEL: define dso_local <8 x half> @test_convertvector_bf16_to_fp16( +// CHECK-SAME: <8 x bfloat> noundef [[V:%.*]]) #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <8 x bfloat> [[V]] to <8 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <8 x float> [[FPEXT]] to <8 x half> +// CHECK-NEXT: ret <8 x half> [[FPTRUNC]] +// +fp16x8 test_convertvector_bf16_to_fp16(bf16x8 v) { + return __builtin_convertvector(v, fp16x8); +} + +// CHECK-LABEL: define dso_local void @test_matrix_cast_bf16_to_f16( +// CHECK-SAME: ptr noundef [[IN:%.*]], ptr noundef [[OUT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load <4 x bfloat>, ptr [[IN]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <4 x bfloat> [[TMP0]] to <4 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <4 x float> [[FPEXT]] to <4 x half> +// CHECK-NEXT: store <4 x half> [[FPTRUNC]], ptr [[OUT]], align 2 +// CHECK-NEXT: ret void +// +void test_matrix_cast_bf16_to_f16(bf16x2x2 *in, f16x2x2 *out) { + *out = (f16x2x2)*in; +} + +// CHECK-LABEL: define dso_local void @test_matrix_cast_f16_to_bf16( +// CHECK-SAME: ptr noundef [[IN:%.*]], ptr noundef [[OUT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load <4 x half>, ptr [[IN]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <4 x half> [[TMP0]] to <4 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <4 x float> [[FPEXT]] to <4 x bfloat> +// CHECK-NEXT: store <4 x bfloat> [[FPTRUNC]], ptr [[OUT]], align 2 +// CHECK-NEXT: ret void +// +void test_matrix_cast_f16_to_bf16(f16x2x2 *in, bf16x2x2 *out) { + *out = (bf16x2x2)*in; +} + +// CHECK-LABEL: define dso_local half @test_reduce_fadd_bf16_start( +// CHECK-SAME: <8 x half> noundef [[V:%.*]], bfloat noundef [[S:%.*]]) #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[S]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: [[RDX_ADDF:%.*]] = call half @llvm.vector.reduce.fadd.v8f16(half [[FPTRUNC]], <8 x half> [[V]]) +// CHECK-NEXT: ret half [[RDX_ADDF]] +// +_Float16 test_reduce_fadd_bf16_start(f16x8 v, __bf16 s) { + return __builtin_reduce_in_order_fadd(v, s); +} + +// CHECK-LABEL: define dso_local float @test_bf16_to_float( +// CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fpext bfloat [[A]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_bf16_to_float(__bf16 a) { + return a; +} + +// CHECK-LABEL: define dso_local bfloat @test_float_to_bf16( +// CHECK-SAME: float noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fptrunc float [[A]] to bfloat +// CHECK-NEXT: ret bfloat [[CONV]] +// +__bf16 test_float_to_bf16(float a) { + return a; +} + +// CHECK-LABEL: define dso_local double @test_bf16_to_double( +// CHECK-SAME: bfloat noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fpext bfloat [[A]] to double +// CHECK-NEXT: ret double [[CONV]] +// +double test_bf16_to_double(__bf16 a) { + return a; +} + +// CHECK-LABEL: define dso_local bfloat @test_double_to_bf16( +// CHECK-SAME: double noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fptrunc double [[A]] to bfloat +// CHECK-NEXT: ret bfloat [[CONV]] +// +__bf16 test_double_to_bf16(double a) { + return a; +} + +// CHECK-LABEL: define dso_local float @test_f16_to_float( +// CHECK-SAME: half noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[A]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_f16_to_float(_Float16 a) { + return a; +} + +// CHECK-LABEL: define dso_local half @test_float_to_f16( +// CHECK-SAME: float noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fptrunc float [[A]] to half +// CHECK-NEXT: ret half [[CONV]] +// +_Float16 test_float_to_f16(float a) { + return a; +} + +// CHECK-LABEL: define dso_local float @test_fp16_to_float( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[P]], align 2 +// CHECK-NEXT: [[CONV:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: ret float [[CONV]] +// +float test_fp16_to_float(__fp16 *p) { + return *p; +} + +// CHECK-LABEL: define dso_local void @test_float_to_fp16( +// CHECK-SAME: ptr noundef [[P:%.*]], float noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fptrunc float [[A]] to half +// CHECK-NEXT: store half [[CONV]], ptr [[P]], align 2 +// CHECK-NEXT: ret void +// +void test_float_to_fp16(__fp16 *p, float a) { + *p = a; +} + +// CHECK-LABEL: define dso_local <8 x float> @test_convertvector_bf16_to_float( +// CHECK-SAME: <8 x bfloat> noundef [[V:%.*]]) #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV:%.*]] = fpext <8 x bfloat> [[V]] to <8 x float> +// CHECK-NEXT: ret <8 x float> [[CONV]] +// +f32x8 test_convertvector_bf16_to_float(bf16x8 v) { + return __builtin_convertvector(v, f32x8); +} diff --git a/clang/test/CodeGen/X86/bfloat16.cpp b/clang/test/CodeGen/X86/bfloat16.cpp index d202de22716f19..db84e442355b9c 100644 --- a/clang/test/CodeGen/X86/bfloat16.cpp +++ b/clang/test/CodeGen/X86/bfloat16.cpp @@ -143,3 +143,92 @@ void test_vector(v8bfloat16 a, v8bfloat16 b) { c = a * b; c = a / b; } + +// GH170434 +// CHECK-LABEL: define dso_local void @_Z30test_compound_assign_fp16_bf16PDhDF16b +// CHECK-SAME: (ptr noundef [[P:%.*]], bfloat noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store bfloat [[A]], ptr [[A_ADDR]], align 2 +// CHECK-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[TMP0]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load half, ptr [[TMP1]], align 2 +// CHECK-NEXT: [[ADD:%.*]] = fadd half [[TMP2]], [[FPTRUNC]] +// CHECK-NEXT: store half [[ADD]], ptr [[TMP1]], align 2 +// CHECK-NEXT: ret void +// +// CHECK-NBF16-LABEL: define dso_local void @_Z30test_compound_assign_fp16_bf16PDhDF16b +// CHECK-NBF16-SAME: (ptr noundef [[P:%.*]], bfloat noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NBF16: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NBF16-NEXT: [[A_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NBF16-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NBF16-NEXT: store bfloat [[A]], ptr [[A_ADDR]], align 2 +// CHECK-NBF16-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2 +// CHECK-NBF16-NEXT: [[FPEXT:%.*]] = fpext bfloat [[TMP0]] to float +// CHECK-NBF16-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NBF16-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NBF16-NEXT: [[TMP2:%.*]] = load half, ptr [[TMP1]], align 2 +// CHECK-NBF16-NEXT: [[ADD:%.*]] = fadd half [[TMP2]], [[FPTRUNC]] +// CHECK-NBF16-NEXT: store half [[ADD]], ptr [[TMP1]], align 2 +// CHECK-NBF16-NEXT: ret void +// +void test_compound_assign_fp16_bf16(__fp16 *p, __bf16 a) { + *p += a; +} + +typedef _Float16 v8float16 __attribute__((__vector_size__(16))); + +// CHECK-LABEL: define dso_local noundef <8 x half> @_Z30test_convertvector_bf16_to_f16Dv8_DF16b +// CHECK-SAME: (<8 x bfloat> noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[A_ADDR:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: store <8 x bfloat> [[A]], ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext <8 x bfloat> [[TMP0]] to <8 x float> +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc <8 x float> [[FPEXT]] to <8 x half> +// CHECK-NEXT: ret <8 x half> [[FPTRUNC]] +// +// CHECK-NBF16-LABEL: define dso_local noundef <8 x half> @_Z30test_convertvector_bf16_to_f16Dv8_DF16b +// CHECK-NBF16-SAME: (<8 x bfloat> noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NBF16: [[A_ADDR:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NBF16-NEXT: store <8 x bfloat> [[A]], ptr [[A_ADDR]], align 16 +// CHECK-NBF16-NEXT: [[TMP0:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NBF16-NEXT: [[FPEXT:%.*]] = fpext <8 x bfloat> [[TMP0]] to <8 x float> +// CHECK-NBF16-NEXT: [[FPTRUNC:%.*]] = fptrunc <8 x float> [[FPEXT]] to <8 x half> +// CHECK-NBF16-NEXT: ret <8 x half> [[FPTRUNC]] +// +v8float16 test_convertvector_bf16_to_f16(v8bfloat16 a) { + return __builtin_convertvector(a, v8float16); +} + +// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z21test_compare_bf16_f16DF16bDF16_ +// CHECK-SAME: (bfloat noundef [[A:%.*]], half noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[A_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca half, align 2 +// CHECK-NEXT: store bfloat [[A]], ptr [[A_ADDR]], align 2 +// CHECK-NEXT: store half [[B]], ptr [[B_ADDR]], align 2 +// CHECK-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2 +// CHECK-NEXT: [[FPEXT:%.*]] = fpext bfloat [[TMP0]] to float +// CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NEXT: [[TMP1:%.*]] = load half, ptr [[B_ADDR]], align 2 +// CHECK-NEXT: [[CMP:%.*]] = fcmp olt half [[FPTRUNC]], [[TMP1]] +// CHECK-NEXT: ret i1 [[CMP]] +// +// CHECK-NBF16-LABEL: define dso_local noundef zeroext i1 @_Z21test_compare_bf16_f16DF16bDF16_ +// CHECK-NBF16-SAME: (bfloat noundef [[A:%.*]], half noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NBF16: [[A_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NBF16-NEXT: [[B_ADDR:%.*]] = alloca half, align 2 +// CHECK-NBF16-NEXT: store bfloat [[A]], ptr [[A_ADDR]], align 2 +// CHECK-NBF16-NEXT: store half [[B]], ptr [[B_ADDR]], align 2 +// CHECK-NBF16-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2 +// CHECK-NBF16-NEXT: [[FPEXT:%.*]] = fpext bfloat [[TMP0]] to float +// CHECK-NBF16-NEXT: [[FPTRUNC:%.*]] = fptrunc float [[FPEXT]] to half +// CHECK-NBF16-NEXT: [[TMP1:%.*]] = load half, ptr [[B_ADDR]], align 2 +// CHECK-NBF16-NEXT: [[CMP:%.*]] = fcmp olt half [[FPTRUNC]], [[TMP1]] +// CHECK-NBF16-NEXT: ret i1 [[CMP]] +// +bool test_compare_bf16_f16(__bf16 a, _Float16 b) { + return a < b; +} diff --git a/clang/test/CodeGen/arm-bf16-params-returns.c b/clang/test/CodeGen/arm-bf16-params-returns.c index 21be0bb151697a..d91f6d73023c82 100644 --- a/clang/test/CodeGen/arm-bf16-params-returns.c +++ b/clang/test/CodeGen/arm-bf16-params-returns.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi hard -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefix=CHECK32-HARD -// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi softfp -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefix=CHECK32-SOFTFP -// RUN: %clang_cc1 -triple aarch64 -target-abi aapcs -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefixes=CHECK64,CHECK64NEON -// RUN: %clang_cc1 -triple aarch64 -target-abi aapcs -target-feature -bf16 -target-feature +neon -DNONEON -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefix=CHECK64 +// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi hard -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefixes=CHECK32-HARD,CHECK +// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi softfp -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefixes=CHECK32-SOFTFP,CHECK +// RUN: %clang_cc1 -triple aarch64 -target-abi aapcs -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefixes=CHECK64,CHECK64NEON,CHECK +// RUN: %clang_cc1 -triple aarch64 -target-abi aapcs -target-feature -bf16 -target-feature +neon -DNONEON -emit-llvm -O2 -o - %s | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefixes=CHECK64,CHECK // REQUIRES: aarch64-registered-target || arm-registered-target @@ -18,6 +18,36 @@ __bf16 test_ret_bf16(__bf16 v) { // CHECK64: define{{.*}} bfloat @test_ret_bf16(bfloat noundef returned %v) {{.*}} { // CHECK64: ret bfloat %v +// GH170434 +__fp16 test_ret_fp16_from_bf16(__bf16 v) { + return v; +} +// CHECK: define{{.*}} half @test_ret_fp16_from_bf16(bfloat noundef %v) {{.*}} { +// CHECK: [[EXT:%.*]] = fpext bfloat %v to float +// CHECK: [[TRUNC:%.*]] = fptrunc float [[EXT]] to half +// CHECK: ret half [[TRUNC]] + +__bf16 test_ret_bf16_from_fp16(__fp16 v) { + return v; +} +// CHECK: define{{.*}} bfloat @test_ret_bf16_from_fp16(half noundef %v) {{.*}} { +// CHECK: [[EXT:%.*]] = fpext half %v to float +// CHECK: [[TRUNC:%.*]] = fptrunc float [[EXT]] to bfloat +// CHECK: ret bfloat [[TRUNC]] + +__bf16 test_fmaxf16_bf16(__bf16 a, __bf16 b) { + return __builtin_fmaxf16(a, b); +} +// CHECK: define{{.*}} bfloat @test_fmaxf16_bf16(bfloat noundef %a, bfloat noundef %b) {{.*}} { +// CHECK: [[EXTA:%.*]] = fpext nsz bfloat %a to float +// CHECK: [[TRUNCA:%.*]] = fptrunc nsz float [[EXTA]] to half +// CHECK: [[EXTB:%.*]] = fpext nsz bfloat %b to float +// CHECK: [[TRUNCB:%.*]] = fptrunc nsz float [[EXTB]] to half +// CHECK: [[MAX:%.*]] = {{.*}}call nsz half @llvm.maxnum.f16(half [[TRUNCA]], half [[TRUNCB]]) +// CHECK: [[EXT:%.*]] = fpext half [[MAX]] to float +// CHECK: [[TRUNC:%.*]] = fptrunc float [[EXT]] to bfloat +// CHECK: ret bfloat [[TRUNC]] + #ifndef NONEON bfloat16x4_t test_ret_bf16x4_t(bfloat16x4_t v) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
