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();
184 if (llvm::any_of(RD->
fields(), IsTrickyField))
189 static CharUnits calculateBaselinePad(
const RecordDecl *RD,
190 const ASTContext &ASTContext,
191 const ASTRecordLayout &RL) {
192 CharUnits PaddingSum;
194 for (
const FieldDecl *FD : RD->
fields()) {
198 if (FD->isZeroSize(ASTContext))
207 PaddingSum += (FieldOffset - Offset);
208 Offset = FieldOffset + FieldSize;
210 PaddingSum += RL.
getSize() - Offset;
230 static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
231 calculateOptimalPad(
const RecordDecl *RD,
const ASTContext &ASTContext,
232 const ASTRecordLayout &RL) {
236 const FieldDecl *
Field;
237 bool operator<(
const FieldInfo &RHS)
const {
241 return std::make_tuple(Align, -Size,
242 Field ? -
static_cast<int>(
Field->getFieldIndex())
245 RHS.Align, -RHS.Size,
246 RHS.Field ? -
static_cast<int>(RHS.Field->getFieldIndex())
250 SmallVector<FieldInfo, 20> Fields;
251 auto GatherSizesAndAlignments = [](
const FieldDecl *FD) {
254 auto &Ctx = FD->getASTContext();
255 auto Info = Ctx.getTypeInfoInChars(FD->getType());
257 RetVal.Align = Info.Align;
258 assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
259 if (
auto Max = FD->getMaxAlignment())
260 RetVal.Align = std::max(Ctx.toCharUnitsFromBits(
Max), RetVal.Align);
264 std::back_inserter(Fields), GatherSizesAndAlignments);
272 SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
273 while (!Fields.empty()) {
274 unsigned TrailingZeros =
275 llvm::countr_zero((
unsigned long long)NewOffset.
getQuantity());
279 long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
287 auto Iter = llvm::upper_bound(Fields, InsertPoint);
288 if (Iter != Fields.begin()) {
291 NewOffset += Iter->Size;
292 OptimalFieldsOrder.push_back(Iter->Field);
298 CharUnits NextOffset = NewOffset.
alignTo(Fields[0].Align);
299 NewPad += NextOffset - NewOffset;
300 NewOffset = NextOffset;
305 NewPad += NewSize - NewOffset;
306 return {NewPad, std::move(OptimalFieldsOrder)};
310 const ASTContext &Ctx,
const RecordDecl *RD, CharUnits BaselinePad,
311 CharUnits OptimalPad,
312 const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder)
const {
313 SmallString<100> Buf;
314 llvm::raw_svector_ostream Os(Buf);
315 Os <<
"Excessive padding in '";
319 if (
auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
323 SourceLocation ILoc = TSD->getPointOfInstantiation();
325 Os <<
" instantiated here: "
329 Os <<
" (" << BaselinePad.
getQuantity() <<
" padding bytes, where "
331 <<
"Optimal fields order: ";
332 for (
const auto *FD : OptimalFieldsOrder)
333 Os << FD->getName() <<
", ";
334 Os <<
"consider reordering the fields or adding explicit padding "
337 PathDiagnosticLocation CELoc =
339 auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc);
340 Report->setDeclWithIssue(RD);
342 BR->emitReport(std::move(
Report));
353 Checker,
"AllowedPad",
"a non-negative value");
356bool 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.
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