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 using namespace clang; 23 using namespace clang::interp; 24 25 /// Implement __builtin_bit_cast and related operations. 26 /// Since our internal representation for data is more complex than 27 /// something we can simply memcpy or memcmp, we first bitcast all the data 28 /// into a buffer, which we then later use to copy the data into the target. 29 30 // TODO: 31 // - Try to minimize heap allocations. 32 // - Optimize the common case of only pushing and pulling full 33 // bytes to/from the buffer. 34 35 /// Used to iterate over pointer fields. 36 using DataFunc = 37 llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset, 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 /// We use this to recursively iterate over all fields and elements of a pointer 77 /// and extract relevant data for a bitcast. 78 static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset, 79 Bits BitsToRead, DataFunc F) { 80 const Descriptor *FieldDesc = P.getFieldDesc(); 81 assert(FieldDesc); 82 83 // Primitives. 84 if (FieldDesc->isPrimitive()) { 85 Bits FullBitWidth = 86 Bits(Ctx.getASTContext().getTypeSize(FieldDesc->getType())); 87 return F(P, FieldDesc->getPrimType(), Offset, FullBitWidth, 88 /*PackedBools=*/false); 89 } 90 91 // Primitive arrays. 92 if (FieldDesc->isPrimitiveArray()) { 93 QualType ElemType = FieldDesc->getElemQualType(); 94 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType)); 95 PrimType ElemT = *Ctx.classify(ElemType); 96 // Special case, since the bools here are packed. 97 bool PackedBools = FieldDesc->getType()->isExtVectorBoolType(); 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 // Composite arrays. 110 if (FieldDesc->isCompositeArray()) { 111 QualType ElemType = FieldDesc->getElemQualType(); 112 Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType)); 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 // Records. 123 if (FieldDesc->isRecord()) { 124 const Record *R = FieldDesc->ElemRecord; 125 const ASTRecordLayout &Layout = 126 Ctx.getASTContext().getASTRecordLayout(R->getDecl()); 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 = 134 Offset + Bits(Layout.getFieldOffset(Fi.Decl->getFieldIndex())); 135 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F); 136 } 137 for (const Record::Base &B : R->bases()) { 138 Pointer Elem = P.atField(B.Offset); 139 CharUnits ByteOffset = 140 Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl)); 141 Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(ByteOffset)); 142 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F); 143 // FIXME: We should only (need to) do this when bitcasting OUT of the 144 // buffer, not when copying data into it. 145 if (Ok) 146 Elem.initialize(); 147 } 148 149 return Ok; 150 } 151 152 llvm_unreachable("Unhandled data type"); 153 } 154 155 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx, 156 Bits BitsToRead, DataFunc F) { 157 return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F); 158 } 159 160 // This function is constexpr if and only if To, From, and the types of 161 // all subobjects of To and From are types T such that... 162 // (3.1) - is_union_v<T> is false; 163 // (3.2) - is_pointer_v<T> is false; 164 // (3.3) - is_member_pointer_v<T> is false; 165 // (3.4) - is_volatile_v<T> is false; and 166 // (3.5) - T has no non-static data members of reference type 167 // 168 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in 169 // ExprConstant.cpp. 170 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, 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 185 << E->getSourceRange(); 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 196 if (T->isUnionType()) 197 return diag(E_Union); 198 if (T->isPointerType()) 199 return diag(E_Pointer); 200 if (T->isMemberPointerType()) 201 return diag(E_MemberPointer); 202 if (T.isVolatileQualified()) 203 return diag(E_Volatile); 204 205 if (const RecordDecl *RD = T->getAsRecordDecl()) { 206 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { 207 for (const CXXBaseSpecifier &BS : CXXRD->bases()) { 208 if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType)) 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); 215 if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType)) 216 return note(C_Member, FD->getType(), FD->getSourceRange()); 217 } 218 } 219 220 if (T->isArrayType() && 221 !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T), 222 IsToType)) 223 return false; 224 225 if (const auto *VT = T->getAs<VectorType>()) { 226 const ASTContext &ASTCtx = S.getASTContext(); 227 QualType EltTy = VT->getElementType(); 228 unsigned NElts = VT->getNumElements(); 229 unsigned EltSize = 230 VT->isExtVectorBoolType() ? 1 : ASTCtx.getTypeSize(EltTy); 231 232 if ((NElts * EltSize) % ASTCtx.getCharWidth() != 0) { 233 // The vector's size in bits is not a multiple of the target's byte size, 234 // so its layout is unspecified. For now, we'll simply treat these cases 235 // as unsupported (this should only be possible with OpenCL bool vectors 236 // whose element count isn't a multiple of the byte size). 237 const Expr *E = S.Current->getExpr(OpPC); 238 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_vector) 239 << QualType(VT, 0) << EltSize << NElts << ASTCtx.getCharWidth(); 240 return false; 241 } 242 243 if (EltTy->isRealFloatingType() && 244 &ASTCtx.getFloatTypeSemantics(EltTy) == &APFloat::x87DoubleExtended()) { 245 // The layout for x86_fp80 vectors seems to be handled very inconsistently 246 // by both clang and LLVM, so for now we won't allow bit_casts involving 247 // it in a constexpr context. 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 257 bool clang::interp::readPointerToBuffer(const Context &Ctx, 258 const Pointer &FromPtr, 259 BitcastBuffer &Buffer, 260 bool ReturnOnUninit) { 261 const ASTContext &ASTCtx = Ctx.getASTContext(); 262 Endian TargetEndianness = 263 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; 264 265 return enumeratePointerFields( 266 FromPtr, Ctx, Buffer.size(), 267 [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth, 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 // Bits will be left uninitialized and diagnosed when reading. 281 if (!P.isInitialized()) 282 return true; 283 284 if (T == PT_Ptr) { 285 assert(P.getType()->isNullPtrType()); 286 // Clang treats nullptr_t has having NO bits in its value 287 // representation. So, we accept it here and leave its bits 288 // uninitialized. 289 return true; 290 } 291 292 assert(P.isInitialized()); 293 auto Buff = std::make_unique<std::byte[]>(FullBitWidth.roundToBytes()); 294 // Work around floating point types that contain unused padding bytes. 295 // This is really just `long double` on x86, which is the only 296 // fundamental type with padding bytes. 297 if (T == PT_Float) { 298 const Floating &F = P.deref<Floating>(); 299 Bits NumBits = Bits( 300 llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics())); 301 assert(NumBits.isFullByte()); 302 assert(NumBits.getQuantity() <= FullBitWidth.getQuantity()); 303 F.bitcastToMemory(Buff.get()); 304 // Now, only (maybe) swap the actual size of the float, excluding 305 // the padding bits. 306 if (llvm::sys::IsBigEndianHost) 307 swapBytes(Buff.get(), NumBits.roundToBytes()); 308 309 Buffer.markInitialized(BitOffset, NumBits); 310 } else { 311 BITCAST_TYPE_SWITCH(T, { P.deref<T>().bitcastToMemory(Buff.get()); }); 312 313 if (llvm::sys::IsBigEndianHost) 314 swapBytes(Buff.get(), FullBitWidth.roundToBytes()); 315 Buffer.markInitialized(BitOffset, BitWidth); 316 } 317 318 Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness); 319 return true; 320 }); 321 } 322 323 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, 324 std::byte *Buff, Bits BitWidth, Bits FullBitWidth, 325 bool &HasIndeterminateBits) { 326 assert(Ptr.isLive()); 327 assert(Ptr.isBlockPointer()); 328 assert(Buff); 329 assert(BitWidth <= FullBitWidth); 330 assert(FullBitWidth.isFullByte()); 331 assert(BitWidth.isFullByte()); 332 333 BitcastBuffer Buffer(FullBitWidth); 334 size_t BuffSize = FullBitWidth.roundToBytes(); 335 if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false)) 336 return false; 337 338 bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer, 339 /*ReturnOnUninit=*/false); 340 HasIndeterminateBits = !Buffer.rangeInitialized(Bits::zero(), BitWidth); 341 342 const ASTContext &ASTCtx = S.getASTContext(); 343 Endian TargetEndianness = 344 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; 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) 351 swapBytes(Buff, BitWidth.roundToBytes()); 352 353 return Success; 354 } 355 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, 356 const Pointer &FromPtr, Pointer &ToPtr) { 357 const ASTContext &ASTCtx = S.getASTContext(); 358 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(ToPtr.getType()); 359 360 return DoBitCastPtr(S, OpPC, FromPtr, ToPtr, ObjectReprChars.getQuantity()); 361 } 362 363 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, 364 const Pointer &FromPtr, Pointer &ToPtr, 365 size_t Size) { 366 assert(FromPtr.isLive()); 367 assert(FromPtr.isBlockPointer()); 368 assert(ToPtr.isBlockPointer()); 369 370 QualType FromType = FromPtr.getType(); 371 QualType ToType = ToPtr.getType(); 372 373 if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true)) 374 return false; 375 if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false)) 376 return false; 377 378 const ASTContext &ASTCtx = S.getASTContext(); 379 BitcastBuffer Buffer(Bytes(Size).toBits()); 380 readPointerToBuffer(S.getContext(), FromPtr, Buffer, 381 /*ReturnOnUninit=*/false); 382 383 // Now read the values out of the buffer again and into ToPtr. 384 Endian TargetEndianness = 385 ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; 386 bool Success = enumeratePointerFields( 387 ToPtr, S.getContext(), Buffer.size(), 388 [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth, 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>() = 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(), 410 (unsigned)FullBitWidth.getQuantity())); 411 else if (T == PT_Bool && PackedBools) 412 BitWidth = Bits(1); 413 else 414 BitWidth = FullBitWidth; 415 416 // If any of the bits are uninitialized, we need to abort unless the 417 // target type is std::byte or unsigned char. 418 bool Initialized = Buffer.rangeInitialized(BitOffset, BitWidth); 419 if (!Initialized) { 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) 436 swapBytes(Memory.get(), FullBitWidth.roundToBytes()); 437 438 BITCAST_TYPE_SWITCH_FIXED_SIZE(T, { 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 449 return Success; 450 } 451 452 bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC, 453 const Pointer &SrcPtr, const Pointer &DestPtr, 454 Bits Size) { 455 assert(SrcPtr.isBlockPointer()); 456 assert(DestPtr.isBlockPointer()); 457 458 unsigned SrcStartOffset = SrcPtr.getByteOffset(); 459 unsigned DestStartOffset = DestPtr.getByteOffset(); 460 461 enumeratePointerFields(SrcPtr, S.getContext(), Size, 462 [&](const Pointer &P, PrimType T, Bits BitOffset, 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<T>() = P.deref<T>(); 474 DestP.initialize(); 475 }); 476 477 return true; 478 }); 479 480 return true; 481 } 482