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