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 <[email protected]> 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@+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@-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 <[email protected]> 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) <[email protected]> --- 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@+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)), ""); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
