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 "Char.h"
12#include "Context.h"
13#include "Floating.h"
14#include "Integral.h"
15#include "InterpState.h"
16#include "MemberPointer.h"
17#include "Pointer.h"
18#include "Record.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/RecordLayout.h"
21#include "clang/Basic/TargetInfo.h"
22
23#include <variant>
24
25using namespace clang;
26using namespace clang::interp;
27
28/// Implement __builtin_bit_cast and related operations.
29/// Since our internal representation for data is more complex than
30/// something we can simply memcpy or memcmp, we first bitcast all the data
31/// into a buffer, which we then later use to copy the data into the target.
32
33// TODO:
34// - Try to minimize heap allocations.
35// - Optimize the common case of only pushing and pulling full
36// bytes to/from the buffer.
37
38enum class Result { Success, Skip, Failure };
39
40/// Used to iterate over pointer fields.
41using DataFunc =
42 llvm::function_ref<Result(PtrView P, PrimType Ty, Bits BitOffset,
43 Bits FullBitWidth, bool PackedBools)>;
44
45#define BITCAST_TYPE_SWITCH(Expr, B) \
46 do { \
47 switch (Expr) { \
48 TYPE_SWITCH_CASE(PT_Sint8, B) \
49 TYPE_SWITCH_CASE(PT_Uint8, B) \
50 TYPE_SWITCH_CASE(PT_Sint16, B) \
51 TYPE_SWITCH_CASE(PT_Uint16, B) \
52 TYPE_SWITCH_CASE(PT_Sint32, B) \
53 TYPE_SWITCH_CASE(PT_Uint32, B) \
54 TYPE_SWITCH_CASE(PT_Sint64, B) \
55 TYPE_SWITCH_CASE(PT_Uint64, B) \
56 TYPE_SWITCH_CASE(PT_IntAP, B) \
57 TYPE_SWITCH_CASE(PT_IntAPS, B) \
58 TYPE_SWITCH_CASE(PT_Bool, B) \
59 default: \
60 llvm_unreachable("Unhandled bitcast type"); \
61 } \
62 } while (0)
63
64#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \
65 do { \
66 switch (Expr) { \
67 TYPE_SWITCH_CASE(PT_Sint8, B) \
68 TYPE_SWITCH_CASE(PT_Uint8, B) \
69 TYPE_SWITCH_CASE(PT_Sint16, B) \
70 TYPE_SWITCH_CASE(PT_Uint16, B) \
71 TYPE_SWITCH_CASE(PT_Sint32, B) \
72 TYPE_SWITCH_CASE(PT_Uint32, B) \
73 TYPE_SWITCH_CASE(PT_Sint64, B) \
74 TYPE_SWITCH_CASE(PT_Uint64, B) \
75 TYPE_SWITCH_CASE(PT_Bool, B) \
76 default: \
77 llvm_unreachable("Unhandled bitcast type"); \
78 } \
79 } while (0)
80
81// FIXME: It is unfortunate that we have this function at all, but we can read
82// from a StringPointer. In the later callback-based reading and writing paths,
83// we do assume a BlockPointer though.
84static std::pair<Block *, std::unique_ptr<Descriptor>>
85convertToBlockPointer(const Context &Ctx, const StringPointer &SP) {
86 const StringLiteral *S = SP.getLiteral();
87 const size_t CharWidth = S->getCharByteWidth();
88 const size_t BitWidth = CharWidth * Ctx.getCharBit();
89 unsigned StringLength = S->getLength();
90
91 OptPrimType CharType =
92 Ctx.classify(T: S->getType()->castAsArrayTypeUnsafe()->getElementType());
93 assert(CharType);
94
95 // Create a descriptor for the string.
96 std::unique_ptr<Descriptor> Desc = std::make_unique<Descriptor>(
97 args&: S, args: S->getType().getTypePtr(), args: *CharType, args: StringLength + 1,
98 /*IsConst=*/args: true,
99 /*isTemporary=*/args: false,
100 /*isMutable=*/args: false,
101 /*IsVolatile=*/args: false);
102
103 // Allocate storage for the string.
104 // The byte length does not include the null terminator.
105 // unsigned GlobalIndex = Globals.size();
106 auto *Memory = new std::byte[sizeof(Block) + Desc->getAllocSize()];
107 auto *B = new (Memory) Block(Ctx.getEvalID(), Desc.get());
108 B->invokeCtor();
109
110 new (B->rawData()) GlobalInlineDescriptor{.InitState: GlobalInitState::Initialized};
111
112 Pointer Ptr(B);
113 if (CharWidth == 1) {
114 std::memcpy(dest: &Ptr.elem<char>(I: 0), src: S->getString().data(), n: StringLength);
115 } else {
116 // Construct the string in storage.
117 for (unsigned I = 0; I <= StringLength; ++I) {
118 uint32_t CodePoint = I == StringLength ? 0 : S->getCodeUnit(I);
119 INT_TYPE_SWITCH_NO_BOOL(*CharType,
120 Ptr.elem<T>(I) = T::from(CodePoint, BitWidth););
121 }
122 }
123 Ptr.initializeAllElements();
124 return std::make_pair(x: std::move(B), y: std::move(Desc));
125}
126
127/// We use this to recursively iterate over all fields and elements of a pointer
128/// and extract relevant data for a bitcast.
129static Result enumerateData(PtrView P, const Context &Ctx, Bits Offset,
130 Bits BitsToRead, DataFunc F, bool Initialize) {
131 const Descriptor *FieldDesc = P.getFieldDesc();
132 assert(FieldDesc);
133
134 // Primitives.
135 if (FieldDesc->isPrimitive()) {
136 Bits FullBitWidth =
137 Bits(Ctx.getASTContext().getTypeSize(T: FieldDesc->getType()));
138 return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth,
139 /*PackedBools=*/false);
140 }
141
142 // Primitive arrays.
143 if (FieldDesc->isPrimitiveArray()) {
144 QualType ElemType = FieldDesc->getElemQualType();
145 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(T: ElemType));
146 PrimType ElemT = *Ctx.classify(T: ElemType);
147 // Special case, since the bools here are packed.
148 bool PackedBools =
149 FieldDesc->getType()->isPackedVectorBoolType(ctx: Ctx.getASTContext());
150 unsigned NumElems = FieldDesc->getNumElems();
151 bool Ok = true;
152 for (unsigned I = P.getIndex(); I != NumElems; ++I) {
153 Result Res = F(P.atIndex(Idx: I), ElemT, Offset, ElemSize, PackedBools);
154
155 Ok = Ok && (Res == Result::Success);
156 Offset += PackedBools ? Bits(1) : ElemSize;
157 if (Offset >= BitsToRead)
158 break;
159 }
160 return Ok ? Result::Success : Result::Skip;
161 }
162
163 // Composite arrays.
164 if (FieldDesc->isCompositeArray()) {
165 QualType ElemType = FieldDesc->getElemQualType();
166 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(T: ElemType));
167 for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {
168 enumerateData(P: P.atIndex(Idx: I).narrow(), Ctx, Offset, BitsToRead, F,
169 Initialize);
170 Offset += ElemSize;
171 if (Offset >= BitsToRead)
172 break;
173 }
174 return Result::Success;
175 }
176
177 // Records.
178 if (FieldDesc->isRecord()) {
179 const Record *R = FieldDesc->ElemRecord;
180 if (R->getDecl()->isInvalidDecl())
181 return Result::Failure;
182 const ASTRecordLayout &Layout =
183 Ctx.getASTContext().getASTRecordLayout(D: R->getDecl());
184 bool Ok = true;
185
186 for (const Record::Field &Fi : R->fields()) {
187 if (Fi.isUnnamedBitField())
188 continue;
189
190 PtrView Elem = P.atField(Offset: Fi.Offset);
191 Bits BitOffset =
192 Offset + Bits(Layout.getFieldOffset(FieldNo: Fi.Decl->getFieldIndex()));
193 Result Res =
194 enumerateData(P: Elem, Ctx, Offset: BitOffset, BitsToRead, F, Initialize);
195 if (Initialize) {
196 if (Res == Result::Success)
197 Elem.initialize();
198 else if (Res == Result::Skip)
199 Elem.startLifetime();
200 }
201 Ok = Ok && Res != Result::Failure;
202 }
203 for (const Record::Base &B : R->bases()) {
204 PtrView Elem = P.atField(Offset: B.Offset);
205 if (!Initialize && !Elem.isInitialized())
206 return Result::Failure;
207
208 CharUnits ByteOffset =
209 Layout.getBaseClassOffset(Base: cast<CXXRecordDecl>(Val: B.Decl));
210 Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(CharSize: ByteOffset));
211 Result Res =
212 enumerateData(P: Elem, Ctx, Offset: BitOffset, BitsToRead, F, Initialize);
213 if (Initialize) {
214 if (Res == Result::Success)
215 Elem.initialize();
216 else if (Res == Result::Skip)
217 Elem.startLifetime();
218 }
219 Ok = Ok && Res != Result::Failure;
220 }
221 return Ok ? Result::Success : Result::Failure;
222 }
223
224 llvm_unreachable("Unhandled data type");
225}
226
227static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
228 Bits BitsToRead, DataFunc F,
229 bool Initialize) {
230
231 if (P.isStringPointer()) {
232 auto [B, Desc] = convertToBlockPointer(Ctx, SP: P.asStringPointer());
233
234 bool Result = enumerateData(P: Pointer(B).atIndex(Idx: P.getIndex()).view(), Ctx,
235 Offset: Bits::zero(), BitsToRead, F,
236 Initialize) == Result::Failure;
237 delete[] reinterpret_cast<std::byte *>(B);
238 return Result;
239 }
240
241 return enumerateData(P: P.view(), Ctx, Offset: Bits::zero(), BitsToRead, F,
242 Initialize) != Result::Failure;
243}
244
245// This function is constexpr if and only if To, From, and the types of
246// all subobjects of To and From are types T such that...
247// (3.1) - is_union_v<T> is false;
248// (3.2) - is_pointer_v<T> is false;
249// (3.3) - is_member_pointer_v<T> is false;
250// (3.4) - is_volatile_v<T> is false; and
251// (3.5) - T has no non-static data members of reference type
252//
253// NOTE: This is a version of checkBitCastConstexprEligibilityType() in
254// ExprConstant.cpp.
255static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
256 bool IsToType) {
257 enum {
258 E_Union = 0,
259 E_Pointer,
260 E_MemberPointer,
261 E_Volatile,
262 E_Reference,
263 };
264 enum { C_Member, C_Base };
265
266 auto diag = [&](int Reason) -> bool {
267 const Expr *E = S.Current->getExpr(PC: OpPC);
268 S.FFDiag(E, DiagId: diag::note_constexpr_bit_cast_invalid_type)
269 << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
270 << E->getSourceRange();
271 return false;
272 };
273 auto note = [&](int Construct, QualType NoteType,
274 SourceRange NoteRange) -> bool {
275 S.Note(Loc: NoteRange.getBegin(), DiagId: diag::note_constexpr_bit_cast_invalid_subtype)
276 << NoteType << Construct << T.getUnqualifiedType() << NoteRange;
277 return false;
278 };
279 auto unsupported = [&](QualType T) -> bool {
280 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
281 DiagId: diag::note_constexpr_bit_cast_unsupported_type)
282 << T;
283 return false;
284 };
285
286 T = T.getCanonicalType();
287
288 if (T->isUnionType())
289 return diag(E_Union);
290 if (T->isPointerType())
291 return diag(E_Pointer);
292 if (T->isMemberPointerType())
293 return diag(E_MemberPointer);
294 if (T.isVolatileQualified())
295 return diag(E_Volatile);
296
297 if (const RecordDecl *RD = T->getAsRecordDecl()) {
298 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(Val: RD)) {
299 for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
300 if (!CheckBitcastType(S, OpPC, T: BS.getType(), IsToType))
301 return note(C_Base, BS.getType(), BS.getBeginLoc());
302 }
303 }
304 for (const FieldDecl *FD : RD->fields()) {
305 if (FD->getType()->isReferenceType())
306 return diag(E_Reference);
307 if (!CheckBitcastType(S, OpPC, T: FD->getType(), IsToType))
308 return note(C_Member, FD->getType(), FD->getSourceRange());
309 }
310 }
311
312 if (T->isArrayType() &&
313 !CheckBitcastType(S, OpPC, T: S.getASTContext().getBaseElementType(QT: T),
314 IsToType))
315 return false;
316
317 if (const auto *VT = T->getAs<VectorType>()) {
318 const ASTContext &ASTCtx = S.getASTContext();
319 QualType EltTy = VT->getElementType();
320 unsigned NElts = VT->getNumElements();
321 unsigned EltSize =
322 VT->isPackedVectorBoolType(ctx: ASTCtx) ? 1 : ASTCtx.getTypeSize(T: EltTy);
323
324 if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) {
325 // The vector's size in bits is not a multiple of the target's byte size,
326 // so its layout is unspecified. For now, we'll simply treat these cases
327 // as unsupported (this should only be possible with OpenCL bool vectors
328 // whose element count isn't a multiple of the byte size).
329 const Expr *E = S.Current->getExpr(PC: OpPC);
330 S.FFDiag(E, DiagId: diag::note_constexpr_bit_cast_invalid_vector)
331 << QualType(VT, 0) << EltSize << NElts << ASTCtx.getCharWidth();
332 return false;
333 }
334
335 if (EltTy->isRealFloatingType() &&
336 &ASTCtx.getFloatTypeSemantics(T: EltTy) == &APFloat::x87DoubleExtended()) {
337 // The layout for x86_fp80 vectors seems to be handled very inconsistently
338 // by both clang and LLVM, so for now we won't allow bit_casts involving
339 // it in a constexpr context.
340 return unsupported(EltTy);
341 }
342 }
343
344 if (T->isBlockPointerType())
345 return unsupported(T);
346
347 return true;
348}
349
350bool clang::interp::readPointerToBuffer(const Context &Ctx,
351 const Pointer &FromPtr,
352 BitcastBuffer &Buffer,
353 bool ReturnOnUninit) {
354 const ASTContext &ASTCtx = Ctx.getASTContext();
355 Endian TargetEndianness =
356 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
357
358 return enumeratePointerFields(
359 P: FromPtr, Ctx, BitsToRead: Buffer.size(),
360 F: [&](PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
361 bool PackedBools) -> Result {
362 Bits BitWidth = FullBitWidth;
363
364 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
365 BitWidth = Bits(std::min(a: FD->getBitWidthValue(),
366 b: (unsigned)FullBitWidth.getQuantity()));
367 else if (T == PT_Bool && PackedBools)
368 BitWidth = Bits(1);
369
370 if (BitWidth.isZero())
371 return Result::Skip;
372
373 // Bits will be left uninitialized and diagnosed when reading.
374 if (!P.isInitialized())
375 return Result::Skip;
376
377 if (T == PT_Ptr) {
378 assert(P.getType()->isNullPtrType());
379 // Clang treats nullptr_t has having NO bits in its value
380 // representation. So, we accept it here and leave its bits
381 // uninitialized.
382 return Result::Skip;
383 }
384
385 assert(P.isInitialized());
386 auto Buff = std::make_unique<std::byte[]>(num: FullBitWidth.roundToBytes());
387 // Work around floating point types that contain unused padding bytes.
388 // This is really just `long double` on x86, which is the only
389 // fundamental type with padding bytes.
390 if (T == PT_Float) {
391 const Floating &F = P.deref<Floating>();
392 Bits NumBits = Bits(
393 llvm::APFloatBase::getSizeInBits(Sem: F.getAPFloat().getSemantics()));
394 assert(NumBits.isFullByte());
395 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
396 F.bitcastToMemory(Buff: Buff.get());
397 // Now, only (maybe) swap the actual size of the float, excluding
398 // the padding bits.
399 if (llvm::sys::IsBigEndianHost)
400 swapBytes(M: Buff.get(), N: NumBits.roundToBytes());
401
402 Buffer.markInitialized(Start: BitOffset, Length: NumBits);
403 } else {
404 BITCAST_TYPE_SWITCH(T, {
405 auto Val = P.deref<T>();
406 if (!Val.isNumber())
407 return Result::Failure;
408 Val.bitcastToMemory(Buff.get());
409 });
410
411 if (llvm::sys::IsBigEndianHost)
412 swapBytes(M: Buff.get(), N: FullBitWidth.roundToBytes());
413 Buffer.markInitialized(Start: BitOffset, Length: BitWidth);
414 }
415
416 Buffer.pushData(In: Buff.get(), BitOffset, BitWidth, TargetEndianness);
417 return Result::Success;
418 },
419 Initialize: false);
420}
421
422bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
423 std::byte *Buff, Bits BitWidth, Bits FullBitWidth,
424 bool &HasIndeterminateBits) {
425 assert(Ptr.isLive());
426 assert(Ptr.isBlockPointer());
427 assert(Buff);
428 assert(BitWidth <= FullBitWidth);
429 assert(FullBitWidth.isFullByte());
430 assert(BitWidth.isFullByte());
431
432 BitcastBuffer Buffer(FullBitWidth);
433 size_t BuffSize = FullBitWidth.roundToBytes();
434 QualType DataType = Ptr.getFieldDesc()->getDataType(Ctx: S.getASTContext());
435 if (!CheckBitcastType(S, OpPC, T: DataType, /*IsToType=*/false))
436 return false;
437
438 bool Success = readPointerToBuffer(Ctx: S.getContext(), FromPtr: Ptr, Buffer,
439 /*ReturnOnUninit=*/false);
440 HasIndeterminateBits = !Buffer.rangeInitialized(Offset: Bits::zero(), Length: BitWidth);
441
442 const ASTContext &ASTCtx = S.getASTContext();
443 Endian TargetEndianness =
444 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
445 auto B =
446 Buffer.copyBits(BitOffset: Bits::zero(), BitWidth, FullBitWidth, TargetEndianness);
447
448 std::memcpy(dest: Buff, src: B.get(), n: BuffSize);
449
450 if (llvm::sys::IsBigEndianHost)
451 swapBytes(M: Buff, N: BitWidth.roundToBytes());
452
453 return Success;
454}
455bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
456 const Pointer &FromPtr, Pointer &ToPtr) {
457 const ASTContext &ASTCtx = S.getASTContext();
458 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(T: ToPtr.getType());
459
460 return DoBitCastPtr(S, OpPC, FromPtr, ToPtr, Size: ObjectReprChars.getQuantity());
461}
462
463bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
464 const Pointer &FromPtr, Pointer &ToPtr,
465 size_t Size) {
466 assert(FromPtr.isLive());
467 assert(FromPtr.isBlockPointer());
468 assert(ToPtr.isBlockPointer());
469
470 QualType FromType = FromPtr.getFieldDesc()->getDataType(Ctx: S.getASTContext());
471 QualType ToType = ToPtr.getFieldDesc()->getDataType(Ctx: S.getASTContext());
472
473 if (!CheckBitcastType(S, OpPC, T: ToType, /*IsToType=*/true))
474 return false;
475 if (!CheckBitcastType(S, OpPC, T: FromType, /*IsToType=*/false))
476 return false;
477
478 const ASTContext &ASTCtx = S.getASTContext();
479 BitcastBuffer Buffer(Bytes(Size).toBits());
480 readPointerToBuffer(Ctx: S.getContext(), FromPtr, Buffer,
481 /*ReturnOnUninit=*/false);
482
483 // Now read the values out of the buffer again and into ToPtr.
484 Endian TargetEndianness =
485 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
486 bool Success = enumeratePointerFields(
487 P: ToPtr, Ctx: S.getContext(), BitsToRead: Buffer.size(),
488 F: [&](PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
489 bool PackedBools) -> Result {
490 QualType PtrType = P.getType();
491 if (T == PT_Float) {
492 const auto &Semantics = ASTCtx.getFloatTypeSemantics(T: PtrType);
493 Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Sem: Semantics));
494 assert(NumBits.isFullByte());
495 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
496 auto M = Buffer.copyBits(BitOffset, BitWidth: NumBits, FullBitWidth,
497 TargetEndianness);
498
499 if (llvm::sys::IsBigEndianHost)
500 swapBytes(M: M.get(), N: NumBits.roundToBytes());
501
502 Floating R = S.allocFloat(Sem: Semantics);
503 Floating::bitcastFromMemory(Buff: M.get(), Sem: Semantics, Result: &R);
504 P.deref<Floating>() = R;
505 P.initialize();
506 return Result::Success;
507 }
508
509 Bits BitWidth;
510 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
511 BitWidth = Bits(std::min(a: FD->getBitWidthValue(),
512 b: (unsigned)FullBitWidth.getQuantity()));
513 else if (T == PT_Bool && PackedBools)
514 BitWidth = Bits(1);
515 else
516 BitWidth = FullBitWidth;
517
518 // If any of the bits are uninitialized, we need to abort unless the
519 // target type is std::byte or unsigned char.
520 bool Initialized = Buffer.rangeInitialized(Offset: BitOffset, Length: BitWidth);
521 if (!Initialized) {
522 if (!PtrType->isStdByteType() &&
523 !PtrType->isSpecificBuiltinType(K: BuiltinType::UChar) &&
524 !PtrType->isSpecificBuiltinType(K: BuiltinType::Char_U)) {
525 const Expr *E = S.Current->getExpr(PC: OpPC);
526 S.FFDiag(E, DiagId: diag::note_constexpr_bit_cast_indet_dest)
527 << PtrType << S.getLangOpts().CharIsSigned
528 << E->getSourceRange();
529
530 return Result::Failure;
531 }
532 return Result::Skip;
533 }
534
535 auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
536 TargetEndianness);
537 if (llvm::sys::IsBigEndianHost)
538 swapBytes(M: Memory.get(), N: FullBitWidth.roundToBytes());
539
540 if (T == PT_IntAPS) {
541 P.deref<IntegralAP<true>>() =
542 S.allocAP<IntegralAP<true>>(BitWidth: FullBitWidth.getQuantity());
543 IntegralAP<true>::bitcastFromMemory(Src: Memory.get(),
544 BitWidth: FullBitWidth.getQuantity(),
545 Result: &P.deref<IntegralAP<true>>());
546 } else if (T == PT_IntAP) {
547 P.deref<IntegralAP<false>>() =
548 S.allocAP<IntegralAP<false>>(BitWidth: FullBitWidth.getQuantity());
549 IntegralAP<false>::bitcastFromMemory(Src: Memory.get(),
550 BitWidth: FullBitWidth.getQuantity(),
551 Result: &P.deref<IntegralAP<false>>());
552 } else {
553 BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {
554 if (BitWidth.nonZero())
555 P.deref<T>() = T::bitcastFromMemory(Memory.get(), T::bitWidth())
556 .truncate(BitWidth.getQuantity());
557 else
558 P.deref<T>() = T::zero();
559 });
560 }
561 P.initialize();
562 return Result::Success;
563 },
564 Initialize: true);
565
566 return Success;
567}
568
569using PrimTypeVariant =
570 std::variant<Pointer, MemberPointer, FixedPoint, Char<false>, Char<true>,
571 Integral<16, false>, Integral<16, true>, Integral<32, false>,
572 Integral<32, true>, Integral<64, false>, Integral<64, true>,
573 IntegralAP<true>, IntegralAP<false>, Boolean, Floating>;
574
575// NB: This implementation isn't exactly ideal, but:
576// 1) We can't just do a bitcast here since we need to be able to
577// copy pointers.
578// 2) This also needs to handle overlapping regions.
579// 3) We currently have no way of iterating over the fields of a pointer
580// backwards.
581bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
582 const Pointer &SrcPtr, const Pointer &DestPtr,
583 Bits Size) {
584 assert(SrcPtr.isReadablePointerType());
585 assert(DestPtr.isBlockPointer());
586
587 llvm::SmallVector<PrimTypeVariant> Values;
588
589 if (SrcPtr.isStringPointer()) {
590 const auto &SP = SrcPtr.asStringPointer();
591
592 auto [B, Desc] = convertToBlockPointer(Ctx: S.getContext(), SP);
593 enumeratePointerFields(
594 P: Pointer(B).atIndex(Idx: SrcPtr.getIndex()), Ctx: S.getContext(), BitsToRead: Size,
595 F: [&](const PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
596 bool PackedBools) -> Result {
597 TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });
598 return Result::Success;
599 },
600 Initialize: false);
601
602 delete[] B;
603 } else {
604
605 enumeratePointerFields(
606 P: SrcPtr, Ctx: S.getContext(), BitsToRead: Size,
607 F: [&](const PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
608 bool PackedBools) -> Result {
609 TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });
610 return Result::Success;
611 },
612 Initialize: false);
613 }
614
615 unsigned ValueIndex = 0;
616 enumeratePointerFields(
617 P: DestPtr, Ctx: S.getContext(), BitsToRead: Size,
618 F: [&](const PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
619 bool PackedBools) -> Result {
620 TYPE_SWITCH(T, {
621 P.deref<T>() = std::get<T>(Values[ValueIndex]);
622 P.initialize();
623 });
624
625 ++ValueIndex;
626 return Result::Success;
627 },
628 Initialize: true);
629
630 // We should've read all the values into DestPtr.
631 assert(ValueIndex == Values.size());
632
633 return true;
634}
635