brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.7 KiB · 4bd9c66 Raw
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