517 lines · cpp
1//===-------------------- InterpBuiltinBitCast.cpp --------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "InterpBuiltinBitCast.h"9#include "BitcastBuffer.h"10#include "Boolean.h"11#include "Context.h"12#include "Floating.h"13#include "Integral.h"14#include "InterpState.h"15#include "MemberPointer.h"16#include "Pointer.h"17#include "Record.h"18#include "clang/AST/ASTContext.h"19#include "clang/AST/RecordLayout.h"20#include "clang/Basic/TargetInfo.h"21 22#include <variant>23 24using namespace clang;25using namespace clang::interp;26 27/// Implement __builtin_bit_cast and related operations.28/// Since our internal representation for data is more complex than29/// something we can simply memcpy or memcmp, we first bitcast all the data30/// into a buffer, which we then later use to copy the data into the target.31 32// TODO:33// - Try to minimize heap allocations.34// - Optimize the common case of only pushing and pulling full35// bytes to/from the buffer.36 37/// Used to iterate over pointer fields.38using DataFunc =39 llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset,40 Bits FullBitWidth, bool PackedBools)>;41 42#define BITCAST_TYPE_SWITCH(Expr, B) \43 do { \44 switch (Expr) { \45 TYPE_SWITCH_CASE(PT_Sint8, B) \46 TYPE_SWITCH_CASE(PT_Uint8, B) \47 TYPE_SWITCH_CASE(PT_Sint16, B) \48 TYPE_SWITCH_CASE(PT_Uint16, B) \49 TYPE_SWITCH_CASE(PT_Sint32, B) \50 TYPE_SWITCH_CASE(PT_Uint32, B) \51 TYPE_SWITCH_CASE(PT_Sint64, B) \52 TYPE_SWITCH_CASE(PT_Uint64, B) \53 TYPE_SWITCH_CASE(PT_IntAP, B) \54 TYPE_SWITCH_CASE(PT_IntAPS, B) \55 TYPE_SWITCH_CASE(PT_Bool, B) \56 default: \57 llvm_unreachable("Unhandled bitcast type"); \58 } \59 } while (0)60 61#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \62 do { \63 switch (Expr) { \64 TYPE_SWITCH_CASE(PT_Sint8, B) \65 TYPE_SWITCH_CASE(PT_Uint8, B) \66 TYPE_SWITCH_CASE(PT_Sint16, B) \67 TYPE_SWITCH_CASE(PT_Uint16, B) \68 TYPE_SWITCH_CASE(PT_Sint32, B) \69 TYPE_SWITCH_CASE(PT_Uint32, B) \70 TYPE_SWITCH_CASE(PT_Sint64, B) \71 TYPE_SWITCH_CASE(PT_Uint64, B) \72 TYPE_SWITCH_CASE(PT_Bool, B) \73 default: \74 llvm_unreachable("Unhandled bitcast type"); \75 } \76 } while (0)77 78/// We use this to recursively iterate over all fields and elements of a pointer79/// and extract relevant data for a bitcast.80static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,81 Bits BitsToRead, DataFunc F) {82 const Descriptor *FieldDesc = P.getFieldDesc();83 assert(FieldDesc);84 85 // Primitives.86 if (FieldDesc->isPrimitive()) {87 Bits FullBitWidth =88 Bits(Ctx.getASTContext().getTypeSize(FieldDesc->getType()));89 return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth,90 /*PackedBools=*/false);91 }92 93 // Primitive arrays.94 if (FieldDesc->isPrimitiveArray()) {95 QualType ElemType = FieldDesc->getElemQualType();96 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));97 PrimType ElemT = *Ctx.classify(ElemType);98 // Special case, since the bools here are packed.99 bool PackedBools =100 FieldDesc->getType()->isPackedVectorBoolType(Ctx.getASTContext());101 unsigned NumElems = FieldDesc->getNumElems();102 bool Ok = true;103 for (unsigned I = P.getIndex(); I != NumElems; ++I) {104 Ok = Ok && F(P.atIndex(I), ElemT, Offset, ElemSize, PackedBools);105 Offset += PackedBools ? Bits(1) : ElemSize;106 if (Offset >= BitsToRead)107 break;108 }109 return Ok;110 }111 112 // Composite arrays.113 if (FieldDesc->isCompositeArray()) {114 QualType ElemType = FieldDesc->getElemQualType();115 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));116 for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {117 enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);118 Offset += ElemSize;119 if (Offset >= BitsToRead)120 break;121 }122 return true;123 }124 125 // Records.126 if (FieldDesc->isRecord()) {127 const Record *R = FieldDesc->ElemRecord;128 const ASTRecordLayout &Layout =129 Ctx.getASTContext().getASTRecordLayout(R->getDecl());130 bool Ok = true;131 132 for (const Record::Field &Fi : R->fields()) {133 if (Fi.isUnnamedBitField())134 continue;135 Pointer Elem = P.atField(Fi.Offset);136 Bits BitOffset =137 Offset + Bits(Layout.getFieldOffset(Fi.Decl->getFieldIndex()));138 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);139 }140 for (const Record::Base &B : R->bases()) {141 Pointer Elem = P.atField(B.Offset);142 CharUnits ByteOffset =143 Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));144 Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(ByteOffset));145 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);146 // FIXME: We should only (need to) do this when bitcasting OUT of the147 // buffer, not when copying data into it.148 if (Ok)149 Elem.initialize();150 }151 152 return Ok;153 }154 155 llvm_unreachable("Unhandled data type");156}157 158static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,159 Bits BitsToRead, DataFunc F) {160 return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F);161}162 163// This function is constexpr if and only if To, From, and the types of164// all subobjects of To and From are types T such that...165// (3.1) - is_union_v<T> is false;166// (3.2) - is_pointer_v<T> is false;167// (3.3) - is_member_pointer_v<T> is false;168// (3.4) - is_volatile_v<T> is false; and169// (3.5) - T has no non-static data members of reference type170//171// NOTE: This is a version of checkBitCastConstexprEligibilityType() in172// ExprConstant.cpp.173static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,174 bool IsToType) {175 enum {176 E_Union = 0,177 E_Pointer,178 E_MemberPointer,179 E_Volatile,180 E_Reference,181 };182 enum { C_Member, C_Base };183 184 auto diag = [&](int Reason) -> bool {185 const Expr *E = S.Current->getExpr(OpPC);186 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)187 << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason188 << E->getSourceRange();189 return false;190 };191 auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {192 S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)193 << NoteType << Construct << T.getUnqualifiedType() << NoteRange;194 return false;195 };196 197 T = T.getCanonicalType();198 199 if (T->isUnionType())200 return diag(E_Union);201 if (T->isPointerType())202 return diag(E_Pointer);203 if (T->isMemberPointerType())204 return diag(E_MemberPointer);205 if (T.isVolatileQualified())206 return diag(E_Volatile);207 208 if (const RecordDecl *RD = T->getAsRecordDecl()) {209 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {210 for (const CXXBaseSpecifier &BS : CXXRD->bases()) {211 if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType))212 return note(C_Base, BS.getType(), BS.getBeginLoc());213 }214 }215 for (const FieldDecl *FD : RD->fields()) {216 if (FD->getType()->isReferenceType())217 return diag(E_Reference);218 if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType))219 return note(C_Member, FD->getType(), FD->getSourceRange());220 }221 }222 223 if (T->isArrayType() &&224 !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T),225 IsToType))226 return false;227 228 if (const auto *VT = T->getAs<VectorType>()) {229 const ASTContext &ASTCtx = S.getASTContext();230 QualType EltTy = VT->getElementType();231 unsigned NElts = VT->getNumElements();232 unsigned EltSize =233 VT->isPackedVectorBoolType(ASTCtx) ? 1 : ASTCtx.getTypeSize(EltTy);234 235 if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) {236 // The vector's size in bits is not a multiple of the target's byte size,237 // so its layout is unspecified. For now, we'll simply treat these cases238 // as unsupported (this should only be possible with OpenCL bool vectors239 // whose element count isn't a multiple of the byte size).240 const Expr *E = S.Current->getExpr(OpPC);241 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_vector)242 << QualType(VT, 0) << EltSize << NElts << ASTCtx.getCharWidth();243 return false;244 }245 246 if (EltTy->isRealFloatingType() &&247 &ASTCtx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) {248 // The layout for x86_fp80 vectors seems to be handled very inconsistently249 // by both clang and LLVM, so for now we won't allow bit_casts involving250 // it in a constexpr context.251 const Expr *E = S.Current->getExpr(OpPC);252 S.FFDiag(E, diag::note_constexpr_bit_cast_unsupported_type) << EltTy;253 return false;254 }255 }256 257 return true;258}259 260bool clang::interp::readPointerToBuffer(const Context &Ctx,261 const Pointer &FromPtr,262 BitcastBuffer &Buffer,263 bool ReturnOnUninit) {264 const ASTContext &ASTCtx = Ctx.getASTContext();265 Endian TargetEndianness =266 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;267 268 return enumeratePointerFields(269 FromPtr, Ctx, Buffer.size(),270 [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,271 bool PackedBools) -> bool {272 Bits BitWidth = FullBitWidth;273 274 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())275 BitWidth = Bits(std::min(FD->getBitWidthValue(),276 (unsigned)FullBitWidth.getQuantity()));277 else if (T == PT_Bool && PackedBools)278 BitWidth = Bits(1);279 280 if (BitWidth.isZero())281 return true;282 283 // Bits will be left uninitialized and diagnosed when reading.284 if (!P.isInitialized())285 return true;286 287 if (T == PT_Ptr) {288 assert(P.getType()->isNullPtrType());289 // Clang treats nullptr_t has having NO bits in its value290 // representation. So, we accept it here and leave its bits291 // uninitialized.292 return true;293 }294 295 assert(P.isInitialized());296 auto Buff = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes());297 // Work around floating point types that contain unused padding bytes.298 // This is really just `long double` on x86, which is the only299 // fundamental type with padding bytes.300 if (T == PT_Float) {301 const Floating &F = P.deref<Floating>();302 Bits NumBits = Bits(303 llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics()));304 assert(NumBits.isFullByte());305 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());306 F.bitcastToMemory(Buff.get());307 // Now, only (maybe) swap the actual size of the float, excluding308 // the padding bits.309 if (llvm::sys::IsBigEndianHost)310 swapBytes(Buff.get(), NumBits.roundToBytes());311 312 Buffer.markInitialized(BitOffset, NumBits);313 } else {314 BITCAST_TYPE_SWITCH(T, { P.deref<T>().bitcastToMemory(Buff.get()); });315 316 if (llvm::sys::IsBigEndianHost)317 swapBytes(Buff.get(), FullBitWidth.roundToBytes());318 Buffer.markInitialized(BitOffset, BitWidth);319 }320 321 Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness);322 return true;323 });324}325 326bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,327 std::byte *Buff, Bits BitWidth, Bits FullBitWidth,328 bool &HasIndeterminateBits) {329 assert(Ptr.isLive());330 assert(Ptr.isBlockPointer());331 assert(Buff);332 assert(BitWidth <= FullBitWidth);333 assert(FullBitWidth.isFullByte());334 assert(BitWidth.isFullByte());335 336 BitcastBuffer Buffer(FullBitWidth);337 size_t BuffSize = FullBitWidth.roundToBytes();338 QualType DataType = Ptr.getFieldDesc()->getDataType(S.getASTContext());339 if (!CheckBitcastType(S, OpPC, DataType, /*IsToType=*/false))340 return false;341 342 bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer,343 /*ReturnOnUninit=*/false);344 HasIndeterminateBits = !Buffer.rangeInitialized(Bits::zero(), BitWidth);345 346 const ASTContext &ASTCtx = S.getASTContext();347 Endian TargetEndianness =348 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;349 auto B =350 Buffer.copyBits(Bits::zero(), BitWidth, FullBitWidth, TargetEndianness);351 352 std::memcpy(Buff, B.get(), BuffSize);353 354 if (llvm::sys::IsBigEndianHost)355 swapBytes(Buff, BitWidth.roundToBytes());356 357 return Success;358}359bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,360 const Pointer &FromPtr, Pointer &ToPtr) {361 const ASTContext &ASTCtx = S.getASTContext();362 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(ToPtr.getType());363 364 return DoBitCastPtr(S, OpPC, FromPtr, ToPtr, ObjectReprChars.getQuantity());365}366 367bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,368 const Pointer &FromPtr, Pointer &ToPtr,369 size_t Size) {370 assert(FromPtr.isLive());371 assert(FromPtr.isBlockPointer());372 assert(ToPtr.isBlockPointer());373 374 QualType FromType = FromPtr.getFieldDesc()->getDataType(S.getASTContext());375 QualType ToType = ToPtr.getFieldDesc()->getDataType(S.getASTContext());376 377 if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))378 return false;379 if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))380 return false;381 382 const ASTContext &ASTCtx = S.getASTContext();383 BitcastBuffer Buffer(Bytes(Size).toBits());384 readPointerToBuffer(S.getContext(), FromPtr, Buffer,385 /*ReturnOnUninit=*/false);386 387 // Now read the values out of the buffer again and into ToPtr.388 Endian TargetEndianness =389 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;390 bool Success = enumeratePointerFields(391 ToPtr, S.getContext(), Buffer.size(),392 [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,393 bool PackedBools) -> bool {394 QualType PtrType = P.getType();395 if (T == PT_Float) {396 const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);397 Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));398 assert(NumBits.isFullByte());399 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());400 auto M = Buffer.copyBits(BitOffset, NumBits, FullBitWidth,401 TargetEndianness);402 403 if (llvm::sys::IsBigEndianHost)404 swapBytes(M.get(), NumBits.roundToBytes());405 406 Floating R = S.allocFloat(Semantics);407 Floating::bitcastFromMemory(M.get(), Semantics, &R);408 P.deref<Floating>() = R;409 P.initialize();410 return true;411 }412 413 Bits BitWidth;414 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())415 BitWidth = Bits(std::min(FD->getBitWidthValue(),416 (unsigned)FullBitWidth.getQuantity()));417 else if (T == PT_Bool && PackedBools)418 BitWidth = Bits(1);419 else420 BitWidth = FullBitWidth;421 422 // If any of the bits are uninitialized, we need to abort unless the423 // target type is std::byte or unsigned char.424 bool Initialized = Buffer.rangeInitialized(BitOffset, BitWidth);425 if (!Initialized) {426 if (!PtrType->isStdByteType() &&427 !PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&428 !PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {429 const Expr *E = S.Current->getExpr(OpPC);430 S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)431 << PtrType << S.getLangOpts().CharIsSigned432 << E->getSourceRange();433 434 return false;435 }436 return true;437 }438 439 auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,440 TargetEndianness);441 if (llvm::sys::IsBigEndianHost)442 swapBytes(Memory.get(), FullBitWidth.roundToBytes());443 444 if (T == PT_IntAPS) {445 P.deref<IntegralAP<true>>() =446 S.allocAP<IntegralAP<true>>(FullBitWidth.getQuantity());447 IntegralAP<true>::bitcastFromMemory(Memory.get(),448 FullBitWidth.getQuantity(),449 &P.deref<IntegralAP<true>>());450 } else if (T == PT_IntAP) {451 P.deref<IntegralAP<false>>() =452 S.allocAP<IntegralAP<false>>(FullBitWidth.getQuantity());453 IntegralAP<false>::bitcastFromMemory(Memory.get(),454 FullBitWidth.getQuantity(),455 &P.deref<IntegralAP<false>>());456 } else {457 BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {458 if (BitWidth.nonZero())459 P.deref<T>() = T::bitcastFromMemory(Memory.get(), T::bitWidth())460 .truncate(BitWidth.getQuantity());461 else462 P.deref<T>() = T::zero();463 });464 }465 P.initialize();466 return true;467 });468 469 return Success;470}471 472using PrimTypeVariant =473 std::variant<Pointer, FunctionPointer, MemberPointer, FixedPoint,474 Integral<8, false>, Integral<8, true>, Integral<16, false>,475 Integral<16, true>, Integral<32, false>, Integral<32, true>,476 Integral<64, false>, Integral<64, true>, IntegralAP<true>,477 IntegralAP<false>, Boolean, Floating>;478 479// NB: This implementation isn't exactly ideal, but:480// 1) We can't just do a bitcast here since we need to be able to481// copy pointers.482// 2) This also needs to handle overlapping regions.483// 3) We currently have no way of iterating over the fields of a pointer484// backwards.485bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,486 const Pointer &SrcPtr, const Pointer &DestPtr,487 Bits Size) {488 assert(SrcPtr.isBlockPointer());489 assert(DestPtr.isBlockPointer());490 491 llvm::SmallVector<PrimTypeVariant> Values;492 enumeratePointerFields(SrcPtr, S.getContext(), Size,493 [&](const Pointer &P, PrimType T, Bits BitOffset,494 Bits FullBitWidth, bool PackedBools) -> bool {495 TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });496 return true;497 });498 499 unsigned ValueIndex = 0;500 enumeratePointerFields(DestPtr, S.getContext(), Size,501 [&](const Pointer &P, PrimType T, Bits BitOffset,502 Bits FullBitWidth, bool PackedBools) -> bool {503 TYPE_SWITCH(T, {504 P.deref<T>() = std::get<T>(Values[ValueIndex]);505 P.initialize();506 });507 508 ++ValueIndex;509 return true;510 });511 512 // We should've read all the values into DestPtr.513 assert(ValueIndex == Values.size());514 515 return true;516}517