diff options
Diffstat (limited to 'clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp')
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 168 |
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; +} |
