llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> Let's see what the CI has to say about this. --- Patch is 35.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/116843.diff 9 Files Affected: - (added) clang/lib/AST/ByteCode/BitcastBuffer.h (+125) - (modified) clang/lib/AST/ByteCode/Boolean.h (+1-1) - (modified) clang/lib/AST/ByteCode/Compiler.cpp (+2) - (modified) clang/lib/AST/ByteCode/Integral.h (+1) - (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (+70-126) - (added) clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp (+300) - (modified) clang/test/AST/ByteCode/builtin-bit-cast.cpp (-87) - (added) clang/unittests/AST/ByteCode/BitcastBuffer.cpp (+81) - (modified) clang/unittests/AST/ByteCode/CMakeLists.txt (+1) ``````````diff diff --git a/clang/lib/AST/ByteCode/BitcastBuffer.h b/clang/lib/AST/ByteCode/BitcastBuffer.h new file mode 100644 index 00000000000000..1b2dcfdcd32a41 --- /dev/null +++ b/clang/lib/AST/ByteCode/BitcastBuffer.h @@ -0,0 +1,125 @@ + + +#ifndef LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H +#define LLVM_CLANG_AST_INTERP_BITCAST_BUFFER_H + +#include "llvm/Support/raw_ostream.h" +#include <bitset> +#include <cassert> +#include <cstring> +#include <memory> +#include <sstream> + +enum class Endian { Little, Big }; + +static inline bool bitof(std::byte B, unsigned BitIndex) { + assert(BitIndex < 8); + return (B & (std::byte{1} << BitIndex)) != std::byte{0}; +} + +static inline bool fullByte(unsigned N) { return N % 8 == 0; } + +/// Track what bits have been initialized to known values and which ones +/// have indeterminate value. +/// All offsets are in bits. +struct BitcastBuffer { + size_t FinalBitSize = 0; + std::unique_ptr<std::byte[]> Data; + + BitcastBuffer(size_t FinalBitSize) : FinalBitSize(FinalBitSize) { + assert(fullByte(FinalBitSize)); + unsigned ByteSize = FinalBitSize / 8; + Data = std::make_unique<std::byte[]>(ByteSize); + } + + size_t size() const { return FinalBitSize; } + + bool allInitialized() const { + // FIXME: Implement. + return true; + } + + /// \p Data must be in the given endianness. + void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth, + size_t FullSize, Endian DataEndianness) { + assert(fullByte(FullSize)); + + for (unsigned It = 0; It != BitWidth; ++It) { + bool BitValue; + BitValue = bitof(data[It / 8], It % 8); + if (!BitValue) + continue; + + unsigned DstBit; + if (DataEndianness == Endian::Big) + DstBit = size() - BitOffset - BitWidth + It; + else + DstBit = BitOffset + It; + unsigned DstByte = (DstBit / 8); + + Data[DstByte] |= std::byte{1} << (DstBit % 8); + } + } + + std::unique_ptr<std::byte[]> copyBits(unsigned BitOffset, unsigned BitWidth, + unsigned FullBitWidth, + Endian DataEndianness) const { + assert(BitWidth <= FullBitWidth); + assert(fullByte(FullBitWidth)); + std::unique_ptr<std::byte[]> Out = + std::make_unique<std::byte[]>(FullBitWidth / 8); + + for (unsigned It = 0; It != BitWidth; ++It) { + unsigned BitIndex; + if (DataEndianness == Endian::Little) + BitIndex = BitOffset + It; + else + BitIndex = size() - BitWidth - BitOffset + It; + + bool BitValue = bitof(Data[BitIndex / 8], BitIndex % 8); + if (!BitValue) + continue; + unsigned DstBit = It; + unsigned DstByte(DstBit / 8); + Out[DstByte] |= std::byte{1} << (DstBit % 8); + } + + return Out; + } + +#if 0 + template<typename T> + static std::string hex(T t) { + std::stringstream stream; + stream << std::hex << (int)t; + return std::string(stream.str()); + } + + + void dump(bool AsHex = true) const { + llvm::errs() << "LSB\n "; + unsigned LineLength = 0; + for (unsigned I = 0; I != (FinalBitSize / 8); ++I) { + std::byte B = Data[I]; + if (AsHex) { + std::stringstream stream; + stream << std::hex << (int)B; + llvm::errs() << stream.str(); + LineLength += stream.str().size() + 1; + } else { + llvm::errs() << std::bitset<8>((int)B).to_string(); + LineLength += 8 + 1; + // llvm::errs() << (int)B; + } + llvm::errs() << ' '; + } + llvm::errs() << '\n'; + + for (unsigned I = 0; I != LineLength; ++I) + llvm::errs() << ' '; + llvm::errs() << "MSB\n"; + } +#endif +}; + +#endif diff --git a/clang/lib/AST/ByteCode/Boolean.h b/clang/lib/AST/ByteCode/Boolean.h index 78d75e75c7531a..bd46523f330609 100644 --- a/clang/lib/AST/ByteCode/Boolean.h +++ b/clang/lib/AST/ByteCode/Boolean.h @@ -84,7 +84,7 @@ class Boolean final { static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) { // Boolean width is currently always 8 for all supported targets. If this // changes we need to get the bool width from the target info. - assert(BitWidth == 8); + // assert(BitWidth == 8); bool Val = static_cast<bool>(*Buff); return Boolean(Val); } diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 7cf2519d6a71fb..e0423dfe2091af 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -2710,6 +2710,8 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( return false; if (!this->visitInitializer(SubExpr)) return false; + if (!this->emitFinishInit(SubExpr)) + return false; if (IsStatic) return this->emitInitGlobalTempComp(TempDecl, E); return true; diff --git a/clang/lib/AST/ByteCode/Integral.h b/clang/lib/AST/ByteCode/Integral.h index ca3674263aef4f..bb1688a8a7622c 100644 --- a/clang/lib/AST/ByteCode/Integral.h +++ b/clang/lib/AST/ByteCode/Integral.h @@ -181,6 +181,7 @@ template <unsigned Bits, bool Signed> class Integral final { } Integral truncate(unsigned TruncBits) const { + assert(TruncBits >= 1); if (TruncBits >= Bits) return *this; const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1; diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index 7e8853d3469317..b089a9b7469cf4 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// #include "InterpBuiltinBitCast.h" +#include "BitcastBuffer.h" #include "Boolean.h" #include "Context.h" #include "Floating.h" @@ -17,6 +18,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" +#include <bitset> +#include <cmath> using namespace clang; using namespace clang::interp; @@ -61,80 +64,11 @@ using DataFunc = llvm::function_ref<bool(const Pointer &P, PrimType Ty, } \ } while (0) -static bool bitof(std::byte B, unsigned BitIndex) { - return (B & (std::byte{1} << BitIndex)) != std::byte{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]); } -/// Track what bits have been initialized to known values and which ones -/// have indeterminate value. -/// All offsets are in bits. -struct BitcastBuffer { - size_t SizeInBits = 0; - llvm::SmallVector<std::byte> Data; - - BitcastBuffer() = default; - - size_t size() const { return SizeInBits; } - - const std::byte *data() const { return Data.data(); } - - std::byte *getBytes(unsigned BitOffset) const { - assert(BitOffset % 8 == 0); - assert(BitOffset < SizeInBits); - return const_cast<std::byte *>(data() + (BitOffset / 8)); - } - - bool allInitialized() const { - // FIXME: Implement. - return true; - } - - bool atByteBoundary() const { return (Data.size() * 8) == SizeInBits; } - - void pushBit(bool Value) { - if (atByteBoundary()) - Data.push_back(std::byte{0}); - - if (Value) - Data.back() |= (std::byte{1} << (SizeInBits % 8)); - ++SizeInBits; - } - - void pushData(const std::byte *data, size_t BitWidth, bool BigEndianTarget) { - bool OnlyFullBytes = BitWidth % 8 == 0; - unsigned NBytes = BitWidth / 8; - - size_t BitsHandled = 0; - // Read all full bytes first - for (size_t I = 0; I != NBytes; ++I) { - std::byte B = - BigEndianTarget ? data[NBytes - OnlyFullBytes - I] : data[I]; - for (unsigned X = 0; X != 8; ++X) { - pushBit(bitof(B, X)); - ++BitsHandled; - } - } - - if (BitsHandled == BitWidth) - return; - - // Rest of the bits. - assert((BitWidth - BitsHandled) < 8); - std::byte B = BigEndianTarget ? data[0] : data[NBytes]; - for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) { - pushBit(bitof(B, I)); - ++BitsHandled; - } - - assert(BitsHandled == BitWidth); - } -}; - /// We use this to recursively iterate over all fields and elemends of a pointer /// and extract relevant data for a bitcast. static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, @@ -148,28 +82,28 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, // Primitive arrays. if (FieldDesc->isPrimitiveArray()) { - bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); QualType ElemType = FieldDesc->getElemQualType(); size_t ElemSizeInBits = 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 = 0; I != FieldDesc->getNumElems(); ++I) { - unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; + for (unsigned I = 0; I != NumElems; ++I) { + unsigned Index = I; Ok = Ok && F(P.atIndex(Index), ElemT, Offset, PackedBools); - Offset += ElemSizeInBits; + Offset += PackedBools ? 1 : ElemSizeInBits; } return Ok; } // Composite arrays. if (FieldDesc->isCompositeArray()) { - bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); + // bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); QualType ElemType = FieldDesc->getElemQualType(); size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { - unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; + unsigned Index = I; enumerateData(P.atIndex(Index).narrow(), Ctx, Offset, F); Offset += ElemSizeInBits; } @@ -178,7 +112,6 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, // Records. if (FieldDesc->isRecord()) { - bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); const Record *R = FieldDesc->ElemRecord; const ASTRecordLayout &Layout = Ctx.getASTContext().getASTRecordLayout(R->getDecl()); @@ -186,8 +119,7 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, auto enumerateFields = [&]() -> void { for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) { - const Record::Field *Fi = - R->getField(BigEndianTarget ? (N - 1 - I) : I); + const Record::Field *Fi = R->getField(I); Pointer Elem = P.atField(Fi->Offset); size_t BitOffset = Offset + Layout.getFieldOffset(Fi->Decl->getFieldIndex()); @@ -196,7 +128,7 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, }; auto enumerateBases = [&]() -> void { for (unsigned I = 0, N = R->getNumBases(); I != N; ++I) { - const Record::Base *B = R->getBase(BigEndianTarget ? (N - 1 - I) : I); + const Record::Base *B = R->getBase(I); Pointer Elem = P.atField(B->Offset); CharUnits ByteOffset = Layout.getBaseClassOffset(cast<CXXRecordDecl>(B->Decl)); @@ -204,14 +136,8 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); } }; - - if (BigEndianTarget) { - enumerateFields(); - enumerateBases(); - } else { - enumerateBases(); - enumerateFields(); - } + enumerateBases(); + enumerateFields(); return Ok; } @@ -295,26 +221,26 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, BitcastBuffer &Buffer, bool ReturnOnUninit) { const ASTContext &ASTCtx = Ctx.getASTContext(); - bool SwapData = (ASTCtx.getTargetInfo().isLittleEndian() != - llvm::sys::IsLittleEndianHost); - bool BigEndianTarget = ASTCtx.getTargetInfo().isBigEndian(); + Endian TargetEndianness = + ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; return enumeratePointerFields( FromPtr, Ctx, [&](const Pointer &P, PrimType T, size_t BitOffset, bool PackedBools) -> bool { - if (!P.isInitialized()) { - assert(false && "Implement uninitialized value tracking"); - return ReturnOnUninit; - } + // if (!P.isInitialized()) { + // assert(false && "Implement uninitialized value tracking"); + // return ReturnOnUninit; + // } - assert(P.isInitialized()); + // 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"); CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); unsigned BitWidth = ASTCtx.toBits(ObjectReprChars); + unsigned FullSize = BitWidth; llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity()); // Work around floating point types that contain unused padding bytes. // This is really just `long double` on x86, which is the only @@ -328,7 +254,7 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, F.bitcastToMemory(Buff.data()); // Now, only (maybe) swap the actual size of the float, excluding the // padding bits. - if (SwapData) + if (llvm::sys::IsBigEndianHost) swapBytes(Buff.data(), NumBits / 8); } else { @@ -337,20 +263,15 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, else if (T == PT_Bool && PackedBools) BitWidth = 1; - BITCAST_TYPE_SWITCH(T, { - T Val = P.deref<T>(); - Val.bitcastToMemory(Buff.data()); - }); - if (SwapData) - swapBytes(Buff.data(), ObjectReprChars.getQuantity()); - } + BITCAST_TYPE_SWITCH(T, + { P.deref<T>().bitcastToMemory(Buff.data()); }); - if (BitWidth != (Buff.size() * 8) && BigEndianTarget) { - Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)), - BitWidth, BigEndianTarget); - } else { - Buffer.pushData(Buff.data(), BitWidth, BigEndianTarget); + if (llvm::sys::IsBigEndianHost) + swapBytes(Buff.data(), FullSize / 8); } + + Buffer.pushData(Buff.data(), BitOffset, BitWidth, FullSize, + TargetEndianness); return true; }); } @@ -362,7 +283,7 @@ bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, assert(Ptr.isBlockPointer()); assert(Buff); - BitcastBuffer Buffer; + BitcastBuffer Buffer(BuffSize * 8); if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false)) return false; @@ -371,13 +292,20 @@ bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, assert(Buffer.size() == BuffSize * 8); HasIndeterminateBits = !Buffer.allInitialized(); - std::memcpy(Buff, Buffer.data(), BuffSize); + + const ASTContext &ASTCtx = S.getASTContext(); + Endian TargetEndianness = + ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; + auto B = Buffer.copyBits(0, BuffSize * 8, BuffSize * 8, TargetEndianness); + + std::memcpy(Buff, B.get(), BuffSize); if (llvm::sys::IsBigEndianHost) swapBytes(Buff, BuffSize); return Success; } +/// --------------------------------------------------------------------------------------------------------------------- bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr) { @@ -394,43 +322,59 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true)) return false; - BitcastBuffer Buffer; + const ASTContext &ASTCtx = S.getASTContext(); + + CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(ToType); + BitcastBuffer Buffer(ASTCtx.toBits(ObjectReprChars)); readPointerToBuffer(S.getContext(), FromPtr, Buffer, /*ReturnOnUninit=*/false); // Now read the values out of the buffer again and into ToPtr. - const ASTContext &ASTCtx = S.getASTContext(); - size_t BitOffset = 0; + Endian TargetEndianness = + ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; bool Success = enumeratePointerFields( ToPtr, S.getContext(), - [&](const Pointer &P, PrimType T, size_t _, bool PackedBools) -> bool { + [&](const Pointer &P, PrimType T, size_t BitOffset, + bool PackedBools) -> bool { + CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); + unsigned FullBitWidth = ASTCtx.toBits(ObjectReprChars); if (T == PT_Float) { - CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType()); unsigned NumBits = llvm::APFloatBase::getSizeInBits(Semantics); assert(NumBits % 8 == 0); assert(NumBits <= ASTCtx.toBits(ObjectReprChars)); - std::byte *M = Buffer.getBytes(BitOffset); + auto M = Buffer.copyBits(BitOffset, NumBits, FullBitWidth, + TargetEndianness); if (llvm::sys::IsBigEndianHost) - swapBytes(M, NumBits / 8); + swapBytes(M.get(), NumBits / 8); - P.deref<Floating>() = Floating::bitcastFromMemory(M, Semantics); + P.deref<Floating>() = Floating::bitcastFromMemory(M.get(), Semantics); P.initialize(); - BitOffset += ASTCtx.toBits(ObjectReprChars); return true; } - BITCAST_TYPE_SWITCH_FIXED_SIZE(T, { - std::byte *M = Buffer.getBytes(BitOffset); + unsigned BitWidth; + if (const FieldDecl *FD = P.getField(); FD && FD->isBitField()) + BitWidth = FD->getBitWidthValue(ASTCtx); + else if (T == PT_Bool && PackedBools) + BitWidth = 1; + else + BitWidth = ASTCtx.toBits(ObjectReprChars); - if (llvm::sys::IsBigEndianHost) - swapBytes(M, T::bitWidth() / 8); + auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth, + TargetEndianness); + if (llvm::sys::IsBigEndianHost) + swapBytes(Memory.get(), FullBitWidth / 8); - P.deref<T>() = T::bitcastFromMemory(M, T::bitWidth()); - P.initialize(); - BitOffset += T::bitWidth(); + BITCAST_TYPE_SWITCH_FIXED_SIZE(T, { + if (BitWidth > 0) + P.deref<T>() = T::bitcastFromMemory(Memory.get(), T::bitWidth()) + .truncate(BitWidth); + else + P.deref<T>() = T::zero(); }); + P.initialize(); return true; }); diff --git a/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp new file mode 100644 index 00000000000000..084ec6e75a4472 --- /dev/null +++ b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp @@ -0,0 +1,300 @@ +// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter %s +// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu -fexperimental-new-constant-interpreter %s +// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64le-unknown-unknown -mabi=ieeelongdouble %s +// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64-unknown-unknown -mabi=ieeelongdouble %s + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define LITTLE_END 1 +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define LITTLE_END 0 +#else +# ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/116843 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits