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 /// Float is a special case that sometimes needs the floating point semantics 52 /// to be available. 53 #define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B) \ 54 do { \ 55 switch (Expr) { \ 56 TYPE_SWITCH_CASE(PT_Sint8, B) \ 57 TYPE_SWITCH_CASE(PT_Uint8, B) \ 58 TYPE_SWITCH_CASE(PT_Sint16, B) \ 59 TYPE_SWITCH_CASE(PT_Uint16, B) \ 60 TYPE_SWITCH_CASE(PT_Sint32, B) \ 61 TYPE_SWITCH_CASE(PT_Uint32, B) \ 62 TYPE_SWITCH_CASE(PT_Sint64, B) \ 63 TYPE_SWITCH_CASE(PT_Uint64, B) \ 64 TYPE_SWITCH_CASE(PT_IntAP, B) \ 65 TYPE_SWITCH_CASE(PT_IntAPS, B) \ 66 TYPE_SWITCH_CASE(PT_Bool, B) \ 67 TYPE_SWITCH_CASE(PT_Float, B) \ 68 default: \ 69 llvm_unreachable("Unhandled bitcast type"); \ 70 } \ 71 } while (0) 72 73 static bool bitof(std::byte B, unsigned BitIndex) { 74 return (B & (std::byte{1} << BitIndex)) != std::byte{0}; 75 } 76 77 static void swapBytes(std::byte *M, size_t N) { 78 for (size_t I = 0; I != (N / 2); ++I) 79 std::swap(M[I], M[N - 1 - I]); 80 } 81 82 /// Track what bits have been initialized to known values and which ones 83 /// have indeterminate value. 84 /// All offsets are in bits. 85 struct BitcastBuffer { 86 size_t SizeInBits = 0; 87 llvm::SmallVector<std::byte> Data; 88 89 BitcastBuffer() = default; 90 91 size_t size() const { return SizeInBits; } 92 93 const std::byte *data() const { return Data.data(); } 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 BitOffset, size_t BitWidth, 112 bool BigEndianTarget) { 113 bool OnlyFullBytes = BitWidth % 8 == 0; 114 unsigned NBytes = BitWidth / 8; 115 116 size_t BitsHandled = 0; 117 // Read all full bytes first 118 for (size_t I = 0; I != NBytes; ++I) { 119 std::byte B = 120 BigEndianTarget ? data[NBytes - OnlyFullBytes - I] : data[I]; 121 for (unsigned X = 0; X != 8; ++X) { 122 pushBit(bitof(B, X)); 123 ++BitsHandled; 124 } 125 } 126 127 if (BitsHandled == BitWidth) 128 return; 129 130 // Rest of the bits. 131 assert((BitWidth - BitsHandled) < 8); 132 std::byte B = BigEndianTarget ? data[0] : data[NBytes]; 133 for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) { 134 pushBit(bitof(B, I)); 135 ++BitsHandled; 136 } 137 138 assert(BitsHandled == BitWidth); 139 } 140 }; 141 142 /// We use this to recursively iterate over all fields and elemends of a pointer 143 /// and extract relevant data for a bitcast. 144 static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset, 145 DataFunc F) { 146 const Descriptor *FieldDesc = P.getFieldDesc(); 147 assert(FieldDesc); 148 149 // Primitives. 150 if (FieldDesc->isPrimitive()) 151 return F(P, FieldDesc->getPrimType(), Offset); 152 153 // Primitive arrays. 154 if (FieldDesc->isPrimitiveArray()) { 155 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 156 QualType ElemType = FieldDesc->getElemQualType(); 157 size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); 158 PrimType ElemT = *Ctx.classify(ElemType); 159 bool Ok = true; 160 for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { 161 unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; 162 Ok = Ok && F(P.atIndex(Index), ElemT, Offset); 163 Offset += ElemSizeInBits; 164 } 165 return Ok; 166 } 167 168 // Composite arrays. 169 if (FieldDesc->isCompositeArray()) { 170 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 171 QualType ElemType = FieldDesc->getElemQualType(); 172 size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType); 173 for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { 174 unsigned Index = BigEndianTarget ? (FieldDesc->getNumElems() - 1 - I) : I; 175 enumerateData(P.atIndex(Index).narrow(), Ctx, Offset, F); 176 Offset += ElemSizeInBits; 177 } 178 return true; 179 } 180 181 // Records. 182 if (FieldDesc->isRecord()) { 183 bool BigEndianTarget = Ctx.getASTContext().getTargetInfo().isBigEndian(); 184 const Record *R = FieldDesc->ElemRecord; 185 const ASTRecordLayout &Layout = 186 Ctx.getASTContext().getASTRecordLayout(R->getDecl()); 187 bool Ok = true; 188 189 auto enumerateFields = [&]() -> void { 190 for (unsigned I = 0, N = R->getNumFields(); I != N; ++I) { 191 const Record::Field *Fi = 192 R->getField(BigEndianTarget ? (N - 1 - I) : I); 193 Pointer Elem = P.atField(Fi->Offset); 194 size_t BitOffset = 195 Offset + Layout.getFieldOffset(Fi->Decl->getFieldIndex()); 196 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); 197 } 198 }; 199 auto enumerateBases = [&]() -> void { 200 for (unsigned I = 0, N = R->getNumBases(); I != N; ++I) { 201 const Record::Base *B = R->getBase(BigEndianTarget ? (N - 1 - I) : I); 202 Pointer Elem = P.atField(B->Offset); 203 CharUnits ByteOffset = 204 Layout.getBaseClassOffset(cast<CXXRecordDecl>(B->Decl)); 205 size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset); 206 Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F); 207 } 208 }; 209 210 if (BigEndianTarget) { 211 enumerateFields(); 212 enumerateBases(); 213 } else { 214 enumerateBases(); 215 enumerateFields(); 216 } 217 218 return Ok; 219 } 220 221 llvm_unreachable("Unhandled data type"); 222 } 223 224 static bool enumeratePointerFields(const Pointer &P, const Context &Ctx, 225 DataFunc F) { 226 return enumerateData(P, Ctx, 0, F); 227 } 228 229 // This function is constexpr if and only if To, From, and the types of 230 // all subobjects of To and From are types T such that... 231 // (3.1) - is_union_v<T> is false; 232 // (3.2) - is_pointer_v<T> is false; 233 // (3.3) - is_member_pointer_v<T> is false; 234 // (3.4) - is_volatile_v<T> is false; and 235 // (3.5) - T has no non-static data members of reference type 236 // 237 // NOTE: This is a version of checkBitCastConstexprEligibilityType() in 238 // ExprConstant.cpp. 239 static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, 240 bool IsToType) { 241 enum { 242 E_Union = 0, 243 E_Pointer, 244 E_MemberPointer, 245 E_Volatile, 246 E_Reference, 247 }; 248 enum { C_Member, C_Base }; 249 250 auto diag = [&](int Reason) -> bool { 251 const Expr *E = S.Current->getExpr(OpPC); 252 S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type) 253 << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason 254 << E->getSourceRange(); 255 return false; 256 }; 257 auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) { 258 S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype) 259 << NoteType << Construct << T << NoteRange; 260 return false; 261 }; 262 263 T = T.getCanonicalType(); 264 265 if (T->isUnionType()) 266 return diag(E_Union); 267 if (T->isPointerType()) 268 return diag(E_Pointer); 269 if (T->isMemberPointerType()) 270 return diag(E_MemberPointer); 271 if (T.isVolatileQualified()) 272 return diag(E_Volatile); 273 274 if (const RecordDecl *RD = T->getAsRecordDecl()) { 275 if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { 276 for (const CXXBaseSpecifier &BS : CXXRD->bases()) { 277 if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType)) 278 return note(C_Base, BS.getType(), BS.getBeginLoc()); 279 } 280 } 281 for (const FieldDecl *FD : RD->fields()) { 282 if (FD->getType()->isReferenceType()) 283 return diag(E_Reference); 284 if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType)) 285 return note(C_Member, FD->getType(), FD->getSourceRange()); 286 } 287 } 288 289 if (T->isArrayType() && 290 !CheckBitcastType(S, OpPC, S.getASTContext().getBaseElementType(T), 291 IsToType)) 292 return false; 293 294 return true; 295 } 296 297 static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, 298 BitcastBuffer &Buffer, bool ReturnOnUninit) { 299 const ASTContext &ASTCtx = Ctx.getASTContext(); 300 bool SwapData = (ASTCtx.getTargetInfo().isLittleEndian() != 301 llvm::sys::IsLittleEndianHost); 302 bool BigEndianTarget = ASTCtx.getTargetInfo().isBigEndian(); 303 304 return enumeratePointerFields( 305 FromPtr, Ctx, 306 [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool { 307 if (!P.isInitialized()) { 308 assert(false && "Implement uninitialized value tracking"); 309 return ReturnOnUninit; 310 } 311 312 assert(P.isInitialized()); 313 // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v. 314 if (T == PT_Ptr) 315 assert(false && "Implement casting to pointer types"); 316 317 CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType()); 318 unsigned BitWidth = ASTCtx.toBits(ObjectReprChars); 319 llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity()); 320 // Work around floating point types that contain unused padding bytes. 321 // This is really just `long double` on x86, which is the only 322 // fundamental type with padding bytes. 323 if (T == PT_Float) { 324 const Floating &F = P.deref<Floating>(); 325 unsigned NumBits = 326 llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics()); 327 assert(NumBits % 8 == 0); 328 assert(NumBits <= (ObjectReprChars.getQuantity() * 8)); 329 F.bitcastToMemory(Buff.data()); 330 // Now, only (maybe) swap the actual size of the float, excluding the 331 // padding bits. 332 if (SwapData) 333 swapBytes(Buff.data(), NumBits / 8); 334 335 } else { 336 if (const FieldDecl *FD = P.getField(); FD && FD->isBitField()) 337 BitWidth = FD->getBitWidthValue(ASTCtx); 338 339 BITCAST_TYPE_SWITCH(T, { 340 T Val = P.deref<T>(); 341 Val.bitcastToMemory(Buff.data()); 342 }); 343 if (SwapData) 344 swapBytes(Buff.data(), ObjectReprChars.getQuantity()); 345 } 346 347 if (BitWidth != (Buff.size() * 8) && BigEndianTarget) { 348 Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)), 349 BitOffset, BitWidth, BigEndianTarget); 350 } else { 351 Buffer.pushData(Buff.data(), BitOffset, BitWidth, BigEndianTarget); 352 } 353 return true; 354 }); 355 } 356 357 bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, 358 std::byte *Buff, size_t BuffSize, 359 bool &HasIndeterminateBits) { 360 assert(Ptr.isLive()); 361 assert(Ptr.isBlockPointer()); 362 assert(Buff); 363 364 BitcastBuffer Buffer; 365 if (!CheckBitcastType(S, OpPC, Ptr.getType(), /*IsToType=*/false)) 366 return false; 367 368 bool Success = readPointerToBuffer(S.getContext(), Ptr, Buffer, 369 /*ReturnOnUninit=*/false); 370 assert(Buffer.size() == BuffSize * 8); 371 372 HasIndeterminateBits = !Buffer.allInitialized(); 373 std::memcpy(Buff, Buffer.data(), BuffSize); 374 375 if (llvm::sys::IsBigEndianHost) 376 swapBytes(Buff, BuffSize); 377 378 return Success; 379 } 380