xref: /llvm-project/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (revision fb30208d1e4c2c1ba34c331f6f90a99552d2df97)
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 /// Float is a special case that sometimes needs the floating point semantics
52 /// to be available.
53 #define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B)                                \
54   do {                                                                         \
55     switch (Expr) {                                                            \
56       TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
57       TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
58       TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
59       TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
60       TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
61       TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
62       TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
63       TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
64       TYPE_SWITCH_CASE(PT_IntAP, B)                                            \
65       TYPE_SWITCH_CASE(PT_IntAPS, B)                                           \
66       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
67       TYPE_SWITCH_CASE(PT_Float, B)                                            \
68     default:                                                                   \
69       llvm_unreachable("Unhandled bitcast type");                              \
70     }                                                                          \
71   } while (0)
72 
73 static bool bitof(std::byte B, unsigned BitIndex) {
74   return (B & (std::byte{1} << BitIndex)) != std::byte{0};
75 }
76 
77 static void swapBytes(std::byte *M, size_t N) {
78   for (size_t I = 0; I != (N / 2); ++I)
79     std::swap(M[I], M[N - 1 - I]);
80 }
81 
82 /// Track what bits have been initialized to known values and which ones
83 /// have indeterminate value.
84 /// All offsets are in bits.
85 struct BitcastBuffer {
86   size_t SizeInBits = 0;
87   llvm::SmallVector<std::byte> Data;
88 
89   BitcastBuffer() = default;
90 
91   size_t size() const { return SizeInBits; }
92 
93   const std::byte *data() const { return Data.data(); }
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 BitOffset, size_t BitWidth,
112                 bool BigEndianTarget) {
113     bool OnlyFullBytes = BitWidth % 8 == 0;
114     unsigned NBytes = BitWidth / 8;
115 
116     size_t BitsHandled = 0;
117     // Read all full bytes first
118     for (size_t I = 0; I != NBytes; ++I) {
119       std::byte B =
120           BigEndianTarget ? data[NBytes - OnlyFullBytes - I] : data[I];
121       for (unsigned X = 0; X != 8; ++X) {
122         pushBit(bitof(B, X));
123         ++BitsHandled;
124       }
125     }
126 
127     if (BitsHandled == BitWidth)
128       return;
129 
130     // Rest of the bits.
131     assert((BitWidth - BitsHandled) < 8);
132     std::byte B = BigEndianTarget ? data[0] : data[NBytes];
133     for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
134       pushBit(bitof(B, I));
135       ++BitsHandled;
136     }
137 
138     assert(BitsHandled == BitWidth);
139   }
140 };
141 
142 /// We use this to recursively iterate over all fields and elemends of a pointer
143 /// and extract relevant data for a bitcast.
144 static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
145                           DataFunc F) {
146   const Descriptor *FieldDesc = P.getFieldDesc();
147   assert(FieldDesc);
148 
149   // Primitives.
150   if (FieldDesc->isPrimitive())
151     return F(P, FieldDesc->getPrimType(), Offset);
152 
153   // Primitive arrays.
154   if (FieldDesc->isPrimitiveArray()) {
155     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
156     QualType ElemType = FieldDesc->getElemQualType();
157     size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
158     PrimType ElemT = *Ctx.classify(ElemType);
159     bool Ok = true;
160     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
161       unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I;
162       Ok = Ok && F(P.atIndex(Index), ElemT, Offset);
163       Offset += ElemSizeInBits;
164     }
165     return Ok;
166   }
167 
168   // Composite arrays.
169   if (FieldDesc->isCompositeArray()) {
170     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
171     QualType ElemType = FieldDesc->getElemQualType();
172     size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
173     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
174       unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I;
175       enumerateData(P.atIndex(Index).narrow(), Ctx, Offset, F);
176       Offset += ElemSizeInBits;
177     }
178     return true;
179   }
180 
181   // Records.
182   if (FieldDesc->isRecord()) {
183     bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian();
184     const Record *R = FieldDesc->ElemRecord;
185     const ASTRecordLayout &Layout =
186         Ctx.getASTContext().getASTRecordLayout(R->getDecl());
187     bool Ok = true;
188 
189     auto enumerateFields = [&]() -> void {
190       for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) {
191         const Record::Field *Fi =
192             R->getField(BigEndianTarget ? (N - 1 - I) : I);
193         Pointer Elem = P.atField(Fi->Offset);
194         size_t BitOffset =
195             Offset + Layout.getFieldOffset(Fi->Decl->getFieldIndex());
196         Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
197       }
198     };
199     auto enumerateBases = [&]() -> void {
200       for (unsigned I = 0, N = R->getNumBases(); I != N; ++I) {
201         const Record::Base *B = R->getBase(BigEndianTarget ? (N - 1 - I) : I);
202         Pointer Elem = P.atField(B->Offset);
203         CharUnits ByteOffset =
204             Layout.getBaseClassOffset(cast<CXXRecordDecl>(B->Decl));
205         size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset);
206         Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
207       }
208     };
209 
210     if (BigEndianTarget) {
211       enumerateFields();
212       enumerateBases();
213     } else {
214       enumerateBases();
215       enumerateFields();
216     }
217 
218     return Ok;
219   }
220 
221   llvm_unreachable("Unhandled data type");
222 }
223 
224 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
225                                    DataFunc F) {
226   return enumerateData(P, Ctx, 0, F);
227 }
228 
229 //  This function is constexpr if and only if To, From, and the types of
230 //  all subobjects of To and From are types T such that...
231 //  (3.1) - is_union_v<T> is false;
232 //  (3.2) - is_pointer_v<T> is false;
233 //  (3.3) - is_member_pointer_v<T> is false;
234 //  (3.4) - is_volatile_v<T> is false; and
235 //  (3.5) - T has no non-static data members of reference type
236 //
237 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in
238 // ExprConstant.cpp.
239 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
240                              bool IsToType) {
241   enum {
242     E_Union = 0,
243     E_Pointer,
244     E_MemberPointer,
245     E_Volatile,
246     E_Reference,
247   };
248   enum { C_Member, C_Base };
249 
250   auto diag = [&](int Reason) -> bool {
251     const Expr *E = S.Current->getExpr(OpPC);
252     S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)
253         << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
254         << E->getSourceRange();
255     return false;
256   };
257   auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {
258     S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)
259         << NoteType << Construct << T << NoteRange;
260     return false;
261   };
262 
263   T = T.getCanonicalType();
264 
265   if (T->isUnionType())
266     return diag(E_Union);
267   if (T->isPointerType())
268     return diag(E_Pointer);
269   if (T->isMemberPointerType())
270     return diag(E_MemberPointer);
271   if (T.isVolatileQualified())
272     return diag(E_Volatile);
273 
274   if (const RecordDecl *RD = T->getAsRecordDecl()) {
275     if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
276       for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
277         if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType))
278           return note(C_Base, BS.getType(), BS.getBeginLoc());
279       }
280     }
281     for (const FieldDecl *FD : RD->fields()) {
282       if (FD->getType()->isReferenceType())
283         return diag(E_Reference);
284       if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType))
285         return note(C_Member, FD->getType(), FD->getSourceRange());
286     }
287   }
288 
289   if (T->isArrayType() &&
290       !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T),
291                         IsToType))
292     return false;
293 
294   return true;
295 }
296 
297 static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
298                                 BitcastBuffer &Buffer, bool ReturnOnUninit) {
299   const ASTContext &ASTCtx = Ctx.getASTContext();
300   bool SwapData = (ASTCtx.getTargetInfo().isLittleEndian() !=
301                    llvm::sys::IsLittleEndianHost);
302   bool BigEndianTarget = ASTCtx.getTargetInfo().isBigEndian();
303 
304   return enumeratePointerFields(
305       FromPtr, Ctx,
306       [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool {
307         if (!P.isInitialized()) {
308           assert(false && "Implement uninitialized value tracking");
309           return ReturnOnUninit;
310         }
311 
312         assert(P.isInitialized());
313         // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
314         if (T == PT_Ptr)
315           assert(false && "Implement casting to pointer types");
316 
317         CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
318         unsigned BitWidth = ASTCtx.toBits(ObjectReprChars);
319         llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity());
320         // Work around floating point types that contain unused padding bytes.
321         // This is really just `long double` on x86, which is the only
322         // fundamental type with padding bytes.
323         if (T == PT_Float) {
324           const Floating &F = P.deref<Floating>();
325           unsigned NumBits =
326               llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics());
327           assert(NumBits % 8 == 0);
328           assert(NumBits <= (ObjectReprChars.getQuantity() * 8));
329           F.bitcastToMemory(Buff.data());
330           // Now, only (maybe) swap the actual size of the float, excluding the
331           // padding bits.
332           if (SwapData)
333             swapBytes(Buff.data(), NumBits / 8);
334 
335         } else {
336           if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
337             BitWidth = FD->getBitWidthValue(ASTCtx);
338 
339           BITCAST_TYPE_SWITCH(T, {
340             T Val = P.deref<T>();
341             Val.bitcastToMemory(Buff.data());
342           });
343           if (SwapData)
344             swapBytes(Buff.data(), ObjectReprChars.getQuantity());
345         }
346 
347         if (BitWidth != (Buff.size() * 8) && BigEndianTarget) {
348           Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)),
349                           BitOffset, BitWidth, BigEndianTarget);
350         } else {
351           Buffer.pushData(Buff.data(), BitOffset, BitWidth, BigEndianTarget);
352         }
353         return true;
354       });
355 }
356 
357 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
358                               std::byte *Buff, size_t BuffSize,
359                               bool &HasIndeterminateBits) {
360   assert(Ptr.isLive());
361   assert(Ptr.isBlockPointer());
362   assert(Buff);
363 
364   BitcastBuffer Buffer;
365   if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false))
366     return false;
367 
368   bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer,
369                                      /*ReturnOnUninit=*/false);
370   assert(Buffer.size() == BuffSize * 8);
371 
372   HasIndeterminateBits = !Buffer.allInitialized();
373   std::memcpy(Buff, Buffer.data(), BuffSize);
374 
375   if (llvm::sys::IsBigEndianHost)
376     swapBytes(Buff, BuffSize);
377 
378   return Success;
379 }
380