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