[clang] [Clang] Add elementwise conversions from encoded FP8 values (PR #212647)
Chinmay Deshpande via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 10 18:29:12 PDT 2026
https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/212647
>From 476eb9d1f5db5351061e21dc34f5b8158fe83ceb Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Mon, 27 Jul 2026 14:49:02 -0400
Subject: [PATCH 1/2] [clang] Add elementwise conversions from encoded FP8
values
Add nine builtins converting Float8E5M2, Float8E4M3FN, and Float8E5M3FNU
encodings to _Float16, __bf16, or float through
llvm.convert.from.arbitrary.fp.
Accept exactly 8-bit integer scalars and generic fixed-length vectors,
preserving vector kinds and element counts without integer promotions.
Also support scalar AArch64 __mfp8 containers. Apply destination type
availability checks, including deferred offload diagnostics, and reject
constant-expression use.
Document the interface and add semantic, template, language-mode,
target-specific, and IR-generation regression coverage.
---
clang/docs/LanguageExtensions.md | 97 ++++++++++
clang/docs/ReleaseNotes.md | 3 +
.../clang/Basic/ArbitraryFPFormats.def | 30 +++
clang/include/clang/Basic/Builtins.td | 18 ++
.../clang/Basic/DiagnosticSemaKinds.td | 10 +
clang/include/clang/Sema/Sema.h | 6 +
clang/lib/CodeGen/CGBuiltin.cpp | 31 ++++
clang/lib/Sema/SemaChecking.cpp | 105 +++++++++++
clang/lib/Sema/SemaType.cpp | 68 +++++--
...ns-elementwise-convert-from-arbitrary-fp.c | 172 ++++++++++++++++++
...ementwise-convert-from-arbitrary-fp-neon.c | 23 +++
...lementwise-convert-from-arbitrary-fp-rvv.c | 5 +
...entwise-convert-from-arbitrary-fp-target.c | 44 +++++
...ns-elementwise-convert-from-arbitrary-fp.c | 135 ++++++++++++++
...-elementwise-convert-from-arbitrary-fp.cpp | 37 ++++
...s-elementwise-convert-from-arbitrary-fp.cl | 38 ++++
16 files changed, 804 insertions(+), 18 deletions(-)
create mode 100644 clang/include/clang/Basic/ArbitraryFPFormats.def
create mode 100644 clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
create mode 100644 clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
create mode 100644 clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
create mode 100644 clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-target.c
create mode 100644 clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
create mode 100644 clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
create mode 100644 clang/test/SemaOpenCL/builtins-elementwise-convert-from-arbitrary-fp.cl
diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 3db8d083c4075..dfff19ee3bd93 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -3682,6 +3682,103 @@ C-style cast applied to each element of the first argument.
Query for this feature with `__has_builtin(__builtin_convertvector)`.
+(langext-builtin-elementwise-convert-from-arbitrary-fp)=
+
+### `__builtin_elementwise_convert_from_*`
+
+The `__builtin_elementwise_convert_from_*` family interprets an integer as the bits of a narrow floating-point format that has no corresponding C type and converts it to a native floating-point type.
+
+**Syntax**:
+
+```c++
+__builtin_elementwise_convert_from_<source_format>_<destination_type>(bits)
+```
+
+**Examples**:
+
+```c++
+typedef unsigned char uchar4 __attribute__((ext_vector_type(4)));
+typedef float float4 __attribute__((ext_vector_type(4)));
+
+unsigned char b; uchar4 vb;
+
+// Interpret b as a Float8E4M3FN value and widen it to _Float16.
+__builtin_elementwise_convert_from_f8e4m3fn_f16(b)
+
+// The same, elementwise, for four Float8E5M2 values.
+__builtin_elementwise_convert_from_f8e5m2_f32(vb)
+```
+
+**Description**:
+
+`bits` is a non-Boolean, non-enumeration integer or a supported fixed-length vector of such integers holding the encoded floating-point value.
+The result is a scalar for a scalar input or a vector with the same number of elements for a vector input.
+Supported vector kinds are GNU `vector_size` and Clang/OpenCL `ext_vector_type`.
+The result preserves which of those two vector kinds the input uses.
+Sizeless vectors and target-specific fixed-length vector kinds are rejected.
+Preserving both the lane count and a target-specific vector kind after widening
+the element type can produce an invalid type or ABI combination, such as a
+widened NEON vector.
+
+The source format suffix determines the interpretation and required integer element width:
+
+| Suffix | Source format | Width |
+| ------------ | -------------- | ----- |
+| `f8e5m2` | `Float8E5M2` | 8 |
+| `f8e4m3fn` | `Float8E4M3FN` | 8 |
+| `f8e5m3fnu` | `Float8E5M3FNU`| 8 |
+
+The destination suffix determines the result element type:
+
+| Suffix | Result element type |
+| ------ | ------------------- |
+| `f16` | `_Float16` |
+| `bf16` | `__bf16` |
+| `f32` | `float` |
+
+The `f16` suffix denotes `_Float16` in every language mode, including OpenCL.
+The three source suffixes and three destination suffixes form exactly nine
+builtin spellings.
+
+`Float8E5M3FNU` has no sign bit and no infinity encoding, and its exponent
+range exceeds that of `_Float16`. Its seven largest finite encodings therefore
+convert to infinity rather than exactly when the destination is `f16`; the
+`bf16` and `f32` destinations are exact.
+
+Only the signedness-free width of `bits` matters, so for an 8-bit format any 8-bit `char`, `signed char`, `unsigned char`, or `_BitInt(8)` of either signedness may be used.
+On targets that have it, `__mfp8` is also accepted as a scalar source, because
+it is an opaque 8-bit floating-point container with no interpretation of its
+own. Its Neon vector types are rejected with the other target-specific vector
+kinds.
+
+Integer promotions and the usual arithmetic conversions are not applied to
+`bits`, so an expression that C promotes to `int` needs an explicit cast back
+to an 8-bit container:
+
+```c
+unsigned char b;
+__builtin_elementwise_convert_from_f8e5m2_f32((unsigned char)(b >> 1));
+```
+
+These builtins are available in C, C++, and OpenCL, but are not supported in constant expressions.
+`__has_constexpr_builtin` therefore returns zero for these builtins.
+Each builtin maps to the `llvm.convert.from.arbitrary.fp` intrinsic; see its description in the LLVM Language Reference for the exact conversion semantics.
+NaN results follow LLVM's general NaN rules; the sign, quiet or signaling state,
+and payload are not guaranteed to be preserved.
+Current generic code generation for the intrinsic is SelectionDAG-only.
+Other code-generation paths are future work.
+
+Normal target and language availability rules apply to the result element type.
+
+**Builtin availability**:
+
+Use `__has_builtin`, for example
+`__has_builtin(__builtin_elementwise_convert_from_f8e4m3fn_f16)`, to test
+whether Clang supports that exact builtin spelling.
+This does not establish destination type availability, native instruction
+availability, or backend-lowering availability.
+Code using `_Float16` or `__bf16` must check type availability separately.
+
### `__builtin_bitreverse`
- `__builtin_bitreverse8`
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 77a1e73bc8af6..3d5178af7e0af 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -256,6 +256,9 @@ features cannot lower the translation-unit ABI level;
- Clang now allows GNU computed `goto` extension in `constexpr` functions, matching the relaxed
`constexpr` function body rules introduced in C++23.
+- Added the `__builtin_elementwise_convert_from_*` family, which converts
+ integer-encoded `Float8E5M2`, `Float8E4M3FN`, and `Float8E5M3FNU` values,
+ scalar or in a fixed-length vector, to `_Float16`, `__bf16`, or `float`.
- Added support for the `__builtin_strlcat` and `__builtin_strlcpy` builtins.
diff --git a/clang/include/clang/Basic/ArbitraryFPFormats.def b/clang/include/clang/Basic/ArbitraryFPFormats.def
new file mode 100644
index 0000000000000..a2696154d840e
--- /dev/null
+++ b/clang/include/clang/Basic/ArbitraryFPFormats.def
@@ -0,0 +1,30 @@
+//===--- ArbitraryFPFormats.def - Arbitrary FP format database --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Source encodings exposed by the __builtin_elementwise_convert_from_* family.
+// Src is the source half of the builtin name suffix; LLVMName is the
+// llvm.convert.from.arbitrary.fp interpretation string.
+//
+// Each entry needs a matching instantiation in Builtins.td: expansions name
+// builtin IDs directly, so a stale entry fails to compile. An entry must also
+// be accepted by APFloat::getArbitraryFPSemantics, or lowering fails.
+//
+// The sub-byte encodings (Float6E3M2FN, Float6E2M3FN, Float4E2M1FN) lower but
+// are not exposed; Clang cannot spell their vector element types coherently.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARBITRARY_FP_FORMAT
+#define ARBITRARY_FP_FORMAT(Src, LLVMName)
+#endif
+
+ARBITRARY_FP_FORMAT(f8e5m2, "Float8E5M2")
+ARBITRARY_FP_FORMAT(f8e4m3fn, "Float8E4M3FN")
+ARBITRARY_FP_FORMAT(f8e5m3fnu, "Float8E5M3FNU")
+
+#undef ARBITRARY_FP_FORMAT
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index ad9a58bb2ad2d..69b69cb28682e 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1481,6 +1481,24 @@ def ConvertVector : Builtin {
let Prototype = "void(...)";
}
+// Keep the instantiations below in sync with ArbitraryFPFormats.def.
+multiclass ElementwiseConvertFromArbitraryFP<string Src> {
+ foreach Dst = ["f16", "bf16", "f32"] in {
+ def _#Dst : Builtin {
+ let Spellings = ["__builtin_elementwise_convert_from_" # Src # "_" # Dst];
+ let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Prototype = "void(...)";
+ }
+ }
+}
+
+defm ElementwiseConvertFromF8E5M2
+ : ElementwiseConvertFromArbitraryFP<"f8e5m2">;
+defm ElementwiseConvertFromF8E4M3FN
+ : ElementwiseConvertFromArbitraryFP<"f8e4m3fn">;
+defm ElementwiseConvertFromF8E5M3FNU
+ : ElementwiseConvertFromArbitraryFP<"f8e5m3fnu">;
+
def MaskedLoad : Builtin {
let Spellings = ["__builtin_masked_load"];
let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 700d8f5684ef9..b458ecb4d4d82 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11485,6 +11485,16 @@ def err_builtin_non_vector_type : Error<
def err_convertvector_incompatible_vector : Error<
"first two arguments to __builtin_convertvector must have the same number of elements">;
+def err_arbitrary_fp_source_type : Error<
+ "argument of type %0 cannot hold an '%1' encoding; expected an integer of "
+ "exactly %2 bits, a vector of such integers, or __mfp8">;
+def err_arbitrary_fp_source_width : Error<
+ "%select{argument type|vector element type}0 %1 must be exactly %2 bits "
+ "wide to hold an '%3' encoding">;
+def err_arbitrary_fp_unsupported_vector : Error<
+ "argument of type %0 has an unsupported vector kind; only GNU "
+ "'vector_size' and Clang 'ext_vector_type' vectors are supported">;
+
def err_first_argument_to_cwsc_not_call : Error<
"first argument to __builtin_call_with_static_chain must be a non-member call expression">;
def err_first_argument_to_cwsc_block_call : Error<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2c4986d56eede..67079fc7d6fb5 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1182,6 +1182,12 @@ class Sema final : public SemaBase {
void checkTypeSupport(QualType Ty, SourceLocation Loc,
ValueDecl *D = nullptr);
+ /// Check if the floating-point type is supported in the current language
+ /// mode and target. If \p DiagnoseTarget is true, diagnose offload code
+ /// according to whether its enclosing function is emitted.
+ bool checkFloatingPointTypeSupport(QualType Ty, SourceLocation Loc,
+ bool DiagnoseTarget = false);
+
/// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit
/// cast. If there is already an implicit cast, merge into the existing one.
/// If isLvalue, the result of the cast is an lvalue.
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 233609605fb21..7cf18aaa8c80e 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4345,6 +4345,37 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Result);
}
+#define ARBITRARY_FP_FORMAT(Src, LLVMName) \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_bf16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f32:
+#include "clang/Basic/ArbitraryFPFormats.def"
+ {
+ StringRef FormatName;
+ switch (BuiltinID) {
+#define ARBITRARY_FP_FORMAT(Src, LLVMName) \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_bf16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f32: \
+ FormatName = LLVMName; \
+ break;
+#include "clang/Basic/ArbitraryFPFormats.def"
+ default:
+ llvm_unreachable("builtin is missing from ArbitraryFPFormats.def");
+ }
+
+ Value *Src = EmitScalarExpr(E->getArg(0));
+ // __mfp8 lowers to <1 x i8>; the intrinsic wants a plain i8.
+ if (E->getArg(0)->getType()->isMFloat8Type())
+ Src = Builder.CreateBitCast(Src, Builder.getInt8Ty());
+ llvm::Type *DstTy = ConvertType(E->getType());
+ llvm::Function *F = CGM.getIntrinsic(
+ llvm::Intrinsic::convert_from_arbitrary_fp, {DstTy, Src->getType()});
+ llvm::Value *Format = llvm::MetadataAsValue::get(
+ getLLVMContext(), llvm::MDString::get(getLLVMContext(), FormatName));
+ return RValue::get(Builder.CreateCall(F, {Src, Format}));
+ }
+
case Builtin::BI__builtin_elementwise_abs: {
Value *Result;
QualType QT = E->getArg(0)->getType();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0531dfa877fbd..a64576b2e7287 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3150,6 +3150,102 @@ static QualType getVectorElementType(ASTContext &Context, QualType VecTy) {
return QualType();
}
+/// Decode a __builtin_elementwise_convert_from_<Src>_<Dst> builtin ID into its
+/// llvm.convert.from.arbitrary.fp interpretation, the source suffix as spelled,
+/// and the destination element type.
+static bool getArbitraryFPConversion(ASTContext &Ctx, unsigned BuiltinID,
+ StringRef &Interpretation,
+ StringRef &SrcSuffix, QualType &DstEltTy) {
+ switch (BuiltinID) {
+#define ARBITRARY_FP_FORMAT(Src, LLVMName) \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f16: \
+ Interpretation = LLVMName; \
+ SrcSuffix = #Src; \
+ DstEltTy = Ctx.Float16Ty; \
+ return true; \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_bf16: \
+ Interpretation = LLVMName; \
+ SrcSuffix = #Src; \
+ DstEltTy = Ctx.BFloat16Ty; \
+ return true; \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f32: \
+ Interpretation = LLVMName; \
+ SrcSuffix = #Src; \
+ DstEltTy = Ctx.FloatTy; \
+ return true;
+#include "clang/Basic/ArbitraryFPFormats.def"
+ default:
+ return false;
+ }
+}
+
+static bool BuiltinElementwiseConvertFromArbitraryFP(Sema &S, CallExpr *TheCall,
+ unsigned BuiltinID) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+
+ StringRef Interpretation, SrcSuffix;
+ QualType DstEltTy;
+ if (!getArbitraryFPConversion(S.Context, BuiltinID, Interpretation, SrcSuffix,
+ DstEltTy))
+ llvm_unreachable("builtin is missing from ArbitraryFPFormats.def");
+
+ if (S.checkFloatingPointTypeSupport(DstEltTy, TheCall->getBeginLoc(),
+ /*DiagnoseTarget=*/true))
+ return true;
+
+ ExprResult ConvertedSrc = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (ConvertedSrc.isInvalid())
+ return true;
+ TheCall->setArg(0, ConvertedSrc.get());
+
+ Expr *Src = ConvertedSrc.get();
+ QualType SrcTy = Src->getType();
+ if (SrcTy->isDependentType()) {
+ TheCall->setType(S.Context.DependentTy);
+ return false;
+ }
+
+ unsigned FormatBits =
+ llvm::APFloatBase::getArbitraryFPFormatSizeInBits(Interpretation);
+
+ // __mfp8 is an opaque 8-bit container, so this builtin supplies the missing
+ // interpretation. Its Neon vector types stay rejected below.
+ if (SrcTy->isMFloat8Type() && FormatBits == 8) {
+ TheCall->setType(DstEltTy);
+ return false;
+ }
+
+ const auto *SrcVecTy = SrcTy->getAs<VectorType>();
+ QualType SrcEltTy = SrcVecTy ? SrcVecTy->getElementType() : SrcTy;
+
+ if (SrcTy->isSizelessVectorType() ||
+ (SrcVecTy && !SrcTy->isExtVectorType() &&
+ SrcVecTy->getVectorKind() != VectorKind::Generic))
+ return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_unsupported_vector)
+ << SrcTy << Src->getSourceRange();
+
+ if (!SrcEltTy->isIntegerType() || SrcEltTy->isBooleanType() ||
+ SrcEltTy->isEnumeralType())
+ return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_source_type)
+ << SrcTy << SrcSuffix << FormatBits << Src->getSourceRange();
+
+ if (S.Context.getIntWidth(SrcEltTy) != FormatBits)
+ return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_source_width)
+ << (SrcVecTy != nullptr) << SrcEltTy << FormatBits << SrcSuffix
+ << Src->getSourceRange();
+
+ QualType DstTy = DstEltTy;
+ if (SrcVecTy)
+ DstTy =
+ SrcTy->isExtVectorType()
+ ? S.Context.getExtVectorType(DstEltTy, SrcVecTy->getNumElements())
+ : S.Context.getVectorType(DstEltTy, SrcVecTy->getNumElements(),
+ VectorKind::Generic);
+ TheCall->setType(DstTy);
+ return false;
+}
+
ExprResult
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CallExpr *TheCall) {
@@ -3867,6 +3963,15 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return ExprError();
break;
+#define ARBITRARY_FP_FORMAT(Src, LLVMName) \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_bf16: \
+ case Builtin::BI__builtin_elementwise_convert_from_##Src##_f32:
+#include "clang/Basic/ArbitraryFPFormats.def"
+ if (BuiltinElementwiseConvertFromArbitraryFP(*this, TheCall, BuiltinID))
+ return ExprError();
+ break;
+
// These builtins restrict the element type to floating point
// types only.
case Builtin::BI__builtin_elementwise_acos:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 1cb96b556971a..03713d90d6799 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -897,6 +897,52 @@ TSTToUnaryTransformType(DeclSpec::TST SwitchTST) {
}
}
+bool Sema::checkFloatingPointTypeSupport(QualType Ty, SourceLocation Loc,
+ bool DiagnoseTarget) {
+ if (Ty->isFloat16Type()) {
+ if (!Context.getTargetInfo().hasFloat16Type()) {
+ if (!getLangOpts().CUDA &&
+ !(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice)) {
+ Diag(Loc, diag::err_type_unsupported) << "_Float16";
+ return true;
+ }
+ if (DiagnoseTarget)
+ return targetDiag(Loc, diag::err_type_unsupported) << "_Float16";
+ }
+ return false;
+ }
+
+ if (Ty->isBFloat16Type()) {
+ if (!Context.getTargetInfo().hasBFloat16Type()) {
+ if (!(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice) &&
+ !getLangOpts().SYCLIsDevice) {
+ Diag(Loc, diag::err_type_unsupported) << "__bf16";
+ return true;
+ }
+ if (DiagnoseTarget)
+ return targetDiag(Loc, diag::err_type_unsupported) << "__bf16";
+ }
+ return false;
+ }
+
+ if (!getLangOpts().OpenCL ||
+ (Ty != Context.DoubleTy && Ty != Context.LongDoubleTy))
+ return false;
+
+ if (!getOpenCLOptions().isSupported("cl_khr_fp64", getLangOpts())) {
+ Diag(Loc, diag::err_opencl_requires_extension)
+ << 0 << Ty
+ << (getLangOpts().getOpenCLCompatibleVersion() >= 300
+ ? "cl_khr_fp64 and __opencl_c_fp64"
+ : "cl_khr_fp64");
+ return true;
+ }
+
+ if (!getOpenCLOptions().isAvailableOption("cl_khr_fp64", getLangOpts()))
+ Diag(Loc, diag::ext_opencl_double_without_pragma);
+ return false;
+}
+
/// Convert the specified declspec to the appropriate type
/// object.
/// \param state Specifies the declarator containing the declaration specifier
@@ -1161,18 +1207,13 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
// CUDA host and device may have different _Float16 support, therefore
// do not diagnose _Float16 usage to avoid false alarm.
// ToDo: more precise diagnostics for CUDA.
- if (!S.Context.getTargetInfo().hasFloat16Type() && !S.getLangOpts().CUDA &&
- !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsTargetDevice))
- S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
- << "_Float16";
+ S.checkFloatingPointTypeSupport(Context.Float16Ty, DS.getTypeSpecTypeLoc());
Result = Context.Float16Ty;
break;
case DeclSpec::TST_half: Result = Context.HalfTy; break;
case DeclSpec::TST_BFloat16:
- if (!S.Context.getTargetInfo().hasBFloat16Type() &&
- !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsTargetDevice) &&
- !S.getLangOpts().SYCLIsDevice)
- S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported) << "__bf16";
+ S.checkFloatingPointTypeSupport(Context.BFloat16Ty,
+ DS.getTypeSpecTypeLoc());
Result = Context.BFloat16Ty;
break;
case DeclSpec::TST_float: Result = Context.FloatTy; break;
@@ -1181,16 +1222,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Context.LongDoubleTy;
else
Result = Context.DoubleTy;
- if (S.getLangOpts().OpenCL) {
- if (!S.getOpenCLOptions().isSupported("cl_khr_fp64", S.getLangOpts()))
- S.Diag(DS.getTypeSpecTypeLoc(), diag::err_opencl_requires_extension)
- << 0 << Result
- << (S.getLangOpts().getOpenCLCompatibleVersion() >= 300
- ? "cl_khr_fp64 and __opencl_c_fp64"
- : "cl_khr_fp64");
- else if (!S.getOpenCLOptions().isAvailableOption("cl_khr_fp64", S.getLangOpts()))
- S.Diag(DS.getTypeSpecTypeLoc(), diag::ext_opencl_double_without_pragma);
- }
+ S.checkFloatingPointTypeSupport(Result, DS.getTypeSpecTypeLoc());
break;
case DeclSpec::TST_float128:
if (!S.Context.getTargetInfo().hasFloat128Type() &&
diff --git a/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c b/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
new file mode 100644
index 0000000000000..01289993d5d6e
--- /dev/null
+++ b/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
@@ -0,0 +1,172 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+typedef unsigned char v4u8 __attribute__((ext_vector_type(4)));
+typedef float v4f32 __attribute__((ext_vector_type(4)));
+typedef _Float16 v4f16 __attribute__((ext_vector_type(4)));
+typedef unsigned char g4u8 __attribute__((vector_size(4)));
+typedef float g4f32 __attribute__((vector_size(16)));
+
+// CHECK-LABEL: define dso_local float @from_f8e5m2_to_f32(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float from_f8e5m2_to_f32(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(b);
+}
+
+// CHECK-LABEL: define dso_local half @from_f8e5m2_to_f16(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret half [[TMP1]]
+//
+_Float16 from_f8e5m2_to_f16(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f16(b);
+}
+
+// CHECK-LABEL: define dso_local bfloat @from_f8e5m2_to_bf16(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call bfloat @llvm.convert.from.arbitrary.fp.bf16.i8(i8 [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret bfloat [[TMP1]]
+//
+__bf16 from_f8e5m2_to_bf16(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e5m2_bf16(b);
+}
+
+// CHECK-LABEL: define dso_local float @from_f8e4m3fn_to_f32(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[TMP0]], metadata !"Float8E4M3FN")
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float from_f8e4m3fn_to_f32(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e4m3fn_f32(b);
+}
+
+// CHECK-LABEL: define dso_local half @from_f8e4m3fn_to_f16(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[TMP0]], metadata !"Float8E4M3FN")
+// CHECK-NEXT: ret half [[TMP1]]
+//
+_Float16 from_f8e4m3fn_to_f16(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e4m3fn_f16(b);
+}
+
+// CHECK-LABEL: define dso_local bfloat @from_f8e4m3fn_to_bf16(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call bfloat @llvm.convert.from.arbitrary.fp.bf16.i8(i8 [[TMP0]], metadata !"Float8E4M3FN")
+// CHECK-NEXT: ret bfloat [[TMP1]]
+//
+__bf16 from_f8e4m3fn_to_bf16(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e4m3fn_bf16(b);
+}
+
+// CHECK-LABEL: define dso_local float @from_f8e5m3fnu_to_f32(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[TMP0]], metadata !"Float8E5M3FNU")
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float from_f8e5m3fnu_to_f32(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e5m3fnu_f32(b);
+}
+
+// CHECK-LABEL: define dso_local half @from_f8e5m3fnu_to_f16(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[TMP0]], metadata !"Float8E5M3FNU")
+// CHECK-NEXT: ret half [[TMP1]]
+//
+_Float16 from_f8e5m3fnu_to_f16(unsigned char b) {
+ return __builtin_elementwise_convert_from_f8e5m3fnu_f16(b);
+}
+
+// CHECK-LABEL: define dso_local float @from_signed_f8e5m2(
+// CHECK-SAME: i8 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float from_signed_f8e5m2(signed char b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(b);
+}
+
+// CHECK-LABEL: define dso_local float @from_atomic_f8e5m2(
+// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8
+// CHECK-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic i8, ptr [[TMP0]] seq_cst, align 1
+// CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[ATOMIC_LOAD]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret float [[TMP1]]
+//
+float from_atomic_f8e5m2(_Atomic(unsigned char) *b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(*b);
+}
+
+// CHECK-LABEL: define dso_local <4 x float> @from_v4f8e5m2(
+// CHECK-SAME: i32 noundef [[B_COERCE:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B:%.*]] = alloca <4 x i8>, align 4
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <4 x i8>, align 4
+// CHECK-NEXT: store i32 [[B_COERCE]], ptr [[B]], align 4
+// CHECK-NEXT: [[B1:%.*]] = load <4 x i8>, ptr [[B]], align 4
+// CHECK-NEXT: store <4 x i8> [[B1]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load <4 x i8>, ptr [[B_ADDR]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = call <4 x float> @llvm.convert.from.arbitrary.fp.v4f32.v4i8(<4 x i8> [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret <4 x float> [[TMP1]]
+//
+v4f32 from_v4f8e5m2(v4u8 b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(b);
+}
+
+// CHECK-LABEL: define dso_local <4 x float> @from_g4f8e5m2(
+// CHECK-SAME: i32 noundef [[B_COERCE:%.*]]) #[[ATTR2]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B:%.*]] = alloca <4 x i8>, align 4
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <4 x i8>, align 4
+// CHECK-NEXT: store i32 [[B_COERCE]], ptr [[B]], align 4
+// CHECK-NEXT: [[B1:%.*]] = load <4 x i8>, ptr [[B]], align 4
+// CHECK-NEXT: store <4 x i8> [[B1]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load <4 x i8>, ptr [[B_ADDR]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = call <4 x float> @llvm.convert.from.arbitrary.fp.v4f32.v4i8(<4 x i8> [[TMP0]], metadata !"Float8E5M2")
+// CHECK-NEXT: ret <4 x float> [[TMP1]]
+//
+g4f32 from_g4f8e5m2(g4u8 b) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(b);
+}
+
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
new file mode 100644
index 0000000000000..ee2a92c3e385d
--- /dev/null
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +neon \
+// RUN: -fsyntax-only -verify %s
+
+typedef unsigned char uint8x8_t __attribute__((neon_vector_type(8)));
+typedef __mfp8 mfloat8x8_t __attribute__((neon_vector_type(8)));
+
+void test_neon_vector(uint8x8_t src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+}
+
+// __mfp8 is an opaque 8-bit container, so it is accepted for the 8-bit
+// encodings.
+void test_mfp8_scalar(__mfp8 src) {
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_f32(src)), float), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e4m3fn_f16(src)), _Float16),
+ "");
+}
+
+void test_mfp8_vector(mfloat8x8_t src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+}
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
new file mode 100644
index 0000000000000..7124e55e075af
--- /dev/null
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -triple riscv64 -target-feature +v -fsyntax-only -verify %s
+
+__rvv_float32m1_t convert(__rvv_uint8m1_t src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+}
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-target.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-target.c
new file mode 100644
index 0000000000000..4cc56654cc208
--- /dev/null
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-target.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -DTEST_BF16 \
+// RUN: -emit-llvm -o /dev/null -verify=bf16 %s
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -DTEST_F16 \
+// RUN: -emit-llvm -o /dev/null -verify=f16 %s
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu -fopenmp \
+// RUN: -fopenmp-is-target-device -DTEST_OMP_BF16 \
+// RUN: -emit-llvm -o /dev/null -verify=omp-bf16 %s
+
+#if !__has_builtin(__builtin_elementwise_convert_from_f8e5m2_f16) || \
+ !__has_builtin(__builtin_elementwise_convert_from_f8e5m2_bf16)
+#error "target-independent builtin spellings must remain available"
+#endif
+
+#if defined(TEST_BF16)
+void test_bf16(unsigned char src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_bf16(src); // bf16-error {{__bf16 is not supported on this target}}
+}
+#endif
+
+#if defined(TEST_F16)
+void test_f16(unsigned char src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f16(src); // f16-error {{_Float16 is not supported on this target}}
+}
+#endif
+
+#if defined(TEST_OMP_BF16)
+void test_omp_bf16_not_emitted(unsigned char src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_bf16(src);
+}
+
+#pragma omp declare target
+void test_omp_bf16(unsigned char src) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_bf16(src); // omp-bf16-error {{__bf16 is not supported on this target}}
+}
+
+void test_omp_bf16_sizeof(unsigned char src) {
+ (void)sizeof(__builtin_elementwise_convert_from_f8e5m2_bf16(src)); // omp-bf16-error {{__bf16 is not supported on this target}}
+}
+
+void test_omp_bf16_auto(unsigned char src) {
+ __auto_type value = __builtin_elementwise_convert_from_f8e5m2_bf16(src); // omp-bf16-error {{__bf16 is not supported on this target}}
+}
+#pragma omp end declare target
+#endif
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
new file mode 100644
index 0000000000000..2c60d7356ecd4
--- /dev/null
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
@@ -0,0 +1,135 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s
+
+typedef unsigned char v4u8 __attribute__((ext_vector_type(4)));
+typedef unsigned short v4u16 __attribute__((ext_vector_type(4)));
+typedef _Bool v4bool __attribute__((ext_vector_type(4)));
+typedef float v4f32 __attribute__((ext_vector_type(4)));
+typedef _Float16 v4f16 __attribute__((ext_vector_type(4)));
+typedef unsigned char g4u8 __attribute__((vector_size(4)));
+typedef float g4f32 __attribute__((vector_size(16)));
+
+enum __attribute__((packed)) byte_enum {
+ BYTE_ZERO,
+};
+
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m2_f16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m2_bf16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m2_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e5m2_f64), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e4m3fn_f16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e4m3fn_bf16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e4m3fn_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e4m3fn_f64), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m3fnu_f16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m3fnu_bf16), "");
+_Static_assert(
+ __has_builtin(__builtin_elementwise_convert_from_f8e5m3fnu_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e5m3fnu_f64), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e3m2fn_f16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e3m2fn_bf16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e3m2fn_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e3m2fn_f64), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e2m3fn_f16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e2m3fn_bf16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e2m3fn_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f6e2m3fn_f64), "");
+// FP4 is deferred: _BitInt(4) vectors have no coherent memory layout yet.
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f4e2m1fn_f16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f4e2m1fn_bf16), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f4e2m1fn_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f4e2m1fn_f64), "");
+
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e5m2fnuz_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e4m3_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e4m3fnuz_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e4m3b11fnuz_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e3m4_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e8m0fnu_f32), "");
+_Static_assert(
+ !__has_builtin(__builtin_elementwise_convert_from_f8e5m2_f80), "");
+_Static_assert(!__has_builtin(__builtin_convert_from_arbitrary_fp), "");
+
+void test_result_types(unsigned char b, v4u8 vb, g4u8 gb) {
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_f16(b)), _Float16), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_bf16(b)), __bf16), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_f32(b)), float), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_f32(vb)), v4f32), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e5m2_f32(gb)), g4f32), "");
+ _Static_assert(__builtin_types_compatible_p(
+ typeof(__builtin_elementwise_convert_from_f8e4m3fn_f16(vb)), v4f16), "");
+}
+
+void test_source_formats(unsigned char b) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b);
+ (void)__builtin_elementwise_convert_from_f8e4m3fn_f32(b);
+ (void)__builtin_elementwise_convert_from_f8e5m3fnu_f32(b);
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32((signed char)b);
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32((unsigned _BitInt(8))b);
+}
+
+// Integer promotions do not apply to the source.
+void test_no_promotion(unsigned char b) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32((unsigned char)(b >> 1));
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b >> 1); // expected-error {{argument type 'int' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+}
+
+void test_arity(unsigned char b) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(); // expected-error {{too few arguments}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b, b); // expected-error {{too many arguments}}
+}
+
+void test_width(unsigned short b16, unsigned _BitInt(4) b4, v4u16 vb16) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b16); // expected-error {{argument type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b4); // expected-error {{argument type 'unsigned _BitInt(4)' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb16); // expected-error {{vector element type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+}
+
+void test_operand_types(float f, void *p) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(f); // expected-error {{argument of type 'float' cannot hold an 'f8e5m2' encoding; expected an integer of exactly 8 bits, a vector of such integers, or __mfp8}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(p); // expected-error {{argument of type 'void *' cannot hold an 'f8e5m2' encoding; expected an integer of exactly 8 bits, a vector of such integers, or __mfp8}}
+}
+
+void test_disallowed_integer_types(_Bool b, enum byte_enum e, v4bool vb) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b); // expected-error {{argument of type '_Bool' cannot hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(e); // expected-error {{argument of type 'enum byte_enum' cannot hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb); // expected-error {{cannot hold an 'f8e5m2' encoding}}
+}
+
+void test_volatile_source(volatile unsigned char *b) {
+ __builtin_assume(
+ __builtin_elementwise_convert_from_f8e5m2_f32(*b)); // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
+}
diff --git a/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp b/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
new file mode 100644
index 0000000000000..8e16ad41cc850
--- /dev/null
+++ b/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 \
+// RUN: -fsyntax-only -verify %s
+
+template <typename Src> float convert(Src src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{argument type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+}
+
+float instantiate_valid(unsigned char src) { return convert(src); }
+
+// expected-note at +1 {{in instantiation of function template specialization 'convert<unsigned short>' requested here}}
+float instantiate_invalid(unsigned short src) { return convert(src); }
+
+template <typename Src>
+auto deduced_result(Src src)
+ -> decltype(__builtin_elementwise_convert_from_f8e5m2_f32(src)) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src);
+}
+
+static_assert(
+ __is_same(decltype(deduced_result((unsigned char)0)), float), "");
+
+using v4u8 = unsigned char __attribute__((ext_vector_type(4)));
+using v4f32 = float __attribute__((ext_vector_type(4)));
+static_assert(__is_same(decltype(deduced_result(v4u8{})), v4f32), "");
+
+void noexcept_check(unsigned char src) {
+ static_assert(
+ noexcept(__builtin_elementwise_convert_from_f8e5m2_f32(src)), "");
+}
+
+static_assert(
+ !__has_constexpr_builtin(
+ __builtin_elementwise_convert_from_f8e5m2_f32), "");
+
+constexpr float constant_evaluation_is_deferred =
+ __builtin_elementwise_convert_from_f8e5m2_f32(
+ (unsigned char)0); // expected-error at -1 {{constexpr variable 'constant_evaluation_is_deferred' must be initialized by a constant expression}}
diff --git a/clang/test/SemaOpenCL/builtins-elementwise-convert-from-arbitrary-fp.cl b/clang/test/SemaOpenCL/builtins-elementwise-convert-from-arbitrary-fp.cl
new file mode 100644
index 0000000000000..386a8d890e041
--- /dev/null
+++ b/clang/test/SemaOpenCL/builtins-elementwise-convert-from-arbitrary-fp.cl
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple spir-unknown-unknown -x cl \
+// RUN: -finclude-default-header -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+#if !__has_builtin(__builtin_elementwise_convert_from_f8e5m2_f32)
+#error "missing elementwise arbitrary FP conversion builtin"
+#endif
+
+#if __has_builtin(__builtin_elementwise_convert_from_f6e3m2fn_f32)
+#error "deferred arbitrary FP conversion builtin is unexpectedly available"
+#endif
+
+float convert_scalar(uchar src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src);
+}
+
+_Float16 convert_f16(uchar src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f16(src);
+}
+
+__bf16 convert_bf16(uchar src) {
+ return __builtin_elementwise_convert_from_f8e5m2_bf16(src);
+}
+
+float4 convert_vector(uchar4 src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src);
+}
+
+// f16 denotes _Float16 even in OpenCL; 'half' comes from the normal
+// conversion rules.
+#pragma OPENCL EXTENSION cl_khr_fp16 : enable
+half convert_to_half(uchar src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f16(src);
+}
+
+half4 convert_to_half_vector(uchar4 src) {
+ return __builtin_elementwise_convert_from_f8e5m2_f16(src);
+}
>From 8847f812655a59cd33b5998e9766528b564b328e Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Fri, 9 Oct 2026 16:20:49 -0400
Subject: [PATCH 2/2] Address review feedback on FP8 conversion builtins
Reuse err_builtin_invalid_arg_type for all source operand errors
instead of builtin-specific diagnostics.
Accept integer constants that fit the format width, such as 0x38,
and std::byte, so common byte values need no explicit cast.
Drop the unused OpenCL fp64 path from checkFloatingPointTypeSupport.
Document floating-point environment and fast-math behavior; trim
implementation detail from the user docs.
Change-Id: I8676a49540bdbbb8a90827c83764803eeea850a5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply at anthropic.com>
---
clang/docs/LanguageExtensions.md | 15 ++--
.../clang/Basic/ArbitraryFPFormats.def | 3 +-
.../clang/Basic/DiagnosticSemaKinds.td | 12 +---
clang/lib/Sema/SemaChecking.cpp | 70 ++++++++++++-------
clang/lib/Sema/SemaType.cpp | 27 +++----
...ns-elementwise-convert-from-arbitrary-fp.c | 9 +++
...ementwise-convert-from-arbitrary-fp-neon.c | 4 +-
...lementwise-convert-from-arbitrary-fp-rvv.c | 2 +-
...ns-elementwise-convert-from-arbitrary-fp.c | 39 ++++++++---
...-elementwise-convert-from-arbitrary-fp.cpp | 25 ++++++-
10 files changed, 133 insertions(+), 73 deletions(-)
diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index dfff19ee3bd93..690ddcf996440 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -3716,9 +3716,6 @@ The result is a scalar for a scalar input or a vector with the same number of el
Supported vector kinds are GNU `vector_size` and Clang/OpenCL `ext_vector_type`.
The result preserves which of those two vector kinds the input uses.
Sizeless vectors and target-specific fixed-length vector kinds are rejected.
-Preserving both the lane count and a target-specific vector kind after widening
-the element type can produce an invalid type or ABI combination, such as a
-widened NEON vector.
The source format suffix determines the interpretation and required integer element width:
@@ -3737,15 +3734,13 @@ The destination suffix determines the result element type:
| `f32` | `float` |
The `f16` suffix denotes `_Float16` in every language mode, including OpenCL.
-The three source suffixes and three destination suffixes form exactly nine
-builtin spellings.
`Float8E5M3FNU` has no sign bit and no infinity encoding, and its exponent
range exceeds that of `_Float16`. Its seven largest finite encodings therefore
convert to infinity rather than exactly when the destination is `f16`; the
`bf16` and `f32` destinations are exact.
-Only the signedness-free width of `bits` matters, so for an 8-bit format any 8-bit `char`, `signed char`, `unsigned char`, or `_BitInt(8)` of either signedness may be used.
+Only the signedness-free width of `bits` matters, so for an 8-bit format any 8-bit `char`, `signed char`, `unsigned char`, `_BitInt(8)` of either signedness, or `std::byte` may be used.
On targets that have it, `__mfp8` is also accepted as a scalar source, because
it is an opaque 8-bit floating-point container with no interpretation of its
own. Its Neon vector types are rejected with the other target-specific vector
@@ -3753,11 +3748,13 @@ kinds.
Integer promotions and the usual arithmetic conversions are not applied to
`bits`, so an expression that C promotes to `int` needs an explicit cast back
-to an 8-bit container:
+to an 8-bit container. Integer constant expressions whose value fits in the
+format width, such as `0x38`, are accepted directly:
```c
unsigned char b;
__builtin_elementwise_convert_from_f8e5m2_f32((unsigned char)(b >> 1));
+__builtin_elementwise_convert_from_f8e5m2_f32(0x38);
```
These builtins are available in C, C++, and OpenCL, but are not supported in constant expressions.
@@ -3765,6 +3762,10 @@ These builtins are available in C, C++, and OpenCL, but are not supported in con
Each builtin maps to the `llvm.convert.from.arbitrary.fp` intrinsic; see its description in the LLVM Language Reference for the exact conversion semantics.
NaN results follow LLVM's general NaN rules; the sign, quiet or signaling state,
and payload are not guaranteed to be preserved.
+The conversion is exact or overflows to infinity, so the rounding mode has no
+effect on the result.
+As with other floating-point operations, `-ffinite-math-only` (implied by
+`-ffast-math`) makes the result undefined if it is a NaN or an infinity.
Current generic code generation for the intrinsic is SelectionDAG-only.
Other code-generation paths are future work.
diff --git a/clang/include/clang/Basic/ArbitraryFPFormats.def b/clang/include/clang/Basic/ArbitraryFPFormats.def
index a2696154d840e..977bd6a3155ae 100644
--- a/clang/include/clang/Basic/ArbitraryFPFormats.def
+++ b/clang/include/clang/Basic/ArbitraryFPFormats.def
@@ -15,7 +15,8 @@
// be accepted by APFloat::getArbitraryFPSemantics, or lowering fails.
//
// The sub-byte encodings (Float6E3M2FN, Float6E2M3FN, Float4E2M1FN) lower but
-// are not exposed; Clang cannot spell their vector element types coherently.
+// are not exposed: _BitInt(6) vectors are rejected, and a _BitInt(4) vector's
+// sizeof disagrees with its bit-packed IR type.
//
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b458ecb4d4d82..3d573d9b58b9d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11485,16 +11485,6 @@ def err_builtin_non_vector_type : Error<
def err_convertvector_incompatible_vector : Error<
"first two arguments to __builtin_convertvector must have the same number of elements">;
-def err_arbitrary_fp_source_type : Error<
- "argument of type %0 cannot hold an '%1' encoding; expected an integer of "
- "exactly %2 bits, a vector of such integers, or __mfp8">;
-def err_arbitrary_fp_source_width : Error<
- "%select{argument type|vector element type}0 %1 must be exactly %2 bits "
- "wide to hold an '%3' encoding">;
-def err_arbitrary_fp_unsupported_vector : Error<
- "argument of type %0 has an unsupported vector kind; only GNU "
- "'vector_size' and Clang 'ext_vector_type' vectors are supported">;
-
def err_first_argument_to_cwsc_not_call : Error<
"first argument to __builtin_call_with_static_chain must be a non-member call expression">;
def err_first_argument_to_cwsc_block_call : Error<
@@ -13449,7 +13439,7 @@ def err_builtin_invalid_arg_type: Error<
"%plural{0:|: }1"
// Second component: integer-like types
"%select{|integer|signed integer|unsigned integer|'int'|"
- "pointer to a valid matrix element|boolean}2"
+ "pointer to a valid matrix element|boolean|8-bit integer}2"
// A space after a non-empty second component
"%plural{0:|: }2"
// An 'or' if non-empty second and third components are combined
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a64576b2e7287..cbfdf30631e27 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3151,26 +3151,22 @@ static QualType getVectorElementType(ASTContext &Context, QualType VecTy) {
}
/// Decode a __builtin_elementwise_convert_from_<Src>_<Dst> builtin ID into its
-/// llvm.convert.from.arbitrary.fp interpretation, the source suffix as spelled,
-/// and the destination element type.
+/// llvm.convert.from.arbitrary.fp interpretation and destination element type.
static bool getArbitraryFPConversion(ASTContext &Ctx, unsigned BuiltinID,
StringRef &Interpretation,
- StringRef &SrcSuffix, QualType &DstEltTy) {
+ QualType &DstEltTy) {
switch (BuiltinID) {
#define ARBITRARY_FP_FORMAT(Src, LLVMName) \
case Builtin::BI__builtin_elementwise_convert_from_##Src##_f16: \
Interpretation = LLVMName; \
- SrcSuffix = #Src; \
DstEltTy = Ctx.Float16Ty; \
return true; \
case Builtin::BI__builtin_elementwise_convert_from_##Src##_bf16: \
Interpretation = LLVMName; \
- SrcSuffix = #Src; \
DstEltTy = Ctx.BFloat16Ty; \
return true; \
case Builtin::BI__builtin_elementwise_convert_from_##Src##_f32: \
Interpretation = LLVMName; \
- SrcSuffix = #Src; \
DstEltTy = Ctx.FloatTy; \
return true;
#include "clang/Basic/ArbitraryFPFormats.def"
@@ -3179,15 +3175,36 @@ static bool getArbitraryFPConversion(ASTContext &Ctx, unsigned BuiltinID,
}
}
+/// Narrow an integer constant such as 0x38 to \p FormatBits if its value fits.
+/// Returns true if it does not.
+static bool narrowFPBitsConstant(Sema &S, CallExpr *TheCall,
+ unsigned FormatBits) {
+ Expr *Src = TheCall->getArg(0);
+
+ // Recheck on instantiation.
+ if (Src->isValueDependent()) {
+ return false;
+ }
+
+ std::optional<llvm::APSInt> Val = Src->getIntegerConstantExpr(S.Context);
+ if (!Val || Val->isNegative() || Val->getActiveBits() > FormatBits) {
+ return true;
+ }
+
+ QualType FitTy =
+ S.Context.getIntTypeForBitwidth(FormatBits, /*Signed=*/false);
+ TheCall->setArg(0, S.ImpCastExprToType(Src, FitTy, CK_IntegralCast).get());
+ return false;
+}
+
static bool BuiltinElementwiseConvertFromArbitraryFP(Sema &S, CallExpr *TheCall,
unsigned BuiltinID) {
if (S.checkArgCount(TheCall, 1))
return true;
- StringRef Interpretation, SrcSuffix;
+ StringRef Interpretation;
QualType DstEltTy;
- if (!getArbitraryFPConversion(S.Context, BuiltinID, Interpretation, SrcSuffix,
- DstEltTy))
+ if (!getArbitraryFPConversion(S.Context, BuiltinID, Interpretation, DstEltTy))
llvm_unreachable("builtin is missing from ArbitraryFPFormats.def");
if (S.checkFloatingPointTypeSupport(DstEltTy, TheCall->getBeginLoc(),
@@ -3219,21 +3236,26 @@ static bool BuiltinElementwiseConvertFromArbitraryFP(Sema &S, CallExpr *TheCall,
const auto *SrcVecTy = SrcTy->getAs<VectorType>();
QualType SrcEltTy = SrcVecTy ? SrcVecTy->getElementType() : SrcTy;
- if (SrcTy->isSizelessVectorType() ||
- (SrcVecTy && !SrcTy->isExtVectorType() &&
- SrcVecTy->getVectorKind() != VectorKind::Generic))
- return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_unsupported_vector)
- << SrcTy << Src->getSourceRange();
-
- if (!SrcEltTy->isIntegerType() || SrcEltTy->isBooleanType() ||
- SrcEltTy->isEnumeralType())
- return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_source_type)
- << SrcTy << SrcSuffix << FormatBits << Src->getSourceRange();
-
- if (S.Context.getIntWidth(SrcEltTy) != FormatBits)
- return S.Diag(Src->getBeginLoc(), diag::err_arbitrary_fp_source_width)
- << (SrcVecTy != nullptr) << SrcEltTy << FormatBits << SrcSuffix
- << Src->getSourceRange();
+ auto DiagInvalidSrc = [&] {
+ return S.Diag(Src->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << /*ordinal=*/1 << /*scalar or vector*/ 5 << /*8-bit integer*/ 7
+ << /*no fp*/ 0 << SrcTy << Src->getSourceRange();
+ };
+
+ bool IsVendorVector = SrcVecTy && !SrcTy->isExtVectorType() &&
+ SrcVecTy->getVectorKind() != VectorKind::Generic;
+ // std::byte is the C++ byte container; other enums are rejected.
+ bool IsIntElt = SrcEltTy->isStdByteType() ||
+ (SrcEltTy->isIntegerType() && !SrcEltTy->isBooleanType() &&
+ !SrcEltTy->isEnumeralType());
+ if (SrcTy->isSizelessVectorType() || IsVendorVector || !IsIntElt) {
+ return DiagInvalidSrc();
+ }
+
+ if (S.Context.getIntWidth(SrcEltTy) != FormatBits &&
+ (SrcVecTy || narrowFPBitsConstant(S, TheCall, FormatBits))) {
+ return DiagInvalidSrc();
+ }
QualType DstTy = DstEltTy;
if (SrcVecTy)
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 03713d90d6799..f3a05c5aced17 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -922,24 +922,8 @@ bool Sema::checkFloatingPointTypeSupport(QualType Ty, SourceLocation Loc,
if (DiagnoseTarget)
return targetDiag(Loc, diag::err_type_unsupported) << "__bf16";
}
- return false;
}
- if (!getLangOpts().OpenCL ||
- (Ty != Context.DoubleTy && Ty != Context.LongDoubleTy))
- return false;
-
- if (!getOpenCLOptions().isSupported("cl_khr_fp64", getLangOpts())) {
- Diag(Loc, diag::err_opencl_requires_extension)
- << 0 << Ty
- << (getLangOpts().getOpenCLCompatibleVersion() >= 300
- ? "cl_khr_fp64 and __opencl_c_fp64"
- : "cl_khr_fp64");
- return true;
- }
-
- if (!getOpenCLOptions().isAvailableOption("cl_khr_fp64", getLangOpts()))
- Diag(Loc, diag::ext_opencl_double_without_pragma);
return false;
}
@@ -1222,7 +1206,16 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
Result = Context.LongDoubleTy;
else
Result = Context.DoubleTy;
- S.checkFloatingPointTypeSupport(Result, DS.getTypeSpecTypeLoc());
+ if (S.getLangOpts().OpenCL) {
+ if (!S.getOpenCLOptions().isSupported("cl_khr_fp64", S.getLangOpts()))
+ S.Diag(DS.getTypeSpecTypeLoc(), diag::err_opencl_requires_extension)
+ << 0 << Result
+ << (S.getLangOpts().getOpenCLCompatibleVersion() >= 300
+ ? "cl_khr_fp64 and __opencl_c_fp64"
+ : "cl_khr_fp64");
+ else if (!S.getOpenCLOptions().isAvailableOption("cl_khr_fp64", S.getLangOpts()))
+ S.Diag(DS.getTypeSpecTypeLoc(), diag::ext_opencl_double_without_pragma);
+ }
break;
case DeclSpec::TST_float128:
if (!S.Context.getTargetInfo().hasFloat128Type() &&
diff --git a/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c b/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
index 01289993d5d6e..3db6b3311f2a1 100644
--- a/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
+++ b/clang/test/CodeGen/builtins-elementwise-convert-from-arbitrary-fp.c
@@ -170,3 +170,12 @@ g4f32 from_g4f8e5m2(g4u8 b) {
return __builtin_elementwise_convert_from_f8e5m2_f32(b);
}
+// CHECK-LABEL: define dso_local float @from_constant_f8e5m2(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 56, metadata !"Float8E5M2")
+// CHECK-NEXT: ret float [[TMP0]]
+//
+float from_constant_f8e5m2(void) {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(0x38);
+}
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
index ee2a92c3e385d..6bf6a4b593ef8 100644
--- a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-neon.c
@@ -5,7 +5,7 @@ typedef unsigned char uint8x8_t __attribute__((neon_vector_type(8)));
typedef __mfp8 mfloat8x8_t __attribute__((neon_vector_type(8)));
void test_neon_vector(uint8x8_t src) {
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'uint8x8_t'}}
}
// __mfp8 is an opaque 8-bit container, so it is accepted for the 8-bit
@@ -19,5 +19,5 @@ void test_mfp8_scalar(__mfp8 src) {
}
void test_mfp8_vector(mfloat8x8_t src) {
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'mfloat8x8_t'}}
}
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
index 7124e55e075af..f0e8c656e59ae 100644
--- a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp-rvv.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple riscv64 -target-feature +v -fsyntax-only -verify %s
__rvv_float32m1_t convert(__rvv_uint8m1_t src) {
- return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{has an unsupported vector kind}}
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was '__rvv_uint8m1_t')}}
}
diff --git a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
index 2c60d7356ecd4..8b52b2cbbdc9c 100644
--- a/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
+++ b/clang/test/Sema/builtins-elementwise-convert-from-arbitrary-fp.c
@@ -104,7 +104,28 @@ void test_source_formats(unsigned char b) {
// Integer promotions do not apply to the source.
void test_no_promotion(unsigned char b) {
(void)__builtin_elementwise_convert_from_f8e5m2_f32((unsigned char)(b >> 1));
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(b >> 1); // expected-error {{argument type 'int' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b >> 1); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'int')}}
+}
+
+// Integer constant expressions are accepted if the value fits in 8 bits.
+void test_constant(int i) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(0x38);
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(0xFF);
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32('a');
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(0x100); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'int')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(-1); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'int')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(i); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'int')}}
+}
+
+struct bit_fields {
+ unsigned u : 8;
+ unsigned char c : 8;
+};
+
+// Bit-field width is not part of the type.
+void test_bit_fields(struct bit_fields s) {
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(s.c);
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(s.u); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'unsigned int')}}
}
void test_arity(unsigned char b) {
@@ -113,20 +134,20 @@ void test_arity(unsigned char b) {
}
void test_width(unsigned short b16, unsigned _BitInt(4) b4, v4u16 vb16) {
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(b16); // expected-error {{argument type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(b4); // expected-error {{argument type 'unsigned _BitInt(4)' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb16); // expected-error {{vector element type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b16); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'unsigned short')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b4); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'unsigned _BitInt(4)')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb16); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'v4u16' (vector of 4 'unsigned short' values))}}
}
void test_operand_types(float f, void *p) {
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(f); // expected-error {{argument of type 'float' cannot hold an 'f8e5m2' encoding; expected an integer of exactly 8 bits, a vector of such integers, or __mfp8}}
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(p); // expected-error {{argument of type 'void *' cannot hold an 'f8e5m2' encoding; expected an integer of exactly 8 bits, a vector of such integers, or __mfp8}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(f); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'float')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(p); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'void *')}}
}
void test_disallowed_integer_types(_Bool b, enum byte_enum e, v4bool vb) {
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(b); // expected-error {{argument of type '_Bool' cannot hold an 'f8e5m2' encoding}}
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(e); // expected-error {{argument of type 'enum byte_enum' cannot hold an 'f8e5m2' encoding}}
- (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb); // expected-error {{cannot hold an 'f8e5m2' encoding}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(b); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was '_Bool')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(e); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'enum byte_enum')}}
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(vb); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'v4bool'}}
}
void test_volatile_source(volatile unsigned char *b) {
diff --git a/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp b/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
index 8e16ad41cc850..4d0bff33f2644 100644
--- a/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
+++ b/clang/test/SemaCXX/builtins-elementwise-convert-from-arbitrary-fp.cpp
@@ -2,7 +2,7 @@
// RUN: -fsyntax-only -verify %s
template <typename Src> float convert(Src src) {
- return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{argument type 'unsigned short' must be exactly 8 bits wide to hold an 'f8e5m2' encoding}}
+ return __builtin_elementwise_convert_from_f8e5m2_f32(src); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'unsigned short')}}
}
float instantiate_valid(unsigned char src) { return convert(src); }
@@ -23,6 +23,29 @@ using v4u8 = unsigned char __attribute__((ext_vector_type(4)));
using v4f32 = float __attribute__((ext_vector_type(4)));
static_assert(__is_same(decltype(deduced_result(v4u8{})), v4f32), "");
+template <int N> float convert_constant() {
+ return __builtin_elementwise_convert_from_f8e5m2_f32(N); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'int')}}
+}
+
+float instantiate_constant() { return convert_constant<0x38>(); }
+
+// expected-note at +1 {{in instantiation of function template specialization 'convert_constant<256>' requested here}}
+float instantiate_constant_invalid() { return convert_constant<0x100>(); }
+
+namespace std {
+enum class byte : unsigned char {};
+} // namespace std
+
+enum class other_byte : unsigned char {};
+
+void test_byte(std::byte b, other_byte o) {
+ static_assert(
+ __is_same(decltype(__builtin_elementwise_convert_from_f8e5m2_f32(b)),
+ float),
+ "");
+ (void)__builtin_elementwise_convert_from_f8e5m2_f32(o); // expected-error {{1st argument must be a scalar or vector of 8-bit integer types (was 'other_byte')}}
+}
+
void noexcept_check(unsigned char src) {
static_assert(
noexcept(__builtin_elementwise_convert_from_f8e5m2_f32(src)), "");
More information about the cfe-commits
mailing list