Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/68...@github.com>
================ @@ -0,0 +1,455 @@ +//===--- InterpBitcast.cpp - Interpreter for the constexpr VM ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "Boolean.h" +#include "Interp.h" +#include "PrimType.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/BitVector.h" + +namespace clang { +namespace interp { + +/// Used to iterate over pointer fields. +using DataFunc = + llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t BitOffset)>; + +#define BITCAST_TYPE_SWITCH(Expr, B) \ + do { \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Sint8, B) \ + TYPE_SWITCH_CASE(PT_Uint8, B) \ + TYPE_SWITCH_CASE(PT_Sint16, B) \ + TYPE_SWITCH_CASE(PT_Uint16, B) \ + TYPE_SWITCH_CASE(PT_Sint32, B) \ + TYPE_SWITCH_CASE(PT_Uint32, B) \ + TYPE_SWITCH_CASE(PT_Sint64, B) \ + TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_Bool, B) \ + default: \ + llvm_unreachable("Unhandled bitcast type"); \ + } \ + } while (0) + +/// Float is a special case that sometimes needs the floating point semantics +/// to be available. +#define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B) \ + do { \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Sint8, B) \ + TYPE_SWITCH_CASE(PT_Uint8, B) \ + TYPE_SWITCH_CASE(PT_Sint16, B) \ + TYPE_SWITCH_CASE(PT_Uint16, B) \ + TYPE_SWITCH_CASE(PT_Sint32, B) \ + TYPE_SWITCH_CASE(PT_Uint32, B) \ + TYPE_SWITCH_CASE(PT_Sint64, B) \ + TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_Bool, B) \ + TYPE_SWITCH_CASE(PT_Float, B) \ + default: \ + llvm_unreachable("Unhandled bitcast type"); \ + } \ + } while (0) + +/// Rotate things around for big endian targets. +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 BitTracker { + llvm::BitVector Initialized; + llvm::BitVector Data_; + + BitTracker() = default; + + size_t size() const { + assert(Initialized.size() == Data_.size()); + return Initialized.size(); + } + + const std::byte *getBytes(size_t BitOffset, int a) { + assert(BitOffset % 8 == 0); + return reinterpret_cast<const std::byte *>(Data_.getData().data()) + + (BitOffset / 8); + } + + bool allInitialized(size_t Offset, size_t Size) const { + return Initialized.find_first_unset_in(Offset, Offset + Size) == -1; + } + + bool allInitialized() const { return Initialized.all(); } + + void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) { + assert(BitOffset >= Data_.size()); + // First, fill up the bit vector until BitOffset. The bits are all 0 + // but we record them as indeterminate. + { + Data_.resize(BitOffset, false); + Initialized.resize(BitOffset, false); + } + + size_t BitsHandled = 0; + // Read all full bytes first + for (size_t I = 0; I != BitWidth / 8; ++I) { + for (unsigned X = 0; X != 8; ++X) { + Data_.push_back((data[I] & std::byte(1 << X)) != std::byte{0}); + Initialized.push_back(true); + ++BitsHandled; + } + } + + // Rest of the bits. + assert((BitWidth - BitsHandled) < 8); + for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) { + Data_.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0}); + Initialized.push_back(true); + ++BitsHandled; + } + } + + void pushZeroes(size_t Amount) { + size_t N = Data_.size(); + Data_.resize(N + Amount, false); + Initialized.resize(N + Amount, true); + } + + void markUninitializedUntil(size_t Offset) { + assert(Offset >= Data_.size()); + Data_.resize(Offset, false); + Initialized.resize(Offset, false); + } +}; + +/// 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, + DataFunc F) { + const Descriptor *FieldDesc = P.getFieldDesc(); + assert(FieldDesc); + + // Primitives. + if (FieldDesc->isPrimitive()) + return F(P, *Ctx.classify(FieldDesc->getType()), Offset); + + // Primitive arrays. + if (FieldDesc->isPrimitiveArray()) { + QualType ElemType = + FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType(); + size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); + PrimType ElemT = *Ctx.classify(ElemType); + bool Ok = true; + for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { + Ok = Ok && F(P.atIndex(I), ElemT, Offset); + Offset += ElemSizeInBits; + } + return Ok; + } + + // Composite arrays. + if (FieldDesc->isCompositeArray()) { + QualType ElemType = + FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType(); + size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); + for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { + enumerateData(P.atIndex(I).narrow(), Ctx, Offset, F); + Offset += ElemSizeInBits; + } + return true; + } + + // Records. + if (FieldDesc->isRecord()) { + const Record *R = FieldDesc->ElemRecord; + const ASTRecordLayout &Layout = + Ctx.getASTContext().getASTRecordLayout(R->getDecl()); + bool Ok = true; + for (const auto &B : R->bases()) { + Pointer Elem = P.atField(B.Offset); + CharUnits ByteOffset = + Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl)); + size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset); + Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); + } + // TODO: Virtual bases? ---------------- cor3ntin wrote: Can an object with virtual base be bit_cast-ed? bit_cast requires trivially copyable, and afaik that excludes virtual bases https://eel.is/c++draft/class.prop#1.2 https://eel.is/c++draft/class.copy.ctor#11.1 https://github.com/llvm/llvm-project/pull/68288 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits