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