10b57cec5SDimitry Andric //=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file defines a checker that checks for padding that could be
100b57cec5SDimitry Andric //  removed by re-ordering members.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
150b57cec5SDimitry Andric #include "clang/AST/CharUnits.h"
160b57cec5SDimitry Andric #include "clang/AST/DeclTemplate.h"
170b57cec5SDimitry Andric #include "clang/AST/RecordLayout.h"
180b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
190b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
240b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
250b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
260b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
270b57cec5SDimitry Andric #include <numeric>
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric using namespace clang;
300b57cec5SDimitry Andric using namespace ento;
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric namespace {
330b57cec5SDimitry Andric class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
340b57cec5SDimitry Andric private:
35647cbc5dSDimitry Andric   const BugType PaddingBug{this, "Excessive Padding", "Performance"};
360b57cec5SDimitry Andric   mutable BugReporter *BR;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric public:
390b57cec5SDimitry Andric   int64_t AllowedPad;
400b57cec5SDimitry Andric 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const410b57cec5SDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
420b57cec5SDimitry Andric                     BugReporter &BRArg) const {
430b57cec5SDimitry Andric     BR = &BRArg;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
460b57cec5SDimitry Andric     // visit template instantiations or lambda classes. We
470b57cec5SDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
480b57cec5SDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
490b57cec5SDimitry Andric       const PaddingChecker *Checker;
500b57cec5SDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
510b57cec5SDimitry Andric       bool shouldVisitImplicitCode() const { return true; }
520b57cec5SDimitry Andric       explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}
530b57cec5SDimitry Andric       bool VisitRecordDecl(const RecordDecl *RD) {
540b57cec5SDimitry Andric         Checker->visitRecord(RD);
550b57cec5SDimitry Andric         return true;
560b57cec5SDimitry Andric       }
570b57cec5SDimitry Andric       bool VisitVarDecl(const VarDecl *VD) {
580b57cec5SDimitry Andric         Checker->visitVariable(VD);
590b57cec5SDimitry Andric         return true;
600b57cec5SDimitry Andric       }
610b57cec5SDimitry Andric       // TODO: Visit array new and mallocs for arrays.
620b57cec5SDimitry Andric     };
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric     LocalVisitor visitor(this);
650b57cec5SDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
660b57cec5SDimitry Andric   }
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   /// Look for records of overly padded types. If padding *
690b57cec5SDimitry Andric   /// PadMultiplier exceeds AllowedPad, then generate a report.
700b57cec5SDimitry Andric   /// PadMultiplier is used to share code with the array padding
710b57cec5SDimitry Andric   /// checker.
visitRecord(const RecordDecl * RD,uint64_t PadMultiplier=1) const720b57cec5SDimitry Andric   void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {
730b57cec5SDimitry Andric     if (shouldSkipDecl(RD))
740b57cec5SDimitry Andric       return;
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric     // TODO: Figure out why we are going through declarations and not only
770b57cec5SDimitry Andric     // definitions.
780b57cec5SDimitry Andric     if (!(RD = RD->getDefinition()))
790b57cec5SDimitry Andric       return;
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric     // This is the simplest correct case: a class with no fields and one base
820b57cec5SDimitry Andric     // class. Other cases are more complicated because of how the base classes
830b57cec5SDimitry Andric     // & fields might interact, so we don't bother dealing with them.
840b57cec5SDimitry Andric     // TODO: Support other combinations of base classes and fields.
850b57cec5SDimitry Andric     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
860b57cec5SDimitry Andric       if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
870b57cec5SDimitry Andric         return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
880b57cec5SDimitry Andric                            PadMultiplier);
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric     auto &ASTContext = RD->getASTContext();
910b57cec5SDimitry Andric     const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
920b57cec5SDimitry Andric     assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric     CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
950b57cec5SDimitry Andric     if (BaselinePad.isZero())
960b57cec5SDimitry Andric       return;
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric     CharUnits OptimalPad;
990b57cec5SDimitry Andric     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
1000b57cec5SDimitry Andric     std::tie(OptimalPad, OptimalFieldsOrder) =
1010b57cec5SDimitry Andric         calculateOptimalPad(RD, ASTContext, RL);
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric     CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
1040b57cec5SDimitry Andric     if (DiffPad.getQuantity() <= AllowedPad) {
1050b57cec5SDimitry Andric       assert(!DiffPad.isNegative() && "DiffPad should not be negative");
1060b57cec5SDimitry Andric       // There is not enough excess padding to trigger a warning.
1070b57cec5SDimitry Andric       return;
1080b57cec5SDimitry Andric     }
1090b57cec5SDimitry Andric     reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
1100b57cec5SDimitry Andric   }
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric   /// Look for arrays of overly padded types. If the padding of the
1130b57cec5SDimitry Andric   /// array type exceeds AllowedPad, then generate a report.
visitVariable(const VarDecl * VD) const1140b57cec5SDimitry Andric   void visitVariable(const VarDecl *VD) const {
1150b57cec5SDimitry Andric     const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
1160b57cec5SDimitry Andric     if (ArrTy == nullptr)
1170b57cec5SDimitry Andric       return;
1180b57cec5SDimitry Andric     uint64_t Elts = 0;
1190b57cec5SDimitry Andric     if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
1200b57cec5SDimitry Andric       Elts = CArrTy->getSize().getZExtValue();
1210b57cec5SDimitry Andric     if (Elts == 0)
1220b57cec5SDimitry Andric       return;
1230b57cec5SDimitry Andric     const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();
1240b57cec5SDimitry Andric     if (RT == nullptr)
1250b57cec5SDimitry Andric       return;
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric     // TODO: Recurse into the fields to see if they have excess padding.
1280b57cec5SDimitry Andric     visitRecord(RT->getDecl(), Elts);
1290b57cec5SDimitry Andric   }
1300b57cec5SDimitry Andric 
shouldSkipDecl(const RecordDecl * RD) const1310b57cec5SDimitry Andric   bool shouldSkipDecl(const RecordDecl *RD) const {
1320b57cec5SDimitry Andric     // TODO: Figure out why we are going through declarations and not only
1330b57cec5SDimitry Andric     // definitions.
1340b57cec5SDimitry Andric     if (!(RD = RD->getDefinition()))
1350b57cec5SDimitry Andric       return true;
1360b57cec5SDimitry Andric     auto Location = RD->getLocation();
1370b57cec5SDimitry Andric     // If the construct doesn't have a source file, then it's not something
1380b57cec5SDimitry Andric     // we want to diagnose.
1390b57cec5SDimitry Andric     if (!Location.isValid())
1400b57cec5SDimitry Andric       return true;
1410b57cec5SDimitry Andric     SrcMgr::CharacteristicKind Kind =
1420b57cec5SDimitry Andric         BR->getSourceManager().getFileCharacteristic(Location);
1430b57cec5SDimitry Andric     // Throw out all records that come from system headers.
1440b57cec5SDimitry Andric     if (Kind != SrcMgr::C_User)
1450b57cec5SDimitry Andric       return true;
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric     // Not going to attempt to optimize unions.
1480b57cec5SDimitry Andric     if (RD->isUnion())
1490b57cec5SDimitry Andric       return true;
1500b57cec5SDimitry Andric     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
1510b57cec5SDimitry Andric       // Tail padding with base classes ends up being very complicated.
1520b57cec5SDimitry Andric       // We will skip objects with base classes for now, unless they do not
1530b57cec5SDimitry Andric       // have fields.
1540b57cec5SDimitry Andric       // TODO: Handle more base class scenarios.
1550b57cec5SDimitry Andric       if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
1560b57cec5SDimitry Andric         return true;
1570b57cec5SDimitry Andric       if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
1580b57cec5SDimitry Andric         return true;
1590b57cec5SDimitry Andric       // Virtual bases are complicated, skipping those for now.
1600b57cec5SDimitry Andric       if (CXXRD->getNumVBases() != 0)
1610b57cec5SDimitry Andric         return true;
1620b57cec5SDimitry Andric       // Can't layout a template, so skip it. We do still layout the
1630b57cec5SDimitry Andric       // instantiations though.
1640b57cec5SDimitry Andric       if (CXXRD->getTypeForDecl()->isDependentType())
1650b57cec5SDimitry Andric         return true;
1660b57cec5SDimitry Andric       if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
1670b57cec5SDimitry Andric         return true;
1680b57cec5SDimitry Andric     }
1690b57cec5SDimitry Andric     // How do you reorder fields if you haven't got any?
1700b57cec5SDimitry Andric     else if (RD->field_empty())
1710b57cec5SDimitry Andric       return true;
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric     auto IsTrickyField = [](const FieldDecl *FD) -> bool {
1740b57cec5SDimitry Andric       // Bitfield layout is hard.
1750b57cec5SDimitry Andric       if (FD->isBitField())
1760b57cec5SDimitry Andric         return true;
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric       // Variable length arrays are tricky too.
1790b57cec5SDimitry Andric       QualType Ty = FD->getType();
1800b57cec5SDimitry Andric       if (Ty->isIncompleteArrayType())
1810b57cec5SDimitry Andric         return true;
1820b57cec5SDimitry Andric       return false;
1830b57cec5SDimitry Andric     };
1840b57cec5SDimitry Andric 
185972a253aSDimitry Andric     if (llvm::any_of(RD->fields(), IsTrickyField))
1860b57cec5SDimitry Andric       return true;
1870b57cec5SDimitry Andric     return false;
1880b57cec5SDimitry Andric   }
1890b57cec5SDimitry Andric 
calculateBaselinePad(const RecordDecl * RD,const ASTContext & ASTContext,const ASTRecordLayout & RL)1900b57cec5SDimitry Andric   static CharUnits calculateBaselinePad(const RecordDecl *RD,
1910b57cec5SDimitry Andric                                         const ASTContext &ASTContext,
1920b57cec5SDimitry Andric                                         const ASTRecordLayout &RL) {
1930b57cec5SDimitry Andric     CharUnits PaddingSum;
1940b57cec5SDimitry Andric     CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
1950b57cec5SDimitry Andric     for (const FieldDecl *FD : RD->fields()) {
196fe6060f1SDimitry Andric       // Skip field that is a subobject of zero size, marked with
197fe6060f1SDimitry Andric       // [[no_unique_address]] or an empty bitfield, because its address can be
198fe6060f1SDimitry Andric       // set the same as the other fields addresses.
199fe6060f1SDimitry Andric       if (FD->isZeroSize(ASTContext))
200fe6060f1SDimitry Andric         continue;
2010b57cec5SDimitry Andric       // This checker only cares about the padded size of the
2020b57cec5SDimitry Andric       // field, and not the data size. If the field is a record
2030b57cec5SDimitry Andric       // with tail padding, then we won't put that number in our
2040b57cec5SDimitry Andric       // total because reordering fields won't fix that problem.
2050b57cec5SDimitry Andric       CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());
2060b57cec5SDimitry Andric       auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());
2070b57cec5SDimitry Andric       CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);
2080b57cec5SDimitry Andric       PaddingSum += (FieldOffset - Offset);
2090b57cec5SDimitry Andric       Offset = FieldOffset + FieldSize;
2100b57cec5SDimitry Andric     }
2110b57cec5SDimitry Andric     PaddingSum += RL.getSize() - Offset;
2120b57cec5SDimitry Andric     return PaddingSum;
2130b57cec5SDimitry Andric   }
2140b57cec5SDimitry Andric 
2150b57cec5SDimitry Andric   /// Optimal padding overview:
2160b57cec5SDimitry Andric   /// 1.  Find a close approximation to where we can place our first field.
2170b57cec5SDimitry Andric   ///     This will usually be at offset 0.
2180b57cec5SDimitry Andric   /// 2.  Try to find the best field that can legally be placed at the current
2190b57cec5SDimitry Andric   ///     offset.
2200b57cec5SDimitry Andric   ///   a.  "Best" is the largest alignment that is legal, but smallest size.
2210b57cec5SDimitry Andric   ///       This is to account for overly aligned types.
2220b57cec5SDimitry Andric   /// 3.  If no fields can fit, pad by rounding the current offset up to the
2230b57cec5SDimitry Andric   ///     smallest alignment requirement of our fields. Measure and track the
2240b57cec5SDimitry Andric   //      amount of padding added. Go back to 2.
2250b57cec5SDimitry Andric   /// 4.  Increment the current offset by the size of the chosen field.
2260b57cec5SDimitry Andric   /// 5.  Remove the chosen field from the set of future possibilities.
2270b57cec5SDimitry Andric   /// 6.  Go back to 2 if there are still unplaced fields.
2280b57cec5SDimitry Andric   /// 7.  Add tail padding by rounding the current offset up to the structure
2290b57cec5SDimitry Andric   ///     alignment. Track the amount of padding added.
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
calculateOptimalPad(const RecordDecl * RD,const ASTContext & ASTContext,const ASTRecordLayout & RL)2320b57cec5SDimitry Andric   calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,
2330b57cec5SDimitry Andric                       const ASTRecordLayout &RL) {
2340b57cec5SDimitry Andric     struct FieldInfo {
2350b57cec5SDimitry Andric       CharUnits Align;
2360b57cec5SDimitry Andric       CharUnits Size;
2370b57cec5SDimitry Andric       const FieldDecl *Field;
2380b57cec5SDimitry Andric       bool operator<(const FieldInfo &RHS) const {
2390b57cec5SDimitry Andric         // Order from small alignments to large alignments,
2400b57cec5SDimitry Andric         // then large sizes to small sizes.
2410b57cec5SDimitry Andric         // then large field indices to small field indices
2420b57cec5SDimitry Andric         return std::make_tuple(Align, -Size,
2430b57cec5SDimitry Andric                                Field ? -static_cast<int>(Field->getFieldIndex())
2440b57cec5SDimitry Andric                                      : 0) <
2450b57cec5SDimitry Andric                std::make_tuple(
2460b57cec5SDimitry Andric                    RHS.Align, -RHS.Size,
2470b57cec5SDimitry Andric                    RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
2480b57cec5SDimitry Andric                              : 0);
2490b57cec5SDimitry Andric       }
2500b57cec5SDimitry Andric     };
2510b57cec5SDimitry Andric     SmallVector<FieldInfo, 20> Fields;
2520b57cec5SDimitry Andric     auto GatherSizesAndAlignments = [](const FieldDecl *FD) {
2530b57cec5SDimitry Andric       FieldInfo RetVal;
2540b57cec5SDimitry Andric       RetVal.Field = FD;
2550b57cec5SDimitry Andric       auto &Ctx = FD->getASTContext();
256e8d8bef9SDimitry Andric       auto Info = Ctx.getTypeInfoInChars(FD->getType());
257fe6060f1SDimitry Andric       RetVal.Size = FD->isZeroSize(Ctx) ? CharUnits::Zero() : Info.Width;
258e8d8bef9SDimitry Andric       RetVal.Align = Info.Align;
2590b57cec5SDimitry Andric       assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
2600b57cec5SDimitry Andric       if (auto Max = FD->getMaxAlignment())
2610b57cec5SDimitry Andric         RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
2620b57cec5SDimitry Andric       return RetVal;
2630b57cec5SDimitry Andric     };
2640b57cec5SDimitry Andric     std::transform(RD->field_begin(), RD->field_end(),
2650b57cec5SDimitry Andric                    std::back_inserter(Fields), GatherSizesAndAlignments);
2660b57cec5SDimitry Andric     llvm::sort(Fields);
2670b57cec5SDimitry Andric     // This lets us skip over vptrs and non-virtual bases,
2680b57cec5SDimitry Andric     // so that we can just worry about the fields in our object.
2690b57cec5SDimitry Andric     // Note that this does cause us to miss some cases where we
2700b57cec5SDimitry Andric     // could pack more bytes in to a base class's tail padding.
2710b57cec5SDimitry Andric     CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
2720b57cec5SDimitry Andric     CharUnits NewPad;
2730b57cec5SDimitry Andric     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
2740b57cec5SDimitry Andric     while (!Fields.empty()) {
2750b57cec5SDimitry Andric       unsigned TrailingZeros =
27606c3fb27SDimitry Andric           llvm::countr_zero((unsigned long long)NewOffset.getQuantity());
2770b57cec5SDimitry Andric       // If NewOffset is zero, then countTrailingZeros will be 64. Shifting
2780b57cec5SDimitry Andric       // 64 will overflow our unsigned long long. Shifting 63 will turn
2790b57cec5SDimitry Andric       // our long long (and CharUnits internal type) negative. So shift 62.
2800b57cec5SDimitry Andric       long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
2810b57cec5SDimitry Andric       CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);
2820b57cec5SDimitry Andric       FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric       // In the typical case, this will find the last element
2850b57cec5SDimitry Andric       // of the vector. We won't find a middle element unless
2860b57cec5SDimitry Andric       // we started on a poorly aligned address or have an overly
2870b57cec5SDimitry Andric       // aligned field.
2880b57cec5SDimitry Andric       auto Iter = llvm::upper_bound(Fields, InsertPoint);
2890b57cec5SDimitry Andric       if (Iter != Fields.begin()) {
2900b57cec5SDimitry Andric         // We found a field that we can layout with the current alignment.
2910b57cec5SDimitry Andric         --Iter;
2920b57cec5SDimitry Andric         NewOffset += Iter->Size;
2930b57cec5SDimitry Andric         OptimalFieldsOrder.push_back(Iter->Field);
2940b57cec5SDimitry Andric         Fields.erase(Iter);
2950b57cec5SDimitry Andric       } else {
2960b57cec5SDimitry Andric         // We are poorly aligned, and we need to pad in order to layout another
2970b57cec5SDimitry Andric         // field. Round up to at least the smallest field alignment that we
2980b57cec5SDimitry Andric         // currently have.
2990b57cec5SDimitry Andric         CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);
3000b57cec5SDimitry Andric         NewPad += NextOffset - NewOffset;
3010b57cec5SDimitry Andric         NewOffset = NextOffset;
3020b57cec5SDimitry Andric       }
3030b57cec5SDimitry Andric     }
3040b57cec5SDimitry Andric     // Calculate tail padding.
3050b57cec5SDimitry Andric     CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());
3060b57cec5SDimitry Andric     NewPad += NewSize - NewOffset;
3070b57cec5SDimitry Andric     return {NewPad, std::move(OptimalFieldsOrder)};
3080b57cec5SDimitry Andric   }
3090b57cec5SDimitry Andric 
reportRecord(const RecordDecl * RD,CharUnits BaselinePad,CharUnits OptimalPad,const SmallVector<const FieldDecl *,20> & OptimalFieldsOrder) const3100b57cec5SDimitry Andric   void reportRecord(
3110b57cec5SDimitry Andric       const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
3120b57cec5SDimitry Andric       const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
3130b57cec5SDimitry Andric     SmallString<100> Buf;
3140b57cec5SDimitry Andric     llvm::raw_svector_ostream Os(Buf);
3150b57cec5SDimitry Andric     Os << "Excessive padding in '";
3160b57cec5SDimitry Andric     Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),
3170b57cec5SDimitry Andric                                 LangOptions())
3180b57cec5SDimitry Andric        << "'";
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric     if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
3210b57cec5SDimitry Andric       // TODO: make this show up better in the console output and in
3220b57cec5SDimitry Andric       // the HTML. Maybe just make it show up in HTML like the path
3230b57cec5SDimitry Andric       // diagnostics show.
3240b57cec5SDimitry Andric       SourceLocation ILoc = TSD->getPointOfInstantiation();
3250b57cec5SDimitry Andric       if (ILoc.isValid())
3260b57cec5SDimitry Andric         Os << " instantiated here: "
3270b57cec5SDimitry Andric            << ILoc.printToString(BR->getSourceManager());
3280b57cec5SDimitry Andric     }
3290b57cec5SDimitry Andric 
3300b57cec5SDimitry Andric     Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
33181ad6265SDimitry Andric        << OptimalPad.getQuantity() << " is optimal). "
33281ad6265SDimitry Andric        << "Optimal fields order: ";
3330b57cec5SDimitry Andric     for (const auto *FD : OptimalFieldsOrder)
33481ad6265SDimitry Andric       Os << FD->getName() << ", ";
3350b57cec5SDimitry Andric     Os << "consider reordering the fields or adding explicit padding "
3360b57cec5SDimitry Andric           "members.";
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric     PathDiagnosticLocation CELoc =
3390b57cec5SDimitry Andric         PathDiagnosticLocation::create(RD, BR->getSourceManager());
340647cbc5dSDimitry Andric     auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc);
3410b57cec5SDimitry Andric     Report->setDeclWithIssue(RD);
3420b57cec5SDimitry Andric     Report->addRange(RD->getSourceRange());
3430b57cec5SDimitry Andric     BR->emitReport(std::move(Report));
3440b57cec5SDimitry Andric   }
3450b57cec5SDimitry Andric };
3460b57cec5SDimitry Andric } // namespace
3470b57cec5SDimitry Andric 
registerPaddingChecker(CheckerManager & Mgr)3480b57cec5SDimitry Andric void ento::registerPaddingChecker(CheckerManager &Mgr) {
3490b57cec5SDimitry Andric   auto *Checker = Mgr.registerChecker<PaddingChecker>();
3500b57cec5SDimitry Andric   Checker->AllowedPad = Mgr.getAnalyzerOptions()
3510b57cec5SDimitry Andric           .getCheckerIntegerOption(Checker, "AllowedPad");
3520b57cec5SDimitry Andric   if (Checker->AllowedPad < 0)
3530b57cec5SDimitry Andric     Mgr.reportInvalidCheckerOptionValue(
3540b57cec5SDimitry Andric         Checker, "AllowedPad", "a non-negative value");
3550b57cec5SDimitry Andric }
3560b57cec5SDimitry Andric 
shouldRegisterPaddingChecker(const CheckerManager & mgr)3575ffd83dbSDimitry Andric bool ento::shouldRegisterPaddingChecker(const CheckerManager &mgr) {
3580b57cec5SDimitry Andric   return true;
3590b57cec5SDimitry Andric }
360