Half-Precision Floating Point
=============================
-Clang supports two half-precision (16-bit) floating point types: ``__fp16`` and
-``_Float16``. These types are supported in all language modes.
+Clang supports three half-precision (16-bit) floating point types: ``__fp16``,
+``_Float16`` and ``__bf16``. These types are supported in all language modes.
``__fp16`` is supported on every target, as it is purely a storage format; see below.
``_Float16`` is currently only supported on the following targets, with further
``_Float16`` will be supported on more targets as they define ABIs for it.
+``__bf16`` is purely a storage format; it is currently only supported on the following targets:
+* 32-bit ARM
+* 64-bit ARM (AArch64)
+
+The ``__bf16`` type is only available when supported in hardware.
+
``__fp16`` is a storage and interchange format only. This means that values of
``__fp16`` are immediately promoted to (at least) ``float`` when used in arithmetic
operations, so that e.g. the result of adding two ``__fp16`` values has type ``float``.
CXType_UShortAccum = 36,
CXType_UAccum = 37,
CXType_ULongAccum = 38,
+ CXType_BFloat16 = 39,
CXType_FirstBuiltin = CXType_Void,
- CXType_LastBuiltin = CXType_ULongAccum,
+ CXType_LastBuiltin = CXType_BFloat16,
CXType_Complex = 100,
CXType_Pointer = 101,
CanQualType SatUnsignedShortFractTy, SatUnsignedFractTy,
SatUnsignedLongFractTy;
CanQualType HalfTy; // [OpenCL 6.1.1.1], ARM NEON
+ CanQualType BFloat16Ty;
CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
CanQualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy;
CanQualType Float128ComplexTy;
// '_Float16'
FLOATING_TYPE(Float16, HalfTy)
+// '__bf16'
+FLOATING_TYPE(BFloat16, BFloat16Ty)
+
// '__float128'
FLOATING_TYPE(Float128, Float128Ty)
bool isFloatingType() const; // C99 6.2.5p11 (real floating + complex)
bool isHalfType() const; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half)
bool isFloat16Type() const; // C11 extension ISO/IEC TS 18661
+ bool isBFloat16Type() const;
bool isFloat128Type() const;
bool isRealType() const; // C99 6.2.5p17 (real floating + integer)
bool isArithmeticType() const; // C99 6.2.5p18 (integer + floating)
return isSpecificBuiltinType(BuiltinType::Float16);
}
+inline bool Type::isBFloat16Type() const {
+ return isSpecificBuiltinType(BuiltinType::BFloat16);
+}
+
inline bool Type::isFloat128Type() const {
return isSpecificBuiltinType(BuiltinType::Float128);
}
InGroup<BadFunctionCast>, DefaultIgnore;
def err_cast_pointer_to_non_pointer_int : Error<
"pointer cannot be cast to type %0">;
+def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">;
+def err_cast_from_bfloat16 : Error<"cannot type-cast from __bf16">;
def err_typecheck_expect_scalar_operand : Error<
"operand of type %0 where arithmetic or pointer type is required">;
def err_typecheck_cond_incompatible_operands : Error<
TST_Float16, // C11 extension ISO/IEC TS 18661-3
TST_Accum, // ISO/IEC JTC1 SC22 WG14 N1169 Extension
TST_Fract,
+ TST_BFloat16,
TST_float,
TST_double,
TST_float128,
Poly128,
Float16,
Float32,
- Float64
+ Float64,
+ BFloat16
};
NeonTypeFlags(unsigned F) : Flags(F) {}
unsigned char BoolWidth, BoolAlign;
unsigned char IntWidth, IntAlign;
unsigned char HalfWidth, HalfAlign;
+ unsigned char BFloat16Width, BFloat16Align;
unsigned char FloatWidth, FloatAlign;
unsigned char DoubleWidth, DoubleAlign;
unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align;
unsigned short MaxVectorAlign;
unsigned short MaxTLSAlign;
- const llvm::fltSemantics *HalfFormat, *FloatFormat, *DoubleFormat,
- *LongDoubleFormat, *Float128Format;
+ const llvm::fltSemantics *HalfFormat, *BFloat16Format, *FloatFormat,
+ *DoubleFormat, *LongDoubleFormat, *Float128Format;
///===---- Target Data Type Query Methods -------------------------------===//
enum IntType {
// LLVM IR type.
bool HasFloat128;
bool HasFloat16;
+ bool HasBFloat16;
unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
unsigned short SimdDefaultAlign;
/// Determine whether the _Float16 type is supported on this target.
virtual bool hasFloat16Type() const { return HasFloat16; }
+ /// Determine whether the _BFloat16 type is supported on this target.
+ virtual bool hasBFloat16Type() const { return HasBFloat16; }
+
/// Return the alignment that is suitable for storing any
/// object with a fundamental alignment requirement.
unsigned getSuitableAlign() const { return SuitableAlign; }
unsigned getFloatAlign() const { return FloatAlign; }
const llvm::fltSemantics &getFloatFormat() const { return *FloatFormat; }
+ /// getBFloat16Width/Align/Format - Return the size/align/format of '__bf16'.
+ unsigned getBFloat16Width() const { return BFloat16Width; }
+ unsigned getBFloat16Align() const { return BFloat16Align; }
+ const llvm::fltSemantics &getBFloat16Format() const { return *BFloat16Format; }
+
/// getDoubleWidth/Align/Format - Return the size/align/format of 'double'.
unsigned getDoubleWidth() const { return DoubleWidth; }
unsigned getDoubleAlign() const { return DoubleAlign; }
/// Return the mangled code of __float128.
virtual const char *getFloat128Mangling() const { return "g"; }
+ /// Return the mangled code of bfloat.
+ virtual const char *getBFloat16Mangling() const {
+ llvm_unreachable("bfloat not implemented on this target");
+ }
+
/// Return the value for the C99 FLT_EVAL_METHOD macro.
virtual unsigned getFloatEvalMethod() const { return 0; }
// ARM NEON extensions.
ALIAS("__fp16", half , KEYALL)
+KEYWORD(__bf16 , KEYALL)
// OpenCL Extension.
KEYWORD(half , HALFSUPPORT)
static const TST TST_int128 = clang::TST_int128;
static const TST TST_extint = clang::TST_extint;
static const TST TST_half = clang::TST_half;
+ static const TST TST_BFloat16 = clang::TST_BFloat16;
static const TST TST_float = clang::TST_float;
static const TST TST_double = clang::TST_double;
static const TST TST_float16 = clang::TST_Float16;
/// A placeholder type for incomplete matrix index operations.
PREDEF_TYPE_INCOMPLETE_MATRIX_IDX = 72,
- /// OpenCL image types with auto numeration
+ /// \brief The '__bf16' type
+ PREDEF_TYPE_BFLOAT16_ID = 73,
+
+ /// OpenCL image types with auto numeration
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
PREDEF_TYPE_##Id##_ID,
#include "clang/Basic/OpenCLImageTypes.def"
using namespace clang;
enum FloatingRank {
- Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank
+ BFloat16Rank, Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank
};
/// \returns location that is relevant when searching for Doc comments related
// half type (OpenCL 6.1.1.1) / ARM NEON __fp16
InitBuiltinType(HalfTy, BuiltinType::Half);
+ InitBuiltinType(BFloat16Ty, BuiltinType::BFloat16);
+
// Builtin type used to help define __builtin_va_list.
VaListTagDecl = nullptr;
switch (T->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("Not a floating point type!");
+ case BuiltinType::BFloat16:
+ return Target->getBFloat16Format();
case BuiltinType::Float16:
case BuiltinType::Half:
return Target->getHalfFormat();
Width = Target->getLongFractWidth();
Align = Target->getLongFractAlign();
break;
+ case BuiltinType::BFloat16:
+ Width = Target->getBFloat16Width();
+ Align = Target->getBFloat16Align();
+ break;
case BuiltinType::Float16:
case BuiltinType::Half:
if (Target->hasFloat16Type() || !getLangOpts().OpenMP ||
case BuiltinType::Double: return DoubleRank;
case BuiltinType::LongDouble: return LongDoubleRank;
case BuiltinType::Float128: return Float128Rank;
+ case BuiltinType::BFloat16: return BFloat16Rank;
}
}
FloatingRank EltRank = getFloatingRank(Size);
if (Domain->isComplexType()) {
switch (EltRank) {
+ case BFloat16Rank: llvm_unreachable("Complex bfloat16 is not supported");
case Float16Rank:
case HalfRank: llvm_unreachable("Complex half is not supported");
case FloatRank: return FloatComplexTy;
assert(Domain->isRealFloatingType() && "Unknown domain!");
switch (EltRank) {
case Float16Rank: return HalfTy;
+ case BFloat16Rank: return BFloat16Ty;
case HalfRank: return HalfTy;
case FloatRank: return FloatTy;
case DoubleRank: return DoubleTy;
case BuiltinType::LongDouble: return 'D';
case BuiltinType::NullPtr: return '*'; // like char*
+ case BuiltinType::BFloat16:
case BuiltinType::Float16:
case BuiltinType::Float128:
case BuiltinType::Half:
// Read the base type.
switch (*Str++) {
default: llvm_unreachable("Unknown builtin type letter!");
+ case 'y':
+ assert(HowLong == 0 && !Signed && !Unsigned &&
+ "Bad modifiers used with 'y'!");
+ Type = Context.BFloat16Ty;
+ break;
case 'v':
assert(HowLong == 0 && !Signed && !Unsigned &&
"Bad modifiers used with 'v'!");
Out << TI->getFloat128Mangling();
break;
}
+ case BuiltinType::BFloat16: {
+ const TargetInfo *TI = &getASTContext().getTargetInfo();
+ Out << TI->getBFloat16Mangling();
+ break;
+ }
case BuiltinType::NullPtr:
Out << "Dn";
break;
case BuiltinType::ULongLong: EltName = "uint64_t"; break;
case BuiltinType::Double: EltName = "float64_t"; break;
case BuiltinType::Float: EltName = "float32_t"; break;
- case BuiltinType::Half: EltName = "float16_t";break;
+ case BuiltinType::Half: EltName = "float16_t"; break;
+ case BuiltinType::BFloat16: EltName = "bfloat16_t"; break;
default:
llvm_unreachable("unexpected Neon vector element type");
}
return "Float32";
case BuiltinType::Double:
return "Float64";
+ case BuiltinType::BFloat16:
+ return "BFloat16";
default:
llvm_unreachable("Unexpected vector element base type");
}
case BuiltinType::SatUShortFract:
case BuiltinType::SatUFract:
case BuiltinType::SatULongFract:
+ case BuiltinType::BFloat16:
case BuiltinType::Float128: {
DiagnosticsEngine &Diags = Context.getDiags();
unsigned DiagID = Diags.getCustomDiagID(
case BuiltinType::OMPArraySection:
case BuiltinType::OMPArrayShaping:
case BuiltinType::OMPIterator:
+ case BuiltinType::BFloat16:
break;
}
case BuiltinType::UInt128:
case BuiltinType::Int128:
case BuiltinType::Half:
+ case BuiltinType::BFloat16:
case BuiltinType::Float16:
case BuiltinType::Float128:
case BuiltinType::ShortAccum:
bool Type::isArithmeticType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->getKind() >= BuiltinType::Bool &&
- BT->getKind() <= BuiltinType::Float128;
+ BT->getKind() <= BuiltinType::Float128 &&
+ BT->getKind() != BuiltinType::BFloat16;
if (const auto *ET = dyn_cast<EnumType>(CanonicalType))
// GCC allows forward declaration of enum types (forbid by C99 6.7.2.3p2).
// If a body isn't seen by the time we get here, return false.
return "unsigned __int128";
case Half:
return Policy.Half ? "half" : "__fp16";
+ case BFloat16:
+ return "__bf16";
case Float:
return "float";
case Double:
case BuiltinType::SatUShortFract:
case BuiltinType::SatUFract:
case BuiltinType::SatULongFract:
+ case BuiltinType::BFloat16:
llvm_unreachable("Builtin type needs extra local data!");
// Fall through, if the impossible happens.
HasLegalHalfType = false;
HasFloat128 = false;
HasFloat16 = false;
+ HasBFloat16 = false;
PointerWidth = PointerAlign = 32;
BoolWidth = BoolAlign = 8;
IntWidth = IntAlign = 32;
LongDoubleWidth = LongDoubleAlign = SuitableAlign = 128;
LongDoubleFormat = &llvm::APFloat::IEEEquad();
+ BFloat16Width = BFloat16Align = 16;
+ BFloat16Format = &llvm::APFloat::BFloat();
+
// Make __builtin_ms_va_list available.
HasBuiltinMSVaList = true;
HasMTE = false;
HasTME = false;
HasMatMul = false;
+ HasBFloat16 = false;
ArchKind = llvm::AArch64::ArchKind::ARMV8A;
for (const auto &Feature : Features) {
HasTME = true;
if (Feature == "+i8mm")
HasMatMul = true;
+ if (Feature == "+bf16")
+ HasBFloat16 = true;
}
setDataLayout();
int getEHDataRegisterNumber(unsigned RegNo) const override;
+ const char *getBFloat16Mangling() const override { return "u6__bf16"; };
bool hasInt128Type() const override;
bool hasExtIntType() const override { return true; }
IsAAPCS = true;
DoubleAlign = LongLongAlign = LongDoubleAlign = SuitableAlign = 64;
+ BFloat16Width = BFloat16Align = 16;
+ BFloat16Format = &llvm::APFloat::BFloat();
+
const llvm::Triple &T = getTriple();
bool IsNetBSD = T.isOSNetBSD();
DoubleAlign = LongLongAlign = LongDoubleAlign = SuitableAlign = 64;
else
DoubleAlign = LongLongAlign = LongDoubleAlign = SuitableAlign = 32;
+ BFloat16Width = BFloat16Align = 16;
+ BFloat16Format = &llvm::APFloat::BFloat();
WCharType = SignedInt;
HasMatMul = 0;
HasFloat16 = true;
ARMCDECoprocMask = 0;
+ HasBFloat16 = false;
// This does not diagnose illegal cases like having both
// "+vfpv2" and "+vfpv3" or having "+neon" and "-fp64".
Feature <= "+cdecp7") {
unsigned Coproc = Feature.back() - '0';
ARMCDECoprocMask |= (1U << Coproc);
+ } else if (Feature == "+bf16") {
+ HasBFloat16 = true;
}
}
.Default(false);
}
+bool ARMTargetInfo::hasBFloat16Type() const {
+ return HasBFloat16 && !SoftFloat;
+}
+
bool ARMTargetInfo::isValidCPUName(StringRef Name) const {
return Name == "generic" ||
llvm::ARM::parseCPUArch(Name) != llvm::ARM::ArchKind::INVALID;
bool hasFeature(StringRef Feature) const override;
+ bool hasBFloat16Type() const override;
+
bool isValidCPUName(StringRef Name) const override;
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
bool hasSjLjLowering() const override;
bool hasExtIntType() const override { return true; }
+
+ const char *getBFloat16Mangling() const override { return "u6__bf16"; };
};
class LLVM_LIBRARY_VISIBILITY ARMleTargetInfo : public ARMTargetInfo {
virtual bool supportsSwift() const { return false; }
+ virtual bool allowBFloatArgsAndRet() const { return false; }
+
CodeGen::CGCXXABI &getCXXABI() const;
ASTContext &getContext() const;
llvm::LLVMContext &getVMContext() const;
static llvm::VectorType *GetNeonType(CodeGenFunction *CGF,
NeonTypeFlags TypeFlags,
- bool HasLegalHalfType=true,
- bool V1Ty=false) {
+ bool HasLegalHalfType = true,
+ bool V1Ty = false,
+ bool AllowBFloatArgsAndRet = true) {
int IsQuad = TypeFlags.isQuad();
switch (TypeFlags.getEltType()) {
case NeonTypeFlags::Int8:
case NeonTypeFlags::Int16:
case NeonTypeFlags::Poly16:
return llvm::FixedVectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad));
+ case NeonTypeFlags::BFloat16:
+ if (AllowBFloatArgsAndRet)
+ return llvm::FixedVectorType::get(CGF->BFloatTy, V1Ty ? 1 : (4 << IsQuad));
+ else
+ return llvm::FixedVectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad));
case NeonTypeFlags::Float16:
if (HasLegalHalfType)
return llvm::FixedVectorType::get(CGF->HalfTy, V1Ty ? 1 : (4 << IsQuad));
bool Usgn = Type.isUnsigned();
bool Quad = Type.isQuad();
const bool HasLegalHalfType = getTarget().hasLegalHalfType();
+ const bool AllowBFloatArgsAndRet =
+ getTargetHooks().getABIInfo().allowBFloatArgsAndRet();
- llvm::VectorType *VTy = GetNeonType(this, Type, HasLegalHalfType);
+ llvm::VectorType *VTy = GetNeonType(this, Type, HasLegalHalfType, false,
+ AllowBFloatArgsAndRet);
llvm::Type *Ty = VTy;
if (!Ty)
return nullptr;
bool rightShift = false;
llvm::VectorType *VTy = GetNeonType(this, Type,
- getTarget().hasLegalHalfType());
+ getTarget().hasLegalHalfType(),
+ false,
+ getTarget().hasBFloat16Type());
llvm::Type *Ty = VTy;
if (!Ty)
return nullptr;
case BuiltinType::Float:
case BuiltinType::LongDouble:
case BuiltinType::Float16:
+ case BuiltinType::BFloat16:
case BuiltinType::Float128:
case BuiltinType::Double:
// FIXME: For targets where long double and __float128 have the same size,
Int32Ty = llvm::Type::getInt32Ty(LLVMContext);
Int64Ty = llvm::Type::getInt64Ty(LLVMContext);
HalfTy = llvm::Type::getHalfTy(LLVMContext);
+ BFloatTy = llvm::Type::getBFloatTy(LLVMContext);
FloatTy = llvm::Type::getFloatTy(LLVMContext);
DoubleTy = llvm::Type::getDoubleTy(LLVMContext);
PointerWidthInBits = C.getTargetInfo().getPointerWidth(0);
/// i8, i16, i32, and i64
llvm::IntegerType *Int8Ty, *Int16Ty, *Int32Ty, *Int64Ty;
- /// float, double
- llvm::Type *HalfTy, *FloatTy, *DoubleTy;
+ /// half, bfloat, float, double
+ llvm::Type *HalfTy, *BFloatTy, *FloatTy, *DoubleTy;
/// int
llvm::IntegerType *IntTy;
else
return llvm::Type::getInt16Ty(VMContext);
}
+ if (&format == &llvm::APFloat::BFloat())
+ return llvm::Type::getBFloatTy(VMContext);
if (&format == &llvm::APFloat::IEEEsingle())
return llvm::Type::getFloatTy(VMContext);
if (&format == &llvm::APFloat::IEEEdouble())
Context.getLangOpts().NativeHalfType ||
!Context.getTargetInfo().useFP16ConversionIntrinsics());
break;
+ case BuiltinType::BFloat16:
case BuiltinType::Float:
case BuiltinType::Double:
case BuiltinType::LongDouble:
case BuiltinType::SatUShortFract:
case BuiltinType::SatUFract:
case BuiltinType::SatULongFract:
+ case BuiltinType::BFloat16:
return false;
case BuiltinType::Dependent:
bool isLegalVectorTypeForSwift(CharUnits totalSize, llvm::Type *eltTy,
unsigned elts) const override;
+
+ bool allowBFloatArgsAndRet() const override {
+ return getTarget().hasBFloat16Type();
+ }
};
class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
private:
ABIKind Kind;
+ bool IsFloatABISoftFP;
public:
ARMABIInfo(CodeGenTypes &CGT, ABIKind _Kind)
: SwiftABIInfo(CGT), Kind(_Kind) {
setCCs();
+ IsFloatABISoftFP = CGT.getCodeGenOpts().FloatABI == "softfp" ||
+ CGT.getCodeGenOpts().FloatABI == ""; // default
}
bool isEABI() const {
ABIKind getABIKind() const { return Kind; }
+ bool allowBFloatArgsAndRet() const override {
+ return !IsFloatABISoftFP && getTarget().hasBFloat16Type();
+ }
+
private:
ABIArgInfo classifyReturnType(QualType RetTy, bool isVariadic,
unsigned functionCallConv) const;
if (isIllegalVectorType(Ty))
return coerceIllegalVector(Ty);
- // _Float16 and __fp16 get passed as if it were an int or float, but with
- // the top 16 bits unspecified. This is not done for OpenCL as it handles the
- // half type natively, and does not need to interwork with AAPCS code.
+ // _Float16 and __fp16 get passed as if it were an int or float, but
+ // with the top 16 bits unspecified. This is not done for OpenCL as it handles
+ // the half type natively, and does not need to interwork with AAPCS code.
if ((Ty->isFloat16Type() || Ty->isHalfType()) &&
!getContext().getLangOpts().NativeHalfArgsAndReturns) {
llvm::Type *ResType = IsAAPCS_VFP ?
return ABIArgInfo::getDirect(ResType);
}
+ // __bf16 gets passed using the bfloat IR type, or using i32 but
+ // with the top 16 bits unspecified.
+ if (Ty->isBFloat16Type() && IsFloatABISoftFP) {
+ llvm::Type *ResType = llvm::Type::getInt32Ty(getVMContext());
+ return ABIArgInfo::getDirect(ResType);
+ }
+
if (!isAggregateTypeForABI(Ty)) {
// Treat an enum type as its underlying type.
if (const EnumType *EnumTy = Ty->getAs<EnumType>()) {
// Large vector types should be returned via memory.
if (getContext().getTypeSize(RetTy) > 128)
return getNaturalAlignIndirect(RetTy);
- // FP16 vectors should be converted to integer vectors
- if (!getTarget().hasLegalHalfType() &&
+ // TODO: FP16/BF16 vectors should be converted to integer vectors
+ // This check is similar to isIllegalVectorType - refactor?
+ if ((!getTarget().hasLegalHalfType() &&
(VT->getElementType()->isFloat16Type() ||
- VT->getElementType()->isHalfType()))
+ VT->getElementType()->isHalfType())) ||
+ (IsFloatABISoftFP &&
+ VT->getElementType()->isBFloat16Type()))
return coerceIllegalVector(RetTy);
}
return ABIArgInfo::getDirect(ResType);
}
+ // if we're using the softfp float abi, __bf16 get returned as if it were an
+ // int but with the top 16 bits unspecified.
+ if (RetTy->isBFloat16Type()) {
+ llvm::Type *ResType = IsAAPCS_VFP ?
+ llvm::Type::getBFloatTy(getVMContext()) :
+ llvm::Type::getInt32Ty(getVMContext());
+ return ABIArgInfo::getDirect(ResType);
+ }
+
if (!isAggregateTypeForABI(RetTy)) {
// Treat an enum type as its underlying type.
if (const EnumType *EnumTy = RetTy->getAs<EnumType>())
/// isIllegalVector - check whether Ty is an illegal vector type.
bool ARMABIInfo::isIllegalVectorType(QualType Ty) const {
if (const VectorType *VT = Ty->getAs<VectorType> ()) {
- // On targets that don't support FP16, FP16 is expanded into float, and we
- // don't want the ABI to depend on whether or not FP16 is supported in
- // hardware. Thus return false to coerce FP16 vectors into integer vectors.
- if (!getTarget().hasLegalHalfType() &&
+ // On targets that don't support half, fp16 or bfloat, they are expanded
+ // into float, and we don't want the ABI to depend on whether or not they
+ // are supported in hardware. Thus return false to coerce vectors of these
+ // types into integer vectors.
+ // We do not depend on hasLegalHalfType for bfloat as it is a
+ // separate IR type.
+ if ((!getTarget().hasLegalHalfType() &&
(VT->getElementType()->isFloat16Type() ||
- VT->getElementType()->isHalfType()))
+ VT->getElementType()->isHalfType())) ||
+ (IsFloatABISoftFP &&
+ VT->getElementType()->isBFloat16Type()))
return true;
if (isAndroid()) {
// Android shipped using Clang 3.1, which supported a slightly different
} else {
if (const VectorType *VT = Ty->getAs<VectorType>())
return (VT->getElementType()->isFloat16Type() ||
+ VT->getElementType()->isBFloat16Type() ||
VT->getElementType()->isHalfType());
return false;
}
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_wchar_t:
case BuiltinType::SatUShortFract:
case BuiltinType::SatUFract:
case BuiltinType::SatULongFract:
+ case BuiltinType::BFloat16:
IgnoreResults = true;
return;
case BuiltinType::ObjCId:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec,
DiagID, Policy);
break;
+ case tok::kw___bf16:
+ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec,
+ DiagID, Policy);
+ break;
case tok::kw_float:
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec,
DiagID, Policy);
case tok::kw_char32_t:
case tok::kw_int:
case tok::kw__ExtInt:
+ case tok::kw___bf16:
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
case tok::kw_int:
case tok::kw__ExtInt:
case tok::kw_half:
+ case tok::kw___bf16:
case tok::kw_float:
case tok::kw_double:
case tok::kw__Accum:
case tok::kw_int:
case tok::kw__ExtInt:
case tok::kw_half:
+ case tok::kw___bf16:
case tok::kw_float:
case tok::kw_double:
case tok::kw__Accum:
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_void:
case tok::kw___int128:
DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy);
break;
+ case tok::kw___bf16:
+ DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, DiagID, Policy);
+ break;
case tok::kw_half:
DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy);
break;
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_void:
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_void:
case TST_unspecified:
case TST_void:
case TST_wchar:
+ case TST_BFloat16:
#define GENERIC_IMAGE_TYPE(ImgType, Id) case TST_##ImgType##_t:
#include "clang/Basic/OpenCLImageTypes.def"
return false;
case DeclSpec::TST_underlyingType: return "__underlying_type";
case DeclSpec::TST_unknown_anytype: return "__unknown_anytype";
case DeclSpec::TST_atomic: return "_Atomic";
+ case DeclSpec::TST_BFloat16: return "__bf16";
#define GENERIC_IMAGE_TYPE(ImgType, Id) \
case DeclSpec::TST_##ImgType##_t: \
return #ImgType "_t";
return;
}
+ // Can't cast to or from bfloat
+ if (DestType->isBFloat16Type() && !SrcType->isBFloat16Type()) {
+ Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_cast_to_bfloat16)
+ << SrcExpr.get()->getSourceRange();
+ SrcExpr = ExprError();
+ return;
+ }
+ if (SrcType->isBFloat16Type() && !DestType->isBFloat16Type()) {
+ Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_cast_from_bfloat16)
+ << SrcExpr.get()->getSourceRange();
+ SrcExpr = ExprError();
+ return;
+ }
+
// If either type is a pointer, the other type has to be either an
// integer or a pointer.
if (!DestType->isArithmeticType()) {
case NeonTypeFlags::Float64:
assert(!shift && "cannot shift float types!");
return (1 << IsQuad) - 1;
+ case NeonTypeFlags::BFloat16:
+ assert(!shift && "cannot shift float types!");
+ return (4 << IsQuad) - 1;
}
llvm_unreachable("Invalid NeonTypeFlag!");
}
return Context.FloatTy;
case NeonTypeFlags::Float64:
return Context.DoubleTy;
+ case NeonTypeFlags::BFloat16:
+ return Context.BFloat16Ty;
}
llvm_unreachable("Invalid NeonTypeFlag!");
}
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_wchar_t:
return ResTy;
}
+ // And if they're both bfloat (which isn't arithmetic), that's fine too.
+ if (LHSTy->isBFloat16Type() && RHSTy->isBFloat16Type()) {
+ return LHSTy;
+ }
+
// If both operands are the same structure or union type, the result is that
// type.
if (const RecordType *LHSRT = LHSTy->getAs<RecordType>()) { // C99 6.5.15p3
// FIXME: disable conversions between long double and __float128 if
// their representation is different until there is back end support
// We of course allow this conversion if long double is really double.
+
+ // Conversions between bfloat and other floats are not permitted.
+ if (FromType == S.Context.BFloat16Ty || ToType == S.Context.BFloat16Ty)
+ return false;
if (&S.Context.getFloatTypeSemantics(FromType) !=
&S.Context.getFloatTypeSemantics(ToType)) {
bool Float128AndLongDouble = ((FromType == S.Context.Float128Ty &&
ToType->isIntegralType(S.Context)) ||
(FromType->isIntegralOrUnscopedEnumerationType() &&
ToType->isRealFloatingType())) {
+ // Conversions between bfloat and int are not permitted.
+ if (FromType->isBFloat16Type() || ToType->isBFloat16Type())
+ return false;
+
// Floating-integral conversions (C++ 4.9).
SCS.Second = ICK_Floating_Integral;
FromType = ToType.getUnqualifiedType();
case TST_auto:
case TST_auto_type:
case TST_decltype_auto:
+ case TST_BFloat16:
#define GENERIC_IMAGE_TYPE(ImgType, Id) case TST_##ImgType##_t:
#include "clang/Basic/OpenCLImageTypes.def"
case TST_unknown_anytype:
Result = Context.Float16Ty;
break;
case DeclSpec::TST_half: Result = Context.HalfTy; break;
+ case DeclSpec::TST_BFloat16:
+ if (!S.Context.getTargetInfo().hasBFloat16Type())
+ S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
+ << "__bf16";
+ Result = Context.BFloat16Ty;
+ break;
case DeclSpec::TST_float: Result = Context.FloatTy; break;
case DeclSpec::TST_double:
if (DS.getTypeSpecWidth() == DeclSpec::TSW_long)
BTy->getKind() == BuiltinType::LongLong ||
BTy->getKind() == BuiltinType::ULongLong ||
BTy->getKind() == BuiltinType::Float ||
- BTy->getKind() == BuiltinType::Half;
+ BTy->getKind() == BuiltinType::Half ||
+ BTy->getKind() == BuiltinType::BFloat16;
}
/// HandleNeonVectorTypeAttr - The "neon_vector_type" and
case BuiltinType::OMPIterator:
ID = PREDEF_TYPE_OMP_ITERATOR;
break;
+ case BuiltinType::BFloat16:
+ ID = PREDEF_TYPE_BFLOAT16_ID;
+ break;
}
return TypeIdx(ID);
case PREDEF_TYPE_INT128_ID:
T = Context.Int128Ty;
break;
+ case PREDEF_TYPE_BFLOAT16_ID:
+ T = Context.BFloat16Ty;
+ break;
case PREDEF_TYPE_HALF_ID:
T = Context.HalfTy;
break;
--- /dev/null
+// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi hard -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -mem2reg -sroa | FileCheck %s --check-prefix=CHECK32-HARD
+// RUN: %clang_cc1 -triple aarch64-arm-none-eabi -target-abi aapcs -mfloat-abi hard -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -mem2reg -sroa | FileCheck %s --check-prefix=CHECK64-HARD
+// RUN: %clang_cc1 -triple armv8.6a-arm-none-eabi -target-abi aapcs -mfloat-abi softfp -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -mem2reg -sroa | FileCheck %s --check-prefix=CHECK32-SOFTFP
+// RUN: %clang_cc1 -triple aarch64-arm-none-eabi -target-abi aapcs -mfloat-abi softfp -target-feature +bf16 -target-feature +neon -emit-llvm -O2 -o - %s | opt -S -mem2reg -sroa | FileCheck %s --check-prefix=CHECK64-SOFTFP
+
+// function return types
+__bf16 test_ret_bf16(__bf16 v) {
+ return v;
+}
+// CHECK32-HARD: define arm_aapcs_vfpcc bfloat @test_ret_bf16(bfloat returned %v) {{.*}} {
+// CHECK32-HARD: ret bfloat %v
+// CHECK64-HARD: define bfloat @test_ret_bf16(bfloat returned %v) {{.*}} {
+// CHECK64-HARD: ret bfloat %v
+// CHECK32-SOFTFP: define i32 @test_ret_bf16(i32 [[V0:.*]]) {{.*}} {
+// CHECK32-SOFTFP: %tmp2.0.insert.ext = and i32 [[V0]], 65535
+// CHECK32-SOFTFP: ret i32 %tmp2.0.insert.ext
+// CHECK64-SOFTFP: define bfloat @test_ret_bf16(bfloat returned %v) {{.*}} {
+// CHECK64-SOFTFP: ret bfloat %v
--- /dev/null
+// RUN: not %clang -target arm-arm-eabi -march=armv8-a+bf16 -mfloat-abi=soft -c %s 2>&1 | FileCheck %s
+
+// CHECK: error: __bf16 is not supported on this target
+extern __bf16 var;
--- /dev/null
+// RUN: %clang_cc1 -triple aarch64-arm-none-eabi -target-feature +bf16 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK64
+// RUN: %clang_cc1 -triple arm-arm-none-eabi -target-feature +bf16 -mfloat-abi hard -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK32-HARD
+// RUN: %clang_cc1 -triple arm-arm-none-eabi -target-feature +bf16 -mfloat-abi softfp -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK32-SOFTFP
+
+// CHECK64: define {{.*}}void @_Z3foou6__bf16(bfloat %b)
+// CHECK32-HARD: define {{.*}}void @_Z3foou6__bf16(bfloat %b)
+// CHECK32-SOFTFP: define {{.*}}void @_Z3foou6__bf16(i32 %b.coerce)
+void foo(__bf16 b) {}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -triple aarch64 -target-feature +bf16 %s
+
+__bf16 test_cast_from_float(float in) {
+ return (__bf16)in; // expected-error {{cannot type-cast to __bf16}}
+}
+
+__bf16 test_cast_from_float_literal(void) {
+ return (__bf16)1.0f; // expected-error {{cannot type-cast to __bf16}}
+}
+
+__bf16 test_cast_from_int(int in) {
+ return (__bf16)in; // expected-error {{cannot type-cast to __bf16}}
+}
+
+__bf16 test_cast_from_int_literal(void) {
+ return (__bf16)1; // expected-error {{cannot type-cast to __bf16}}
+}
+
+__bf16 test_cast_bfloat(__bf16 in) {
+ return (__bf16)in; // this one should work
+}
+
+float test_cast_to_float(__bf16 in) {
+ return (float)in; // expected-error {{cannot type-cast from __bf16}}
+}
+
+int test_cast_to_int(__bf16 in) {
+ return (int)in; // expected-error {{cannot type-cast from __bf16}}
+}
+
+__bf16 test_implicit_from_float(float in) {
+ return in; // expected-error {{returning 'float' from a function with incompatible result type '__bf16'}}
+}
+
+__bf16 test_implicit_from_float_literal(void) {
+ return 1.0f; // expected-error {{returning 'float' from a function with incompatible result type '__bf16'}}
+}
+
+__bf16 test_implicit_from_int(int in) {
+ return in; // expected-error {{returning 'int' from a function with incompatible result type '__bf16'}}
+}
+
+__bf16 test_implicit_from_int_literal(void) {
+ return 1; // expected-error {{returning 'int' from a function with incompatible result type '__bf16'}}
+}
+
+__bf16 test_implicit_bfloat(__bf16 in) {
+ return in; // this one should work
+}
+
+float test_implicit_to_float(__bf16 in) {
+ return in; // expected-error {{returning '__bf16' from a function with incompatible result type 'float'}}
+}
+
+int test_implicit_to_int(__bf16 in) {
+ return in; // expected-error {{returning '__bf16' from a function with incompatible result type 'int'}}
+}
+
+__bf16 test_cond(__bf16 a, __bf16 b, _Bool which) {
+ // Conditional operator _should_ be supported, without nonsense
+ // complaints like 'types __bf16 and __bf16 are not compatible'
+ return which ? a : b;
+}
+
+__bf16 test_cond_float(__bf16 a, __bf16 b, _Bool which) {
+ return which ? a : 1.0f; // expected-error {{incompatible operand types ('__bf16' and 'float')}}
+}
+
+__bf16 test_cond_int(__bf16 a, __bf16 b, _Bool which) {
+ return which ? a : 1; // expected-error {{incompatible operand types ('__bf16' and 'int')}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -triple aarch64 -target-feature +bf16 %s
+
+__bf16 test_static_cast_from_float(float in) {
+ return static_cast<__bf16>(in); // expected-error {{static_cast from 'float' to '__bf16' is not allowed}}
+}
+
+__bf16 test_static_cast_from_float_literal(void) {
+ return static_cast<__bf16>(1.0f); // expected-error {{static_cast from 'float' to '__bf16' is not allowed}}
+}
+
+__bf16 test_static_cast_from_int(int in) {
+ return static_cast<__bf16>(in); // expected-error {{static_cast from 'int' to '__bf16' is not allowed}}
+}
+
+__bf16 test_static_cast_from_int_literal(void) {
+ return static_cast<__bf16>(1); // expected-error {{static_cast from 'int' to '__bf16' is not allowed}}
+}
+
+__bf16 test_static_cast_bfloat(__bf16 in) {
+ return static_cast<__bf16>(in); // this one should work
+}
+
+float test_static_cast_to_float(__bf16 in) {
+ return static_cast<float>(in); // expected-error {{static_cast from '__bf16' to 'float' is not allowed}}
+}
+
+int test_static_cast_to_int(__bf16 in) {
+ return static_cast<int>(in); // expected-error {{static_cast from '__bf16' to 'int' is not allowed}}
+}
+
+__bf16 test_implicit_from_float(float in) {
+ return in; // expected-error {{cannot initialize return object of type '__bf16' with an lvalue of type 'float'}}
+}
+
+__bf16 test_implicit_from_float_literal() {
+ return 1.0f; // expected-error {{cannot initialize return object of type '__bf16' with an rvalue of type 'float'}}
+}
+
+__bf16 test_implicit_from_int(int in) {
+ return in; // expected-error {{cannot initialize return object of type '__bf16' with an lvalue of type 'int'}}
+}
+
+__bf16 test_implicit_from_int_literal() {
+ return 1; // expected-error {{cannot initialize return object of type '__bf16' with an rvalue of type 'int'}}
+}
+
+__bf16 test_implicit_bfloat(__bf16 in) {
+ return in; // this one should work
+}
+
+float test_implicit_to_float(__bf16 in) {
+ return in; // expected-error {{cannot initialize return object of type 'float' with an lvalue of type '__bf16'}}
+}
+
+int test_implicit_to_int(__bf16 in) {
+ return in; // expected-error {{cannot initialize return object of type 'int' with an lvalue of type '__bf16'}}
+}
+
+__bf16 test_cond(__bf16 a, __bf16 b, bool which) {
+ // Conditional operator _should_ be supported, without nonsense
+ // complaints like 'types __bf16 and __bf16 are not compatible'
+ return which ? a : b;
+}
+
+__bf16 test_cond_float(__bf16 a, __bf16 b, bool which) {
+ return which ? a : 1.0f; // expected-error {{incompatible operand types ('__bf16' and 'float')}}
+}
+
+__bf16 test_cond_int(__bf16 a, __bf16 b, bool which) {
+ return which ? a : 1; // expected-error {{incompatible operand types ('__bf16' and 'int')}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 \
+// RUN: -triple aarch64-arm-none-eabi -target-cpu cortex-a75 \
+// RUN: -target-feature +bf16 -target-feature +neon %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 \
+// RUN: -triple arm-arm-none-eabi -target-cpu cortex-a53 \
+// RUN: -target-feature +bf16 -target-feature +neon %s
+
+void test(bool b) {
+ __bf16 bf16;
+
+ bf16 + bf16; // expected-error {{invalid operands to binary expression ('__bf16' and '__bf16')}}
+ bf16 - bf16; // expected-error {{invalid operands to binary expression ('__bf16' and '__bf16')}}
+ bf16 * bf16; // expected-error {{invalid operands to binary expression ('__bf16' and '__bf16')}}
+ bf16 / bf16; // expected-error {{invalid operands to binary expression ('__bf16' and '__bf16')}}
+
+ __fp16 fp16;
+
+ bf16 + fp16; // expected-error {{invalid operands to binary expression ('__bf16' and '__fp16')}}
+ fp16 + bf16; // expected-error {{invalid operands to binary expression ('__fp16' and '__bf16')}}
+ bf16 - fp16; // expected-error {{invalid operands to binary expression ('__bf16' and '__fp16')}}
+ fp16 - bf16; // expected-error {{invalid operands to binary expression ('__fp16' and '__bf16')}}
+ bf16 * fp16; // expected-error {{invalid operands to binary expression ('__bf16' and '__fp16')}}
+ fp16 * bf16; // expected-error {{invalid operands to binary expression ('__fp16' and '__bf16')}}
+ bf16 / fp16; // expected-error {{invalid operands to binary expression ('__bf16' and '__fp16')}}
+ fp16 / bf16; // expected-error {{invalid operands to binary expression ('__fp16' and '__bf16')}}
+ bf16 = fp16; // expected-error {{assigning to '__bf16' from incompatible type '__fp16'}}
+ fp16 = bf16; // expected-error {{assigning to '__fp16' from incompatible type '__bf16'}}
+ bf16 + (b ? fp16 : bf16); // expected-error {{incompatible operand types ('__fp16' and '__bf16')}}
+}
TKIND(Elaborated);
TKIND(Pipe);
TKIND(Attributed);
+ TKIND(BFloat16);
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) TKIND(Id);
#include "clang/Basic/OpenCLImageTypes.def"
#undef IMAGE_TYPE