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

Reply via email to