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