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