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