23#include "llvm/Support/MathExtras.h"
24#include "llvm/Support/raw_ostream.h"
30class PaddingChecker :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
32 const BugType PaddingBug{
this,
"Excessive Padding",
"Performance"};
33 mutable BugReporter *BR;
38 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
39 BugReporter &BRArg)
const {
46 const PaddingChecker *Checker;
47 explicit LocalVisitor(
const PaddingChecker *Checker) : Checker(Checker) {
48 ShouldVisitTemplateInstantiations =
true;
49 ShouldVisitImplicitCode =
true;
51 bool VisitRecordDecl(RecordDecl *RD)
override {
52 Checker->visitRecord(RD);
55 bool VisitVarDecl(VarDecl *VD)
override {
56 Checker->visitVariable(VD);
62 LocalVisitor visitor(
this);
63 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
70 void visitRecord(
const RecordDecl *RD, uint64_t PadMultiplier = 1)
const {
71 if (shouldSkipDecl(RD))
86 if (
auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
87 if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
88 return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
95 CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
100 SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
101 std::tie(OptimalPad, OptimalFieldsOrder) =
102 calculateOptimalPad(RD, ASTContext, RL);
104 CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
106 assert(!DiffPad.
isNegative() &&
"DiffPad should not be negative");
110 reportRecord(ASTContext, RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
115 void visitVariable(
const VarDecl *VD)
const {
117 if (ArrTy ==
nullptr)
120 if (
const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
121 Elts = CArrTy->getZExtSize();
129 visitRecord(RD, Elts);
132 bool shouldSkipDecl(
const RecordDecl *RD)
const {
140 if (!Location.isValid())
143 BR->getSourceManager().getFileCharacteristic(Location);
151 if (
auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
156 if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
158 if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
161 if (CXXRD->getNumVBases() != 0)
165 if (CXXRD->isDependentType())
172 auto IsTrickyField = [](
const FieldDecl *FD) ->
bool {
174 if (FD->isBitField())
178 QualType Ty = FD->getType();
195 if (llvm::any_of(RD->
fields(), IsTrickyField))
200 static CharUnits calculateBaselinePad(
const RecordDecl *RD,
201 const ASTContext &ASTContext,
202 const ASTRecordLayout &RL) {
203 CharUnits PaddingSum;
205 for (
const FieldDecl *FD : RD->
fields()) {
209 if (FD->isZeroSize(ASTContext))
218 PaddingSum += (FieldOffset - Offset);
219 Offset = FieldOffset + FieldSize;
221 PaddingSum += RL.
getSize() - Offset;
241 static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
242 calculateOptimalPad(
const RecordDecl *RD,
const ASTContext &ASTContext,
243 const ASTRecordLayout &RL) {
247 const FieldDecl *
Field;
248 bool operator<(
const FieldInfo &RHS)
const {
252 return std::make_tuple(Align, -Size,
253 Field ? -
static_cast<int>(
Field->getFieldIndex())
256 RHS.Align, -RHS.Size,
257 RHS.Field ? -
static_cast<int>(RHS.Field->getFieldIndex())
261 SmallVector<FieldInfo, 20> Fields;
262 auto GatherSizesAndAlignments = [](
const FieldDecl *FD) {
265 auto &Ctx = FD->getASTContext();
266 auto Info = Ctx.getTypeInfoInChars(FD->getType());
268 RetVal.Align = Info.Align;
269 assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
270 if (
auto Max = FD->getMaxAlignment())
271 RetVal.Align = std::max(Ctx.toCharUnitsFromBits(
Max), RetVal.Align);
275 std::back_inserter(Fields), GatherSizesAndAlignments);
283 SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
284 while (!Fields.empty()) {
285 unsigned TrailingZeros =
286 llvm::countr_zero((
unsigned long long)NewOffset.
getQuantity());
290 long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
298 auto Iter = llvm::upper_bound(Fields, InsertPoint);
299 if (Iter != Fields.begin()) {
302 NewOffset += Iter->Size;
303 OptimalFieldsOrder.push_back(Iter->Field);
309 CharUnits NextOffset = NewOffset.
alignTo(Fields[0].Align);
310 NewPad += NextOffset - NewOffset;
311 NewOffset = NextOffset;
316 NewPad += NewSize - NewOffset;
317 return {NewPad, std::move(OptimalFieldsOrder)};
321 const ASTContext &Ctx,
const RecordDecl *RD, CharUnits BaselinePad,
322 CharUnits OptimalPad,
323 const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder)
const {
324 SmallString<100> Buf;
325 llvm::raw_svector_ostream Os(Buf);
326 Os <<
"Excessive padding in '";
330 if (
auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
334 SourceLocation ILoc = TSD->getPointOfInstantiation();
336 Os <<
" instantiated here: "
340 Os <<
" (" << BaselinePad.
getQuantity() <<
" padding bytes, where "
342 <<
"Optimal fields order: ";
343 for (
const auto *FD : OptimalFieldsOrder)
344 Os << FD->getName() <<
", ";
345 Os <<
"consider reordering the fields or adding explicit padding "
348 PathDiagnosticLocation CELoc =
350 auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc);
351 Report->setDeclWithIssue(RD);
353 BR->emitReport(std::move(
Report));
364 Checker,
"AllowedPad",
"a non-negative value");
367bool ento::shouldRegisterPaddingChecker(
const CheckerManager &mgr) {
Defines the C++ template declaration subclasses.
const ASTRecordLayout & getASTRecordLayout(const RecordDecl *D) const
Get or compute information about the layout of the specified record (struct/union/class) D,...
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
CharUnits toCharUnitsFromBits(int64_t BitSize) const
Convert a size in bits to a size in characters.
CanQualType getCanonicalTagType(const TagDecl *TD) const
CharUnits getAlignment() const
getAlignment - Get the record alignment in characters.
CharUnits getSize() const
getSize - Get the record size in characters.
uint64_t getFieldOffset(unsigned FieldNo) const
getFieldOffset - Get the offset of the given field index, in bits.
int getCheckerIntegerOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
QualType getElementType() const
bool isNegative() const
Test whether the quantity is less than zero.
bool isZero() const
Test whether the quantity equals zero.
CharUnits alignTo(CharUnits Align) const
Returns the next integer (mod 2**64) that is greater than or equal to this quantity and is a multiple...
QuantityType getQuantity() const
Get the raw integer representation of this quantity.
static CharUnits fromQuantity(QuantityType Quantity)
Construct a CharUnits quantity from a raw integer type.
static CharUnits Zero()
Construct a CharUnits quantity of zero.
ASTContext & getASTContext() const LLVM_READONLY
bool isInvalidDecl() const
SourceLocation getLocation() const
field_iterator field_end() const
field_range fields() const
RecordDecl * getDefinition() const
Returns the RecordDecl that actually defines this struct/union/class.
field_iterator field_begin() const
std::string printToString(const SourceManager &SM) const
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
bool isIncompleteArrayType() const
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
bool isDependentType() const
Whether this type is a dependent type, meaning that its definition somehow depends on a template para...
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
void reportInvalidCheckerOptionValue(const CheckerFrontend *Checker, StringRef OptionName, StringRef ExpectedValueDesc) const
Emits an error through a DiagnosticsEngine about an invalid user supplied checker option value.
Simple checker classes that implement one frontend (i.e.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Top level wrappers for InstallAPI frontend operations.
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
__builtin_bitreverse32 uint32_t __builtin_riscv_aaddu_u32 __builtin_riscv_asubu_u32 __builtin_elementwise_add_sat __builtin_elementwise_sub_sat int32_t uint32_t uint32_t __builtin_riscv_mhacc_i32 __builtin_riscv_mhaccu_u32 __builtin_riscv_mhaccsu_i32 __builtin_riscv_mulq_i32 int64_t