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