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 "Boolean.h" 10 #include "Context.h" 11 #include "Floating.h" 12 #include "Integral.h" 13 #include "InterpState.h" 14 #include "MemberPointer.h" 15 #include "Pointer.h" 16 #include "Record.h" 17 #include "clang/AST/ASTContext.h" 18 #include "clang/AST/RecordLayout.h" 19 #include "clang/Basic/TargetInfo.h" 20 21 using namespace clang; 22 using namespace clang::interp; 23 24 /// Used to iterate over pointer fields. 25 using DataFunc = llvm::function_ref<bool(const Pointer &P, PrimType Ty, 26 size_t BitOffset, bool PackedBools)>; 27 28 #define BITCAST_TYPE_SWITCH(Expr, B) \ 29 do { \ 30 switch (Expr) { \ 31 TYPE_SWITCH_CASE(PT_Sint8, B) \ 32 TYPE_SWITCH_CASE(PT_Uint8, B) \ 33 TYPE_SWITCH_CASE(PT_Sint16, B) \ 34 TYPE_SWITCH_CASE(PT_Uint16, B) \ 35 TYPE_SWITCH_CASE(PT_Sint32, B) \ 36 TYPE_SWITCH_CASE(PT_Uint32, B) \ 37 TYPE_SWITCH_CASE(PT_Sint64, B) \ 38 TYPE_SWITCH_CASE(PT_Uint64, B) \ 39 TYPE_SWITCH_CASE(PT_IntAP, B) \ 40 TYPE_SWITCH_CASE(PT_IntAPS, B) \ 41 TYPE_SWITCH_CASE(PT_Bool, B) \ 42 default: \ 43 llvm_unreachable("Unhandled bitcast type"); \ 44 } \ 45 } while (0) 46 47 #define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \ 48 do { \ 49 switch (Expr) { \ 50 TYPE_SWITCH_CASE(PT_Sint8, B) \ 51 TYPE_SWITCH_CASE(PT_Uint8, B) \ 52 TYPE_SWITCH_CASE(PT_Sint16, B) \ 53 TYPE_SWITCH_CASE(PT_Uint16, B) \ 54 TYPE_SWITCH_CASE(PT_Sint32, B) \ 55 TYPE_SWITCH_CASE(PT_Uint32, B) \ 56 TYPE_SWITCH_CASE(PT_Sint64, B) \ 57 TYPE_SWITCH_CASE(PT_Uint64, B) \ 58 TYPE_SWITCH_CASE(PT_Bool, B) \ 59 default: \ 60 llvm_unreachable("Unhandled bitcast type"); \ 61 } \ 62 } while (0) 63 64 static bool bitof(std::byte B, unsigned BitIndex) { 65 return (B & (std::byte{1} << BitIndex)) != std::byte{0}; 66 } 67 68 static void swapBytes(std::byte *M, size_t N) { 69 for (size_t I = 0; I != (N / 2); ++I) 70 std::swap(M[I], M[N - 1 - I]); 71 } 72 73 /// Track what bits have been initialized to known values and which ones 74 /// have indeterminate value. 75 /// All offsets are in bits. 76 struct BitcastBuffer { 77 size_t SizeInBits = 0; 78 llvm::SmallVector<std::byte> Data; 79 80 BitcastBuffer() = default; 81 82 size_t size() const { return SizeInBits; } 83 84 const std::byte *data() const { return Data.data(); } 85 86 std::byte *getBytes(unsigned BitOffset) const { 87 assert(BitOffset % 8 == 0); 88 assert(BitOffset < SizeInBits); 89 return const_cast<std::byte *>(data() + (BitOffset / 8)); 90 } 91 92 bool allInitialized() const { 93 // FIXME: Implement. 94 return true; 95 } 96 97 bool atByteBoundary() const { return (Data.size() * 8) == SizeInBits; } 98 99 void pushBit(bool Value) { 100 if (atByteBoundary()) 101 Data.push_back(std::byte{0}); 102 103 if (Value) 104 Data.back() |= (std::byte{1} << (SizeInBits % 8)); 105 ++SizeInBits; 106 } 107 108 void pushData(const std::byte *data, size_t BitWidth, bool BigEndianTarget) { 109 bool OnlyFullBytes = BitWidth % 8 == 0; 110 unsigned NBytes = BitWidth / 8; 111 112 size_t BitsHandled = 0; 113 // Read all full bytes first 114 for (size_t I = 0; I != NBytes; ++I) { 115 std::byte B = 116 BigEndianTarget ? data[NBytes - OnlyFullBytes - I] : data[I]; 117 for (unsigned X = 0; X != 8; ++X) { 118 pushBit(bitof(B, X)); 119 ++BitsHandled; 120 } 121 } 122 123 if (BitsHandled == BitWidth) 124 return; 125 126 // Rest of the bits. 127 assert((BitWidth - BitsHandled) < 8); 128 std::byte B = BigEndianTarget ? data[0] : data[NBytes]; 129 for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) { 130 pushBit(bitof(B, I)); 131 ++BitsHandled; 132 } 133 134 assert(BitsHandled == BitWidth); 135 } 136 }; 137 138 /// We use this to recursively iterate over all fields and elemends of a pointer 139 /// and extract relevant data for a bitcast. 140 static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, 141 DataFunc F) { 142 const Descriptor *FieldDesc = P.getFieldDesc(); 143 assert(FieldDesc); 144 145 // Primitives. 146 if (FieldDesc->isPrimitive()) 147 return F(P, FieldDesc->getPrimType(), Offset, false); 148 149 // Primitive arrays. 150 if (FieldDesc->isPrimitiveArray()) { 151 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 152 QualType ElemType = FieldDesc->getElemQualType(); 153 size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); 154 PrimType ElemT = *Ctx.classify(ElemType); 155 // Special case, since the bools here are packed. 156 bool PackedBools = FieldDesc->getType()->isExtVectorBoolType(); 157 bool Ok = true; 158 for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { 159 unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; 160 Ok = Ok && F(P.atIndex(Index), ElemT, Offset, PackedBools); 161 Offset += ElemSizeInBits; 162 } 163 return Ok; 164 } 165 166 // Composite arrays. 167 if (FieldDesc->isCompositeArray()) { 168 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 169 QualType ElemType = FieldDesc->getElemQualType(); 170 size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); 171 for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { 172 unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; 173 enumerateData(P.atIndex(Index).narrow(), Ctx, Offset, F); 174 Offset += ElemSizeInBits; 175 } 176 return true; 177 } 178 179 // Records. 180 if (FieldDesc->isRecord()) { 181 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 182 const Record *R = FieldDesc->ElemRecord; 183 const ASTRecordLayout &Layout = 184 Ctx.getASTContext().getASTRecordLayout(R->getDecl()); 185 bool Ok = true; 186 187 auto enumerateFields = [&]() -> void { 188 for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) { 189 const Record::Field *Fi = 190 R->getField(BigEndianTarget ? (N - 1 - I) : I); 191 Pointer Elem = P.atField(Fi->Offset); 192 size_t BitOffset = 193 Offset + Layout.getFieldOffset(Fi->Decl->getFieldIndex()); 194 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); 195 } 196 }; 197 auto enumerateBases = [&]() -> void { 198 for (unsigned I = 0, N = R->getNumBases(); I != N; ++I) { 199 const Record::Base *B = R->getBase(BigEndianTarget ? (N - 1 - I) : I); 200 Pointer Elem = P.atField(B->Offset); 201 CharUnits ByteOffset = 202 Layout.getBaseClassOffset(cast<CXXRecordDecl>(B->Decl)); 203 size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset); 204 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); 205 } 206 }; 207 208 if (BigEndianTarget) { 209 enumerateFields(); 210 enumerateBases(); 211 } else { 212 enumerateBases(); 213 enumerateFields(); 214 } 215 216 return Ok; 217 } 218 219 llvm_unreachable("Unhandled data type"); 220 } 221 222 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx, 223 DataFunc F) { 224 return enumerateData(P, Ctx, 0, F); 225 } 226 227 // This function is constexpr if and only if To, From, and the types of 228 // all subobjects of To and From are types T such that... 229 // (3.1) - is_union_v<T> is false; 230 // (3.2) - is_pointer_v<T> is false; 231 // (3.3) - is_member_pointer_v<T> is false; 232 // (3.4) - is_volatile_v<T> is false; and 233 // (3.5) - T has no non-static data members of reference type 234 // 235 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in 236 // ExprConstant.cpp. 237 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, 238 bool IsToType) { 239 enum { 240 E_Union = 0, 241 E_Pointer, 242 E_MemberPointer, 243 E_Volatile, 244 E_Reference, 245 }; 246 enum { C_Member, C_Base }; 247 248 auto diag = [&](int Reason) -> bool { 249 const Expr *E = S.Current->getExpr(OpPC); 250 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type) 251 << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason 252 << E->getSourceRange(); 253 return false; 254 }; 255 auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) { 256 S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype) 257 << NoteType << Construct << T.getUnqualifiedType() << NoteRange; 258 return false; 259 }; 260 261 T = T.getCanonicalType(); 262 263 if (T->isUnionType()) 264 return diag(E_Union); 265 if (T->isPointerType()) 266 return diag(E_Pointer); 267 if (T->isMemberPointerType()) 268 return diag(E_MemberPointer); 269 if (T.isVolatileQualified()) 270 return diag(E_Volatile); 271 272 if (const RecordDecl *RD = T->getAsRecordDecl()) { 273 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { 274 for (const CXXBaseSpecifier &BS : CXXRD->bases()) { 275 if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType)) 276 return note(C_Base, BS.getType(), BS.getBeginLoc()); 277 } 278 } 279 for (const FieldDecl *FD : RD->fields()) { 280 if (FD->getType()->isReferenceType()) 281 return diag(E_Reference); 282 if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType)) 283 return note(C_Member, FD->getType(), FD->getSourceRange()); 284 } 285 } 286 287 if (T->isArrayType() && 288 !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T), 289 IsToType)) 290 return false; 291 292 return true; 293 } 294 295 static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, 296 BitcastBuffer &Buffer, bool ReturnOnUninit) { 297 const ASTContext &ASTCtx = Ctx.getASTContext(); 298 bool SwapData = (ASTCtx.getTargetInfo().isLittleEndian() != 299 llvm::sys::IsLittleEndianHost); 300 bool BigEndianTarget = ASTCtx.getTargetInfo().isBigEndian(); 301 302 return enumeratePointerFields( 303 FromPtr, Ctx, 304 [&](const Pointer &P, PrimType T, size_t BitOffset, 305 bool PackedBools) -> bool { 306 if (!P.isInitialized()) { 307 assert(false && "Implement uninitialized value tracking"); 308 return ReturnOnUninit; 309 } 310 311 assert(P.isInitialized()); 312 // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v. 313 if (T == PT_Ptr) 314 assert(false && "Implement casting to pointer types"); 315 316 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); 317 unsigned BitWidth = ASTCtx.toBits(ObjectReprChars); 318 llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity()); 319 // Work around floating point types that contain unused padding bytes. 320 // This is really just `long double` on x86, which is the only 321 // fundamental type with padding bytes. 322 if (T == PT_Float) { 323 const Floating &F = P.deref<Floating>(); 324 unsigned NumBits = 325 llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics()); 326 assert(NumBits % 8 == 0); 327 assert(NumBits <= (ObjectReprChars.getQuantity() * 8)); 328 F.bitcastToMemory(Buff.data()); 329 // Now, only (maybe) swap the actual size of the float, excluding the 330 // padding bits. 331 if (SwapData) 332 swapBytes(Buff.data(), NumBits / 8); 333 334 } else { 335 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField()) 336 BitWidth = FD->getBitWidthValue(ASTCtx); 337 else if (T == PT_Bool && PackedBools) 338 BitWidth = 1; 339 340 BITCAST_TYPE_SWITCH(T, { 341 T Val = P.deref<T>(); 342 Val.bitcastToMemory(Buff.data()); 343 }); 344 if (SwapData) 345 swapBytes(Buff.data(), ObjectReprChars.getQuantity()); 346 } 347 348 if (BitWidth != (Buff.size() * 8) && BigEndianTarget) { 349 Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)), 350 BitWidth, BigEndianTarget); 351 } else { 352 Buffer.pushData(Buff.data(), BitWidth, BigEndianTarget); 353 } 354 return true; 355 }); 356 } 357 358 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, 359 std::byte *Buff, size_t BuffSize, 360 bool &HasIndeterminateBits) { 361 assert(Ptr.isLive()); 362 assert(Ptr.isBlockPointer()); 363 assert(Buff); 364 365 BitcastBuffer Buffer; 366 if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false)) 367 return false; 368 369 bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer, 370 /*ReturnOnUninit=*/false); 371 assert(Buffer.size() == BuffSize * 8); 372 373 HasIndeterminateBits = !Buffer.allInitialized(); 374 std::memcpy(Buff, Buffer.data(), BuffSize); 375 376 if (llvm::sys::IsBigEndianHost) 377 swapBytes(Buff, BuffSize); 378 379 return Success; 380 } 381 382 bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, 383 const Pointer &FromPtr, Pointer &ToPtr) { 384 assert(FromPtr.isLive()); 385 assert(FromPtr.isBlockPointer()); 386 assert(ToPtr.isBlockPointer()); 387 388 QualType FromType = FromPtr.getType(); 389 QualType ToType = ToPtr.getType(); 390 391 if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true)) 392 return false; 393 if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false)) 394 return false; 395 396 BitcastBuffer Buffer; 397 readPointerToBuffer(S.getContext(), FromPtr, Buffer, 398 /*ReturnOnUninit=*/false); 399 400 // Now read the values out of the buffer again and into ToPtr. 401 const ASTContext &ASTCtx = S.getASTContext(); 402 size_t BitOffset = 0; 403 bool Success = enumeratePointerFields( 404 ToPtr, S.getContext(), 405 [&](const Pointer &P, PrimType T, size_t _, bool PackedBools) -> bool { 406 if (T == PT_Float) { 407 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); 408 const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType()); 409 unsigned NumBits = llvm::APFloatBase::getSizeInBits(Semantics); 410 assert(NumBits % 8 == 0); 411 assert(NumBits <= ASTCtx.toBits(ObjectReprChars)); 412 std::byte *M = Buffer.getBytes(BitOffset); 413 414 if (llvm::sys::IsBigEndianHost) 415 swapBytes(M, NumBits / 8); 416 417 P.deref<Floating>() = Floating::bitcastFromMemory(M, Semantics); 418 P.initialize(); 419 BitOffset += ASTCtx.toBits(ObjectReprChars); 420 return true; 421 } 422 423 BITCAST_TYPE_SWITCH_FIXED_SIZE(T, { 424 std::byte *M = Buffer.getBytes(BitOffset); 425 426 if (llvm::sys::IsBigEndianHost) 427 swapBytes(M, T::bitWidth() / 8); 428 429 P.deref<T>() = T::bitcastFromMemory(M, T::bitWidth()); 430 P.initialize(); 431 BitOffset += T::bitWidth(); 432 }); 433 return true; 434 }); 435 436 return Success; 437 } 438