Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
213799 views
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.
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
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.
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
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
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
}
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
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.
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
504