xref: /llvm-project/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (revision 476b208e0115e766605e9f850982996a1d51c287)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "InterpBuiltinBitCast.h"
9 #include "Boolean.h"
10 #include "Context.h"
11 #include "Floating.h"
12 #include "Integral.h"
13 #include "InterpState.h"
14 #include "MemberPointer.h"
15 #include "Pointer.h"
16 #include "Record.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/RecordLayout.h"
19 #include "clang/Basic/TargetInfo.h"
20 
21 using namespace clang;
22 using namespace clang::interp;
23 
24 /// Used to iterate over pointer fields.
25 using DataFunc = llvm::function_ref<bool(const Pointer &P, PrimType Ty,
26                                          size_t BitOffset, bool PackedBools)>;
27 
28 #define BITCAST_TYPE_SWITCH(Expr, B)                                           \
29   do {                                                                         \
30     switch (Expr) {                                                            \
31       TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
32       TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
33       TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
34       TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
35       TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
36       TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
37       TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
38       TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
39       TYPE_SWITCH_CASE(PT_IntAP, B)                                            \
40       TYPE_SWITCH_CASE(PT_IntAPS, B)                                           \
41       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
42     default:                                                                   \
43       llvm_unreachable("Unhandled bitcast type");                              \
44     }                                                                          \
45   } while (0)
46 
47 #define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B)                                \
48   do {                                                                         \
49     switch (Expr) {                                                            \
50       TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
51       TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
52       TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
53       TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
54       TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
55       TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
56       TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
57       TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
58       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
59     default:                                                                   \
60       llvm_unreachable("Unhandled bitcast type");                              \
61     }                                                                          \
62   } while (0)
63 
64 static bool bitof(std::byte B, unsigned BitIndex) {
65   return (B & (std::byte{1} << BitIndex)) != std::byte{0};
66 }
67 
68 static void swapBytes(std::byte *M, size_t N) {
69   for (size_t I = 0; I != (N / 2); ++I)
70     std::swap(M[I], M[N - 1 - I]);
71 }
72 
73 /// Track what bits have been initialized to known values and which ones
74 /// have indeterminate value.
75 /// All offsets are in bits.
76 struct BitcastBuffer {
77   size_t SizeInBits = 0;
78   llvm::SmallVector<std::byte> Data;
79 
80   BitcastBuffer() = default;
81 
82   size_t size() const { return SizeInBits; }
83 
84   const std::byte *data() const { return Data.data(); }
85 
86   std::byte *getBytes(unsigned BitOffset) const {
87     assert(BitOffset % 8 == 0);
88     assert(BitOffset < SizeInBits);
89     return const_cast<std::byte *>(data() + (BitOffset / 8));
90   }
91 
92   bool allInitialized() const {
93     // FIXME: Implement.
94     return true;
95   }
96 
97   bool atByteBoundary() const { return (Data.size() * 8) == SizeInBits; }
98 
99   void pushBit(bool Value) {
100     if (atByteBoundary())
101       Data.push_back(std::byte{0});
102 
103     if (Value)
104       Data.back() |= (std::byte{1} << (SizeInBits % 8));
105     ++SizeInBits;
106   }
107 
108   void pushData(const std::byte *data, size_t BitWidth, bool BigEndianTarget) {
109     bool OnlyFullBytes = BitWidth % 8 == 0;
110     unsigned NBytes = BitWidth / 8;
111 
112     size_t BitsHandled = 0;
113     // Read all full bytes first
114     for (size_t I = 0; I != NBytes; ++I) {
115       std::byte B =
116           BigEndianTarget ? data[NBytes - OnlyFullBytes - I] : data[I];
117       for (unsigned X = 0; X != 8; ++X) {
118         pushBit(bitof(B, X));
119         ++BitsHandled;
120       }
121     }
122 
123     if (BitsHandled == BitWidth)
124       return;
125 
126     // Rest of the bits.
127     assert((BitWidth - BitsHandled) < 8);
128     std::byte B = BigEndianTarget ? data[0] : data[NBytes];
129     for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
130       pushBit(bitof(B, I));
131       ++BitsHandled;
132     }
133 
134     assert(BitsHandled == BitWidth);
135   }
136 };
137 
138 /// We use this to recursively iterate over all fields and elemends of a pointer
139 /// and extract relevant data for a bitcast.
140 static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
141                           DataFunc F) {
142   const Descriptor *FieldDesc = P.getFieldDesc();
143   assert(FieldDesc);
144 
145   // Primitives.
146   if (FieldDesc->isPrimitive())
147     return F(P, FieldDesc->getPrimType(), Offset, false);
148 
149   // Primitive arrays.
150   if (FieldDesc->isPrimitiveArray()) {
151     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
152     QualType ElemType = FieldDesc->getElemQualType();
153     size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
154     PrimType ElemT = *Ctx.classify(ElemType);
155     // Special case, since the bools here are packed.
156     bool PackedBools = FieldDesc->getType()->isExtVectorBoolType();
157     bool Ok = true;
158     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
159       unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I;
160       Ok = Ok && F(P.atIndex(Index), ElemT, Offset, PackedBools);
161       Offset += ElemSizeInBits;
162     }
163     return Ok;
164   }
165 
166   // Composite arrays.
167   if (FieldDesc->isCompositeArray()) {
168     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
169     QualType ElemType = FieldDesc->getElemQualType();
170     size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
171     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
172       unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I;
173       enumerateData(P.atIndex(Index).narrow(), Ctx, Offset, F);
174       Offset += ElemSizeInBits;
175     }
176     return true;
177   }
178 
179   // Records.
180   if (FieldDesc->isRecord()) {
181     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
182     const Record *R = FieldDesc->ElemRecord;
183     const ASTRecordLayout &Layout =
184         Ctx.getASTContext().getASTRecordLayout(R->getDecl());
185     bool Ok = true;
186 
187     auto enumerateFields = [&]() -> void {
188       for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) {
189         const Record::Field *Fi =
190             R->getField(BigEndianTarget ? (N - 1 - I) : I);
191         Pointer Elem = P.atField(Fi->Offset);
192         size_t BitOffset =
193             Offset + Layout.getFieldOffset(Fi->Decl->getFieldIndex());
194         Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
195       }
196     };
197     auto enumerateBases = [&]() -> void {
198       for (unsigned I = 0, N = R->getNumBases(); I != N; ++I) {
199         const Record::Base *B = R->getBase(BigEndianTarget ? (N - 1 - I) : I);
200         Pointer Elem = P.atField(B->Offset);
201         CharUnits ByteOffset =
202             Layout.getBaseClassOffset(cast<CXXRecordDecl>(B->Decl));
203         size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset);
204         Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
205       }
206     };
207 
208     if (BigEndianTarget) {
209       enumerateFields();
210       enumerateBases();
211     } else {
212       enumerateBases();
213       enumerateFields();
214     }
215 
216     return Ok;
217   }
218 
219   llvm_unreachable("Unhandled data type");
220 }
221 
222 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
223                                    DataFunc F) {
224   return enumerateData(P, Ctx, 0, F);
225 }
226 
227 //  This function is constexpr if and only if To, From, and the types of
228 //  all subobjects of To and From are types T such that...
229 //  (3.1) - is_union_v<T> is false;
230 //  (3.2) - is_pointer_v<T> is false;
231 //  (3.3) - is_member_pointer_v<T> is false;
232 //  (3.4) - is_volatile_v<T> is false; and
233 //  (3.5) - T has no non-static data members of reference type
234 //
235 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in
236 // ExprConstant.cpp.
237 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
238                              bool IsToType) {
239   enum {
240     E_Union = 0,
241     E_Pointer,
242     E_MemberPointer,
243     E_Volatile,
244     E_Reference,
245   };
246   enum { C_Member, C_Base };
247 
248   auto diag = [&](int Reason) -> bool {
249     const Expr *E = S.Current->getExpr(OpPC);
250     S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)
251         << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
252         << E->getSourceRange();
253     return false;
254   };
255   auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {
256     S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)
257         << NoteType << Construct << T.getUnqualifiedType() << NoteRange;
258     return false;
259   };
260 
261   T = T.getCanonicalType();
262 
263   if (T->isUnionType())
264     return diag(E_Union);
265   if (T->isPointerType())
266     return diag(E_Pointer);
267   if (T->isMemberPointerType())
268     return diag(E_MemberPointer);
269   if (T.isVolatileQualified())
270     return diag(E_Volatile);
271 
272   if (const RecordDecl *RD = T->getAsRecordDecl()) {
273     if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
274       for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
275         if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType))
276           return note(C_Base, BS.getType(), BS.getBeginLoc());
277       }
278     }
279     for (const FieldDecl *FD : RD->fields()) {
280       if (FD->getType()->isReferenceType())
281         return diag(E_Reference);
282       if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType))
283         return note(C_Member, FD->getType(), FD->getSourceRange());
284     }
285   }
286 
287   if (T->isArrayType() &&
288       !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T),
289                         IsToType))
290     return false;
291 
292   return true;
293 }
294 
295 static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
296                                 BitcastBuffer &Buffer, bool ReturnOnUninit) {
297   const ASTContext &ASTCtx = Ctx.getASTContext();
298   bool SwapData = (ASTCtx.getTargetInfo().isLittleEndian() !=
299                    llvm::sys::IsLittleEndianHost);
300   bool BigEndianTarget = ASTCtx.getTargetInfo().isBigEndian();
301 
302   return enumeratePointerFields(
303       FromPtr, Ctx,
304       [&](const Pointer &P, PrimType T, size_t BitOffset,
305           bool PackedBools) -> bool {
306         if (!P.isInitialized()) {
307           assert(false && "Implement uninitialized value tracking");
308           return ReturnOnUninit;
309         }
310 
311         assert(P.isInitialized());
312         // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
313         if (T == PT_Ptr)
314           assert(false && "Implement casting to pointer types");
315 
316         CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
317         unsigned BitWidth = ASTCtx.toBits(ObjectReprChars);
318         llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity());
319         // Work around floating point types that contain unused padding bytes.
320         // This is really just `long double` on x86, which is the only
321         // fundamental type with padding bytes.
322         if (T == PT_Float) {
323           const Floating &F = P.deref<Floating>();
324           unsigned NumBits =
325               llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics());
326           assert(NumBits % 8 == 0);
327           assert(NumBits <= (ObjectReprChars.getQuantity() * 8));
328           F.bitcastToMemory(Buff.data());
329           // Now, only (maybe) swap the actual size of the float, excluding the
330           // padding bits.
331           if (SwapData)
332             swapBytes(Buff.data(), NumBits / 8);
333 
334         } else {
335           if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
336             BitWidth = FD->getBitWidthValue(ASTCtx);
337           else if (T == PT_Bool && PackedBools)
338             BitWidth = 1;
339 
340           BITCAST_TYPE_SWITCH(T, {
341             T Val = P.deref<T>();
342             Val.bitcastToMemory(Buff.data());
343           });
344           if (SwapData)
345             swapBytes(Buff.data(), ObjectReprChars.getQuantity());
346         }
347 
348         if (BitWidth != (Buff.size() * 8) && BigEndianTarget) {
349           Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)),
350                           BitWidth, BigEndianTarget);
351         } else {
352           Buffer.pushData(Buff.data(), BitWidth, BigEndianTarget);
353         }
354         return true;
355       });
356 }
357 
358 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
359                               std::byte *Buff, size_t BuffSize,
360                               bool &HasIndeterminateBits) {
361   assert(Ptr.isLive());
362   assert(Ptr.isBlockPointer());
363   assert(Buff);
364 
365   BitcastBuffer Buffer;
366   if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false))
367     return false;
368 
369   bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer,
370                                      /*ReturnOnUninit=*/false);
371   assert(Buffer.size() == BuffSize * 8);
372 
373   HasIndeterminateBits = !Buffer.allInitialized();
374   std::memcpy(Buff, Buffer.data(), BuffSize);
375 
376   if (llvm::sys::IsBigEndianHost)
377     swapBytes(Buff, BuffSize);
378 
379   return Success;
380 }
381 
382 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
383                                  const Pointer &FromPtr, Pointer &ToPtr) {
384   assert(FromPtr.isLive());
385   assert(FromPtr.isBlockPointer());
386   assert(ToPtr.isBlockPointer());
387 
388   QualType FromType = FromPtr.getType();
389   QualType ToType = ToPtr.getType();
390 
391   if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))
392     return false;
393   if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))
394     return false;
395 
396   BitcastBuffer Buffer;
397   readPointerToBuffer(S.getContext(), FromPtr, Buffer,
398                       /*ReturnOnUninit=*/false);
399 
400   // Now read the values out of the buffer again and into ToPtr.
401   const ASTContext &ASTCtx = S.getASTContext();
402   size_t BitOffset = 0;
403   bool Success = enumeratePointerFields(
404       ToPtr, S.getContext(),
405       [&](const Pointer &P, PrimType T, size_t _, bool PackedBools) -> bool {
406         if (T == PT_Float) {
407           CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
408           const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType());
409           unsigned NumBits = llvm::APFloatBase::getSizeInBits(Semantics);
410           assert(NumBits % 8 == 0);
411           assert(NumBits <= ASTCtx.toBits(ObjectReprChars));
412           std::byte *M = Buffer.getBytes(BitOffset);
413 
414           if (llvm::sys::IsBigEndianHost)
415             swapBytes(M, NumBits / 8);
416 
417           P.deref<Floating>() = Floating::bitcastFromMemory(M, Semantics);
418           P.initialize();
419           BitOffset += ASTCtx.toBits(ObjectReprChars);
420           return true;
421         }
422 
423         BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {
424           std::byte *M = Buffer.getBytes(BitOffset);
425 
426           if (llvm::sys::IsBigEndianHost)
427             swapBytes(M, T::bitWidth() / 8);
428 
429           P.deref<T>() = T::bitcastFromMemory(M, T::bitWidth());
430           P.initialize();
431           BitOffset += T::bitWidth();
432         });
433         return true;
434       });
435 
436   return Success;
437 }
438