https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/215823
>From 3518d08a4d299cbfb528864c22e1b2a2dc645e0a Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 12 Aug 2026 06:38:46 -0700 Subject: [PATCH 1/5] [NFC] Split PaddingClearer out into a CodeGenUtils shared directory We have plenty of stuff that needs to be shared between Classic-Codegen and CIR, that we've been copy/pasting too often. This patch proposes/produces a shared directory of CodeGenUtils that will be for things that don't really belong elsewhere. This functionality (calculating padding for __builtin_clear_padding) is both too complex to copy/paste, and doesn't really have reason to live anywhere except CodeGen/CIR-CodeGen. The type has been split out as faithfully as possible, with only minor changes to make it LLVM-agnostic. The part of it that is required to generate the LLVM-IR is left in CGBuiltin.cpp, but takes the list of padding-intervals as an input, which will be useful for CIR's representation. --- .../include/clang/CodeGenUtils/CodeGenUtils.h | 283 +++++++++++++ clang/lib/CodeGen/CGBuiltin.cpp | 395 +++--------------- 2 files changed, 352 insertions(+), 326 deletions(-) create mode 100644 clang/include/clang/CodeGenUtils/CodeGenUtils.h diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h new file mode 100644 index 0000000000000..2da4ddc5aea36 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h @@ -0,0 +1,283 @@ +//===--- CodeGenUtils.h - Shared Classic CodeGen/CIR CodeGen Utils--C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/APFloat.h" + +#include <algorithm> +#include <utility> + +namespace clang::CodeGenUtils { +// A type that helps represent a padding interval. +struct BitInterval { + // [First, Last) + uint64_t First; + uint64_t Last; +}; + +// PaddingCalculator is a utility class that calculates the padding bits in a +// c/c++ type. It traverses the type recursively, collecting occupied +// bit intervals, and then computes the padding intervals. +// If a byte only contains some padding bits, it gets intervals for only those +// bits. This is the case for bit-fields. +struct PaddingCalculator { + PaddingCalculator(const ASTContext &Ctx, const TargetInfo &TI, + unsigned PointerSizeInBits) + : Ctx(Ctx), TI(TI), PointerSizeInBits(PointerSizeInBits) {} + + void run(QualType Ty) { + OccuppiedIntervals.clear(); + Stack.clear(); + + TySizeInBits = Ctx.getTypeSize(Ty); + + Stack.push_back(Data{0, Ty, true}); + while (!Stack.empty()) { + Data Current = Stack.back(); + Stack.pop_back(); + Visit(Current); + } + MergeOccuppiedIntervals(); + } + + llvm::SmallVector<BitInterval> GetPaddingIntervals() { + llvm::SmallVector<BitInterval> Results; + if (OccuppiedIntervals.size() == 1 && + OccuppiedIntervals.front().First == 0 && + OccuppiedIntervals.front().Last == TySizeInBits) { + return Results; + } + Results.reserve(OccuppiedIntervals.size() + 1); + uint64_t CurrentPos = 0; + for (const BitInterval &OccupiedInterval : OccuppiedIntervals) { + if (OccupiedInterval.First > CurrentPos) { + Results.push_back(BitInterval{CurrentPos, OccupiedInterval.First}); + } + CurrentPos = OccupiedInterval.Last; + } + if (TySizeInBits > CurrentPos) { + Results.push_back(BitInterval{CurrentPos, TySizeInBits}); + } + return Results; + } + +private: + struct Data { + uint64_t StartBitOffset; + QualType Ty; + bool VisitVirtualBase; + }; + + // Return the number of non padding bits of a scalar type. + // + // The property that we specifically care about here is whether the scalar + // type has padding bits, i.e. are there bits in the type which are not + // specified by the ABI. + // + // We currently don't care about this anywhere else in clang: layout cares + // about the ABI size, calling convention code cares about specific types, + // but nothing cares about padding specifically. And it's not something we can + // easily query from LLVM due to the type system mismatches. + // DL.getTypeSizeInBits(convertTypeForLoadStore(T)) is probably close, but the + // DataLayout methods aren't really designed for this usage. + // + // Therefore, it is better to explicitly list all the scalar types + // containing padding bits that we know of, namely, _BitInt(N) and x87 long + // double. + uint64_t getScalarOccupiedSizeInBits(QualType Ty) const { + if (const auto *BIT = Ty->getAs<BitIntType>()) + return BIT->getNumBits(); + + if (const auto *BT = Ty->getAs<BuiltinType>()) { + if (BT->getKind() == BuiltinType::LongDouble && + &TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended()) + return llvm::APFloat::getSizeInBits(TI.getLongDoubleFormat()); + } + + return Ctx.getTypeSize(Ty); + } + + void Visit(const Data &D) { + if (auto *AT = dyn_cast<ConstantArrayType>(D.Ty)) { + VisitArray(AT, D.StartBitOffset); + return; + } + + if (auto *Record = D.Ty->getAsRecordDecl()) { + VisitStruct(Record, D.StartBitOffset, D.VisitVirtualBase); + return; + } + + if (D.Ty->isAtomicType()) { + auto Unwrapped = D; + Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType(); + Stack.push_back(Unwrapped); + return; + } + + if (const auto *Complex = D.Ty->getAs<ComplexType>()) { + VisitComplex(Complex, D.StartBitOffset); + return; + } + + if (const auto *VT = D.Ty->getAs<clang::VectorType>()) { + VisitVector(VT, D.StartBitOffset); + return; + } + + uint64_t SizeBit = getScalarOccupiedSizeInBits(D.Ty); + OccuppiedIntervals.push_back( + BitInterval{D.StartBitOffset, D.StartBitOffset + SizeBit}); + } + + void VisitArray(const ConstantArrayType *AT, uint64_t StartBitOffset) { + for (uint64_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue(); + ++ArrIndex) { + + QualType ElementQualType = AT->getElementType(); + auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); + auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); + auto Offset = ElementSize.alignTo(ElementAlign); + + Stack.push_back(Data{StartBitOffset + ArrIndex * Offset.getQuantity() * + Ctx.getCharWidth(), + ElementQualType, /*VisitVirtualBase*/ true}); + } + } + + void VisitStruct(const RecordDecl *R, uint64_t StartBitOffset, + bool VisitVirtualBase) { + const ASTRecordLayout &ASTLayout = Ctx.getASTRecordLayout(R); + auto *CXXRecord = dyn_cast<CXXRecordDecl>(R); + + if (CXXRecord) { + if (ASTLayout.hasOwnVFPtr()) { + OccuppiedIntervals.push_back( + BitInterval{StartBitOffset, StartBitOffset + PointerSizeInBits}); + } + + if (ASTLayout.hasOwnVBPtr()) { + auto Offset = ASTLayout.getVBPtrOffset().getQuantity(); + auto StartVBPtr = StartBitOffset + Offset * Ctx.getCharWidth(); + OccuppiedIntervals.push_back( + BitInterval{StartVBPtr, StartVBPtr + PointerSizeInBits}); + } + + const auto VisitBase = [&ASTLayout, StartBitOffset, this]( + const CXXBaseSpecifier &Base, auto GetOffset) { + auto *BaseRecord = Base.getType()->getAsCXXRecordDecl(); + if (!BaseRecord) { + return; + } + auto BaseOffset = + std::invoke(GetOffset, ASTLayout, BaseRecord).getQuantity(); + + Stack.push_back(Data{StartBitOffset + BaseOffset * Ctx.getCharWidth(), + Base.getType(), /*VisitVirtualBase*/ false}); + }; + + for (auto Base : CXXRecord->bases()) { + if (!Base.isVirtual()) { + VisitBase(Base, &ASTRecordLayout::getBaseClassOffset); + } + } + + if (VisitVirtualBase) { + for (auto VBase : CXXRecord->vbases()) { + VisitBase(VBase, &ASTRecordLayout::getVBaseClassOffset); + } + } + } + + for (auto *Field : R->fields()) { + // Treat unnamed bitfields as padding. + if (Field->isUnnamedBitField()) + continue; + + auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex()); + if (Field->isBitField()) { + OccuppiedIntervals.push_back(BitInterval{ + StartBitOffset + FieldOffset, + StartBitOffset + FieldOffset + Field->getBitWidthValue()}); + } else { + Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(), + /*VisitVirtualBase*/ true}); + } + } + } + + void VisitComplex(const ComplexType *CT, uint64_t StartBitOffset) { + QualType ElementQualType = CT->getElementType(); + auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); + auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); + auto ImgOffset = ElementSize.alignTo(ElementAlign); + + Stack.push_back( + Data{StartBitOffset, ElementQualType, /*VisitVirtualBase*/ true}); + Stack.push_back( + Data{StartBitOffset + ImgOffset.getQuantity() * Ctx.getCharWidth(), + ElementQualType, /*VisitVirtualBase*/ true}); + } + + void VisitVector(const clang::VectorType *VT, uint64_t StartBitOffset) { + uint64_t SizeBit = [&]() -> uint64_t { + if (VT->isPackedVectorBoolType(Ctx)) + return VT->getNumElements(); + return getScalarOccupiedSizeInBits(VT->getElementType()) * + VT->getNumElements(); + }(); + OccuppiedIntervals.push_back( + BitInterval{StartBitOffset, StartBitOffset + SizeBit}); + } + + void MergeOccuppiedIntervals() { + std::sort(OccuppiedIntervals.begin(), OccuppiedIntervals.end(), + [](const BitInterval &lhs, const BitInterval &rhs) { + return std::tie(lhs.First, lhs.Last) < + std::tie(rhs.First, rhs.Last); + }); + + llvm::SmallVector<BitInterval> Merged; + Merged.reserve(OccuppiedIntervals.size()); + + for (const BitInterval &NextInterval : OccuppiedIntervals) { + if (Merged.empty()) { + Merged.push_back(NextInterval); + continue; + } + auto &LastInterval = Merged.back(); + + if (NextInterval.First > LastInterval.Last) { + Merged.push_back(NextInterval); + } else { + LastInterval.Last = std::max(LastInterval.Last, NextInterval.Last); + } + } + + OccuppiedIntervals = Merged; + } + + const ASTContext &Ctx; + const TargetInfo &TI; + unsigned PointerSizeInBits; + uint64_t TySizeInBits = 0; + llvm::SmallVector<Data> Stack; + llvm::SmallVector<BitInterval> OccuppiedIntervals; +}; + +// Calculate and gets the 'padding intervals' inside of a type. +llvm::SmallVector<BitInterval> +CalculatePaddingIntervals(const ASTContext &Ctx, const TargetInfo &TI, + QualType Ty, unsigned PointerSizeInBits) { + PaddingCalculator pc{Ctx, TI, PointerSizeInBits}; + pc.run(Ty); + return pc.GetPaddingIntervals(); +} +} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 3521fc10f1387..9fa54b8f18b13 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -28,6 +28,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CodeGenUtils/CodeGenUtils.h" #include "llvm/ADT/APFloat.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instruction.h" @@ -2598,277 +2599,40 @@ RValue CodeGenFunction::emitStdcFirstBit(const CallExpr *E, Intrinsic::ID IntID, return RValue::get(Result); } -namespace { - -// PaddingClearer is a utility class that clears padding bits in a -// c/c++ type. It traverses the type recursively, collecting occupied -// bit intervals, and then computes the padding intervals. -// In the end, it clears the padding bits by writing zeros -// to the padding intervals bytes-by-bytes. If a byte only contains -// some padding bits, it writes zeros to only those bits. This is -// the case for bit-fields. -struct PaddingClearer { - PaddingClearer(CodeGenFunction &F) - : CGF(F), CharWidth(CGF.getContext().getCharWidth()) {} - - void run(Address Src, QualType Ty) { - OccuppiedIntervals.clear(); - Stack.clear(); - - Stack.push_back(Data{0, Ty, true}); - while (!Stack.empty()) { - auto Current = Stack.back(); - Stack.pop_back(); - Visit(Current); - } - - MergeOccuppiedIntervals(); - auto PaddingIntervals = - GetPaddingIntervals(CGF.getContext().getTypeSize(Ty)); - for (const auto &Interval : PaddingIntervals) { - ClearPadding(Src, Interval); - } - } - -private: - struct BitInterval { - // [First, Last) - uint64_t First; - uint64_t Last; - }; - - struct Data { - uint64_t StartBitOffset; - QualType Ty; - bool VisitVirtualBase; - }; - - // Return the number of non padding bits of a scalar type. - // - // The property that we specifically care about here is whether the scalar - // type has padding bits, i.e. are there bits in the type which are not - // specified by the ABI. - // - // We currently don't care about this anywhere else in clang: layout cares - // about the ABI size, calling convention code cares about specific types, but - // nothing cares about padding specifically. And it's not something we can - // easily query from LLVM due to the type system mismatches. - // DL.getTypeSizeInBits(convertTypeForLoadStore(T)) is probably close, but the - // DataLayout methods aren't really designed for this usage. - // - // Therefore, it is better to explicitly list all the scalar types containing - // padding bits that we know of, namely, _BitInt(N) and x87 long double. - uint64_t getScalarOccupiedSizeInBits(QualType Ty) const { - if (const auto *BIT = Ty->getAs<BitIntType>()) - return BIT->getNumBits(); - - if (const auto *BT = Ty->getAs<BuiltinType>()) { - if (BT->getKind() == BuiltinType::LongDouble && - &CGF.getTarget().getLongDoubleFormat() == - &APFloat::x87DoubleExtended()) - return APFloat::getSizeInBits(CGF.getTarget().getLongDoubleFormat()); - } - - return CGF.getContext().getTypeSize(Ty); - } - - void Visit(const Data &D) { - if (auto *AT = dyn_cast<ConstantArrayType>(D.Ty)) { - VisitArray(AT, D.StartBitOffset); - return; - } - - if (auto *Record = D.Ty->getAsRecordDecl()) { - VisitStruct(Record, D.StartBitOffset, D.VisitVirtualBase); - return; - } - - if (D.Ty->isAtomicType()) { - auto Unwrapped = D; - Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType(); - Stack.push_back(Unwrapped); - return; - } - - if (const auto *Complex = D.Ty->getAs<ComplexType>()) { - VisitComplex(Complex, D.StartBitOffset); - return; - } - - if (const auto *VT = D.Ty->getAs<clang::VectorType>()) { - VisitVector(VT, D.StartBitOffset); - return; - } - - uint64_t SizeBit = getScalarOccupiedSizeInBits(D.Ty); - OccuppiedIntervals.push_back( - BitInterval{D.StartBitOffset, D.StartBitOffset + SizeBit}); - } - - void VisitArray(const ConstantArrayType *AT, uint64_t StartBitOffset) { - for (uint64_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue(); - ++ArrIndex) { - - QualType ElementQualType = AT->getElementType(); - auto ElementSize = CGF.getContext().getTypeSizeInChars(ElementQualType); - auto ElementAlign = CGF.getContext().getTypeAlignInChars(ElementQualType); - auto Offset = ElementSize.alignTo(ElementAlign); - - Stack.push_back( - Data{StartBitOffset + ArrIndex * Offset.getQuantity() * CharWidth, - ElementQualType, /*VisitVirtualBase*/ true}); - } - } - - void VisitStruct(const RecordDecl *R, uint64_t StartBitOffset, - bool VisitVirtualBase) { - const auto &DL = CGF.CGM.getModule().getDataLayout(); - const ASTRecordLayout &ASTLayout = CGF.getContext().getASTRecordLayout(R); - - auto *CXXRecord = dyn_cast<CXXRecordDecl>(R); - - if (CXXRecord) { - if (ASTLayout.hasOwnVFPtr()) { - OccuppiedIntervals.push_back(BitInterval{ - StartBitOffset, StartBitOffset + DL.getPointerSizeInBits()}); - } - - if (ASTLayout.hasOwnVBPtr()) { - auto Offset = ASTLayout.getVBPtrOffset().getQuantity(); - auto StartVBPtr = StartBitOffset + Offset * CharWidth; - OccuppiedIntervals.push_back( - BitInterval{StartVBPtr, StartVBPtr + DL.getPointerSizeInBits()}); - } - - const auto VisitBase = [&ASTLayout, StartBitOffset, this]( - const CXXBaseSpecifier &Base, auto GetOffset) { - auto *BaseRecord = Base.getType()->getAsCXXRecordDecl(); - if (!BaseRecord) { - return; - } - auto BaseOffset = - std::invoke(GetOffset, ASTLayout, BaseRecord).getQuantity(); - - Stack.push_back(Data{StartBitOffset + BaseOffset * CharWidth, - Base.getType(), /*VisitVirtualBase*/ false}); - }; - - for (auto Base : CXXRecord->bases()) { - if (!Base.isVirtual()) { - VisitBase(Base, &ASTRecordLayout::getBaseClassOffset); - } - } - - if (VisitVirtualBase) { - for (auto VBase : CXXRecord->vbases()) { - VisitBase(VBase, &ASTRecordLayout::getVBaseClassOffset); - } - } - } - - for (auto *Field : R->fields()) { - // Treat unnamed bitfields as padding. - if (Field->isUnnamedBitField()) - continue; - - auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex()); - if (Field->isBitField()) { - OccuppiedIntervals.push_back(BitInterval{ - StartBitOffset + FieldOffset, - StartBitOffset + FieldOffset + Field->getBitWidthValue()}); - } else { - Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(), - /*VisitVirtualBase*/ true}); - } - } - } - - void VisitComplex(const ComplexType *CT, uint64_t StartBitOffset) { - QualType ElementQualType = CT->getElementType(); - auto ElementSize = CGF.getContext().getTypeSizeInChars(ElementQualType); - auto ElementAlign = CGF.getContext().getTypeAlignInChars(ElementQualType); - auto ImgOffset = ElementSize.alignTo(ElementAlign); - - Stack.push_back( - Data{StartBitOffset, ElementQualType, /*VisitVirtualBase*/ true}); - Stack.push_back(Data{StartBitOffset + ImgOffset.getQuantity() * CharWidth, - ElementQualType, /*VisitVirtualBase*/ true}); - } - - void VisitVector(const clang::VectorType *VT, uint64_t StartBitOffset) { - ASTContext &Ctx = CGF.getContext(); - uint64_t SizeBit = [&]() -> uint64_t { - if (VT->isPackedVectorBoolType(Ctx)) - return VT->getNumElements(); - return getScalarOccupiedSizeInBits(VT->getElementType()) * - VT->getNumElements(); - }(); - OccuppiedIntervals.push_back( - BitInterval{StartBitOffset, StartBitOffset + SizeBit}); - } - - void MergeOccuppiedIntervals() { - std::sort(OccuppiedIntervals.begin(), OccuppiedIntervals.end(), - [](const BitInterval &lhs, const BitInterval &rhs) { - return std::tie(lhs.First, lhs.Last) < - std::tie(rhs.First, rhs.Last); - }); - - llvm::SmallVector<BitInterval> Merged; - Merged.reserve(OccuppiedIntervals.size()); - - for (const BitInterval &NextInterval : OccuppiedIntervals) { - if (Merged.empty()) { - Merged.push_back(NextInterval); - continue; - } - auto &LastInterval = Merged.back(); - - if (NextInterval.First > LastInterval.Last) { - Merged.push_back(NextInterval); - } else { - LastInterval.Last = std::max(LastInterval.Last, NextInterval.Last); - } - } - - OccuppiedIntervals = Merged; - } - - llvm::SmallVector<BitInterval> - GetPaddingIntervals(uint64_t SizeInBits) const { - llvm::SmallVector<BitInterval> Results; - if (OccuppiedIntervals.size() == 1 && - OccuppiedIntervals.front().First == 0 && - OccuppiedIntervals.front().Last == SizeInBits) { - return Results; - } - Results.reserve(OccuppiedIntervals.size() + 1); - uint64_t CurrentPos = 0; - for (const BitInterval &OccupiedInterval : OccuppiedIntervals) { - if (OccupiedInterval.First > CurrentPos) { - Results.push_back(BitInterval{CurrentPos, OccupiedInterval.First}); - } - CurrentPos = OccupiedInterval.Last; - } - if (SizeInBits > CurrentPos) { - Results.push_back(BitInterval{CurrentPos, SizeInBits}); - } - return Results; - } - - void ClearPadding(Address Src, const BitInterval &PaddingInterval) { - auto *I8Ptr = - CGF.Builder.CreateBitCast(Src.getBasePointer(), CGF.Int8PtrTy); - auto *Zero = ConstantInt::get(CGF.Int8Ty, 0); - - // Calculate byte indices and bit positions - auto StartByte = PaddingInterval.First / CharWidth; - auto StartBit = PaddingInterval.First % CharWidth; - auto EndByte = PaddingInterval.Last / CharWidth; - auto EndBit = PaddingInterval.Last % CharWidth; - - if (StartByte == EndByte) { - // Interval is within a single byte +static void ClearPadding(CodeGenFunction &CGF, Address Src, + const CodeGenUtils::BitInterval &PaddingInterval) { + uint64_t CharWidth = CGF.getContext().getCharWidth(); + + auto *I8Ptr = CGF.Builder.CreateBitCast(Src.getBasePointer(), CGF.Int8PtrTy); + auto *Zero = ConstantInt::get(CGF.Int8Ty, 0); + + // Calculate byte indices and bit positions + auto StartByte = PaddingInterval.First / CharWidth; + auto StartBit = PaddingInterval.First % CharWidth; + auto EndByte = PaddingInterval.Last / CharWidth; + auto EndBit = PaddingInterval.Last % CharWidth; + + if (StartByte == EndByte) { + // Interval is within a single byte + auto *Index = ConstantInt::get(CGF.IntTy, StartByte); + auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); + Address ElementAddr(Element, CGF.Int8Ty, + Src.getAlignment().alignmentAtOffset( + CharUnits::fromQuantity(StartByte))); + + auto *Value = CGF.Builder.CreateLoad(ElementAddr); + + // Create mask to clear bits within the byte + // We want to clear bits from StartBit to EndBit-1 + uint8_t bitsToClear = ((1 << EndBit) - 1) & ~((1 << StartBit) - 1); + uint8_t bitsToKeep = ~bitsToClear; + auto *MaskValue = ConstantInt::get(CGF.Int8Ty, bitsToKeep); + auto *NewValue = CGF.Builder.CreateAnd(Value, MaskValue); + + CGF.Builder.CreateStore(NewValue, ElementAddr); + } else { + // Handle the start byte + if (StartBit != 0) { auto *Index = ConstantInt::get(CGF.IntTy, StartByte); auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); Address ElementAddr(Element, CGF.Int8Ty, @@ -2877,72 +2641,45 @@ struct PaddingClearer { auto *Value = CGF.Builder.CreateLoad(ElementAddr); - // Create mask to clear bits within the byte - // We want to clear bits from StartBit to EndBit-1 - uint8_t bitsToClear = ((1 << EndBit) - 1) & ~((1 << StartBit) - 1); + uint8_t bitsToClear = ((1 << (CharWidth - StartBit)) - 1) << StartBit; uint8_t bitsToKeep = ~bitsToClear; auto *MaskValue = ConstantInt::get(CGF.Int8Ty, bitsToKeep); auto *NewValue = CGF.Builder.CreateAnd(Value, MaskValue); CGF.Builder.CreateStore(NewValue, ElementAddr); - } else { - // Handle the start byte - if (StartBit != 0) { - auto *Index = ConstantInt::get(CGF.IntTy, StartByte); - auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); - Address ElementAddr(Element, CGF.Int8Ty, - Src.getAlignment().alignmentAtOffset( - CharUnits::fromQuantity(StartByte))); - - auto *Value = CGF.Builder.CreateLoad(ElementAddr); - - uint8_t bitsToClear = ((1 << (CharWidth - StartBit)) - 1) << StartBit; - uint8_t bitsToKeep = ~bitsToClear; - auto *MaskValue = ConstantInt::get(CGF.Int8Ty, bitsToKeep); - auto *NewValue = CGF.Builder.CreateAnd(Value, MaskValue); - - CGF.Builder.CreateStore(NewValue, ElementAddr); - ++StartByte; - } + ++StartByte; + } - // Handle full bytes in the middle - for (auto Offset = StartByte; Offset < EndByte; ++Offset) { - auto *Index = ConstantInt::get(CGF.IntTy, Offset); - auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); - Address ElementAddr(Element, CGF.Int8Ty, - Src.getAlignment().alignmentAtOffset( - CharUnits::fromQuantity(Offset))); + // Handle full bytes in the middle + for (auto Offset = StartByte; Offset < EndByte; ++Offset) { + auto *Index = ConstantInt::get(CGF.IntTy, Offset); + auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); + Address ElementAddr(Element, CGF.Int8Ty, + Src.getAlignment().alignmentAtOffset( + CharUnits::fromQuantity(Offset))); - CGF.Builder.CreateStore(Zero, ElementAddr); - } + CGF.Builder.CreateStore(Zero, ElementAddr); + } - // Handle the end byte - if (EndBit != 0) { - auto *Index = ConstantInt::get(CGF.IntTy, EndByte); - auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); - Address ElementAddr(Element, CGF.Int8Ty, - Src.getAlignment().alignmentAtOffset( - CharUnits::fromQuantity(EndByte))); + // Handle the end byte + if (EndBit != 0) { + auto *Index = ConstantInt::get(CGF.IntTy, EndByte); + auto *Element = CGF.Builder.CreateGEP(CGF.Int8Ty, I8Ptr, Index); + Address ElementAddr(Element, CGF.Int8Ty, + Src.getAlignment().alignmentAtOffset( + CharUnits::fromQuantity(EndByte))); - auto *Value = CGF.Builder.CreateLoad(ElementAddr); + auto *Value = CGF.Builder.CreateLoad(ElementAddr); - uint8_t bitsToClear = (1 << EndBit) - 1; - uint8_t bitsToKeep = ~bitsToClear; - auto *MaskValue = ConstantInt::get(CGF.Int8Ty, bitsToKeep); - auto *NewValue = CGF.Builder.CreateAnd(Value, MaskValue); + uint8_t bitsToClear = (1 << EndBit) - 1; + uint8_t bitsToKeep = ~bitsToClear; + auto *MaskValue = ConstantInt::get(CGF.Int8Ty, bitsToKeep); + auto *NewValue = CGF.Builder.CreateAnd(Value, MaskValue); - CGF.Builder.CreateStore(NewValue, ElementAddr); - } + CGF.Builder.CreateStore(NewValue, ElementAddr); } } - - CodeGenFunction &CGF; - const uint64_t CharWidth; - llvm::SmallVector<Data> Stack; - llvm::SmallVector<BitInterval> OccuppiedIntervals; -}; - -} // namespace +} RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, const CallExpr *E, @@ -5473,8 +5210,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_clear_padding: { Address Src = EmitPointerWithAlignment(E->getArg(0)); auto PointeeTy = E->getArg(0)->getType()->getPointeeType(); - PaddingClearer clearer{*this}; - clearer.run(Src, PointeeTy); + + llvm::SmallVector<CodeGenUtils::BitInterval> Padding = + CodeGenUtils::CalculatePaddingIntervals( + getContext(), getTarget(), PointeeTy, + CGM.getDataLayout().getPointerSizeInBits()); + for (const auto &Interval : Padding) + ClearPadding(*this, Src, Interval); + return RValue::get(nullptr); } case Builtin::BI__sync_fetch_and_add: >From b0dd9b3514156e4542021572c33b1d527082ce64 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 12 Aug 2026 09:02:52 -0700 Subject: [PATCH 2/5] Make generator function inline so that CIR can use it without being a duplicate symbol :) --- clang/include/clang/CodeGenUtils/CodeGenUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h index 2da4ddc5aea36..21c0d2de37978 100644 --- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h +++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h @@ -273,7 +273,7 @@ struct PaddingCalculator { }; // Calculate and gets the 'padding intervals' inside of a type. -llvm::SmallVector<BitInterval> +inline llvm::SmallVector<BitInterval> CalculatePaddingIntervals(const ASTContext &Ctx, const TargetInfo &TI, QualType Ty, unsigned PointerSizeInBits) { PaddingCalculator pc{Ctx, TI, PointerSizeInBits}; >From d9b3fc8c7b055f11a5f2a7f18fdee5eb25b12a97 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 12 Aug 2026 12:13:01 -0700 Subject: [PATCH 3/5] Move bit calculations to ASTContext --- clang/include/clang/AST/ASTContext.h | 13 + .../include/clang/CodeGenUtils/CodeGenUtils.h | 283 ------------------ clang/lib/AST/ASTContext.cpp | 273 +++++++++++++++++ clang/lib/CodeGen/CGBuiltin.cpp | 9 +- 4 files changed, 289 insertions(+), 289 deletions(-) delete mode 100644 clang/include/clang/CodeGenUtils/CodeGenUtils.h diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 42c9aa0fcf73b..be0d76dabb249 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1881,7 +1881,20 @@ class ASTContext : public RefCountedBase<ASTContext> { QualType adjustStringLiteralBaseType(QualType StrLTy) const; + // Represents an inclusive-first/exclusive last bit-offset into a type. + struct BitInterval { + // [First, Last) + uint64_t First; + uint64_t Last; + }; + + // Calculate and get the 'padding intervals' inside of a type. + llvm::SmallVector<BitInterval> getPaddingIntervals(QualType Ty) const; + private: + mutable llvm::DenseMap<QualType, llvm::SmallVector<BitInterval>> + PaddingIntervalCache; + /// Return a normal function type with a typed argument list. QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args, const FunctionProtoType::ExtProtoInfo &EPI, diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h deleted file mode 100644 index 21c0d2de37978..0000000000000 --- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h +++ /dev/null @@ -1,283 +0,0 @@ -//===--- CodeGenUtils.h - Shared Classic CodeGen/CIR CodeGen Utils--C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/ASTContext.h" -#include "clang/AST/Type.h" -#include "clang/Basic/TargetInfo.h" -#include "llvm/ADT/APFloat.h" - -#include <algorithm> -#include <utility> - -namespace clang::CodeGenUtils { -// A type that helps represent a padding interval. -struct BitInterval { - // [First, Last) - uint64_t First; - uint64_t Last; -}; - -// PaddingCalculator is a utility class that calculates the padding bits in a -// c/c++ type. It traverses the type recursively, collecting occupied -// bit intervals, and then computes the padding intervals. -// If a byte only contains some padding bits, it gets intervals for only those -// bits. This is the case for bit-fields. -struct PaddingCalculator { - PaddingCalculator(const ASTContext &Ctx, const TargetInfo &TI, - unsigned PointerSizeInBits) - : Ctx(Ctx), TI(TI), PointerSizeInBits(PointerSizeInBits) {} - - void run(QualType Ty) { - OccuppiedIntervals.clear(); - Stack.clear(); - - TySizeInBits = Ctx.getTypeSize(Ty); - - Stack.push_back(Data{0, Ty, true}); - while (!Stack.empty()) { - Data Current = Stack.back(); - Stack.pop_back(); - Visit(Current); - } - MergeOccuppiedIntervals(); - } - - llvm::SmallVector<BitInterval> GetPaddingIntervals() { - llvm::SmallVector<BitInterval> Results; - if (OccuppiedIntervals.size() == 1 && - OccuppiedIntervals.front().First == 0 && - OccuppiedIntervals.front().Last == TySizeInBits) { - return Results; - } - Results.reserve(OccuppiedIntervals.size() + 1); - uint64_t CurrentPos = 0; - for (const BitInterval &OccupiedInterval : OccuppiedIntervals) { - if (OccupiedInterval.First > CurrentPos) { - Results.push_back(BitInterval{CurrentPos, OccupiedInterval.First}); - } - CurrentPos = OccupiedInterval.Last; - } - if (TySizeInBits > CurrentPos) { - Results.push_back(BitInterval{CurrentPos, TySizeInBits}); - } - return Results; - } - -private: - struct Data { - uint64_t StartBitOffset; - QualType Ty; - bool VisitVirtualBase; - }; - - // Return the number of non padding bits of a scalar type. - // - // The property that we specifically care about here is whether the scalar - // type has padding bits, i.e. are there bits in the type which are not - // specified by the ABI. - // - // We currently don't care about this anywhere else in clang: layout cares - // about the ABI size, calling convention code cares about specific types, - // but nothing cares about padding specifically. And it's not something we can - // easily query from LLVM due to the type system mismatches. - // DL.getTypeSizeInBits(convertTypeForLoadStore(T)) is probably close, but the - // DataLayout methods aren't really designed for this usage. - // - // Therefore, it is better to explicitly list all the scalar types - // containing padding bits that we know of, namely, _BitInt(N) and x87 long - // double. - uint64_t getScalarOccupiedSizeInBits(QualType Ty) const { - if (const auto *BIT = Ty->getAs<BitIntType>()) - return BIT->getNumBits(); - - if (const auto *BT = Ty->getAs<BuiltinType>()) { - if (BT->getKind() == BuiltinType::LongDouble && - &TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended()) - return llvm::APFloat::getSizeInBits(TI.getLongDoubleFormat()); - } - - return Ctx.getTypeSize(Ty); - } - - void Visit(const Data &D) { - if (auto *AT = dyn_cast<ConstantArrayType>(D.Ty)) { - VisitArray(AT, D.StartBitOffset); - return; - } - - if (auto *Record = D.Ty->getAsRecordDecl()) { - VisitStruct(Record, D.StartBitOffset, D.VisitVirtualBase); - return; - } - - if (D.Ty->isAtomicType()) { - auto Unwrapped = D; - Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType(); - Stack.push_back(Unwrapped); - return; - } - - if (const auto *Complex = D.Ty->getAs<ComplexType>()) { - VisitComplex(Complex, D.StartBitOffset); - return; - } - - if (const auto *VT = D.Ty->getAs<clang::VectorType>()) { - VisitVector(VT, D.StartBitOffset); - return; - } - - uint64_t SizeBit = getScalarOccupiedSizeInBits(D.Ty); - OccuppiedIntervals.push_back( - BitInterval{D.StartBitOffset, D.StartBitOffset + SizeBit}); - } - - void VisitArray(const ConstantArrayType *AT, uint64_t StartBitOffset) { - for (uint64_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue(); - ++ArrIndex) { - - QualType ElementQualType = AT->getElementType(); - auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); - auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); - auto Offset = ElementSize.alignTo(ElementAlign); - - Stack.push_back(Data{StartBitOffset + ArrIndex * Offset.getQuantity() * - Ctx.getCharWidth(), - ElementQualType, /*VisitVirtualBase*/ true}); - } - } - - void VisitStruct(const RecordDecl *R, uint64_t StartBitOffset, - bool VisitVirtualBase) { - const ASTRecordLayout &ASTLayout = Ctx.getASTRecordLayout(R); - auto *CXXRecord = dyn_cast<CXXRecordDecl>(R); - - if (CXXRecord) { - if (ASTLayout.hasOwnVFPtr()) { - OccuppiedIntervals.push_back( - BitInterval{StartBitOffset, StartBitOffset + PointerSizeInBits}); - } - - if (ASTLayout.hasOwnVBPtr()) { - auto Offset = ASTLayout.getVBPtrOffset().getQuantity(); - auto StartVBPtr = StartBitOffset + Offset * Ctx.getCharWidth(); - OccuppiedIntervals.push_back( - BitInterval{StartVBPtr, StartVBPtr + PointerSizeInBits}); - } - - const auto VisitBase = [&ASTLayout, StartBitOffset, this]( - const CXXBaseSpecifier &Base, auto GetOffset) { - auto *BaseRecord = Base.getType()->getAsCXXRecordDecl(); - if (!BaseRecord) { - return; - } - auto BaseOffset = - std::invoke(GetOffset, ASTLayout, BaseRecord).getQuantity(); - - Stack.push_back(Data{StartBitOffset + BaseOffset * Ctx.getCharWidth(), - Base.getType(), /*VisitVirtualBase*/ false}); - }; - - for (auto Base : CXXRecord->bases()) { - if (!Base.isVirtual()) { - VisitBase(Base, &ASTRecordLayout::getBaseClassOffset); - } - } - - if (VisitVirtualBase) { - for (auto VBase : CXXRecord->vbases()) { - VisitBase(VBase, &ASTRecordLayout::getVBaseClassOffset); - } - } - } - - for (auto *Field : R->fields()) { - // Treat unnamed bitfields as padding. - if (Field->isUnnamedBitField()) - continue; - - auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex()); - if (Field->isBitField()) { - OccuppiedIntervals.push_back(BitInterval{ - StartBitOffset + FieldOffset, - StartBitOffset + FieldOffset + Field->getBitWidthValue()}); - } else { - Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(), - /*VisitVirtualBase*/ true}); - } - } - } - - void VisitComplex(const ComplexType *CT, uint64_t StartBitOffset) { - QualType ElementQualType = CT->getElementType(); - auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); - auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); - auto ImgOffset = ElementSize.alignTo(ElementAlign); - - Stack.push_back( - Data{StartBitOffset, ElementQualType, /*VisitVirtualBase*/ true}); - Stack.push_back( - Data{StartBitOffset + ImgOffset.getQuantity() * Ctx.getCharWidth(), - ElementQualType, /*VisitVirtualBase*/ true}); - } - - void VisitVector(const clang::VectorType *VT, uint64_t StartBitOffset) { - uint64_t SizeBit = [&]() -> uint64_t { - if (VT->isPackedVectorBoolType(Ctx)) - return VT->getNumElements(); - return getScalarOccupiedSizeInBits(VT->getElementType()) * - VT->getNumElements(); - }(); - OccuppiedIntervals.push_back( - BitInterval{StartBitOffset, StartBitOffset + SizeBit}); - } - - void MergeOccuppiedIntervals() { - std::sort(OccuppiedIntervals.begin(), OccuppiedIntervals.end(), - [](const BitInterval &lhs, const BitInterval &rhs) { - return std::tie(lhs.First, lhs.Last) < - std::tie(rhs.First, rhs.Last); - }); - - llvm::SmallVector<BitInterval> Merged; - Merged.reserve(OccuppiedIntervals.size()); - - for (const BitInterval &NextInterval : OccuppiedIntervals) { - if (Merged.empty()) { - Merged.push_back(NextInterval); - continue; - } - auto &LastInterval = Merged.back(); - - if (NextInterval.First > LastInterval.Last) { - Merged.push_back(NextInterval); - } else { - LastInterval.Last = std::max(LastInterval.Last, NextInterval.Last); - } - } - - OccuppiedIntervals = Merged; - } - - const ASTContext &Ctx; - const TargetInfo &TI; - unsigned PointerSizeInBits; - uint64_t TySizeInBits = 0; - llvm::SmallVector<Data> Stack; - llvm::SmallVector<BitInterval> OccuppiedIntervals; -}; - -// Calculate and gets the 'padding intervals' inside of a type. -inline llvm::SmallVector<BitInterval> -CalculatePaddingIntervals(const ASTContext &Ctx, const TargetInfo &TI, - QualType Ty, unsigned PointerSizeInBits) { - PaddingCalculator pc{Ctx, TI, PointerSizeInBits}; - pc.run(Ty); - return pc.GetPaddingIntervals(); -} -} // namespace clang::CodeGenUtils diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 47028c616e45c..847b7a5d94a3c 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -15731,3 +15731,276 @@ void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) { if (FieldDecl *FD = Comp.getField(); isPFPField(FD)) PFPFieldsWithEvaluatedOffset.insert(FD); } + +namespace { +// PaddingCalculator is a utility class that calculates the padding bits in a +// c/c++ type. It traverses the type recursively, collecting occupied +// bit intervals, and then computes the padding intervals. +// If a byte only contains some padding bits, it gets intervals for only those +// bits. This is the case for bit-fields. +struct PaddingCalculator { + PaddingCalculator(const ASTContext &Ctx) : Ctx(Ctx) {} + + void run(QualType Ty) { + OccuppiedIntervals.clear(); + Stack.clear(); + + TySizeInBits = Ctx.getTypeSize(Ty); + + Stack.push_back(Data{0, Ty, true}); + while (!Stack.empty()) { + Data Current = Stack.back(); + Stack.pop_back(); + Visit(Current); + } + MergeOccuppiedIntervals(); + } + + llvm::SmallVector<ASTContext::BitInterval> GetPaddingIntervals() { + llvm::SmallVector<ASTContext::BitInterval> Results; + if (OccuppiedIntervals.size() == 1 && + OccuppiedIntervals.front().First == 0 && + OccuppiedIntervals.front().Last == TySizeInBits) { + return Results; + } + Results.reserve(OccuppiedIntervals.size() + 1); + uint64_t CurrentPos = 0; + for (const ASTContext::BitInterval &OccupiedInterval : OccuppiedIntervals) { + if (OccupiedInterval.First > CurrentPos) { + Results.push_back( + ASTContext::BitInterval{CurrentPos, OccupiedInterval.First}); + } + CurrentPos = OccupiedInterval.Last; + } + if (TySizeInBits > CurrentPos) { + Results.push_back(ASTContext::BitInterval{CurrentPos, TySizeInBits}); + } + return Results; + } + +private: + struct Data { + uint64_t StartBitOffset; + QualType Ty; + bool VisitVirtualBase; + }; + + // Return the number of non padding bits of a scalar type. + // + // The property that we specifically care about here is whether the scalar + // type has padding bits, i.e. are there bits in the type which are not + // specified by the ABI. + // + // We currently don't care about this anywhere else in clang: layout cares + // about the ABI size, calling convention code cares about specific types, + // but nothing cares about padding specifically. And it's not something we can + // easily query from LLVM due to the type system mismatches. + // DL.getTypeSizeInBits(convertTypeForLoadStore(T)) is probably close, but the + // DataLayout methods aren't really designed for this usage. + // + // Therefore, it is better to explicitly list all the scalar types + // containing padding bits that we know of, namely, _BitInt(N) and x87 long + // double. + uint64_t getScalarOccupiedSizeInBits(QualType Ty) const { + if (const auto *BIT = Ty->getAs<BitIntType>()) + return BIT->getNumBits(); + + if (const auto *BT = Ty->getAs<BuiltinType>()) { + if (BT->getKind() == BuiltinType::LongDouble && + &Ctx.getTargetInfo().getLongDoubleFormat() == + &llvm::APFloat::x87DoubleExtended()) + return llvm::APFloat::getSizeInBits( + Ctx.getTargetInfo().getLongDoubleFormat()); + } + + return Ctx.getTypeSize(Ty); + } + + void Visit(const Data &D) { + if (auto *AT = dyn_cast<ConstantArrayType>(D.Ty)) { + VisitArray(AT, D.StartBitOffset); + return; + } + + if (auto *Record = D.Ty->getAsRecordDecl()) { + VisitStruct(Record, D.StartBitOffset, D.VisitVirtualBase); + return; + } + + if (D.Ty->isAtomicType()) { + auto Unwrapped = D; + Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType(); + Stack.push_back(Unwrapped); + return; + } + + if (const auto *Complex = D.Ty->getAs<ComplexType>()) { + VisitComplex(Complex, D.StartBitOffset); + return; + } + + if (const auto *VT = D.Ty->getAs<clang::VectorType>()) { + VisitVector(VT, D.StartBitOffset); + return; + } + + uint64_t SizeBit = getScalarOccupiedSizeInBits(D.Ty); + OccuppiedIntervals.push_back( + ASTContext::BitInterval{D.StartBitOffset, D.StartBitOffset + SizeBit}); + } + + void VisitArray(const ConstantArrayType *AT, uint64_t StartBitOffset) { + for (uint64_t ArrIndex = 0; ArrIndex < AT->getSize().getLimitedValue(); + ++ArrIndex) { + + QualType ElementQualType = AT->getElementType(); + auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); + auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); + auto Offset = ElementSize.alignTo(ElementAlign); + + Stack.push_back(Data{StartBitOffset + ArrIndex * Offset.getQuantity() * + Ctx.getCharWidth(), + ElementQualType, /*VisitVirtualBase*/ true}); + } + } + + void VisitStruct(const RecordDecl *R, uint64_t StartBitOffset, + bool VisitVirtualBase) { + const ASTRecordLayout &ASTLayout = Ctx.getASTRecordLayout(R); + auto *CXXRecord = dyn_cast<CXXRecordDecl>(R); + + unsigned PointerSizeInBits = Ctx.getTypeSize(Ctx.NullPtrTy); + + if (CXXRecord) { + if (ASTLayout.hasOwnVFPtr()) { + OccuppiedIntervals.push_back(ASTContext::BitInterval{ + StartBitOffset, StartBitOffset + PointerSizeInBits}); + } + + if (ASTLayout.hasOwnVBPtr()) { + auto Offset = ASTLayout.getVBPtrOffset().getQuantity(); + auto StartVBPtr = StartBitOffset + Offset * Ctx.getCharWidth(); + OccuppiedIntervals.push_back(ASTContext::BitInterval{ + StartVBPtr, StartVBPtr + PointerSizeInBits}); + } + + const auto VisitBase = [&ASTLayout, StartBitOffset, this]( + const CXXBaseSpecifier &Base, auto GetOffset) { + auto *BaseRecord = Base.getType()->getAsCXXRecordDecl(); + if (!BaseRecord) { + return; + } + auto BaseOffset = + std::invoke(GetOffset, ASTLayout, BaseRecord).getQuantity(); + + Stack.push_back(Data{StartBitOffset + BaseOffset * Ctx.getCharWidth(), + Base.getType(), /*VisitVirtualBase*/ + false}); + }; + + for (auto Base : CXXRecord->bases()) { + if (!Base.isVirtual()) { + VisitBase(Base, &ASTRecordLayout::getBaseClassOffset); + } + } + + if (VisitVirtualBase) { + for (auto VBase : CXXRecord->vbases()) { + VisitBase(VBase, &ASTRecordLayout::getVBaseClassOffset); + } + } + } + + for (auto *Field : R->fields()) { + // Treat unnamed bitfields as padding. + if (Field->isUnnamedBitField()) + continue; + + auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex()); + if (Field->isBitField()) { + OccuppiedIntervals.push_back(ASTContext::BitInterval{ + StartBitOffset + FieldOffset, + StartBitOffset + FieldOffset + Field->getBitWidthValue()}); + } else { + Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(), + /*VisitVirtualBase*/ true}); + } + } + } + + void VisitComplex(const ComplexType *CT, uint64_t StartBitOffset) { + QualType ElementQualType = CT->getElementType(); + auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); + auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); + auto ImgOffset = ElementSize.alignTo(ElementAlign); + + Stack.push_back( + Data{StartBitOffset, ElementQualType, /*VisitVirtualBase*/ true}); + Stack.push_back( + Data{StartBitOffset + ImgOffset.getQuantity() * Ctx.getCharWidth(), + ElementQualType, /*VisitVirtualBase*/ true}); + } + + void VisitVector(const clang::VectorType *VT, uint64_t StartBitOffset) { + uint64_t SizeBit = [&]() -> uint64_t { + if (VT->isPackedVectorBoolType(Ctx)) + return VT->getNumElements(); + return getScalarOccupiedSizeInBits(VT->getElementType()) * + VT->getNumElements(); + }(); + OccuppiedIntervals.push_back( + ASTContext::BitInterval{StartBitOffset, StartBitOffset + SizeBit}); + } + + void MergeOccuppiedIntervals() { + std::sort(OccuppiedIntervals.begin(), OccuppiedIntervals.end(), + [](const ASTContext::BitInterval &lhs, + const ASTContext::BitInterval &rhs) { + return std::tie(lhs.First, lhs.Last) < + std::tie(rhs.First, rhs.Last); + }); + + llvm::SmallVector<ASTContext::BitInterval> Merged; + Merged.reserve(OccuppiedIntervals.size()); + + for (const ASTContext::BitInterval &NextInterval : OccuppiedIntervals) { + if (Merged.empty()) { + Merged.push_back(NextInterval); + continue; + } + auto &LastInterval = Merged.back(); + + if (NextInterval.First > LastInterval.Last) { + Merged.push_back(NextInterval); + } else { + LastInterval.Last = std::max(LastInterval.Last, NextInterval.Last); + } + } + + OccuppiedIntervals = Merged; + } + + const ASTContext &Ctx; + // unsigned PointerSizeInBits; + uint64_t TySizeInBits = 0; + llvm::SmallVector<Data> Stack; + llvm::SmallVector<ASTContext::BitInterval> OccuppiedIntervals; +}; +} // namespace + +llvm::SmallVector<ASTContext::BitInterval> +ASTContext::getPaddingIntervals(QualType Ty) const { + Ty = Ty.getCanonicalType(); + auto cached = PaddingIntervalCache.find(Ty); + if (cached != PaddingIntervalCache.end()) + return cached->second; + + PaddingCalculator pc{*this}; + pc.run(Ty); + + auto result = pc.GetPaddingIntervals(); + auto [itr, res] = PaddingIntervalCache.insert_or_assign(Ty, result); + assert(res && "Failed to insert?"); + + return result; +} diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 9fa54b8f18b13..92c711a4a7062 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -28,7 +28,6 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/TargetInfo.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "llvm/ADT/APFloat.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instruction.h" @@ -2600,7 +2599,7 @@ RValue CodeGenFunction::emitStdcFirstBit(const CallExpr *E, Intrinsic::ID IntID, } static void ClearPadding(CodeGenFunction &CGF, Address Src, - const CodeGenUtils::BitInterval &PaddingInterval) { + const ASTContext::BitInterval &PaddingInterval) { uint64_t CharWidth = CGF.getContext().getCharWidth(); auto *I8Ptr = CGF.Builder.CreateBitCast(Src.getBasePointer(), CGF.Int8PtrTy); @@ -5211,10 +5210,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Address Src = EmitPointerWithAlignment(E->getArg(0)); auto PointeeTy = E->getArg(0)->getType()->getPointeeType(); - llvm::SmallVector<CodeGenUtils::BitInterval> Padding = - CodeGenUtils::CalculatePaddingIntervals( - getContext(), getTarget(), PointeeTy, - CGM.getDataLayout().getPointerSizeInBits()); + llvm::SmallVector<ASTContext::BitInterval> Padding = + getContext().getPaddingIntervals(PointeeTy); for (const auto &Interval : Padding) ClearPadding(*this, Src, Interval); >From 86f84cedf7786c2a58ed3467ae3fd0c768deec27 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 12 Aug 2026 12:22:31 -0700 Subject: [PATCH 4/5] Change function to return array-ref to prevent copies... riskier for anything that wants to store these long-term, but I don't believe that to be the case anywhere --- clang/include/clang/AST/ASTContext.h | 6 ++++-- clang/lib/AST/ASTContext.cpp | 8 ++++---- clang/lib/CodeGen/CGBuiltin.cpp | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index be0d76dabb249..9e3c437f82c3a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1888,8 +1888,10 @@ class ASTContext : public RefCountedBase<ASTContext> { uint64_t Last; }; - // Calculate and get the 'padding intervals' inside of a type. - llvm::SmallVector<BitInterval> getPaddingIntervals(QualType Ty) const; + // Calculate and get the 'padding intervals' inside of a type. Note: calls to + // this potentially invalidate all ArrayRef objects, so effort must be made to + // copy the data if necessary. + llvm::ArrayRef<BitInterval> getPaddingIntervals(QualType Ty) const; private: mutable llvm::DenseMap<QualType, llvm::SmallVector<BitInterval>> diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 847b7a5d94a3c..7004a2ad52ad1 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -15988,7 +15988,7 @@ struct PaddingCalculator { }; } // namespace -llvm::SmallVector<ASTContext::BitInterval> +llvm::ArrayRef<ASTContext::BitInterval> ASTContext::getPaddingIntervals(QualType Ty) const { Ty = Ty.getCanonicalType(); auto cached = PaddingIntervalCache.find(Ty); @@ -15998,9 +15998,9 @@ ASTContext::getPaddingIntervals(QualType Ty) const { PaddingCalculator pc{*this}; pc.run(Ty); - auto result = pc.GetPaddingIntervals(); - auto [itr, res] = PaddingIntervalCache.insert_or_assign(Ty, result); + auto [itr, res] = + PaddingIntervalCache.insert_or_assign(Ty, pc.GetPaddingIntervals()); assert(res && "Failed to insert?"); - return result; + return itr->second; } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 92c711a4a7062..3f7b090315f5e 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -5210,7 +5210,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Address Src = EmitPointerWithAlignment(E->getArg(0)); auto PointeeTy = E->getArg(0)->getType()->getPointeeType(); - llvm::SmallVector<ASTContext::BitInterval> Padding = + llvm::ArrayRef<ASTContext::BitInterval> Padding = getContext().getPaddingIntervals(PointeeTy); for (const auto &Interval : Padding) ClearPadding(*this, Src, Interval); >From 1c7368eef9bdff26fa7e489b84f258f328f6c421 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Thu, 13 Aug 2026 07:07:57 -0700 Subject: [PATCH 5/5] Use canonical type, add a FIXME to consider more types. --- clang/lib/AST/ASTContext.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 7004a2ad52ad1..c8625e993110c 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -15747,7 +15747,7 @@ struct PaddingCalculator { TySizeInBits = Ctx.getTypeSize(Ty); - Stack.push_back(Data{0, Ty, true}); + Stack.push_back(Data{0, Ty.getCanonicalType(), true}); while (!Stack.empty()) { Data Current = Stack.back(); Stack.pop_back(); @@ -15801,6 +15801,13 @@ struct PaddingCalculator { // Therefore, it is better to explicitly list all the scalar types // containing padding bits that we know of, namely, _BitInt(N) and x87 long // double. + // + // FIXME: There are likely other scalar types we need to think about here, as + // brought up in review for #215823: + // - bool + // - enums(both with/without fixed underlying type) + // - nullptr_t + // - more? uint64_t getScalarOccupiedSizeInBits(QualType Ty) const { if (const auto *BIT = Ty->getAs<BitIntType>()) return BIT->getNumBits(); @@ -15829,7 +15836,7 @@ struct PaddingCalculator { if (D.Ty->isAtomicType()) { auto Unwrapped = D; - Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType(); + Unwrapped.Ty = D.Ty.getAtomicUnqualifiedType().getCanonicalType(); Stack.push_back(Unwrapped); return; } @@ -15858,9 +15865,9 @@ struct PaddingCalculator { auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); auto Offset = ElementSize.alignTo(ElementAlign); - Stack.push_back(Data{StartBitOffset + ArrIndex * Offset.getQuantity() * - Ctx.getCharWidth(), - ElementQualType, /*VisitVirtualBase*/ true}); + Stack.push_back(Data{ + StartBitOffset + ArrIndex * Offset.getQuantity() * Ctx.getCharWidth(), + ElementQualType.getCanonicalType(), /*VisitVirtualBase*/ true}); } } @@ -15893,9 +15900,10 @@ struct PaddingCalculator { auto BaseOffset = std::invoke(GetOffset, ASTLayout, BaseRecord).getQuantity(); - Stack.push_back(Data{StartBitOffset + BaseOffset * Ctx.getCharWidth(), - Base.getType(), /*VisitVirtualBase*/ - false}); + Stack.push_back( + Data{StartBitOffset + BaseOffset * Ctx.getCharWidth(), + Base.getType().getCanonicalType(), /*VisitVirtualBase*/ + false}); }; for (auto Base : CXXRecord->bases()) { @@ -15922,14 +15930,15 @@ struct PaddingCalculator { StartBitOffset + FieldOffset, StartBitOffset + FieldOffset + Field->getBitWidthValue()}); } else { - Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(), + Stack.push_back(Data{StartBitOffset + FieldOffset, + Field->getType().getCanonicalType(), /*VisitVirtualBase*/ true}); } } } void VisitComplex(const ComplexType *CT, uint64_t StartBitOffset) { - QualType ElementQualType = CT->getElementType(); + QualType ElementQualType = CT->getElementType().getCanonicalType(); auto ElementSize = Ctx.getTypeSizeInChars(ElementQualType); auto ElementAlign = Ctx.getTypeAlignInChars(ElementQualType); auto ImgOffset = ElementSize.alignTo(ElementAlign); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
