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
isNegative - Test whether the quantity is less than zero.
bool isZero() const
isZero - Test whether the quantity equals zero.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
CharUnits alignTo(const CharUnits &Align) const
alignTo - Returns the next integer (mod 2**64) that is greater than or equal to this quantity and is ...
static CharUnits Zero()
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