Skip to content

Commit b45236f

Browse files
authored
[clang] Implement constexpr bit_cast for vectors (#66894)
This makes __builtin_bit_cast support converting to and from vector types in a constexpr context.
1 parent 0f8615f commit b45236f

File tree

6 files changed

+297
-99
lines changed

6 files changed

+297
-99
lines changed

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ def note_constexpr_memcpy_unsupported : Note<
317317
"source is not a contiguous array of at least %4 elements of type %3|"
318318
"destination is not a contiguous array of at least %4 elements of type %3}2">;
319319
def note_constexpr_bit_cast_unsupported_type : Note<
320-
"constexpr bit_cast involving type %0 is not yet supported">;
320+
"constexpr bit cast involving type %0 is not yet supported">;
321321
def note_constexpr_bit_cast_unsupported_bitfield : Note<
322322
"constexpr bit_cast involving bit-field is not yet supported">;
323323
def note_constexpr_bit_cast_invalid_type : Note<
@@ -326,6 +326,9 @@ def note_constexpr_bit_cast_invalid_type : Note<
326326
"%select{type|member}1 is not allowed in a constant expression">;
327327
def note_constexpr_bit_cast_invalid_subtype : Note<
328328
"invalid type %0 is a %select{member|base}1 of %2">;
329+
def note_constexpr_bit_cast_invalid_vector : Note<
330+
"bit_cast involving type %0 is not allowed in a constant expression; "
331+
"element size %1 * element count %2 is not a multiple of the byte size %3">;
329332
def note_constexpr_bit_cast_indet_dest : Note<
330333
"indeterminate value can only initialize an object of type 'unsigned char'"
331334
"%select{, 'char',|}1 or 'std::byte'; %0 is invalid">;

clang/lib/AST/ExprConstant.cpp

Lines changed: 175 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,53 +2737,6 @@ static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E,
27372737
return true;
27382738
}
27392739

2740-
static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E,
2741-
llvm::APInt &Res) {
2742-
APValue SVal;
2743-
if (!Evaluate(SVal, Info, E))
2744-
return false;
2745-
if (SVal.isInt()) {
2746-
Res = SVal.getInt();
2747-
return true;
2748-
}
2749-
if (SVal.isFloat()) {
2750-
Res = SVal.getFloat().bitcastToAPInt();
2751-
return true;
2752-
}
2753-
if (SVal.isVector()) {
2754-
QualType VecTy = E->getType();
2755-
unsigned VecSize = Info.Ctx.getTypeSize(VecTy);
2756-
QualType EltTy = VecTy->castAs<VectorType>()->getElementType();
2757-
unsigned EltSize = Info.Ctx.getTypeSize(EltTy);
2758-
bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
2759-
Res = llvm::APInt::getZero(VecSize);
2760-
for (unsigned i = 0; i < SVal.getVectorLength(); i++) {
2761-
APValue &Elt = SVal.getVectorElt(i);
2762-
llvm::APInt EltAsInt;
2763-
if (Elt.isInt()) {
2764-
EltAsInt = Elt.getInt();
2765-
} else if (Elt.isFloat()) {
2766-
EltAsInt = Elt.getFloat().bitcastToAPInt();
2767-
} else {
2768-
// Don't try to handle vectors of anything other than int or float
2769-
// (not sure if it's possible to hit this case).
2770-
Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
2771-
return false;
2772-
}
2773-
unsigned BaseEltSize = EltAsInt.getBitWidth();
2774-
if (BigEndian)
2775-
Res |= EltAsInt.zextOrTrunc(VecSize).rotr(i*EltSize+BaseEltSize);
2776-
else
2777-
Res |= EltAsInt.zextOrTrunc(VecSize).rotl(i*EltSize);
2778-
}
2779-
return true;
2780-
}
2781-
// Give up if the input isn't an int, float, or vector. For example, we
2782-
// reject "(v4i16)(intptr_t)&a".
2783-
Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
2784-
return false;
2785-
}
2786-
27872740
/// Perform the given integer operation, which is known to need at most BitWidth
27882741
/// bits, and check for overflow in the original type (if that type was not an
27892742
/// unsigned type).
@@ -7023,10 +6976,11 @@ class APValueToBufferConverter {
70236976
return visitArray(Val, Ty, Offset);
70246977
case APValue::Struct:
70256978
return visitRecord(Val, Ty, Offset);
6979+
case APValue::Vector:
6980+
return visitVector(Val, Ty, Offset);
70266981

70276982
case APValue::ComplexInt:
70286983
case APValue::ComplexFloat:
7029-
case APValue::Vector:
70306984
case APValue::FixedPoint:
70316985
// FIXME: We should support these.
70326986

@@ -7113,6 +7067,72 @@ class APValueToBufferConverter {
71137067
return true;
71147068
}
71157069

7070+
bool visitVector(const APValue &Val, QualType Ty, CharUnits Offset) {
7071+
const VectorType *VTy = Ty->castAs<VectorType>();
7072+
QualType EltTy = VTy->getElementType();
7073+
unsigned NElts = VTy->getNumElements();
7074+
unsigned EltSize =
7075+
VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy);
7076+
7077+
if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) {
7078+
// The vector's size in bits is not a multiple of the target's byte size,
7079+
// so its layout is unspecified. For now, we'll simply treat these cases
7080+
// as unsupported (this should only be possible with OpenCL bool vectors
7081+
// whose element count isn't a multiple of the byte size).
7082+
Info.FFDiag(BCE->getBeginLoc(),
7083+
diag::note_constexpr_bit_cast_invalid_vector)
7084+
<< Ty.getCanonicalType() << EltSize << NElts
7085+
<< Info.Ctx.getCharWidth();
7086+
return false;
7087+
}
7088+
7089+
if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) ==
7090+
&APFloat::x87DoubleExtended()) {
7091+
// The layout for x86_fp80 vectors seems to be handled very inconsistently
7092+
// by both clang and LLVM, so for now we won't allow bit_casts involving
7093+
// it in a constexpr context.
7094+
Info.FFDiag(BCE->getBeginLoc(),
7095+
diag::note_constexpr_bit_cast_unsupported_type)
7096+
<< EltTy;
7097+
return false;
7098+
}
7099+
7100+
if (VTy->isExtVectorBoolType()) {
7101+
// Special handling for OpenCL bool vectors:
7102+
// Since these vectors are stored as packed bits, but we can't write
7103+
// individual bits to the BitCastBuffer, we'll buffer all of the elements
7104+
// together into an appropriately sized APInt and write them all out at
7105+
// once. Because we don't accept vectors where NElts * EltSize isn't a
7106+
// multiple of the char size, there will be no padding space, so we don't
7107+
// have to worry about writing data which should have been left
7108+
// uninitialized.
7109+
bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
7110+
7111+
llvm::APInt Res = llvm::APInt::getZero(NElts);
7112+
for (unsigned I = 0; I < NElts; ++I) {
7113+
const llvm::APSInt &EltAsInt = Val.getVectorElt(I).getInt();
7114+
assert(EltAsInt.isUnsigned() && EltAsInt.getBitWidth() == 1 &&
7115+
"bool vector element must be 1-bit unsigned integer!");
7116+
7117+
Res.insertBits(EltAsInt, BigEndian ? (NElts - I - 1) : I);
7118+
}
7119+
7120+
SmallVector<uint8_t, 8> Bytes(NElts / 8);
7121+
llvm::StoreIntToMemory(Res, &*Bytes.begin(), NElts / 8);
7122+
Buffer.writeObject(Offset, Bytes);
7123+
} else {
7124+
// Iterate over each of the elements and write them out to the buffer at
7125+
// the appropriate offset.
7126+
CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy);
7127+
for (unsigned I = 0; I < NElts; ++I) {
7128+
if (!visit(Val.getVectorElt(I), EltTy, Offset + I * EltSizeChars))
7129+
return false;
7130+
}
7131+
}
7132+
7133+
return true;
7134+
}
7135+
71167136
bool visitInt(const APSInt &Val, QualType Ty, CharUnits Offset) {
71177137
APSInt AdjustedVal = Val;
71187138
unsigned Width = AdjustedVal.getBitWidth();
@@ -7121,7 +7141,7 @@ class APValueToBufferConverter {
71217141
AdjustedVal = AdjustedVal.extend(Width);
71227142
}
71237143

7124-
SmallVector<unsigned char, 8> Bytes(Width / 8);
7144+
SmallVector<uint8_t, 8> Bytes(Width / 8);
71257145
llvm::StoreIntToMemory(AdjustedVal, &*Bytes.begin(), Width / 8);
71267146
Buffer.writeObject(Offset, Bytes);
71277147
return true;
@@ -7322,6 +7342,77 @@ class BufferToAPValueConverter {
73227342
return ArrayValue;
73237343
}
73247344

7345+
std::optional<APValue> visit(const VectorType *VTy, CharUnits Offset) {
7346+
QualType EltTy = VTy->getElementType();
7347+
unsigned NElts = VTy->getNumElements();
7348+
unsigned EltSize =
7349+
VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy);
7350+
7351+
if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) {
7352+
// The vector's size in bits is not a multiple of the target's byte size,
7353+
// so its layout is unspecified. For now, we'll simply treat these cases
7354+
// as unsupported (this should only be possible with OpenCL bool vectors
7355+
// whose element count isn't a multiple of the byte size).
7356+
Info.FFDiag(BCE->getBeginLoc(),
7357+
diag::note_constexpr_bit_cast_invalid_vector)
7358+
<< QualType(VTy, 0) << EltSize << NElts << Info.Ctx.getCharWidth();
7359+
return std::nullopt;
7360+
}
7361+
7362+
if (EltTy->isRealFloatingType() && &Info.Ctx.getFloatTypeSemantics(EltTy) ==
7363+
&APFloat::x87DoubleExtended()) {
7364+
// The layout for x86_fp80 vectors seems to be handled very inconsistently
7365+
// by both clang and LLVM, so for now we won't allow bit_casts involving
7366+
// it in a constexpr context.
7367+
Info.FFDiag(BCE->getBeginLoc(),
7368+
diag::note_constexpr_bit_cast_unsupported_type)
7369+
<< EltTy;
7370+
return std::nullopt;
7371+
}
7372+
7373+
SmallVector<APValue, 4> Elts;
7374+
Elts.reserve(NElts);
7375+
if (VTy->isExtVectorBoolType()) {
7376+
// Special handling for OpenCL bool vectors:
7377+
// Since these vectors are stored as packed bits, but we can't read
7378+
// individual bits from the BitCastBuffer, we'll buffer all of the
7379+
// elements together into an appropriately sized APInt and write them all
7380+
// out at once. Because we don't accept vectors where NElts * EltSize
7381+
// isn't a multiple of the char size, there will be no padding space, so
7382+
// we don't have to worry about reading any padding data which didn't
7383+
// actually need to be accessed.
7384+
bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
7385+
7386+
SmallVector<uint8_t, 8> Bytes;
7387+
Bytes.reserve(NElts / 8);
7388+
if (!Buffer.readObject(Offset, CharUnits::fromQuantity(NElts / 8), Bytes))
7389+
return std::nullopt;
7390+
7391+
APSInt SValInt(NElts, true);
7392+
llvm::LoadIntFromMemory(SValInt, &*Bytes.begin(), Bytes.size());
7393+
7394+
for (unsigned I = 0; I < NElts; ++I) {
7395+
llvm::APInt Elt =
7396+
SValInt.extractBits(1, (BigEndian ? NElts - I - 1 : I) * EltSize);
7397+
Elts.emplace_back(
7398+
APSInt(std::move(Elt), !EltTy->isSignedIntegerType()));
7399+
}
7400+
} else {
7401+
// Iterate over each of the elements and read them from the buffer at
7402+
// the appropriate offset.
7403+
CharUnits EltSizeChars = Info.Ctx.getTypeSizeInChars(EltTy);
7404+
for (unsigned I = 0; I < NElts; ++I) {
7405+
std::optional<APValue> EltValue =
7406+
visitType(EltTy, Offset + I * EltSizeChars);
7407+
if (!EltValue)
7408+
return std::nullopt;
7409+
Elts.push_back(std::move(*EltValue));
7410+
}
7411+
}
7412+
7413+
return APValue(Elts.data(), Elts.size());
7414+
}
7415+
73257416
std::optional<APValue> visit(const Type *Ty, CharUnits Offset) {
73267417
return unsupportedType(QualType(Ty, 0));
73277418
}
@@ -7421,25 +7512,15 @@ static bool checkBitCastConstexprEligibility(EvalInfo *Info,
74217512
return SourceOK;
74227513
}
74237514

7424-
static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
7425-
APValue &SourceValue,
7515+
static bool handleRValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
7516+
const APValue &SourceRValue,
74267517
const CastExpr *BCE) {
74277518
assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 &&
74287519
"no host or target supports non 8-bit chars");
7429-
assert(SourceValue.isLValue() &&
7430-
"LValueToRValueBitcast requires an lvalue operand!");
74317520

74327521
if (!checkBitCastConstexprEligibility(&Info, Info.Ctx, BCE))
74337522
return false;
74347523

7435-
LValue SourceLValue;
7436-
APValue SourceRValue;
7437-
SourceLValue.setFrom(Info.Ctx, SourceValue);
7438-
if (!handleLValueToRValueConversion(
7439-
Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue,
7440-
SourceRValue, /*WantObjectRepresentation=*/true))
7441-
return false;
7442-
74437524
// Read out SourceValue into a char buffer.
74447525
std::optional<BitCastBuffer> Buffer =
74457526
APValueToBufferConverter::convert(Info, SourceRValue, BCE);
@@ -7456,6 +7537,25 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
74567537
return true;
74577538
}
74587539

7540+
static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
7541+
APValue &SourceValue,
7542+
const CastExpr *BCE) {
7543+
assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 &&
7544+
"no host or target supports non 8-bit chars");
7545+
assert(SourceValue.isLValue() &&
7546+
"LValueToRValueBitcast requires an lvalue operand!");
7547+
7548+
LValue SourceLValue;
7549+
APValue SourceRValue;
7550+
SourceLValue.setFrom(Info.Ctx, SourceValue);
7551+
if (!handleLValueToRValueConversion(
7552+
Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue,
7553+
SourceRValue, /*WantObjectRepresentation=*/true))
7554+
return false;
7555+
7556+
return handleRValueToRValueBitCast(Info, DestValue, SourceRValue, BCE);
7557+
}
7558+
74597559
template <class Derived>
74607560
class ExprEvaluatorBase
74617561
: public ConstStmtVisitor<Derived, bool> {
@@ -10540,41 +10640,22 @@ bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) {
1054010640
return Success(Elts, E);
1054110641
}
1054210642
case CK_BitCast: {
10543-
// Evaluate the operand into an APInt we can extract from.
10544-
llvm::APInt SValInt;
10545-
if (!EvalAndBitcastToAPInt(Info, SE, SValInt))
10643+
APValue SVal;
10644+
if (!Evaluate(SVal, Info, SE))
10645+
return false;
10646+
10647+
if (!SVal.isInt() && !SVal.isFloat() && !SVal.isVector()) {
10648+
// Give up if the input isn't an int, float, or vector. For example, we
10649+
// reject "(v4i16)(intptr_t)&a".
10650+
Info.FFDiag(E, diag::note_constexpr_invalid_cast)
10651+
<< 2 << Info.Ctx.getLangOpts().CPlusPlus;
1054610652
return false;
10547-
// Extract the elements
10548-
QualType EltTy = VTy->getElementType();
10549-
unsigned EltSize = Info.Ctx.getTypeSize(EltTy);
10550-
bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
10551-
SmallVector<APValue, 4> Elts;
10552-
if (EltTy->isRealFloatingType()) {
10553-
const llvm::fltSemantics &Sem = Info.Ctx.getFloatTypeSemantics(EltTy);
10554-
unsigned FloatEltSize = EltSize;
10555-
if (&Sem == &APFloat::x87DoubleExtended())
10556-
FloatEltSize = 80;
10557-
for (unsigned i = 0; i < NElts; i++) {
10558-
llvm::APInt Elt;
10559-
if (BigEndian)
10560-
Elt = SValInt.rotl(i * EltSize + FloatEltSize).trunc(FloatEltSize);
10561-
else
10562-
Elt = SValInt.rotr(i * EltSize).trunc(FloatEltSize);
10563-
Elts.push_back(APValue(APFloat(Sem, Elt)));
10564-
}
10565-
} else if (EltTy->isIntegerType()) {
10566-
for (unsigned i = 0; i < NElts; i++) {
10567-
llvm::APInt Elt;
10568-
if (BigEndian)
10569-
Elt = SValInt.rotl(i*EltSize+EltSize).zextOrTrunc(EltSize);
10570-
else
10571-
Elt = SValInt.rotr(i*EltSize).zextOrTrunc(EltSize);
10572-
Elts.push_back(APValue(APSInt(Elt, !EltTy->isSignedIntegerType())));
10573-
}
10574-
} else {
10575-
return Error(E);
1057610653
}
10577-
return Success(Elts, E);
10654+
10655+
if (!handleRValueToRValueBitCast(Info, Result, SVal, E))
10656+
return false;
10657+
10658+
return true;
1057810659
}
1057910660
default:
1058010661
return ExprEvaluatorBaseTy::VisitCastExpr(E);

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,9 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value,
21522152
Inits[I] = llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt());
21532153
else if (Elt.isFloat())
21542154
Inits[I] = llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat());
2155+
else if (Elt.isIndeterminate())
2156+
Inits[I] = llvm::UndefValue::get(CGM.getTypes().ConvertType(
2157+
DestType->castAs<VectorType>()->getElementType()));
21552158
else
21562159
llvm_unreachable("unsupported vector element type");
21572160
}

clang/test/CodeGen/const-init.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,12 @@ void g28(void) {
140140
typedef short v12i16 __attribute((vector_size(24)));
141141
typedef long double v2f80 __attribute((vector_size(24)));
142142
// CHECK: @g28.a = internal global <1 x i64> <i64 10>
143-
// 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>
144-
// CHECK: @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32
143+
// @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>
144+
// @g28.c = internal global <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, align 32
145145
static v1i64 a = (v1i64)10LL;
146-
static v12i16 b = (v12i16)(v2f80){1,2};
147-
static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0};
146+
//FIXME: support constant bitcast between vectors of x86_fp80
147+
//static v12i16 b = (v12i16)(v2f80){1,2};
148+
//static v2f80 c = (v2f80)(v12i16){0,0,0,-32768,16383,0,0,0,0,-32768,16384,0};
148149
}
149150

150151
// PR13643

0 commit comments

Comments
 (0)