24 #include "llvm/ADT/SmallString.h" 25 #include "llvm/Support/MathExtras.h" 26 #include "llvm/Support/raw_ostream.h" 29 using namespace clang;
33 class PaddingChecker :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
35 mutable std::unique_ptr<BugType> PaddingBug;
36 mutable int64_t AllowedPad;
45 assert(AllowedPad >= 0 &&
"AllowedPad option should be non-negative");
52 bool shouldVisitTemplateInstantiations()
const {
return true; }
53 bool shouldVisitImplicitCode()
const {
return true; }
54 explicit LocalVisitor(
const PaddingChecker *Checker) : Checker(Checker) {}
56 Checker->visitRecord(RD);
59 bool VisitVarDecl(
const VarDecl *VD) {
60 Checker->visitVariable(VD);
66 LocalVisitor visitor(
this);
67 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
74 void visitRecord(
const RecordDecl *RD, uint64_t PadMultiplier = 1)
const {
75 if (shouldSkipDecl(RD))
88 std::tie(OptimalPad, OptimalFieldsOrder) =
91 CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
93 assert(!DiffPad.
isNegative() &&
"DiffPad should not be negative");
97 reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
102 void visitVariable(
const VarDecl *VD)
const {
104 if (ArrTy ==
nullptr)
108 Elts = CArrTy->getSize().getZExtValue();
117 visitRecord(RT->
getDecl(), Elts);
120 bool shouldSkipDecl(
const RecordDecl *RD)
const {
124 if (!Location.isValid())
138 if (
auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
141 if (CXXRD->getNumBases() != 0)
144 if (CXXRD->getNumVBases() != 0)
148 if (CXXRD->getTypeForDecl()->isDependentType())
150 if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
153 auto IsTrickyField = [](
const FieldDecl *FD) ->
bool {
155 if (FD->isBitField())
183 PaddingSum += (FieldOffset -
Offset);
184 Offset = FieldOffset + FieldSize;
206 static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
207 calculateOptimalPad(
const RecordDecl *RD,
const ASTContext &ASTContext,
213 bool operator<(
const FieldInfo &RHS)
const {
217 return std::make_tuple(Align, -Size,
221 RHS.Align, -RHS.Size,
222 RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
227 auto GatherSizesAndAlignments = [](
const FieldDecl *FD) {
230 auto &Ctx = FD->getASTContext();
231 std::tie(RetVal.Size, RetVal.Align) =
232 Ctx.getTypeInfoInChars(FD->getType());
233 assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
234 if (
auto Max = FD->getMaxAlignment())
235 RetVal.Align =
std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
239 std::back_inserter(Fields), GatherSizesAndAlignments);
240 llvm::sort(Fields.begin(), Fields.end());
248 while (!Fields.empty()) {
249 unsigned TrailingZeros =
250 llvm::countTrailingZeros((
unsigned long long)NewOffset.getQuantity());
254 long long CurAlignmentBits = 1ull << (
std::min)(TrailingZeros, 62u);
257 auto CurBegin = Fields.begin();
258 auto CurEnd = Fields.end();
264 auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint);
265 if (Iter != CurBegin) {
268 NewOffset += Iter->Size;
269 OptimalFieldsOrder.push_back(Iter->Field);
276 NewPad += NextOffset - NewOffset;
277 NewOffset = NextOffset;
282 NewPad += NewSize - NewOffset;
283 return {NewPad, std::move(OptimalFieldsOrder)};
291 llvm::make_unique<BugType>(
this,
"Excessive Padding",
"Performance");
294 llvm::raw_svector_ostream Os(Buf);
295 Os <<
"Excessive padding in '";
300 if (
auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
306 Os <<
" instantiated here: " 310 Os <<
" (" << BaselinePad.
getQuantity() <<
" padding bytes, where " 312 <<
"Optimal fields order: \n";
313 for (
const auto *FD : OptimalFieldsOrder)
314 Os << FD->getName() <<
", \n";
315 Os <<
"consider reordering the fields or adding explicit padding " 320 auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);
321 Report->setDeclWithIssue(RD);
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 ...
A (possibly-)qualified type.
const ASTRecordLayout & getASTRecordLayout(const RecordDecl *D) const
Get or compute information about the layout of the specified record (struct/union/class) D...
const Type * getTypeForDecl() const
Defines the C++ template declaration subclasses.
unsigned getFieldIndex() const
Returns the index of this field within its record, as appropriate for passing to ASTRecordLayout::get...
Represents an array type, per C99 6.7.5.2 - Array Declarators.
bool isZero() const
isZero - Test whether the quantity equals zero.
CharUnits getAlignment() const
getAlignment - Get the record alignment in characters.
QualType getElementType() const
Represents a variable declaration or definition.
const T * getAs() const
Member-template getAs<specific type>'.
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
The collection of all-type qualifiers we support.
Represents a struct/union/class.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
field_range fields() const
Represents a member of a struct/union/class.
static CharUnits Zero()
Zero - Construct a CharUnits quantity of zero.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CharUnits - This is an opaque type for sizes expressed in character units.
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type...
field_iterator field_begin() const
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool isNegative() const
isNegative - Test whether the quantity is less than zero.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
ASTRecordLayout - This class contains layout information for one RecordDecl, which is a struct/union/...
AnalyzerOptions & getAnalyzerOptions() override
field_iterator field_end() const
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const
Return the file characteristic of the specified source location, indicating whether this is a normal ...
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
std::string getAsString() const
CHECKER * registerChecker(AT... Args)
Used to register checkers.
RecordDecl * getDecl() const
BugReporter is a utility class for generating PathDiagnostics for analysis.
uint64_t getFieldOffset(unsigned FieldNo) const
getFieldOffset - Get the offset of the given field index, in bits.
Encodes a location in the source.
ASTContext & getASTContext() const LLVM_READONLY
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
Dataflow Directional Tag Classes.
bool isValid() const
Return true if this is a valid SourceLocation object.
CharUnits getSize() const
getSize - Get the record size in characters.
bool isIncompleteArrayType() const
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
CharUnits toCharUnitsFromBits(int64_t BitSize) const
Convert a size in bits to a size in characters.
SourceManager & getSourceManager()
std::string printToString(const SourceManager &SM) const
__DEVICE__ int max(int __a, int __b)
The top declaration context.
__DEVICE__ int min(int __a, int __b)
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
int getOptionAsInteger(StringRef Name, int DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option's string value as an integer value.
Represents the canonical version of C arrays with a specified constant size.
SourceLocation getLocation() const