xref: /llvm-project/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (revision 8713914d76cb9d6b54278dd75fecb68bb93f6ea5)
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 "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 using namespace clang;
23 using namespace clang::interp;
24 
25 /// Implement __builtin_bit_cast and related operations.
26 /// Since our internal representation for data is more complex than
27 /// something we can simply memcpy or memcmp, we first bitcast all the data
28 /// into a buffer, which we then later use to copy the data into the target.
29 
30 // TODO:
31 //  - Try to minimize heap allocations.
32 //  - Optimize the common case of only pushing and pulling full
33 //    bytes to/from the buffer.
34 
35 /// Used to iterate over pointer fields.
36 using DataFunc =
37     llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset,
38                             Bits FullBitWidth, bool PackedBools)>;
39 
40 #define BITCAST_TYPE_SWITCH(Expr, B)                                           \
41   do {                                                                         \
42     switch (Expr) {                                                            \
43       TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
44       TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
45       TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
46       TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
47       TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
48       TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
49       TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
50       TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
51       TYPE_SWITCH_CASE(PT_IntAP, B)                                            \
52       TYPE_SWITCH_CASE(PT_IntAPS, B)                                           \
53       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
54     default:                                                                   \
55       llvm_unreachable("Unhandled bitcast type");                              \
56     }                                                                          \
57   } while (0)
58 
59 #define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B)                                \
60   do {                                                                         \
61     switch (Expr) {                                                            \
62       TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
63       TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
64       TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
65       TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
66       TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
67       TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
68       TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
69       TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
70       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
71     default:                                                                   \
72       llvm_unreachable("Unhandled bitcast type");                              \
73     }                                                                          \
74   } while (0)
75 
76 static void swapBytes(std::byte *M, size_t N) {
77   for (size_t I = 0; I != (N / 2); ++I)
78     std::swap(M[I], M[N - 1 - I]);
79 }
80 
81 /// We use this to recursively iterate over all fields and elements of a pointer
82 /// and extract relevant data for a bitcast.
83 static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
84                           Bits BitsToRead, DataFunc F) {
85   const Descriptor *FieldDesc = P.getFieldDesc();
86   assert(FieldDesc);
87 
88   // Primitives.
89   if (FieldDesc->isPrimitive()) {
90     Bits FullBitWidth =
91         Bits(Ctx.getASTContext().getTypeSize(FieldDesc->getType()));
92     return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth,
93              /*PackedBools=*/false);
94   }
95 
96   // Primitive arrays.
97   if (FieldDesc->isPrimitiveArray()) {
98     QualType ElemType = FieldDesc->getElemQualType();
99     Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
100     PrimType ElemT = *Ctx.classify(ElemType);
101     // Special case, since the bools here are packed.
102     bool PackedBools = FieldDesc->getType()->isExtVectorBoolType();
103     unsigned NumElems = FieldDesc->getNumElems();
104     bool Ok = true;
105     for (unsigned I = P.getIndex(); I != NumElems; ++I) {
106       Ok = Ok && F(P.atIndex(I), ElemT, Offset, ElemSize, PackedBools);
107       Offset += PackedBools ? Bits(1) : ElemSize;
108       if (Offset >= BitsToRead)
109         break;
110     }
111     return Ok;
112   }
113 
114   // Composite arrays.
115   if (FieldDesc->isCompositeArray()) {
116     QualType ElemType = FieldDesc->getElemQualType();
117     Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
118     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
119       enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);
120       Offset += ElemSize;
121       if (Offset >= BitsToRead)
122         break;
123     }
124     return true;
125   }
126 
127   // Records.
128   if (FieldDesc->isRecord()) {
129     const Record *R = FieldDesc->ElemRecord;
130     const ASTRecordLayout &Layout =
131         Ctx.getASTContext().getASTRecordLayout(R->getDecl());
132     bool Ok = true;
133 
134     for (const Record::Field &Fi : R->fields()) {
135       if (Fi.isUnnamedBitField())
136         continue;
137       Pointer Elem = P.atField(Fi.Offset);
138       Bits BitOffset =
139           Offset + Bits(Layout.getFieldOffset(Fi.Decl->getFieldIndex()));
140       Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);
141     }
142     for (const Record::Base &B : R->bases()) {
143       Pointer Elem = P.atField(B.Offset);
144       CharUnits ByteOffset =
145           Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));
146       Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(ByteOffset));
147       Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);
148       // FIXME: We should only (need to) do this when bitcasting OUT of the
149       // buffer, not when copying data into it.
150       if (Ok)
151         Elem.initialize();
152     }
153 
154     return Ok;
155   }
156 
157   llvm_unreachable("Unhandled data type");
158 }
159 
160 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
161                                    Bits BitsToRead, DataFunc F) {
162   return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F);
163 }
164 
165 //  This function is constexpr if and only if To, From, and the types of
166 //  all subobjects of To and From are types T such that...
167 //  (3.1) - is_union_v<T> is false;
168 //  (3.2) - is_pointer_v<T> is false;
169 //  (3.3) - is_member_pointer_v<T> is false;
170 //  (3.4) - is_volatile_v<T> is false; and
171 //  (3.5) - T has no non-static data members of reference type
172 //
173 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in
174 // ExprConstant.cpp.
175 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
176                              bool IsToType) {
177   enum {
178     E_Union = 0,
179     E_Pointer,
180     E_MemberPointer,
181     E_Volatile,
182     E_Reference,
183   };
184   enum { C_Member, C_Base };
185 
186   auto diag = [&](int Reason) -> bool {
187     const Expr *E = S.Current->getExpr(OpPC);
188     S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)
189         << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
190         << E->getSourceRange();
191     return false;
192   };
193   auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {
194     S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)
195         << NoteType << Construct << T.getUnqualifiedType() << NoteRange;
196     return false;
197   };
198 
199   T = T.getCanonicalType();
200 
201   if (T->isUnionType())
202     return diag(E_Union);
203   if (T->isPointerType())
204     return diag(E_Pointer);
205   if (T->isMemberPointerType())
206     return diag(E_MemberPointer);
207   if (T.isVolatileQualified())
208     return diag(E_Volatile);
209 
210   if (const RecordDecl *RD = T->getAsRecordDecl()) {
211     if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
212       for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
213         if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType))
214           return note(C_Base, BS.getType(), BS.getBeginLoc());
215       }
216     }
217     for (const FieldDecl *FD : RD->fields()) {
218       if (FD->getType()->isReferenceType())
219         return diag(E_Reference);
220       if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType))
221         return note(C_Member, FD->getType(), FD->getSourceRange());
222     }
223   }
224 
225   if (T->isArrayType() &&
226       !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T),
227                         IsToType))
228     return false;
229 
230   if (const auto *VT = T->getAs<VectorType>()) {
231     const ASTContext &ASTCtx = S.getASTContext();
232     QualType EltTy = VT->getElementType();
233     unsigned NElts = VT->getNumElements();
234     unsigned EltSize =
235         VT->isExtVectorBoolType() ? 1 : ASTCtx.getTypeSize(EltTy);
236 
237     if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) {
238       // The vector's size in bits is not a multiple of the target's byte size,
239       // so its layout is unspecified. For now, we'll simply treat these cases
240       // as unsupported (this should only be possible with OpenCL bool vectors
241       // whose element count isn't a multiple of the byte size).
242       const Expr *E = S.Current->getExpr(OpPC);
243       S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_vector)
244           << QualType(VT, 0) << EltSize << NElts << ASTCtx.getCharWidth();
245       return false;
246     }
247 
248     if (EltTy->isRealFloatingType() &&
249         &ASTCtx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) {
250       // The layout for x86_fp80 vectors seems to be handled very inconsistently
251       // by both clang and LLVM, so for now we won't allow bit_casts involving
252       // it in a constexpr context.
253       const Expr *E = S.Current->getExpr(OpPC);
254       S.FFDiag(E, diag::note_constexpr_bit_cast_unsupported_type) << EltTy;
255       return false;
256     }
257   }
258 
259   return true;
260 }
261 
262 bool clang::interp::readPointerToBuffer(const Context &Ctx,
263                                         const Pointer &FromPtr,
264                                         BitcastBuffer &Buffer,
265                                         bool ReturnOnUninit) {
266   const ASTContext &ASTCtx = Ctx.getASTContext();
267   Endian TargetEndianness =
268       ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
269 
270   return enumeratePointerFields(
271       FromPtr, Ctx, Buffer.size(),
272       [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
273           bool PackedBools) -> bool {
274         Bits BitWidth = FullBitWidth;
275 
276         if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
277           BitWidth = Bits(std::min(FD->getBitWidthValue(ASTCtx),
278                                    (unsigned)FullBitWidth.getQuantity()));
279         else if (T == PT_Bool && PackedBools)
280           BitWidth = Bits(1);
281 
282         if (BitWidth.isZero())
283           return true;
284 
285         // Bits will be left uninitialized and diagnosed when reading.
286         if (!P.isInitialized())
287           return true;
288 
289         if (T == PT_Ptr) {
290           assert(P.getType()->isNullPtrType());
291           // Clang treats nullptr_t has having NO bits in its value
292           // representation. So, we accept it here and leave its bits
293           // uninitialized.
294           return true;
295         }
296 
297         assert(P.isInitialized());
298         auto Buff = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes());
299         // Work around floating point types that contain unused padding bytes.
300         // This is really just `long double` on x86, which is the only
301         // fundamental type with padding bytes.
302         if (T == PT_Float) {
303           const Floating &F = P.deref<Floating>();
304           Bits NumBits = Bits(
305               llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics()));
306           assert(NumBits.isFullByte());
307           assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
308           F.bitcastToMemory(Buff.get());
309           // Now, only (maybe) swap the actual size of the float, excluding the
310           // padding bits.
311           if (llvm::sys::IsBigEndianHost)
312             swapBytes(Buff.get(), NumBits.roundToBytes());
313 
314           Buffer.markInitialized(BitOffset, NumBits);
315         } else {
316           BITCAST_TYPE_SWITCH(T, { P.deref<T>().bitcastToMemory(Buff.get()); });
317 
318           if (llvm::sys::IsBigEndianHost)
319             swapBytes(Buff.get(), FullBitWidth.roundToBytes());
320           Buffer.markInitialized(BitOffset, BitWidth);
321         }
322 
323         Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness);
324         return true;
325       });
326 }
327 
328 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
329                               std::byte *Buff, Bits BitWidth, Bits FullBitWidth,
330                               bool &HasIndeterminateBits) {
331   assert(Ptr.isLive());
332   assert(Ptr.isBlockPointer());
333   assert(Buff);
334   assert(BitWidth <= FullBitWidth);
335   assert(FullBitWidth.isFullByte());
336   assert(BitWidth.isFullByte());
337 
338   BitcastBuffer Buffer(FullBitWidth);
339   size_t BuffSize = FullBitWidth.roundToBytes();
340   if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false))
341     return false;
342 
343   bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer,
344                                      /*ReturnOnUninit=*/false);
345   HasIndeterminateBits = !Buffer.rangeInitialized(Bits::zero(), BitWidth);
346 
347   const ASTContext &ASTCtx = S.getASTContext();
348   Endian TargetEndianness =
349       ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
350   auto B =
351       Buffer.copyBits(Bits::zero(), BitWidth, FullBitWidth, TargetEndianness);
352 
353   std::memcpy(Buff, B.get(), BuffSize);
354 
355   if (llvm::sys::IsBigEndianHost)
356     swapBytes(Buff, BitWidth.roundToBytes());
357 
358   return Success;
359 }
360 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
361                                  const Pointer &FromPtr, Pointer &ToPtr) {
362   const ASTContext &ASTCtx = S.getASTContext();
363   CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(ToPtr.getType());
364 
365   return DoBitCastPtr(S, OpPC, FromPtr, ToPtr, ObjectReprChars.getQuantity());
366 }
367 
368 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
369                                  const Pointer &FromPtr, Pointer &ToPtr,
370                                  size_t Size) {
371   assert(FromPtr.isLive());
372   assert(FromPtr.isBlockPointer());
373   assert(ToPtr.isBlockPointer());
374 
375   QualType FromType = FromPtr.getType();
376   QualType ToType = ToPtr.getType();
377 
378   if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))
379     return false;
380   if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))
381     return false;
382 
383   const ASTContext &ASTCtx = S.getASTContext();
384   BitcastBuffer Buffer(Bytes(Size).toBits());
385   readPointerToBuffer(S.getContext(), FromPtr, Buffer,
386                       /*ReturnOnUninit=*/false);
387 
388   // Now read the values out of the buffer again and into ToPtr.
389   Endian TargetEndianness =
390       ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
391   bool Success = enumeratePointerFields(
392       ToPtr, S.getContext(), Buffer.size(),
393       [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
394           bool PackedBools) -> bool {
395         QualType PtrType = P.getType();
396         if (T == PT_Float) {
397           const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
398           Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
399           assert(NumBits.isFullByte());
400           assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
401           auto M = Buffer.copyBits(BitOffset, NumBits, FullBitWidth,
402                                    TargetEndianness);
403 
404           if (llvm::sys::IsBigEndianHost)
405             swapBytes(M.get(), NumBits.roundToBytes());
406 
407           P.deref<Floating>() = Floating::bitcastFromMemory(M.get(), Semantics);
408           P.initialize();
409           return true;
410         }
411 
412         Bits BitWidth;
413         if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
414           BitWidth = Bits(std::min(FD->getBitWidthValue(ASTCtx),
415                                    (unsigned)FullBitWidth.getQuantity()));
416         else if (T == PT_Bool && PackedBools)
417           BitWidth = Bits(1);
418         else
419           BitWidth = FullBitWidth;
420 
421         // If any of the bits are uninitialized, we need to abort unless the
422         // target type is std::byte or unsigned char.
423         bool Initialized = Buffer.rangeInitialized(BitOffset, BitWidth);
424         if (!Initialized) {
425           if (!PtrType->isStdByteType() &&
426               !PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&
427               !PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {
428             const Expr *E = S.Current->getExpr(OpPC);
429             S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
430                 << PtrType << S.getLangOpts().CharIsSigned
431                 << E->getSourceRange();
432 
433             return false;
434           }
435           return true;
436         }
437 
438         auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
439                                       TargetEndianness);
440         if (llvm::sys::IsBigEndianHost)
441           swapBytes(Memory.get(), FullBitWidth.roundToBytes());
442 
443         BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {
444           if (BitWidth.nonZero())
445             P.deref<T>() = T::bitcastFromMemory(Memory.get(), T::bitWidth())
446                                .truncate(BitWidth.getQuantity());
447           else
448             P.deref<T>() = T::zero();
449         });
450         P.initialize();
451         return true;
452       });
453 
454   return Success;
455 }
456