-//=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//\r
-//\r
-// The LLVM Compiler Infrastructure\r
-//\r
-// This file is distributed under the University of Illinois Open Source\r
-// License. See LICENSE.TXT for details.\r
-//\r
-//===----------------------------------------------------------------------===//\r
-//\r
-// This file defines a checker that checks for padding that could be\r
-// removed by re-ordering members.\r
-//\r
-//===----------------------------------------------------------------------===//\r
-\r
-#include "ClangSACheckers.h"\r
-#include "clang/AST/CharUnits.h"\r
-#include "clang/AST/DeclTemplate.h"\r
-#include "clang/AST/RecordLayout.h"\r
-#include "clang/AST/RecursiveASTVisitor.h"\r
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"\r
-#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"\r
-#include "clang/StaticAnalyzer/Core/Checker.h"\r
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"\r
-#include "llvm/ADT/SmallString.h"\r
-#include "llvm/Support/MathExtras.h"\r
-#include "llvm/Support/raw_ostream.h"\r
-#include <numeric>\r
-\r
-using namespace clang;\r
-using namespace ento;\r
-\r
-namespace {\r
-class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {\r
-private:\r
- mutable std::unique_ptr<BugType> PaddingBug;\r
- mutable int64_t AllowedPad;\r
- mutable BugReporter *BR;\r
-\r
-public:\r
- void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,\r
- BugReporter &BRArg) const {\r
- BR = &BRArg;\r
- AllowedPad =\r
- MGR.getAnalyzerOptions().getOptionAsInteger("AllowedPad", 24, this);\r
- assert(AllowedPad >= 0 && "AllowedPad option should be non-negative");\r
-\r
- // The calls to checkAST* from AnalysisConsumer don't\r
- // visit template instantiations or lambda classes. We\r
- // want to visit those, so we make our own RecursiveASTVisitor.\r
- struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {\r
- const PaddingChecker *Checker;\r
- bool shouldVisitTemplateInstantiations() const { return true; }\r
- bool shouldVisitImplicitCode() const { return true; }\r
- explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}\r
- bool VisitRecordDecl(const RecordDecl *RD) {\r
- Checker->visitRecord(RD);\r
- return true;\r
- }\r
- bool VisitVarDecl(const VarDecl *VD) {\r
- Checker->visitVariable(VD);\r
- return true;\r
- }\r
- // TODO: Visit array new and mallocs for arrays.\r
- };\r
-\r
- LocalVisitor visitor(this);\r
- visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));\r
- }\r
-\r
- /// \brief Look for records of overly padded types. If padding *\r
- /// PadMultiplier exceeds AllowedPad, then generate a report.\r
- /// PadMultiplier is used to share code with the array padding\r
- /// checker.\r
- void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {\r
- if (shouldSkipDecl(RD))\r
- return;\r
-\r
- auto &ASTContext = RD->getASTContext();\r
- const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);\r
- assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));\r
-\r
- CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);\r
- if (BaselinePad.isZero())\r
- return;\r
-\r
- CharUnits OptimalPad;\r
- SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;\r
- std::tie(OptimalPad, OptimalFieldsOrder) =\r
- calculateOptimalPad(RD, ASTContext, RL);\r
-\r
- CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);\r
- if (DiffPad.getQuantity() <= AllowedPad) {\r
- assert(!DiffPad.isNegative() && "DiffPad should not be negative");\r
- // There is not enough excess padding to trigger a warning.\r
- return;\r
- }\r
- reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);\r
- }\r
-\r
- /// \brief Look for arrays of overly padded types. If the padding of the\r
- /// array type exceeds AllowedPad, then generate a report.\r
- void visitVariable(const VarDecl *VD) const {\r
- const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();\r
- if (ArrTy == nullptr)\r
- return;\r
- uint64_t Elts = 0;\r
- if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))\r
- Elts = CArrTy->getSize().getZExtValue();\r
- if (Elts == 0)\r
- return;\r
- const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();\r
- if (RT == nullptr)\r
- return;\r
-\r
- // TODO: Recurse into the fields and base classes to see if any\r
- // of those have excess padding.\r
- visitRecord(RT->getDecl(), Elts);\r
- }\r
-\r
- bool shouldSkipDecl(const RecordDecl *RD) const {\r
- auto Location = RD->getLocation();\r
- // If the construct doesn't have a source file, then it's not something\r
- // we want to diagnose.\r
- if (!Location.isValid())\r
- return true;\r
- SrcMgr::CharacteristicKind Kind =\r
- BR->getSourceManager().getFileCharacteristic(Location);\r
- // Throw out all records that come from system headers.\r
- if (Kind != SrcMgr::C_User)\r
- return true;\r
-\r
- // Not going to attempt to optimize unions.\r
- if (RD->isUnion())\r
- return true;\r
- // How do you reorder fields if you haven't got any?\r
- if (RD->field_empty())\r
- return true;\r
- if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {\r
- // Tail padding with base classes ends up being very complicated.\r
- // We will skip objects with base classes for now.\r
- if (CXXRD->getNumBases() != 0)\r
- return true;\r
- // Virtual bases are complicated, skipping those for now.\r
- if (CXXRD->getNumVBases() != 0)\r
- return true;\r
- // Can't layout a template, so skip it. We do still layout the\r
- // instantiations though.\r
- if (CXXRD->getTypeForDecl()->isDependentType())\r
- return true;\r
- if (CXXRD->getTypeForDecl()->isInstantiationDependentType())\r
- return true;\r
- }\r
- auto IsTrickyField = [](const FieldDecl *FD) -> bool {\r
- // Bitfield layout is hard.\r
- if (FD->isBitField())\r
- return true;\r
-\r
- // Variable length arrays are tricky too.\r
- QualType Ty = FD->getType();\r
- if (Ty->isIncompleteArrayType())\r
- return true;\r
- return false;\r
- };\r
-\r
- if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))\r
- return true;\r
- return false;\r
- }\r
-\r
- static CharUnits calculateBaselinePad(const RecordDecl *RD,\r
- const ASTContext &ASTContext,\r
- const ASTRecordLayout &RL) {\r
- CharUnits PaddingSum;\r
- CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));\r
- for (const FieldDecl *FD : RD->fields()) {\r
- // This checker only cares about the padded size of the\r
- // field, and not the data size. If the field is a record\r
- // with tail padding, then we won't put that number in our\r
- // total because reordering fields won't fix that problem.\r
- CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());\r
- auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());\r
- CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);\r
- PaddingSum += (FieldOffset - Offset);\r
- Offset = FieldOffset + FieldSize;\r
- }\r
- PaddingSum += RL.getSize() - Offset;\r
- return PaddingSum;\r
- }\r
-\r
- /// Optimal padding overview:\r
- /// 1. Find a close approximation to where we can place our first field.\r
- /// This will usually be at offset 0.\r
- /// 2. Try to find the best field that can legally be placed at the current\r
- /// offset.\r
- /// a. "Best" is the largest alignment that is legal, but smallest size.\r
- /// This is to account for overly aligned types.\r
- /// 3. If no fields can fit, pad by rounding the current offset up to the\r
- /// smallest alignment requirement of our fields. Measure and track the\r
- // amount of padding added. Go back to 2.\r
- /// 4. Increment the current offset by the size of the chosen field.\r
- /// 5. Remove the chosen field from the set of future possibilities.\r
- /// 6. Go back to 2 if there are still unplaced fields.\r
- /// 7. Add tail padding by rounding the current offset up to the structure\r
- /// alignment. Track the amount of padding added.\r
-\r
- static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>\r
- calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,\r
- const ASTRecordLayout &RL) {\r
- struct FieldInfo {\r
- CharUnits Align;\r
- CharUnits Size;\r
- const FieldDecl *Field;\r
- bool operator<(const FieldInfo &RHS) const {\r
- // Order from small alignments to large alignments,\r
- // then large sizes to small sizes.\r
- // then large field indices to small field indices\r
- return std::make_tuple(Align, -Size,\r
- Field ? -static_cast<int>(Field->getFieldIndex())\r
- : 0) <\r
- std::make_tuple(\r
- RHS.Align, -RHS.Size,\r
- RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())\r
- : 0);\r
- }\r
- };\r
- SmallVector<FieldInfo, 20> Fields;\r
- auto GatherSizesAndAlignments = [](const FieldDecl *FD) {\r
- FieldInfo RetVal;\r
- RetVal.Field = FD;\r
- auto &Ctx = FD->getASTContext();\r
- std::tie(RetVal.Size, RetVal.Align) =\r
- Ctx.getTypeInfoInChars(FD->getType());\r
- assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));\r
- if (auto Max = FD->getMaxAlignment())\r
- RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);\r
- return RetVal;\r
- };\r
- std::transform(RD->field_begin(), RD->field_end(),\r
- std::back_inserter(Fields), GatherSizesAndAlignments);\r
- llvm::sort(Fields.begin(), Fields.end());\r
- // This lets us skip over vptrs and non-virtual bases,\r
- // so that we can just worry about the fields in our object.\r
- // Note that this does cause us to miss some cases where we\r
- // could pack more bytes in to a base class's tail padding.\r
- CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));\r
- CharUnits NewPad;\r
- SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;\r
- while (!Fields.empty()) {\r
- unsigned TrailingZeros =\r
- llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());\r
- // If NewOffset is zero, then countTrailingZeros will be 64. Shifting\r
- // 64 will overflow our unsigned long long. Shifting 63 will turn\r
- // our long long (and CharUnits internal type) negative. So shift 62.\r
- long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);\r
- CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);\r
- FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};\r
- auto CurBegin = Fields.begin();\r
- auto CurEnd = Fields.end();\r
-\r
- // In the typical case, this will find the last element\r
- // of the vector. We won't find a middle element unless\r
- // we started on a poorly aligned address or have an overly\r
- // aligned field.\r
- auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint);\r
- if (Iter != CurBegin) {\r
- // We found a field that we can layout with the current alignment.\r
- --Iter;\r
- NewOffset += Iter->Size;\r
- OptimalFieldsOrder.push_back(Iter->Field);\r
- Fields.erase(Iter);\r
- } else {\r
- // We are poorly aligned, and we need to pad in order to layout another\r
- // field. Round up to at least the smallest field alignment that we\r
- // currently have.\r
- CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);\r
- NewPad += NextOffset - NewOffset;\r
- NewOffset = NextOffset;\r
- }\r
- }\r
- // Calculate tail padding.\r
- CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());\r
- NewPad += NewSize - NewOffset;\r
- return {NewPad, std::move(OptimalFieldsOrder)};\r
- }\r
-\r
- void reportRecord(\r
- const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,\r
- const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {\r
- if (!PaddingBug)\r
- PaddingBug =\r
- llvm::make_unique<BugType>(this, "Excessive Padding", "Performance");\r
-\r
- SmallString<100> Buf;\r
- llvm::raw_svector_ostream Os(Buf);\r
- Os << "Excessive padding in '";\r
- Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),\r
- LangOptions())\r
- << "'";\r
-\r
- if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {\r
- // TODO: make this show up better in the console output and in\r
- // the HTML. Maybe just make it show up in HTML like the path\r
- // diagnostics show.\r
- SourceLocation ILoc = TSD->getPointOfInstantiation();\r
- if (ILoc.isValid())\r
- Os << " instantiated here: "\r
- << ILoc.printToString(BR->getSourceManager());\r
- }\r
-\r
- Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "\r
- << OptimalPad.getQuantity() << " is optimal). \n"\r
- << "Optimal fields order: \n";\r
- for (const auto *FD : OptimalFieldsOrder)\r
- Os << FD->getName() << ", \n";\r
- Os << "consider reordering the fields or adding explicit padding "\r
- "members.";\r
-\r
- PathDiagnosticLocation CELoc =\r
- PathDiagnosticLocation::create(RD, BR->getSourceManager());\r
- auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);\r
- Report->setDeclWithIssue(RD);\r
- Report->addRange(RD->getSourceRange());\r
- BR->emitReport(std::move(Report));\r
- }\r
-};\r
-}\r
-\r
-void ento::registerPaddingChecker(CheckerManager &Mgr) {\r
- Mgr.registerChecker<PaddingChecker>();\r
-}\r
+//=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that checks for padding that could be
+// removed by re-ordering members.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <numeric>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ mutable std::unique_ptr<BugType> PaddingBug;
+ mutable int64_t AllowedPad;
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+ AllowedPad =
+ MGR.getAnalyzerOptions().getOptionAsInteger("AllowedPad", 24, this);
+ assert(AllowedPad >= 0 && "AllowedPad option should be non-negative");
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const PaddingChecker *Checker;
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return true; }
+ explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}
+ bool VisitRecordDecl(const RecordDecl *RD) {
+ Checker->visitRecord(RD);
+ return true;
+ }
+ bool VisitVarDecl(const VarDecl *VD) {
+ Checker->visitVariable(VD);
+ return true;
+ }
+ // TODO: Visit array new and mallocs for arrays.
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ /// \brief Look for records of overly padded types. If padding *
+ /// PadMultiplier exceeds AllowedPad, then generate a report.
+ /// PadMultiplier is used to share code with the array padding
+ /// checker.
+ void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ auto &ASTContext = RD->getASTContext();
+ const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
+ assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
+
+ CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
+ if (BaselinePad.isZero())
+ return;
+
+ CharUnits OptimalPad;
+ SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
+ std::tie(OptimalPad, OptimalFieldsOrder) =
+ calculateOptimalPad(RD, ASTContext, RL);
+
+ CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
+ if (DiffPad.getQuantity() <= AllowedPad) {
+ assert(!DiffPad.isNegative() && "DiffPad should not be negative");
+ // There is not enough excess padding to trigger a warning.
+ return;
+ }
+ reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
+ }
+
+ /// \brief Look for arrays of overly padded types. If the padding of the
+ /// array type exceeds AllowedPad, then generate a report.
+ void visitVariable(const VarDecl *VD) const {
+ const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
+ if (ArrTy == nullptr)
+ return;
+ uint64_t Elts = 0;
+ if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
+ Elts = CArrTy->getSize().getZExtValue();
+ if (Elts == 0)
+ return;
+ const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();
+ if (RT == nullptr)
+ return;
+
+ // TODO: Recurse into the fields and base classes to see if any
+ // of those have excess padding.
+ visitRecord(RT->getDecl(), Elts);
+ }
+
+ bool shouldSkipDecl(const RecordDecl *RD) const {
+ auto Location = RD->getLocation();
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ if (!Location.isValid())
+ return true;
+ SrcMgr::CharacteristicKind Kind =
+ BR->getSourceManager().getFileCharacteristic(Location);
+ // Throw out all records that come from system headers.
+ if (Kind != SrcMgr::C_User)
+ return true;
+
+ // Not going to attempt to optimize unions.
+ if (RD->isUnion())
+ return true;
+ // How do you reorder fields if you haven't got any?
+ if (RD->field_empty())
+ return true;
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ // Tail padding with base classes ends up being very complicated.
+ // We will skip objects with base classes for now.
+ if (CXXRD->getNumBases() != 0)
+ return true;
+ // Virtual bases are complicated, skipping those for now.
+ if (CXXRD->getNumVBases() != 0)
+ return true;
+ // Can't layout a template, so skip it. We do still layout the
+ // instantiations though.
+ if (CXXRD->getTypeForDecl()->isDependentType())
+ return true;
+ if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
+ return true;
+ }
+ auto IsTrickyField = [](const FieldDecl *FD) -> bool {
+ // Bitfield layout is hard.
+ if (FD->isBitField())
+ return true;
+
+ // Variable length arrays are tricky too.
+ QualType Ty = FD->getType();
+ if (Ty->isIncompleteArrayType())
+ return true;
+ return false;
+ };
+
+ if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
+ return true;
+ return false;
+ }
+
+ static CharUnits calculateBaselinePad(const RecordDecl *RD,
+ const ASTContext &ASTContext,
+ const ASTRecordLayout &RL) {
+ CharUnits PaddingSum;
+ CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
+ for (const FieldDecl *FD : RD->fields()) {
+ // This checker only cares about the padded size of the
+ // field, and not the data size. If the field is a record
+ // with tail padding, then we won't put that number in our
+ // total because reordering fields won't fix that problem.
+ CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());
+ auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());
+ CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);
+ PaddingSum += (FieldOffset - Offset);
+ Offset = FieldOffset + FieldSize;
+ }
+ PaddingSum += RL.getSize() - Offset;
+ return PaddingSum;
+ }
+
+ /// Optimal padding overview:
+ /// 1. Find a close approximation to where we can place our first field.
+ /// This will usually be at offset 0.
+ /// 2. Try to find the best field that can legally be placed at the current
+ /// offset.
+ /// a. "Best" is the largest alignment that is legal, but smallest size.
+ /// This is to account for overly aligned types.
+ /// 3. If no fields can fit, pad by rounding the current offset up to the
+ /// smallest alignment requirement of our fields. Measure and track the
+ // amount of padding added. Go back to 2.
+ /// 4. Increment the current offset by the size of the chosen field.
+ /// 5. Remove the chosen field from the set of future possibilities.
+ /// 6. Go back to 2 if there are still unplaced fields.
+ /// 7. Add tail padding by rounding the current offset up to the structure
+ /// alignment. Track the amount of padding added.
+
+ static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
+ calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,
+ const ASTRecordLayout &RL) {
+ struct FieldInfo {
+ CharUnits Align;
+ CharUnits Size;
+ const FieldDecl *Field;
+ bool operator<(const FieldInfo &RHS) const {
+ // Order from small alignments to large alignments,
+ // then large sizes to small sizes.
+ // then large field indices to small field indices
+ return std::make_tuple(Align, -Size,
+ Field ? -static_cast<int>(Field->getFieldIndex())
+ : 0) <
+ std::make_tuple(
+ RHS.Align, -RHS.Size,
+ RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
+ : 0);
+ }
+ };
+ SmallVector<FieldInfo, 20> Fields;
+ auto GatherSizesAndAlignments = [](const FieldDecl *FD) {
+ FieldInfo RetVal;
+ RetVal.Field = FD;
+ auto &Ctx = FD->getASTContext();
+ std::tie(RetVal.Size, RetVal.Align) =
+ Ctx.getTypeInfoInChars(FD->getType());
+ assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
+ if (auto Max = FD->getMaxAlignment())
+ RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
+ return RetVal;
+ };
+ std::transform(RD->field_begin(), RD->field_end(),
+ std::back_inserter(Fields), GatherSizesAndAlignments);
+ llvm::sort(Fields.begin(), Fields.end());
+ // This lets us skip over vptrs and non-virtual bases,
+ // so that we can just worry about the fields in our object.
+ // Note that this does cause us to miss some cases where we
+ // could pack more bytes in to a base class's tail padding.
+ CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
+ CharUnits NewPad;
+ SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
+ while (!Fields.empty()) {
+ unsigned TrailingZeros =
+ llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());
+ // If NewOffset is zero, then countTrailingZeros will be 64. Shifting
+ // 64 will overflow our unsigned long long. Shifting 63 will turn
+ // our long long (and CharUnits internal type) negative. So shift 62.
+ long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
+ CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);
+ FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};
+ auto CurBegin = Fields.begin();
+ auto CurEnd = Fields.end();
+
+ // In the typical case, this will find the last element
+ // of the vector. We won't find a middle element unless
+ // we started on a poorly aligned address or have an overly
+ // aligned field.
+ auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint);
+ if (Iter != CurBegin) {
+ // We found a field that we can layout with the current alignment.
+ --Iter;
+ NewOffset += Iter->Size;
+ OptimalFieldsOrder.push_back(Iter->Field);
+ Fields.erase(Iter);
+ } else {
+ // We are poorly aligned, and we need to pad in order to layout another
+ // field. Round up to at least the smallest field alignment that we
+ // currently have.
+ CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);
+ NewPad += NextOffset - NewOffset;
+ NewOffset = NextOffset;
+ }
+ }
+ // Calculate tail padding.
+ CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());
+ NewPad += NewSize - NewOffset;
+ return {NewPad, std::move(OptimalFieldsOrder)};
+ }
+
+ void reportRecord(
+ const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
+ const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
+ if (!PaddingBug)
+ PaddingBug =
+ llvm::make_unique<BugType>(this, "Excessive Padding", "Performance");
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+ Os << "Excessive padding in '";
+ Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),
+ LangOptions())
+ << "'";
+
+ if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ // TODO: make this show up better in the console output and in
+ // the HTML. Maybe just make it show up in HTML like the path
+ // diagnostics show.
+ SourceLocation ILoc = TSD->getPointOfInstantiation();
+ if (ILoc.isValid())
+ Os << " instantiated here: "
+ << ILoc.printToString(BR->getSourceManager());
+ }
+
+ Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
+ << OptimalPad.getQuantity() << " is optimal). \n"
+ << "Optimal fields order: \n";
+ for (const auto *FD : OptimalFieldsOrder)
+ Os << FD->getName() << ", \n";
+ Os << "consider reordering the fields or adding explicit padding "
+ "members.";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::create(RD, BR->getSourceManager());
+ auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);
+ Report->setDeclWithIssue(RD);
+ Report->addRange(RD->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+}
+
+void ento::registerPaddingChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<PaddingChecker>();
+}