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 410b57cec5SDimitry 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. 720b57cec5SDimitry 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. 1140b57cec5SDimitry 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)) 120*0fca6ea1SDimitry Andric Elts = CArrTy->getZExtSize(); 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 1310b57cec5SDimitry 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 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>> 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 3100b57cec5SDimitry 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 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 3575ffd83dbSDimitry Andric bool ento::shouldRegisterPaddingChecker(const CheckerManager &mgr) { 3580b57cec5SDimitry Andric return true; 3590b57cec5SDimitry Andric } 360