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

194 T = T.getCanonicalType();

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 (CheckBitcastType(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

443 P.deref<T>() = T::zero();

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.