clang: lib/AST/ByteCode/InterpBuiltinBitCast.cpp Source File (original) (raw)
1
2
3
4
5
6
7
21
22using namespace clang;
24
25
26
27
28
29
30
31
32
33
34
35
38 Bits FullBitWidth, bool PackedBools)>;
39
40#define BITCAST_TYPE_SWITCH(Expr, B) \
41 do { \
42 switch (Expr) { \
43 TYPE_SWITCH_CASE(PT_Sint8, B) \
44 TYPE_SWITCH_CASE(PT_Uint8, B) \
45 TYPE_SWITCH_CASE(PT_Sint16, B) \
46 TYPE_SWITCH_CASE(PT_Uint16, B) \
47 TYPE_SWITCH_CASE(PT_Sint32, B) \
48 TYPE_SWITCH_CASE(PT_Uint32, B) \
49 TYPE_SWITCH_CASE(PT_Sint64, B) \
50 TYPE_SWITCH_CASE(PT_Uint64, B) \
51 TYPE_SWITCH_CASE(PT_IntAP, B) \
52 TYPE_SWITCH_CASE(PT_IntAPS, B) \
53 TYPE_SWITCH_CASE(PT_Bool, B) \
54 default: \
55 llvm_unreachable("Unhandled bitcast type"); \
56 } \
57 } while (0)
58
59#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \
60 do { \
61 switch (Expr) { \
62 TYPE_SWITCH_CASE(PT_Sint8, B) \
63 TYPE_SWITCH_CASE(PT_Uint8, B) \
64 TYPE_SWITCH_CASE(PT_Sint16, B) \
65 TYPE_SWITCH_CASE(PT_Uint16, B) \
66 TYPE_SWITCH_CASE(PT_Sint32, B) \
67 TYPE_SWITCH_CASE(PT_Uint32, B) \
68 TYPE_SWITCH_CASE(PT_Sint64, B) \
69 TYPE_SWITCH_CASE(PT_Uint64, B) \
70 TYPE_SWITCH_CASE(PT_Bool, B) \
71 default: \
72 llvm_unreachable("Unhandled bitcast type"); \
73 } \
74 } while (0)
75
76
77
80 const Descriptor *FieldDesc = P.getFieldDesc();
81 assert(FieldDesc);
82
83
85 Bits FullBitWidth =
87 return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth,
88 false);
89 }
90
91
96
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, ElemSize, PackedBools);
102 Offset += PackedBools ? Bits(1) : ElemSize;
103 if (Offset >= BitsToRead)
104 break;
105 }
106 return Ok;
107 }
108
109
113 for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {
114 enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);
115 Offset += ElemSize;
116 if (Offset >= BitsToRead)
117 break;
118 }
119 return true;
120 }
121
122
127 bool Ok = true;
128
129 for (const Record::Field &Fi : R->fields()) {
130 if (Fi.isUnnamedBitField())
131 continue;
132 Pointer Elem = P.atField(Fi.Offset);
133 Bits BitOffset =
136 }
137 for (const Record::Base &B : R->bases()) {
138 Pointer Elem = P.atField(B.Offset);
143
144
145 if (Ok)
147 }
148
149 return Ok;
150 }
151
152 llvm_unreachable("Unhandled data type");
153}
154
158}
159
160
161
162
163
164
165
166
167
168
169
171 bool IsToType) {
172 enum {
173 E_Union = 0,
174 E_Pointer,
175 E_MemberPointer,
176 E_Volatile,
177 E_Reference,
178 };
179 enum { C_Member, C_Base };
180
181 auto diag = [&](int Reason) -> bool {
182 const Expr *E = S.Current->getExpr(OpPC);
183 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)
184 << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
186 return false;
187 };
188 auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {
189 S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)
190 << NoteType << Construct << T.getUnqualifiedType() << NoteRange;
191 return false;
192 };
193
195
197 return diag(E_Union);
199 return diag(E_Pointer);
201 return diag(E_MemberPointer);
202 if (T.isVolatileQualified())
203 return diag(E_Volatile);
204
206 if (const auto *CXXRD = dyn_cast(RD)) {
209 return note(C_Base, BS.getType(), BS.getBeginLoc());
210 }
211 }
212 for (const FieldDecl *FD : RD->fields()) {
213 if (FD->getType()->isReferenceType())
214 return diag(E_Reference);
216 return note(C_Member, FD->getType(), FD->getSourceRange());
217 }
218 }
219
222 IsToType))
223 return false;
224
227 QualType EltTy = VT->getElementType();
228 unsigned NElts = VT->getNumElements();
229 unsigned EltSize =
231
232 if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) {
233
234
235
236
237 const Expr *E = S.Current->getExpr(OpPC);
238 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_vector)
240 return false;
241 }
242
245
246
247
248 const Expr *E = S.Current->getExpr(OpPC);
249 S.FFDiag(E, diag::note_constexpr_bit_cast_unsupported_type) << EltTy;
250 return false;
251 }
252 }
253
254 return true;
255}
256
260 bool ReturnOnUninit) {
262 Endian TargetEndianness =
264
266 FromPtr, Ctx, Buffer.size(),
268 bool PackedBools) -> bool {
269 Bits BitWidth = FullBitWidth;
270
271 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
272 BitWidth = Bits(std::min(FD->getBitWidthValue(),
273 (unsigned)FullBitWidth.getQuantity()));
274 else if (T == PT_Bool && PackedBools)
275 BitWidth = Bits(1);
276
277 if (BitWidth.isZero())
278 return true;
279
280
281 if (!P.isInitialized())
282 return true;
283
284 if (T == PT_Ptr) {
285 assert(P.getType()->isNullPtrType());
286
287
288
289 return true;
290 }
291
292 assert(P.isInitialized());
293 auto Buff = std::make_uniquestd::byte\[\](FullBitWidth.roundToBytes());
294
295
296
300 llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics()));
301 assert(NumBits.isFullByte());
302 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
304
305
306 if (llvm::sys::IsBigEndianHost)
307 swapBytes(Buff.get(), NumBits.roundToBytes());
308
310 } else {
312
313 if (llvm::sys::IsBigEndianHost)
316 }
317
318 Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness);
319 return true;
320 });
321}
322
324 std::byte *Buff, Bits BitWidth, Bits FullBitWidth,
325 bool &HasIndeterminateBits) {
326 assert(Ptr.isLive());
328 assert(Buff);
329 assert(BitWidth <= FullBitWidth);
332
334 size_t BuffSize = FullBitWidth.roundToBytes();
336 return false;
337
339 false);
341
343 Endian TargetEndianness =
345 auto B =
346 Buffer.copyBits(Bits::zero(), BitWidth, FullBitWidth, TargetEndianness);
347
348 std::memcpy(Buff, B.get(), BuffSize);
349
350 if (llvm::sys::IsBigEndianHost)
352
354}
359
361}
362
365 size_t Size) {
366 assert(FromPtr.isLive());
369
372
374 return false;
375 if ((S, OpPC, FromType, false))
376 return false;
377
381 false);
382
383
384 Endian TargetEndianness =
387 ToPtr, S.getContext(), Buffer.size(),
389 bool PackedBools) -> bool {
390 QualType PtrType = P.getType();
391 if (T == PT_Float) {
392 const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
393 Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
394 assert(NumBits.isFullByte());
395 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
396 auto M = Buffer.copyBits(BitOffset, NumBits, FullBitWidth,
397 TargetEndianness);
398
399 if (llvm::sys::IsBigEndianHost)
400 swapBytes(M.get(), NumBits.roundToBytes());
401
402 P.deref() = Floating::bitcastFromMemory(M.get(), Semantics);
403 P.initialize();
404 return true;
405 }
406
407 Bits BitWidth;
408 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
409 BitWidth = Bits(std::min(FD->getBitWidthValue(),
411 else if (T == PT_Bool && PackedBools)
412 BitWidth = Bits(1);
413 else
414 BitWidth = FullBitWidth;
415
416
417
420 if (!PtrType->isStdByteType() &&
421 !PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&
422 !PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {
423 const Expr *E = S.Current->getExpr(OpPC);
424 S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
425 << PtrType << S.getLangOpts().CharIsSigned
426 << E->getSourceRange();
427
428 return false;
429 }
430 return true;
431 }
432
433 auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
434 TargetEndianness);
435 if (llvm::sys::IsBigEndianHost)
437
439 if (BitWidth.nonZero())
440 P.deref<T>() = T::bitcastFromMemory(Memory.get(), T::bitWidth())
441 .truncate(BitWidth.getQuantity());
442 else
444 });
445 P.initialize();
446 return true;
447 });
448
450}
451
457
458 unsigned SrcStartOffset = SrcPtr.getByteOffset();
459 unsigned DestStartOffset = DestPtr.getByteOffset();
460
463 Bits FullBitWidth, bool PackedBools) -> bool {
464 unsigned SrcOffsetDiff =
465 P.getByteOffset() - SrcStartOffset;
466
467 Pointer DestP =
468 Pointer(DestPtr.asBlockPointer().Pointee,
469 DestPtr.asBlockPointer().Base,
470 DestStartOffset + SrcOffsetDiff);
471
472 TYPE_SWITCH(T, {
473 DestP.deref() = P.deref();
474 DestP.initialize();
475 });
476
477 return true;
478 });
479
480 return true;
481}
Defines the clang::ASTContext interface.
#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B)
llvm::function_ref< bool(const Pointer &P, PrimType Ty, Bits BitOffset, Bits FullBitWidth, bool PackedBools)> DataFunc
Implement __builtin_bit_cast and related operations.
#define BITCAST_TYPE_SWITCH(Expr, B)
static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, bool IsToType)
static bool enumeratePointerFields(const Pointer &P, const Context &Ctx, Bits BitsToRead, DataFunc F)
static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset, Bits BitsToRead, DataFunc F)
We use this to recursively iterate over all fields and elements of a pointer and extract relevant dat...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const llvm::fltSemantics & getFloatTypeSemantics(QualType T) const
Return the APFloat 'semantics' for the specified scalar floating point type.
const ASTRecordLayout & getASTRecordLayout(const RecordDecl *D) const
Get or compute information about the layout of the specified record (struct/union/class) D,...
QualType getBaseElementType(const ArrayType *VAT) const
Return the innermost element type of an array type.
int64_t toBits(CharUnits CharSize) const
Convert a size in characters to a size in bits.
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
const TargetInfo & getTargetInfo() const
uint64_t getCharWidth() const
Return the size of the character type, in bits.
ASTRecordLayout - This class contains layout information for one RecordDecl, which is a struct/union/...
uint64_t getFieldOffset(unsigned FieldNo) const
getFieldOffset - Get the offset of the given field index, in bits.
CharUnits getBaseClassOffset(const CXXRecordDecl *Base) const
getBaseClassOffset - Get the offset, in chars, for the given base class.
Represents a base class of a C++ class.
CharUnits - This is an opaque type for sizes expressed in character units.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
This represents one expression.
Represents a member of a struct/union/class.
A (possibly-)qualified type.
Represents a struct/union/class.
ASTContext & getASTContext() const
A trivial tuple used to represent a source range.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isLittleEndian() const
bool isPointerType() const
bool isExtVectorBoolType() const
bool isMemberPointerType() const
bool isRealFloatingType() const
Floating point categories.
const T * getAs() const
Member-template getAs'.
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Represents a GCC generic vector type.
Pointer into the code segment.
Holds all information required to evaluate constexpr code in a module.
ASTContext & getASTContext() const
Returns the AST context.
std::optional< PrimType > classify(QualType T) const
Classifies a type.
const APFloat & getAPFloat() const
void bitcastToMemory(std::byte *Buff) const
A pointer to a memory block, live or dead.
QualType getType() const
Returns the type of the innermost field.
bool isLive() const
Checks if the pointer is live.
uint64_t getByteOffset() const
Returns the byte offset from the start.
bool isBlockPointer() const
void initialize() const
Initializes a field.
Structure/Class descriptor.
const RecordDecl * getDecl() const
Returns the underlying declaration.
llvm::iterator_range< const_base_iter > bases() const
llvm::iterator_range< const_field_iter > fields() const
Defines the clang::TargetInfo interface.
bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, BitcastBuffer &Buffer, bool ReturnOnUninit)
bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr)
PrimType
Enumeration of the primitive types of the VM.
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest)
Copy the contents of Src into Dest.
bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, std::byte *Buff, Bits BitWidth, Bits FullBitWidth, bool &HasIndeterminateBits)
static void swapBytes(std::byte *M, size_t N)
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
const FunctionProtoType * T
@ Success
Template argument deduction was successful.
Track what bits have been initialized to known values and which ones have indeterminate value.
std::unique_ptr< std::byte[]> copyBits(Bits BitOffset, Bits BitWidth, Bits FullBitWidth, Endian TargetEndianness) const
Copy BitWidth bits at offset BitOffset from the buffer.
void markInitialized(Bits Start, Bits Length)
Marks the bits in the given range as initialized.
bool rangeInitialized(Bits Offset, Bits Length) const
Bits size() const
Returns the buffer size in bits.
void pushData(const std::byte *In, Bits BitOffset, Bits BitWidth, Endian TargetEndianness)
Push BitWidth bits at BitOffset from In into the buffer.
size_t roundToBytes() const
size_t getQuantity() const
Describes a memory block created by an allocation site.
unsigned getNumElems() const
Returns the number of elements stored in the block.
bool isPrimitive() const
Checks if the descriptor is of a primitive.
QualType getElemQualType() const
bool isCompositeArray() const
Checks if the descriptor is of an array of composites.
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
PrimType getPrimType() const
bool isRecord() const
Checks if the descriptor is of a record.
const Record *const ElemRecord
Pointer to the record, if block contains records.