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