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