Loading clang/include/clang/Basic/DiagnosticASTKinds.td +4 −1 Original line number Diff line number Diff line Loading @@ -317,7 +317,7 @@ def note_constexpr_memcpy_unsupported : Note< "source is not a contiguous array of at least %4 elements of type %3|" "destination is not a contiguous array of at least %4 elements of type %3}2">; def note_constexpr_bit_cast_unsupported_type : Note< "constexpr bit_cast involving type %0 is not yet supported">; "constexpr bit cast involving type %0 is not yet supported">; def note_constexpr_bit_cast_unsupported_bitfield : Note< "constexpr bit_cast involving bit-field is not yet supported">; def note_constexpr_bit_cast_invalid_type : Note< Loading @@ -326,6 +326,9 @@ def note_constexpr_bit_cast_invalid_type : Note< "%select{type|member}1 is not allowed in a constant expression">; def note_constexpr_bit_cast_invalid_subtype : Note< "invalid type %0 is a %select{member|base}1 of %2">; def note_constexpr_bit_cast_invalid_vector : Note< "bit_cast involving type %0 is not allowed in a constant expression; " "element size %1 * element count %2 is not a multiple of the byte size %3">; def note_constexpr_bit_cast_indet_dest : Note< "indeterminate value can only initialize an object of type 'unsigned char'" "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; Loading clang/lib/AST/ExprConstant.cpp +175 −94 Original line number Diff line number Diff line Loading @@ -2737,53 +2737,6 @@ static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E, return true; } static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E, llvm::APInt &Res) { APValue SVal; if (!Evaluate(SVal, Info, E)) return false; if (SVal.isInt()) { Res = SVal.getInt(); return true; } if (SVal.isFloat()) { Res = SVal.getFloat().bitcastToAPInt(); return true; } if (SVal.isVector()) { QualType VecTy = E->getType(); unsigned VecSize = Info.Ctx.getTypeSize(VecTy); QualType EltTy = VecTy->castAs<VectorType>()->getElementType(); unsigned EltSize = Info.Ctx.getTypeSize(EltTy); bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); Res = llvm::APInt::getZero(VecSize); for (unsigned i = 0; i < SVal.getVectorLength(); i++) { APValue &Elt = SVal.getVectorElt(i); llvm::APInt EltAsInt; if (Elt.isInt()) { EltAsInt = Elt.getInt(); } else if (Elt.isFloat()) { EltAsInt = Elt.getFloat().bitcastToAPInt(); } else { // Don't try to handle vectors of anything other than int or float // (not sure if it's possible to hit this case). Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } unsigned BaseEltSize = EltAsInt.getBitWidth(); if (BigEndian) Res |= EltAsInt.zextOrTrunc(VecSize).rotr(i*EltSize+BaseEltSize); else Res |= EltAsInt.zextOrTrunc(VecSize).rotl(i*EltSize); } return true; } // Give up if the input isn't an int, float, or vector. For example, we // reject "(v4i16)(intptr_t)&a". Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } /// Perform the given integer operation, which is known to need at most BitWidth /// bits, and check for overflow in the original type (if that type was not an /// unsigned type). Loading Loading @@ -7023,10 +6976,11 @@ class APValueToBufferConverter { return visitArray(Val, Ty, Offset); case APValue::Struct: return visitRecord(Val, Ty, Offset); case APValue::Vector: return visitVector(Val, Ty, Offset); case APValue::ComplexInt: case APValue::ComplexFloat: case APValue::Vector: case APValue::FixedPoint: // FIXME: We should support these. Loading Loading @@ -7113,6 +7067,72 @@ class APValueToBufferConverter { return true; } bool visitVector(const APValue &Val, QualType Ty, CharUnits Offset) { const VectorType *VTy = Ty->castAs<VectorType>(); QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy); if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, // so its layout is unspecified. For now, we'll simply treat these cases // as unsupported (this should only be possible with OpenCL bool vectors // whose element count isn't a multiple of the byte size). Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_vector) << Ty.getCanonicalType() << EltSize << NElts << Info.Ctx.getCharWidth(); return false; } if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) { // The layout for x86_fp80 vectors seems to be handled very inconsistently // by both clang and LLVM, so for now we won't allow bit_casts involving // it in a constexpr context. Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << EltTy; return false; } if (VTy->isExtVectorBoolType()) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't write // individual bits to the BitCastBuffer, we'll buffer all of the elements // together into an appropriately sized APInt and write them all out at // once. Because we don't accept vectors where NElts * EltSize isn't a // multiple of the char size, there will be no padding space, so we don't // have to worry about writing data which should have been left // uninitialized. bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); llvm::APInt Res = llvm::APInt::getZero(NElts); for (unsigned I = 0; I < NElts; ++I) { const llvm::APSInt &EltAsInt = Val.getVectorElt(I).getInt(); assert(EltAsInt.isUnsigned() && EltAsInt.getBitWidth() == 1 && "bool vector element must be 1-bit unsigned integer!"); Res.insertBits(EltAsInt, BigEndian ? (NElts - I - 1) : I); } SmallVector<uint8_t, 8> Bytes(NElts / 8); llvm::StoreIntToMemory(Res, &*Bytes.begin(), NElts / 8); Buffer.writeObject(Offset, Bytes); } else { // Iterate over each of the elements and write them out to the buffer at // the appropriate offset. CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy); for (unsigned I = 0; I < NElts; ++I) { if (!visit(Val.getVectorElt(I), EltTy, Offset + I * EltSizeChars)) return false; } } return true; } bool visitInt(const APSInt &Val, QualType Ty, CharUnits Offset) { APSInt AdjustedVal = Val; unsigned Width = AdjustedVal.getBitWidth(); Loading @@ -7121,7 +7141,7 @@ class APValueToBufferConverter { AdjustedVal = AdjustedVal.extend(Width); } SmallVector<unsigned char, 8> Bytes(Width / 8); SmallVector<uint8_t, 8> Bytes(Width / 8); llvm::StoreIntToMemory(AdjustedVal, &*Bytes.begin(), Width / 8); Buffer.writeObject(Offset, Bytes); return true; Loading Loading @@ -7322,6 +7342,77 @@ class BufferToAPValueConverter { return ArrayValue; } std::optional<APValue> visit(const VectorType *VTy, CharUnits Offset) { QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy); if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, // so its layout is unspecified. For now, we'll simply treat these cases // as unsupported (this should only be possible with OpenCL bool vectors // whose element count isn't a multiple of the byte size). Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_vector) << QualType(VTy, 0) << EltSize << NElts << Info.Ctx.getCharWidth(); return std::nullopt; } if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) { // The layout for x86_fp80 vectors seems to be handled very inconsistently // by both clang and LLVM, so for now we won't allow bit_casts involving // it in a constexpr context. Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << EltTy; return std::nullopt; } SmallVector<APValue, 4> Elts; Elts.reserve(NElts); if (VTy->isExtVectorBoolType()) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't read // individual bits from the BitCastBuffer, we'll buffer all of the // elements together into an appropriately sized APInt and write them all // out at once. Because we don't accept vectors where NElts * EltSize // isn't a multiple of the char size, there will be no padding space, so // we don't have to worry about reading any padding data which didn't // actually need to be accessed. bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); SmallVector<uint8_t, 8> Bytes; Bytes.reserve(NElts / 8); if (!Buffer.readObject(Offset, CharUnits::fromQuantity(NElts / 8), Bytes)) return std::nullopt; APSInt SValInt(NElts, true); llvm::LoadIntFromMemory(SValInt, &*Bytes.begin(), Bytes.size()); for (unsigned I = 0; I < NElts; ++I) { llvm::APInt Elt = SValInt.extractBits(1, (BigEndian ? NElts - I - 1 : I) * EltSize); Elts.emplace_back( APSInt(std::move(Elt), !EltTy->isSignedIntegerType())); } } else { // Iterate over each of the elements and read them from the buffer at // the appropriate offset. CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy); for (unsigned I = 0; I < NElts; ++I) { std::optional<APValue> EltValue = visitType(EltTy, Offset + I * EltSizeChars); if (!EltValue) return std::nullopt; Elts.push_back(std::move(*EltValue)); } } return APValue(Elts.data(), Elts.size()); } std::optional<APValue> visit(const Type *Ty, CharUnits Offset) { return unsupportedType(QualType(Ty, 0)); } Loading Loading @@ -7421,25 +7512,15 @@ static bool checkBitCastConstexprEligibility(EvalInfo *Info, return SourceOK; } static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, APValue &SourceValue, static bool handleRValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, const APValue &SourceRValue, const CastExpr *BCE) { assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 && "no host or target supports non 8-bit chars"); assert(SourceValue.isLValue() && "LValueToRValueBitcast requires an lvalue operand!"); if (!checkBitCastConstexprEligibility(&Info, Info.Ctx, BCE)) return false; LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); if (!handleLValueToRValueConversion( Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, SourceRValue, /*WantObjectRepresentation=*/true)) return false; // Read out SourceValue into a char buffer. std::optional<BitCastBuffer> Buffer = APValueToBufferConverter::convert(Info, SourceRValue, BCE); Loading @@ -7456,6 +7537,25 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, return true; } static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, APValue &SourceValue, const CastExpr *BCE) { assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 && "no host or target supports non 8-bit chars"); assert(SourceValue.isLValue() && "LValueToRValueBitcast requires an lvalue operand!"); LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); if (!handleLValueToRValueConversion( Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, SourceRValue, /*WantObjectRepresentation=*/true)) return false; return handleRValueToRValueBitCast(Info, DestValue, SourceRValue, BCE); } template <class Derived> class ExprEvaluatorBase : public ConstStmtVisitor<Derived, bool> { Loading Loading @@ -10540,41 +10640,22 @@ bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) { return Success(Elts, E); } case CK_BitCast: { // Evaluate the operand into an APInt we can extract from. llvm::APInt SValInt; if (!EvalAndBitcastToAPInt(Info, SE, SValInt)) APValue SVal; if (!Evaluate(SVal, Info, SE)) return false; if (!SVal.isInt() && !SVal.isFloat() && !SVal.isVector()) { // Give up if the input isn't an int, float, or vector. For example, we // reject "(v4i16)(intptr_t)&a". Info.FFDiag(E, diag::note_constexpr_invalid_cast) << 2 << Info.Ctx.getLangOpts().CPlusPlus; return false; // Extract the elements QualType EltTy = VTy->getElementType(); unsigned EltSize = Info.Ctx.getTypeSize(EltTy); bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); SmallVector<APValue, 4> Elts; if (EltTy->isRealFloatingType()) { const llvm::fltSemantics &Sem = Info.Ctx.getFloatTypeSemantics(EltTy); unsigned FloatEltSize = EltSize; if (&Sem == &APFloat::x87DoubleExtended()) FloatEltSize = 80; for (unsigned i = 0; i < NElts; i++) { llvm::APInt Elt; if (BigEndian) Elt = SValInt.rotl(i * EltSize + FloatEltSize).trunc(FloatEltSize); else Elt = SValInt.rotr(i * EltSize).trunc(FloatEltSize); Elts.push_back(APValue(APFloat(Sem, Elt))); } } else if (EltTy->isIntegerType()) { for (unsigned i = 0; i < NElts; i++) { llvm::APInt Elt; if (BigEndian) Elt = SValInt.rotl(i*EltSize+EltSize).zextOrTrunc(EltSize); else Elt = SValInt.rotr(i*EltSize).zextOrTrunc(EltSize); Elts.push_back(APValue(APSInt(Elt, !EltTy->isSignedIntegerType()))); } } else { return Error(E); } return Success(Elts, E); if (!handleRValueToRValueBitCast(Info, Result, SVal, E)) return false; return true; } default: return ExprEvaluatorBaseTy::VisitCastExpr(E); Loading clang/lib/CodeGen/CGExprConstant.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -2152,6 +2152,9 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, Inits[I] = llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt()); else if (Elt.isFloat()) Inits[I] = llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat()); else if (Elt.isIndeterminate()) Inits[I] = llvm::UndefValue::get(CGM.getTypes().ConvertType( DestType->castAs<VectorType>()->getElementType())); else llvm_unreachable("unsupported vector element type"); } Loading clang/test/CodeGen/const-init.c +5 −4 Original line number Diff line number Diff line Loading @@ -140,11 +140,12 @@ void g28(void) { typedef short v12i16 __attribute((vector_size(24))); typedef long double v2f80 __attribute((vector_size(24))); // CHECK: @g28.a = internal global <1 x i64> <i64 10> // CHECK: @g28.b = internal global <12 x i16> <i16 0, i16 0, i16 0, i16 -32768, i16 16383, i16 0, i16 0, i16 0, i16 0, i16 -32768, i16 16384, i16 0> // CHECK: @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32 // @g28.b = internal global <12 x i16> <i16 0, i16 0, i16 0, i16 -32768, i16 16383, i16 0, i16 0, i16 0, i16 0, i16 -32768, i16 16384, i16 0> // @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32 static v1i64 a = (v1i64)10LL; static v12i16 b = (v12i16)(v2f80){1,2}; static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; //FIXME: support constant bitcast between vectors of x86_fp80 //static v12i16 b = (v12i16)(v2f80){1,2}; //static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; } // PR13643 Loading clang/test/SemaCXX/constexpr-builtin-bit-cast-fp80.cpp 0 → 100644 +66 −0 Original line number Diff line number Diff line // RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple i386-pc-linux-gnu %s // This is separate from constexpr-builtin-bit-cast.cpp because we want to // compile for i386 so that sizeof(long double) is 12. typedef long double fp80x2_v __attribute__((ext_vector_type(2))); static_assert(sizeof(long double) == 12, ""); static_assert(sizeof(fp80x2_v) == 32, ""); struct fp80x2_s { char _data[2 * 10]; unsigned char _pad[sizeof(fp80x2_v) - 2 * 10]; constexpr bool operator==(const fp80x2_s& rhs) const { for (int i = 0; i < 2 * 10; ++i) if (_data[i] != rhs._data[i]) return false; return true; } }; namespace builtin_bit_cast { constexpr static fp80x2_v test_vec_fp80 = { 1, 2 }; constexpr static fp80x2_s test_str_fp80 = { { 0, 0, 0, 0, 0, 0, 0, -128, -1, 63, 0, 0, 0, 0, 0, 0, 0, -128, 0, 64 }, {} }; // expected-error@+2 {{static assertion expression is not an integral constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} static_assert(__builtin_bit_cast(fp80x2_s, test_vec_fp80) == test_str_fp80, ""); // expected-error@+2 {{static assertion expression is not an integral constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} static_assert(__builtin_bit_cast(fp80x2_s, __builtin_bit_cast(fp80x2_v, test_str_fp80)) == test_str_fp80, ""); // expected-error@+2 {{constexpr variable 'bad_str_fp80_0' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_0 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[0]; // expected-error@+2 {{constexpr variable 'bad_str_fp80_1' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_1 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[1]; // expected-error@+2 {{constexpr variable 'bad_str_fp80_11' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_11 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[11]; // expected-error@+2 {{constexpr variable 'struct2v' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static fp80x2_v struct2v = __builtin_bit_cast(fp80x2_v, test_str_fp80); } namespace c_cast { typedef short v12i16 __attribute((vector_size(24))); typedef long double v2f80 __attribute((vector_size(24))); // FIXME: re-enable the corresponding test cases in CodeGen/const-init.c when // constexpr bitcast with x86_fp80 is supported // expected-error@+2 {{constexpr variable 'b' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static v12i16 b = (v12i16)(v2f80){1,2}; // expected-error@+2 {{constexpr variable 'c' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; } Loading
clang/include/clang/Basic/DiagnosticASTKinds.td +4 −1 Original line number Diff line number Diff line Loading @@ -317,7 +317,7 @@ def note_constexpr_memcpy_unsupported : Note< "source is not a contiguous array of at least %4 elements of type %3|" "destination is not a contiguous array of at least %4 elements of type %3}2">; def note_constexpr_bit_cast_unsupported_type : Note< "constexpr bit_cast involving type %0 is not yet supported">; "constexpr bit cast involving type %0 is not yet supported">; def note_constexpr_bit_cast_unsupported_bitfield : Note< "constexpr bit_cast involving bit-field is not yet supported">; def note_constexpr_bit_cast_invalid_type : Note< Loading @@ -326,6 +326,9 @@ def note_constexpr_bit_cast_invalid_type : Note< "%select{type|member}1 is not allowed in a constant expression">; def note_constexpr_bit_cast_invalid_subtype : Note< "invalid type %0 is a %select{member|base}1 of %2">; def note_constexpr_bit_cast_invalid_vector : Note< "bit_cast involving type %0 is not allowed in a constant expression; " "element size %1 * element count %2 is not a multiple of the byte size %3">; def note_constexpr_bit_cast_indet_dest : Note< "indeterminate value can only initialize an object of type 'unsigned char'" "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; Loading
clang/lib/AST/ExprConstant.cpp +175 −94 Original line number Diff line number Diff line Loading @@ -2737,53 +2737,6 @@ static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E, return true; } static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E, llvm::APInt &Res) { APValue SVal; if (!Evaluate(SVal, Info, E)) return false; if (SVal.isInt()) { Res = SVal.getInt(); return true; } if (SVal.isFloat()) { Res = SVal.getFloat().bitcastToAPInt(); return true; } if (SVal.isVector()) { QualType VecTy = E->getType(); unsigned VecSize = Info.Ctx.getTypeSize(VecTy); QualType EltTy = VecTy->castAs<VectorType>()->getElementType(); unsigned EltSize = Info.Ctx.getTypeSize(EltTy); bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); Res = llvm::APInt::getZero(VecSize); for (unsigned i = 0; i < SVal.getVectorLength(); i++) { APValue &Elt = SVal.getVectorElt(i); llvm::APInt EltAsInt; if (Elt.isInt()) { EltAsInt = Elt.getInt(); } else if (Elt.isFloat()) { EltAsInt = Elt.getFloat().bitcastToAPInt(); } else { // Don't try to handle vectors of anything other than int or float // (not sure if it's possible to hit this case). Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } unsigned BaseEltSize = EltAsInt.getBitWidth(); if (BigEndian) Res |= EltAsInt.zextOrTrunc(VecSize).rotr(i*EltSize+BaseEltSize); else Res |= EltAsInt.zextOrTrunc(VecSize).rotl(i*EltSize); } return true; } // Give up if the input isn't an int, float, or vector. For example, we // reject "(v4i16)(intptr_t)&a". Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } /// Perform the given integer operation, which is known to need at most BitWidth /// bits, and check for overflow in the original type (if that type was not an /// unsigned type). Loading Loading @@ -7023,10 +6976,11 @@ class APValueToBufferConverter { return visitArray(Val, Ty, Offset); case APValue::Struct: return visitRecord(Val, Ty, Offset); case APValue::Vector: return visitVector(Val, Ty, Offset); case APValue::ComplexInt: case APValue::ComplexFloat: case APValue::Vector: case APValue::FixedPoint: // FIXME: We should support these. Loading Loading @@ -7113,6 +7067,72 @@ class APValueToBufferConverter { return true; } bool visitVector(const APValue &Val, QualType Ty, CharUnits Offset) { const VectorType *VTy = Ty->castAs<VectorType>(); QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy); if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, // so its layout is unspecified. For now, we'll simply treat these cases // as unsupported (this should only be possible with OpenCL bool vectors // whose element count isn't a multiple of the byte size). Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_vector) << Ty.getCanonicalType() << EltSize << NElts << Info.Ctx.getCharWidth(); return false; } if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) { // The layout for x86_fp80 vectors seems to be handled very inconsistently // by both clang and LLVM, so for now we won't allow bit_casts involving // it in a constexpr context. Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << EltTy; return false; } if (VTy->isExtVectorBoolType()) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't write // individual bits to the BitCastBuffer, we'll buffer all of the elements // together into an appropriately sized APInt and write them all out at // once. Because we don't accept vectors where NElts * EltSize isn't a // multiple of the char size, there will be no padding space, so we don't // have to worry about writing data which should have been left // uninitialized. bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); llvm::APInt Res = llvm::APInt::getZero(NElts); for (unsigned I = 0; I < NElts; ++I) { const llvm::APSInt &EltAsInt = Val.getVectorElt(I).getInt(); assert(EltAsInt.isUnsigned() && EltAsInt.getBitWidth() == 1 && "bool vector element must be 1-bit unsigned integer!"); Res.insertBits(EltAsInt, BigEndian ? (NElts - I - 1) : I); } SmallVector<uint8_t, 8> Bytes(NElts / 8); llvm::StoreIntToMemory(Res, &*Bytes.begin(), NElts / 8); Buffer.writeObject(Offset, Bytes); } else { // Iterate over each of the elements and write them out to the buffer at // the appropriate offset. CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy); for (unsigned I = 0; I < NElts; ++I) { if (!visit(Val.getVectorElt(I), EltTy, Offset + I * EltSizeChars)) return false; } } return true; } bool visitInt(const APSInt &Val, QualType Ty, CharUnits Offset) { APSInt AdjustedVal = Val; unsigned Width = AdjustedVal.getBitWidth(); Loading @@ -7121,7 +7141,7 @@ class APValueToBufferConverter { AdjustedVal = AdjustedVal.extend(Width); } SmallVector<unsigned char, 8> Bytes(Width / 8); SmallVector<uint8_t, 8> Bytes(Width / 8); llvm::StoreIntToMemory(AdjustedVal, &*Bytes.begin(), Width / 8); Buffer.writeObject(Offset, Bytes); return true; Loading Loading @@ -7322,6 +7342,77 @@ class BufferToAPValueConverter { return ArrayValue; } std::optional<APValue> visit(const VectorType *VTy, CharUnits Offset) { QualType EltTy = VTy->getElementType(); unsigned NElts = VTy->getNumElements(); unsigned EltSize = VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy); if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) { // The vector's size in bits is not a multiple of the target's byte size, // so its layout is unspecified. For now, we'll simply treat these cases // as unsupported (this should only be possible with OpenCL bool vectors // whose element count isn't a multiple of the byte size). Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_vector) << QualType(VTy, 0) << EltSize << NElts << Info.Ctx.getCharWidth(); return std::nullopt; } if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) { // The layout for x86_fp80 vectors seems to be handled very inconsistently // by both clang and LLVM, so for now we won't allow bit_casts involving // it in a constexpr context. Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << EltTy; return std::nullopt; } SmallVector<APValue, 4> Elts; Elts.reserve(NElts); if (VTy->isExtVectorBoolType()) { // Special handling for OpenCL bool vectors: // Since these vectors are stored as packed bits, but we can't read // individual bits from the BitCastBuffer, we'll buffer all of the // elements together into an appropriately sized APInt and write them all // out at once. Because we don't accept vectors where NElts * EltSize // isn't a multiple of the char size, there will be no padding space, so // we don't have to worry about reading any padding data which didn't // actually need to be accessed. bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); SmallVector<uint8_t, 8> Bytes; Bytes.reserve(NElts / 8); if (!Buffer.readObject(Offset, CharUnits::fromQuantity(NElts / 8), Bytes)) return std::nullopt; APSInt SValInt(NElts, true); llvm::LoadIntFromMemory(SValInt, &*Bytes.begin(), Bytes.size()); for (unsigned I = 0; I < NElts; ++I) { llvm::APInt Elt = SValInt.extractBits(1, (BigEndian ? NElts - I - 1 : I) * EltSize); Elts.emplace_back( APSInt(std::move(Elt), !EltTy->isSignedIntegerType())); } } else { // Iterate over each of the elements and read them from the buffer at // the appropriate offset. CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy); for (unsigned I = 0; I < NElts; ++I) { std::optional<APValue> EltValue = visitType(EltTy, Offset + I * EltSizeChars); if (!EltValue) return std::nullopt; Elts.push_back(std::move(*EltValue)); } } return APValue(Elts.data(), Elts.size()); } std::optional<APValue> visit(const Type *Ty, CharUnits Offset) { return unsupportedType(QualType(Ty, 0)); } Loading Loading @@ -7421,25 +7512,15 @@ static bool checkBitCastConstexprEligibility(EvalInfo *Info, return SourceOK; } static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, APValue &SourceValue, static bool handleRValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, const APValue &SourceRValue, const CastExpr *BCE) { assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 && "no host or target supports non 8-bit chars"); assert(SourceValue.isLValue() && "LValueToRValueBitcast requires an lvalue operand!"); if (!checkBitCastConstexprEligibility(&Info, Info.Ctx, BCE)) return false; LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); if (!handleLValueToRValueConversion( Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, SourceRValue, /*WantObjectRepresentation=*/true)) return false; // Read out SourceValue into a char buffer. std::optional<BitCastBuffer> Buffer = APValueToBufferConverter::convert(Info, SourceRValue, BCE); Loading @@ -7456,6 +7537,25 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, return true; } static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, APValue &SourceValue, const CastExpr *BCE) { assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 && "no host or target supports non 8-bit chars"); assert(SourceValue.isLValue() && "LValueToRValueBitcast requires an lvalue operand!"); LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); if (!handleLValueToRValueConversion( Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, SourceRValue, /*WantObjectRepresentation=*/true)) return false; return handleRValueToRValueBitCast(Info, DestValue, SourceRValue, BCE); } template <class Derived> class ExprEvaluatorBase : public ConstStmtVisitor<Derived, bool> { Loading Loading @@ -10540,41 +10640,22 @@ bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) { return Success(Elts, E); } case CK_BitCast: { // Evaluate the operand into an APInt we can extract from. llvm::APInt SValInt; if (!EvalAndBitcastToAPInt(Info, SE, SValInt)) APValue SVal; if (!Evaluate(SVal, Info, SE)) return false; if (!SVal.isInt() && !SVal.isFloat() && !SVal.isVector()) { // Give up if the input isn't an int, float, or vector. For example, we // reject "(v4i16)(intptr_t)&a". Info.FFDiag(E, diag::note_constexpr_invalid_cast) << 2 << Info.Ctx.getLangOpts().CPlusPlus; return false; // Extract the elements QualType EltTy = VTy->getElementType(); unsigned EltSize = Info.Ctx.getTypeSize(EltTy); bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); SmallVector<APValue, 4> Elts; if (EltTy->isRealFloatingType()) { const llvm::fltSemantics &Sem = Info.Ctx.getFloatTypeSemantics(EltTy); unsigned FloatEltSize = EltSize; if (&Sem == &APFloat::x87DoubleExtended()) FloatEltSize = 80; for (unsigned i = 0; i < NElts; i++) { llvm::APInt Elt; if (BigEndian) Elt = SValInt.rotl(i * EltSize + FloatEltSize).trunc(FloatEltSize); else Elt = SValInt.rotr(i * EltSize).trunc(FloatEltSize); Elts.push_back(APValue(APFloat(Sem, Elt))); } } else if (EltTy->isIntegerType()) { for (unsigned i = 0; i < NElts; i++) { llvm::APInt Elt; if (BigEndian) Elt = SValInt.rotl(i*EltSize+EltSize).zextOrTrunc(EltSize); else Elt = SValInt.rotr(i*EltSize).zextOrTrunc(EltSize); Elts.push_back(APValue(APSInt(Elt, !EltTy->isSignedIntegerType()))); } } else { return Error(E); } return Success(Elts, E); if (!handleRValueToRValueBitCast(Info, Result, SVal, E)) return false; return true; } default: return ExprEvaluatorBaseTy::VisitCastExpr(E); Loading
clang/lib/CodeGen/CGExprConstant.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -2152,6 +2152,9 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, Inits[I] = llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt()); else if (Elt.isFloat()) Inits[I] = llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat()); else if (Elt.isIndeterminate()) Inits[I] = llvm::UndefValue::get(CGM.getTypes().ConvertType( DestType->castAs<VectorType>()->getElementType())); else llvm_unreachable("unsupported vector element type"); } Loading
clang/test/CodeGen/const-init.c +5 −4 Original line number Diff line number Diff line Loading @@ -140,11 +140,12 @@ void g28(void) { typedef short v12i16 __attribute((vector_size(24))); typedef long double v2f80 __attribute((vector_size(24))); // CHECK: @g28.a = internal global <1 x i64> <i64 10> // CHECK: @g28.b = internal global <12 x i16> <i16 0, i16 0, i16 0, i16 -32768, i16 16383, i16 0, i16 0, i16 0, i16 0, i16 -32768, i16 16384, i16 0> // CHECK: @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32 // @g28.b = internal global <12 x i16> <i16 0, i16 0, i16 0, i16 -32768, i16 16383, i16 0, i16 0, i16 0, i16 0, i16 -32768, i16 16384, i16 0> // @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32 static v1i64 a = (v1i64)10LL; static v12i16 b = (v12i16)(v2f80){1,2}; static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; //FIXME: support constant bitcast between vectors of x86_fp80 //static v12i16 b = (v12i16)(v2f80){1,2}; //static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; } // PR13643 Loading
clang/test/SemaCXX/constexpr-builtin-bit-cast-fp80.cpp 0 → 100644 +66 −0 Original line number Diff line number Diff line // RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple i386-pc-linux-gnu %s // This is separate from constexpr-builtin-bit-cast.cpp because we want to // compile for i386 so that sizeof(long double) is 12. typedef long double fp80x2_v __attribute__((ext_vector_type(2))); static_assert(sizeof(long double) == 12, ""); static_assert(sizeof(fp80x2_v) == 32, ""); struct fp80x2_s { char _data[2 * 10]; unsigned char _pad[sizeof(fp80x2_v) - 2 * 10]; constexpr bool operator==(const fp80x2_s& rhs) const { for (int i = 0; i < 2 * 10; ++i) if (_data[i] != rhs._data[i]) return false; return true; } }; namespace builtin_bit_cast { constexpr static fp80x2_v test_vec_fp80 = { 1, 2 }; constexpr static fp80x2_s test_str_fp80 = { { 0, 0, 0, 0, 0, 0, 0, -128, -1, 63, 0, 0, 0, 0, 0, 0, 0, -128, 0, 64 }, {} }; // expected-error@+2 {{static assertion expression is not an integral constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} static_assert(__builtin_bit_cast(fp80x2_s, test_vec_fp80) == test_str_fp80, ""); // expected-error@+2 {{static assertion expression is not an integral constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} static_assert(__builtin_bit_cast(fp80x2_s, __builtin_bit_cast(fp80x2_v, test_str_fp80)) == test_str_fp80, ""); // expected-error@+2 {{constexpr variable 'bad_str_fp80_0' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_0 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[0]; // expected-error@+2 {{constexpr variable 'bad_str_fp80_1' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_1 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[1]; // expected-error@+2 {{constexpr variable 'bad_str_fp80_11' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static char bad_str_fp80_11 = __builtin_bit_cast(fp80x2_s, test_vec_fp80)._pad[11]; // expected-error@+2 {{constexpr variable 'struct2v' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static fp80x2_v struct2v = __builtin_bit_cast(fp80x2_v, test_str_fp80); } namespace c_cast { typedef short v12i16 __attribute((vector_size(24))); typedef long double v2f80 __attribute((vector_size(24))); // FIXME: re-enable the corresponding test cases in CodeGen/const-init.c when // constexpr bitcast with x86_fp80 is supported // expected-error@+2 {{constexpr variable 'b' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static v12i16 b = (v12i16)(v2f80){1,2}; // expected-error@+2 {{constexpr variable 'c' must be initialized by a constant expression}} // expected-note@+1 {{constexpr bit cast involving type 'long double' is not yet supported}} constexpr static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0}; }