24#include "llvm/ADT/SmallString.h"
25#include "llvm/Support/MathExtras.h"
26#include "llvm/Support/raw_ostream.h"
33class PaddingChecker :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
35 const BugType PaddingBug{
this,
"Excessive Padding",
"Performance"};
51 ShouldVisitTemplateInstantiations =
true;
52 ShouldVisitImplicitCode =
true;
54 bool VisitRecordDecl(
RecordDecl *RD)
override {
58 bool VisitVarDecl(
VarDecl *VD)
override {
65 LocalVisitor visitor(
this);
73 void visitRecord(
const RecordDecl *RD, uint64_t PadMultiplier = 1)
const {
74 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(),
101 std::tie(OptimalPad, OptimalFieldsOrder) =
104 CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
106 assert(!DiffPad.
isNegative() &&
"DiffPad should not be negative");
110 reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
115 void visitVariable(
const VarDecl *VD)
const {
117 if (ArrTy ==
nullptr)
121 Elts = CArrTy->getZExtSize();
129 visitRecord(RT->
getDecl(), Elts);
132 bool shouldSkipDecl(
const RecordDecl *RD)
const {
140 if (!Location.isValid())
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->getTypeForDecl()->isDependentType())
167 if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
174 auto IsTrickyField = [](
const FieldDecl *FD) ->
bool {
176 if (FD->isBitField())
186 if (llvm::any_of(RD->
fields(), IsTrickyField))
209 PaddingSum += (FieldOffset - Offset);
210 Offset = FieldOffset + FieldSize;
212 PaddingSum += RL.
getSize() - Offset;
232 static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
239 bool operator<(
const FieldInfo &RHS)
const {
243 return std::make_tuple(Align, -Size,
244 Field ? -
static_cast<int>(
Field->getFieldIndex())
247 RHS.Align, -RHS.Size,
248 RHS.Field ? -
static_cast<int>(RHS.Field->getFieldIndex())
253 auto GatherSizesAndAlignments = [](
const FieldDecl *FD) {
256 auto &Ctx = FD->getASTContext();
257 auto Info = Ctx.getTypeInfoInChars(FD->getType());
259 RetVal.Align = Info.Align;
260 assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
261 if (
auto Max = FD->getMaxAlignment())
262 RetVal.Align = std::max(Ctx.toCharUnitsFromBits(
Max), RetVal.Align);
266 std::back_inserter(Fields), GatherSizesAndAlignments);
275 while (!Fields.empty()) {
276 unsigned TrailingZeros =
277 llvm::countr_zero((
unsigned long long)NewOffset.
getQuantity());
281 long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
289 auto Iter = llvm::upper_bound(Fields, InsertPoint);
290 if (
Iter != Fields.begin()) {
293 NewOffset +=
Iter->Size;
294 OptimalFieldsOrder.push_back(
Iter->Field);
301 NewPad += NextOffset - NewOffset;
302 NewOffset = NextOffset;
307 NewPad += NewSize - NewOffset;
308 return {NewPad, std::move(OptimalFieldsOrder)};
315 llvm::raw_svector_ostream Os(Buf);
316 Os <<
"Excessive padding in '";
321 if (
auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
327 Os <<
" instantiated here: "
331 Os <<
" (" << BaselinePad.
getQuantity() <<
" padding bytes, where "
333 <<
"Optimal fields order: ";
334 for (
const auto *FD : OptimalFieldsOrder)
335 Os << FD->getName() <<
", ";
336 Os <<
"consider reordering the fields or adding explicit padding "
341 auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc);
342 Report->setDeclWithIssue(RD);
355 Checker,
"AllowedPad",
"a non-negative value");
358bool ento::shouldRegisterPaddingChecker(
const CheckerManager &mgr) {
Defines the C++ template declaration subclasses.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
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.
ASTRecordLayout - This class contains layout information for one RecordDecl, which is a struct/union/...
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 an integer value.
Represents an array type, per C99 6.7.5.2 - Array Declarators.
QualType getElementType() const
CharUnits - This is an opaque type for sizes expressed in character units.
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.
Represents the canonical version of C arrays with a specified constant size.
ASTContext & getASTContext() const LLVM_READONLY
SourceLocation getLocation() const
Recursive AST visitor that supports extension via dynamic dispatch.
Represents a member of a struct/union/class.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
A (possibly-)qualified type.
std::string getAsString() const
The collection of all-type qualifiers we support.
Represents a struct/union/class.
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
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
RecordDecl * getDecl() const
Encodes a location in the source.
std::string printToString(const SourceManager &SM) const
bool isValid() const
Return true if this is a valid SourceLocation object.
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const
Return the file characteristic of the specified source location, indicating whether this is a normal ...
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
The top declaration context.
const Type * getTypeForDecl() const
bool isIncompleteArrayType() const
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type.
const T * getAs() const
Member-template getAs<specific type>'.
Represents a variable declaration or definition.
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) const
Emits an error through a DiagnosticsEngine about an invalid user supplied checker option value.
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...
The JSON file list parser is used to communicate input to InstallAPI.
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.