summaryrefslogtreecommitdiff
path: root/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp')
-rw-r--r--clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp168
1 files changed, 126 insertions, 42 deletions
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 03c556cd70bb..57c1fab5d6ab 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -33,8 +33,9 @@ using namespace clang::interp;
// bytes to/from the buffer.
/// Used to iterate over pointer fields.
-using DataFunc = llvm::function_ref<bool(const Pointer &P, PrimType Ty,
- Bits BitOffset, bool PackedBools)>;
+using DataFunc =
+ llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset,
+ Bits FullBitWidth, bool PackedBools)>;
#define BITCAST_TYPE_SWITCH(Expr, B) \
do { \
@@ -72,11 +73,6 @@ using DataFunc = llvm::function_ref<bool(const Pointer &P, PrimType Ty,
} \
} while (0)
-static void swapBytes(std::byte *M, size_t N) {
- for (size_t I = 0; I != (N / 2); ++I)
- std::swap(M[I], M[N - 1 - I]);
-}
-
/// We use this to recursively iterate over all fields and elements of a pointer
/// and extract relevant data for a bitcast.
static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
@@ -85,21 +81,25 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
assert(FieldDesc);
// Primitives.
- if (FieldDesc->isPrimitive())
- return F(P, FieldDesc->getPrimType(), Offset, /*PackedBools=*/false);
+ if (FieldDesc->isPrimitive()) {
+ Bits FullBitWidth =
+ Bits(Ctx.getASTContext().getTypeSize(FieldDesc->getType()));
+ return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth,
+ /*PackedBools=*/false);
+ }
// Primitive arrays.
if (FieldDesc->isPrimitiveArray()) {
QualType ElemType = FieldDesc->getElemQualType();
- size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
+ Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
PrimType ElemT = *Ctx.classify(ElemType);
// Special case, since the bools here are packed.
bool PackedBools = FieldDesc->getType()->isExtVectorBoolType();
unsigned NumElems = FieldDesc->getNumElems();
bool Ok = true;
for (unsigned I = P.getIndex(); I != NumElems; ++I) {
- Ok = Ok && F(P.atIndex(I), ElemT, Offset, PackedBools);
- Offset += PackedBools ? 1 : ElemSizeInBits;
+ Ok = Ok && F(P.atIndex(I), ElemT, Offset, ElemSize, PackedBools);
+ Offset += PackedBools ? Bits(1) : ElemSize;
if (Offset >= BitsToRead)
break;
}
@@ -109,10 +109,10 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
// Composite arrays.
if (FieldDesc->isCompositeArray()) {
QualType ElemType = FieldDesc->getElemQualType();
- size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
- for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+ Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
+ for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {
enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);
- Offset += ElemSizeInBits;
+ Offset += ElemSize;
if (Offset >= BitsToRead)
break;
}
@@ -222,44 +222,75 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
IsToType))
return false;
+ if (const auto *VT = T->getAs<VectorType>()) {
+ const ASTContext &ASTCtx = S.getASTContext();
+ QualType EltTy = VT->getElementType();
+ unsigned NElts = VT->getNumElements();
+ unsigned EltSize =
+ VT->isExtVectorBoolType() ? 1 : ASTCtx.getTypeSize(EltTy);
+
+ if ((NElts * EltSize) % ASTCtx.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).
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_vector)
+ << QualType(VT, 0) << EltSize << NElts << ASTCtx.getCharWidth();
+ return false;
+ }
+
+ if (EltTy->isRealFloatingType() &&
+ &ASTCtx.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.
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.FFDiag(E, diag::note_constexpr_bit_cast_unsupported_type) << EltTy;
+ return false;
+ }
+ }
+
return true;
}
-static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
- BitcastBuffer &Buffer, bool ReturnOnUninit) {
+bool clang::interp::readPointerToBuffer(const Context &Ctx,
+ const Pointer &FromPtr,
+ BitcastBuffer &Buffer,
+ bool ReturnOnUninit) {
const ASTContext &ASTCtx = Ctx.getASTContext();
Endian TargetEndianness =
ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
return enumeratePointerFields(
FromPtr, Ctx, Buffer.size(),
- [&](const Pointer &P, PrimType T, Bits BitOffset,
+ [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
bool PackedBools) -> bool {
- CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
- Bits BitWidth = Bits(ASTCtx.toBits(ObjectReprChars));
- Bits FullBitWidth = BitWidth;
+ Bits BitWidth = FullBitWidth;
- if (const FieldDecl *FD = P.getField(); FD && FD->isBitField()) {
+ if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
BitWidth = Bits(std::min(FD->getBitWidthValue(ASTCtx),
(unsigned)FullBitWidth.getQuantity()));
- } else if (T == PT_Bool && PackedBools)
+ else if (T == PT_Bool && PackedBools)
BitWidth = Bits(1);
if (BitWidth.isZero())
return true;
- if (!P.isInitialized()) {
- assert(false && "Implement uninitialized value tracking");
- return ReturnOnUninit;
+ // Bits will be left uninitialized and diagnosed when reading.
+ if (!P.isInitialized())
+ return true;
+
+ if (T == PT_Ptr) {
+ assert(P.getType()->isNullPtrType());
+ // Clang treats nullptr_t has having NO bits in its value
+ // representation. So, we accept it here and leave its bits
+ // uninitialized.
+ return true;
}
assert(P.isInitialized());
- // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
- if (T == PT_Ptr)
- assert(false && "Implement casting to pointer types");
-
- auto Buff =
- std::make_unique<std::byte[]>(ObjectReprChars.getQuantity());
+ auto Buff = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes());
// Work around floating point types that contain unused padding bytes.
// This is really just `long double` on x86, which is the only
// fundamental type with padding bytes.
@@ -275,11 +306,13 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
if (llvm::sys::IsBigEndianHost)
swapBytes(Buff.get(), NumBits.roundToBytes());
+ Buffer.markInitialized(BitOffset, NumBits);
} else {
BITCAST_TYPE_SWITCH(T, { P.deref<T>().bitcastToMemory(Buff.get()); });
if (llvm::sys::IsBigEndianHost)
swapBytes(Buff.get(), FullBitWidth.roundToBytes());
+ Buffer.markInitialized(BitOffset, BitWidth);
}
Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness);
@@ -288,30 +321,34 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
}
bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- std::byte *Buff, size_t BuffSize,
+ std::byte *Buff, Bits BitWidth, Bits FullBitWidth,
bool &HasIndeterminateBits) {
assert(Ptr.isLive());
assert(Ptr.isBlockPointer());
assert(Buff);
+ assert(BitWidth <= FullBitWidth);
+ assert(FullBitWidth.isFullByte());
+ assert(BitWidth.isFullByte());
- Bits BitSize = Bytes(BuffSize).toBits();
- BitcastBuffer Buffer(BitSize);
+ BitcastBuffer Buffer(FullBitWidth);
+ size_t BuffSize = FullBitWidth.roundToBytes();
if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false))
return false;
bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer,
/*ReturnOnUninit=*/false);
- HasIndeterminateBits = !Buffer.allInitialized();
+ HasIndeterminateBits = !Buffer.rangeInitialized(Bits::zero(), BitWidth);
const ASTContext &ASTCtx = S.getASTContext();
Endian TargetEndianness =
ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
- auto B = Buffer.copyBits(Bits::zero(), BitSize, BitSize, TargetEndianness);
+ auto B =
+ Buffer.copyBits(Bits::zero(), BitWidth, FullBitWidth, TargetEndianness);
std::memcpy(Buff, B.get(), BuffSize);
if (llvm::sys::IsBigEndianHost)
- swapBytes(Buff, BuffSize);
+ swapBytes(Buff, BitWidth.roundToBytes());
return Success;
}
@@ -348,12 +385,11 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
bool Success = enumeratePointerFields(
ToPtr, S.getContext(), Buffer.size(),
- [&](const Pointer &P, PrimType T, Bits BitOffset,
+ [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
bool PackedBools) -> bool {
- CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
- Bits FullBitWidth = Bits(ASTCtx.toBits(ObjectReprChars));
+ QualType PtrType = P.getType();
if (T == PT_Float) {
- const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType());
+ const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
assert(NumBits.isFullByte());
assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
@@ -377,6 +413,23 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
else
BitWidth = FullBitWidth;
+ // If any of the bits are uninitialized, we need to abort unless the
+ // target type is std::byte or unsigned char.
+ bool Initialized = Buffer.rangeInitialized(BitOffset, BitWidth);
+ if (!Initialized) {
+ if (!PtrType->isStdByteType() &&
+ !PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&
+ !PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
+ << PtrType << S.getLangOpts().CharIsSigned
+ << E->getSourceRange();
+
+ return false;
+ }
+ return true;
+ }
+
auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
TargetEndianness);
if (llvm::sys::IsBigEndianHost)
@@ -395,3 +448,34 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
return Success;
}
+
+bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
+ const Pointer &SrcPtr, const Pointer &DestPtr,
+ Bits Size) {
+ assert(SrcPtr.isBlockPointer());
+ assert(DestPtr.isBlockPointer());
+
+ unsigned SrcStartOffset = SrcPtr.getByteOffset();
+ unsigned DestStartOffset = DestPtr.getByteOffset();
+
+ enumeratePointerFields(SrcPtr, S.getContext(), Size,
+ [&](const Pointer &P, PrimType T, Bits BitOffset,
+ Bits FullBitWidth, bool PackedBools) -> bool {
+ unsigned SrcOffsetDiff =
+ P.getByteOffset() - SrcStartOffset;
+
+ Pointer DestP =
+ Pointer(DestPtr.asBlockPointer().Pointee,
+ DestPtr.asBlockPointer().Base,
+ DestStartOffset + SrcOffsetDiff);
+
+ TYPE_SWITCH(T, {
+ DestP.deref<T>() = P.deref<T>();
+ DestP.initialize();
+ });
+
+ return true;
+ });
+
+ return true;
+}