1*0b57cec5SDimitry Andric //=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file defines a checker that checks for padding that could be 10*0b57cec5SDimitry Andric // removed by re-ordering members. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15*0b57cec5SDimitry Andric #include "clang/AST/CharUnits.h" 16*0b57cec5SDimitry Andric #include "clang/AST/DeclTemplate.h" 17*0b57cec5SDimitry Andric #include "clang/AST/RecordLayout.h" 18*0b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 19*0b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 20*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 21*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 22*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 23*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 24*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 25*0b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 26*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 27*0b57cec5SDimitry Andric #include <numeric> 28*0b57cec5SDimitry Andric 29*0b57cec5SDimitry Andric using namespace clang; 30*0b57cec5SDimitry Andric using namespace ento; 31*0b57cec5SDimitry Andric 32*0b57cec5SDimitry Andric namespace { 33*0b57cec5SDimitry Andric class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> { 34*0b57cec5SDimitry Andric private: 35*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> PaddingBug; 36*0b57cec5SDimitry Andric mutable BugReporter *BR; 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric public: 39*0b57cec5SDimitry Andric int64_t AllowedPad; 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 42*0b57cec5SDimitry Andric BugReporter &BRArg) const { 43*0b57cec5SDimitry Andric BR = &BRArg; 44*0b57cec5SDimitry Andric 45*0b57cec5SDimitry Andric // The calls to checkAST* from AnalysisConsumer don't 46*0b57cec5SDimitry Andric // visit template instantiations or lambda classes. We 47*0b57cec5SDimitry Andric // want to visit those, so we make our own RecursiveASTVisitor. 48*0b57cec5SDimitry Andric struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 49*0b57cec5SDimitry Andric const PaddingChecker *Checker; 50*0b57cec5SDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 51*0b57cec5SDimitry Andric bool shouldVisitImplicitCode() const { return true; } 52*0b57cec5SDimitry Andric explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {} 53*0b57cec5SDimitry Andric bool VisitRecordDecl(const RecordDecl *RD) { 54*0b57cec5SDimitry Andric Checker->visitRecord(RD); 55*0b57cec5SDimitry Andric return true; 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric bool VisitVarDecl(const VarDecl *VD) { 58*0b57cec5SDimitry Andric Checker->visitVariable(VD); 59*0b57cec5SDimitry Andric return true; 60*0b57cec5SDimitry Andric } 61*0b57cec5SDimitry Andric // TODO: Visit array new and mallocs for arrays. 62*0b57cec5SDimitry Andric }; 63*0b57cec5SDimitry Andric 64*0b57cec5SDimitry Andric LocalVisitor visitor(this); 65*0b57cec5SDimitry Andric visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 66*0b57cec5SDimitry Andric } 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric /// Look for records of overly padded types. If padding * 69*0b57cec5SDimitry Andric /// PadMultiplier exceeds AllowedPad, then generate a report. 70*0b57cec5SDimitry Andric /// PadMultiplier is used to share code with the array padding 71*0b57cec5SDimitry Andric /// checker. 72*0b57cec5SDimitry Andric void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const { 73*0b57cec5SDimitry Andric if (shouldSkipDecl(RD)) 74*0b57cec5SDimitry Andric return; 75*0b57cec5SDimitry Andric 76*0b57cec5SDimitry Andric // TODO: Figure out why we are going through declarations and not only 77*0b57cec5SDimitry Andric // definitions. 78*0b57cec5SDimitry Andric if (!(RD = RD->getDefinition())) 79*0b57cec5SDimitry Andric return; 80*0b57cec5SDimitry Andric 81*0b57cec5SDimitry Andric // This is the simplest correct case: a class with no fields and one base 82*0b57cec5SDimitry Andric // class. Other cases are more complicated because of how the base classes 83*0b57cec5SDimitry Andric // & fields might interact, so we don't bother dealing with them. 84*0b57cec5SDimitry Andric // TODO: Support other combinations of base classes and fields. 85*0b57cec5SDimitry Andric if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) 86*0b57cec5SDimitry Andric if (CXXRD->field_empty() && CXXRD->getNumBases() == 1) 87*0b57cec5SDimitry Andric return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(), 88*0b57cec5SDimitry Andric PadMultiplier); 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric auto &ASTContext = RD->getASTContext(); 91*0b57cec5SDimitry Andric const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD); 92*0b57cec5SDimitry Andric assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity())); 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL); 95*0b57cec5SDimitry Andric if (BaselinePad.isZero()) 96*0b57cec5SDimitry Andric return; 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric CharUnits OptimalPad; 99*0b57cec5SDimitry Andric SmallVector<const FieldDecl *, 20> OptimalFieldsOrder; 100*0b57cec5SDimitry Andric std::tie(OptimalPad, OptimalFieldsOrder) = 101*0b57cec5SDimitry Andric calculateOptimalPad(RD, ASTContext, RL); 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad); 104*0b57cec5SDimitry Andric if (DiffPad.getQuantity() <= AllowedPad) { 105*0b57cec5SDimitry Andric assert(!DiffPad.isNegative() && "DiffPad should not be negative"); 106*0b57cec5SDimitry Andric // There is not enough excess padding to trigger a warning. 107*0b57cec5SDimitry Andric return; 108*0b57cec5SDimitry Andric } 109*0b57cec5SDimitry Andric reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder); 110*0b57cec5SDimitry Andric } 111*0b57cec5SDimitry Andric 112*0b57cec5SDimitry Andric /// Look for arrays of overly padded types. If the padding of the 113*0b57cec5SDimitry Andric /// array type exceeds AllowedPad, then generate a report. 114*0b57cec5SDimitry Andric void visitVariable(const VarDecl *VD) const { 115*0b57cec5SDimitry Andric const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe(); 116*0b57cec5SDimitry Andric if (ArrTy == nullptr) 117*0b57cec5SDimitry Andric return; 118*0b57cec5SDimitry Andric uint64_t Elts = 0; 119*0b57cec5SDimitry Andric if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy)) 120*0b57cec5SDimitry Andric Elts = CArrTy->getSize().getZExtValue(); 121*0b57cec5SDimitry Andric if (Elts == 0) 122*0b57cec5SDimitry Andric return; 123*0b57cec5SDimitry Andric const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>(); 124*0b57cec5SDimitry Andric if (RT == nullptr) 125*0b57cec5SDimitry Andric return; 126*0b57cec5SDimitry Andric 127*0b57cec5SDimitry Andric // TODO: Recurse into the fields to see if they have excess padding. 128*0b57cec5SDimitry Andric visitRecord(RT->getDecl(), Elts); 129*0b57cec5SDimitry Andric } 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric bool shouldSkipDecl(const RecordDecl *RD) const { 132*0b57cec5SDimitry Andric // TODO: Figure out why we are going through declarations and not only 133*0b57cec5SDimitry Andric // definitions. 134*0b57cec5SDimitry Andric if (!(RD = RD->getDefinition())) 135*0b57cec5SDimitry Andric return true; 136*0b57cec5SDimitry Andric auto Location = RD->getLocation(); 137*0b57cec5SDimitry Andric // If the construct doesn't have a source file, then it's not something 138*0b57cec5SDimitry Andric // we want to diagnose. 139*0b57cec5SDimitry Andric if (!Location.isValid()) 140*0b57cec5SDimitry Andric return true; 141*0b57cec5SDimitry Andric SrcMgr::CharacteristicKind Kind = 142*0b57cec5SDimitry Andric BR->getSourceManager().getFileCharacteristic(Location); 143*0b57cec5SDimitry Andric // Throw out all records that come from system headers. 144*0b57cec5SDimitry Andric if (Kind != SrcMgr::C_User) 145*0b57cec5SDimitry Andric return true; 146*0b57cec5SDimitry Andric 147*0b57cec5SDimitry Andric // Not going to attempt to optimize unions. 148*0b57cec5SDimitry Andric if (RD->isUnion()) 149*0b57cec5SDimitry Andric return true; 150*0b57cec5SDimitry Andric if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { 151*0b57cec5SDimitry Andric // Tail padding with base classes ends up being very complicated. 152*0b57cec5SDimitry Andric // We will skip objects with base classes for now, unless they do not 153*0b57cec5SDimitry Andric // have fields. 154*0b57cec5SDimitry Andric // TODO: Handle more base class scenarios. 155*0b57cec5SDimitry Andric if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0) 156*0b57cec5SDimitry Andric return true; 157*0b57cec5SDimitry Andric if (CXXRD->field_empty() && CXXRD->getNumBases() != 1) 158*0b57cec5SDimitry Andric return true; 159*0b57cec5SDimitry Andric // Virtual bases are complicated, skipping those for now. 160*0b57cec5SDimitry Andric if (CXXRD->getNumVBases() != 0) 161*0b57cec5SDimitry Andric return true; 162*0b57cec5SDimitry Andric // Can't layout a template, so skip it. We do still layout the 163*0b57cec5SDimitry Andric // instantiations though. 164*0b57cec5SDimitry Andric if (CXXRD->getTypeForDecl()->isDependentType()) 165*0b57cec5SDimitry Andric return true; 166*0b57cec5SDimitry Andric if (CXXRD->getTypeForDecl()->isInstantiationDependentType()) 167*0b57cec5SDimitry Andric return true; 168*0b57cec5SDimitry Andric } 169*0b57cec5SDimitry Andric // How do you reorder fields if you haven't got any? 170*0b57cec5SDimitry Andric else if (RD->field_empty()) 171*0b57cec5SDimitry Andric return true; 172*0b57cec5SDimitry Andric 173*0b57cec5SDimitry Andric auto IsTrickyField = [](const FieldDecl *FD) -> bool { 174*0b57cec5SDimitry Andric // Bitfield layout is hard. 175*0b57cec5SDimitry Andric if (FD->isBitField()) 176*0b57cec5SDimitry Andric return true; 177*0b57cec5SDimitry Andric 178*0b57cec5SDimitry Andric // Variable length arrays are tricky too. 179*0b57cec5SDimitry Andric QualType Ty = FD->getType(); 180*0b57cec5SDimitry Andric if (Ty->isIncompleteArrayType()) 181*0b57cec5SDimitry Andric return true; 182*0b57cec5SDimitry Andric return false; 183*0b57cec5SDimitry Andric }; 184*0b57cec5SDimitry Andric 185*0b57cec5SDimitry Andric if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField)) 186*0b57cec5SDimitry Andric return true; 187*0b57cec5SDimitry Andric return false; 188*0b57cec5SDimitry Andric } 189*0b57cec5SDimitry Andric 190*0b57cec5SDimitry Andric static CharUnits calculateBaselinePad(const RecordDecl *RD, 191*0b57cec5SDimitry Andric const ASTContext &ASTContext, 192*0b57cec5SDimitry Andric const ASTRecordLayout &RL) { 193*0b57cec5SDimitry Andric CharUnits PaddingSum; 194*0b57cec5SDimitry Andric CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); 195*0b57cec5SDimitry Andric for (const FieldDecl *FD : RD->fields()) { 196*0b57cec5SDimitry Andric // This checker only cares about the padded size of the 197*0b57cec5SDimitry Andric // field, and not the data size. If the field is a record 198*0b57cec5SDimitry Andric // with tail padding, then we won't put that number in our 199*0b57cec5SDimitry Andric // total because reordering fields won't fix that problem. 200*0b57cec5SDimitry Andric CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType()); 201*0b57cec5SDimitry Andric auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex()); 202*0b57cec5SDimitry Andric CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits); 203*0b57cec5SDimitry Andric PaddingSum += (FieldOffset - Offset); 204*0b57cec5SDimitry Andric Offset = FieldOffset + FieldSize; 205*0b57cec5SDimitry Andric } 206*0b57cec5SDimitry Andric PaddingSum += RL.getSize() - Offset; 207*0b57cec5SDimitry Andric return PaddingSum; 208*0b57cec5SDimitry Andric } 209*0b57cec5SDimitry Andric 210*0b57cec5SDimitry Andric /// Optimal padding overview: 211*0b57cec5SDimitry Andric /// 1. Find a close approximation to where we can place our first field. 212*0b57cec5SDimitry Andric /// This will usually be at offset 0. 213*0b57cec5SDimitry Andric /// 2. Try to find the best field that can legally be placed at the current 214*0b57cec5SDimitry Andric /// offset. 215*0b57cec5SDimitry Andric /// a. "Best" is the largest alignment that is legal, but smallest size. 216*0b57cec5SDimitry Andric /// This is to account for overly aligned types. 217*0b57cec5SDimitry Andric /// 3. If no fields can fit, pad by rounding the current offset up to the 218*0b57cec5SDimitry Andric /// smallest alignment requirement of our fields. Measure and track the 219*0b57cec5SDimitry Andric // amount of padding added. Go back to 2. 220*0b57cec5SDimitry Andric /// 4. Increment the current offset by the size of the chosen field. 221*0b57cec5SDimitry Andric /// 5. Remove the chosen field from the set of future possibilities. 222*0b57cec5SDimitry Andric /// 6. Go back to 2 if there are still unplaced fields. 223*0b57cec5SDimitry Andric /// 7. Add tail padding by rounding the current offset up to the structure 224*0b57cec5SDimitry Andric /// alignment. Track the amount of padding added. 225*0b57cec5SDimitry Andric 226*0b57cec5SDimitry Andric static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>> 227*0b57cec5SDimitry Andric calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext, 228*0b57cec5SDimitry Andric const ASTRecordLayout &RL) { 229*0b57cec5SDimitry Andric struct FieldInfo { 230*0b57cec5SDimitry Andric CharUnits Align; 231*0b57cec5SDimitry Andric CharUnits Size; 232*0b57cec5SDimitry Andric const FieldDecl *Field; 233*0b57cec5SDimitry Andric bool operator<(const FieldInfo &RHS) const { 234*0b57cec5SDimitry Andric // Order from small alignments to large alignments, 235*0b57cec5SDimitry Andric // then large sizes to small sizes. 236*0b57cec5SDimitry Andric // then large field indices to small field indices 237*0b57cec5SDimitry Andric return std::make_tuple(Align, -Size, 238*0b57cec5SDimitry Andric Field ? -static_cast<int>(Field->getFieldIndex()) 239*0b57cec5SDimitry Andric : 0) < 240*0b57cec5SDimitry Andric std::make_tuple( 241*0b57cec5SDimitry Andric RHS.Align, -RHS.Size, 242*0b57cec5SDimitry Andric RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex()) 243*0b57cec5SDimitry Andric : 0); 244*0b57cec5SDimitry Andric } 245*0b57cec5SDimitry Andric }; 246*0b57cec5SDimitry Andric SmallVector<FieldInfo, 20> Fields; 247*0b57cec5SDimitry Andric auto GatherSizesAndAlignments = [](const FieldDecl *FD) { 248*0b57cec5SDimitry Andric FieldInfo RetVal; 249*0b57cec5SDimitry Andric RetVal.Field = FD; 250*0b57cec5SDimitry Andric auto &Ctx = FD->getASTContext(); 251*0b57cec5SDimitry Andric std::tie(RetVal.Size, RetVal.Align) = 252*0b57cec5SDimitry Andric Ctx.getTypeInfoInChars(FD->getType()); 253*0b57cec5SDimitry Andric assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity())); 254*0b57cec5SDimitry Andric if (auto Max = FD->getMaxAlignment()) 255*0b57cec5SDimitry Andric RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align); 256*0b57cec5SDimitry Andric return RetVal; 257*0b57cec5SDimitry Andric }; 258*0b57cec5SDimitry Andric std::transform(RD->field_begin(), RD->field_end(), 259*0b57cec5SDimitry Andric std::back_inserter(Fields), GatherSizesAndAlignments); 260*0b57cec5SDimitry Andric llvm::sort(Fields); 261*0b57cec5SDimitry Andric // This lets us skip over vptrs and non-virtual bases, 262*0b57cec5SDimitry Andric // so that we can just worry about the fields in our object. 263*0b57cec5SDimitry Andric // Note that this does cause us to miss some cases where we 264*0b57cec5SDimitry Andric // could pack more bytes in to a base class's tail padding. 265*0b57cec5SDimitry Andric CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); 266*0b57cec5SDimitry Andric CharUnits NewPad; 267*0b57cec5SDimitry Andric SmallVector<const FieldDecl *, 20> OptimalFieldsOrder; 268*0b57cec5SDimitry Andric while (!Fields.empty()) { 269*0b57cec5SDimitry Andric unsigned TrailingZeros = 270*0b57cec5SDimitry Andric llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity()); 271*0b57cec5SDimitry Andric // If NewOffset is zero, then countTrailingZeros will be 64. Shifting 272*0b57cec5SDimitry Andric // 64 will overflow our unsigned long long. Shifting 63 will turn 273*0b57cec5SDimitry Andric // our long long (and CharUnits internal type) negative. So shift 62. 274*0b57cec5SDimitry Andric long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u); 275*0b57cec5SDimitry Andric CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits); 276*0b57cec5SDimitry Andric FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr}; 277*0b57cec5SDimitry Andric 278*0b57cec5SDimitry Andric // In the typical case, this will find the last element 279*0b57cec5SDimitry Andric // of the vector. We won't find a middle element unless 280*0b57cec5SDimitry Andric // we started on a poorly aligned address or have an overly 281*0b57cec5SDimitry Andric // aligned field. 282*0b57cec5SDimitry Andric auto Iter = llvm::upper_bound(Fields, InsertPoint); 283*0b57cec5SDimitry Andric if (Iter != Fields.begin()) { 284*0b57cec5SDimitry Andric // We found a field that we can layout with the current alignment. 285*0b57cec5SDimitry Andric --Iter; 286*0b57cec5SDimitry Andric NewOffset += Iter->Size; 287*0b57cec5SDimitry Andric OptimalFieldsOrder.push_back(Iter->Field); 288*0b57cec5SDimitry Andric Fields.erase(Iter); 289*0b57cec5SDimitry Andric } else { 290*0b57cec5SDimitry Andric // We are poorly aligned, and we need to pad in order to layout another 291*0b57cec5SDimitry Andric // field. Round up to at least the smallest field alignment that we 292*0b57cec5SDimitry Andric // currently have. 293*0b57cec5SDimitry Andric CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align); 294*0b57cec5SDimitry Andric NewPad += NextOffset - NewOffset; 295*0b57cec5SDimitry Andric NewOffset = NextOffset; 296*0b57cec5SDimitry Andric } 297*0b57cec5SDimitry Andric } 298*0b57cec5SDimitry Andric // Calculate tail padding. 299*0b57cec5SDimitry Andric CharUnits NewSize = NewOffset.alignTo(RL.getAlignment()); 300*0b57cec5SDimitry Andric NewPad += NewSize - NewOffset; 301*0b57cec5SDimitry Andric return {NewPad, std::move(OptimalFieldsOrder)}; 302*0b57cec5SDimitry Andric } 303*0b57cec5SDimitry Andric 304*0b57cec5SDimitry Andric void reportRecord( 305*0b57cec5SDimitry Andric const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad, 306*0b57cec5SDimitry Andric const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const { 307*0b57cec5SDimitry Andric if (!PaddingBug) 308*0b57cec5SDimitry Andric PaddingBug = 309*0b57cec5SDimitry Andric llvm::make_unique<BugType>(this, "Excessive Padding", "Performance"); 310*0b57cec5SDimitry Andric 311*0b57cec5SDimitry Andric SmallString<100> Buf; 312*0b57cec5SDimitry Andric llvm::raw_svector_ostream Os(Buf); 313*0b57cec5SDimitry Andric Os << "Excessive padding in '"; 314*0b57cec5SDimitry Andric Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(), 315*0b57cec5SDimitry Andric LangOptions()) 316*0b57cec5SDimitry Andric << "'"; 317*0b57cec5SDimitry Andric 318*0b57cec5SDimitry Andric if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { 319*0b57cec5SDimitry Andric // TODO: make this show up better in the console output and in 320*0b57cec5SDimitry Andric // the HTML. Maybe just make it show up in HTML like the path 321*0b57cec5SDimitry Andric // diagnostics show. 322*0b57cec5SDimitry Andric SourceLocation ILoc = TSD->getPointOfInstantiation(); 323*0b57cec5SDimitry Andric if (ILoc.isValid()) 324*0b57cec5SDimitry Andric Os << " instantiated here: " 325*0b57cec5SDimitry Andric << ILoc.printToString(BR->getSourceManager()); 326*0b57cec5SDimitry Andric } 327*0b57cec5SDimitry Andric 328*0b57cec5SDimitry Andric Os << " (" << BaselinePad.getQuantity() << " padding bytes, where " 329*0b57cec5SDimitry Andric << OptimalPad.getQuantity() << " is optimal). \n" 330*0b57cec5SDimitry Andric << "Optimal fields order: \n"; 331*0b57cec5SDimitry Andric for (const auto *FD : OptimalFieldsOrder) 332*0b57cec5SDimitry Andric Os << FD->getName() << ", \n"; 333*0b57cec5SDimitry Andric Os << "consider reordering the fields or adding explicit padding " 334*0b57cec5SDimitry Andric "members."; 335*0b57cec5SDimitry Andric 336*0b57cec5SDimitry Andric PathDiagnosticLocation CELoc = 337*0b57cec5SDimitry Andric PathDiagnosticLocation::create(RD, BR->getSourceManager()); 338*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc); 339*0b57cec5SDimitry Andric Report->setDeclWithIssue(RD); 340*0b57cec5SDimitry Andric Report->addRange(RD->getSourceRange()); 341*0b57cec5SDimitry Andric BR->emitReport(std::move(Report)); 342*0b57cec5SDimitry Andric } 343*0b57cec5SDimitry Andric }; 344*0b57cec5SDimitry Andric } // namespace 345*0b57cec5SDimitry Andric 346*0b57cec5SDimitry Andric void ento::registerPaddingChecker(CheckerManager &Mgr) { 347*0b57cec5SDimitry Andric auto *Checker = Mgr.registerChecker<PaddingChecker>(); 348*0b57cec5SDimitry Andric Checker->AllowedPad = Mgr.getAnalyzerOptions() 349*0b57cec5SDimitry Andric .getCheckerIntegerOption(Checker, "AllowedPad"); 350*0b57cec5SDimitry Andric if (Checker->AllowedPad < 0) 351*0b57cec5SDimitry Andric Mgr.reportInvalidCheckerOptionValue( 352*0b57cec5SDimitry Andric Checker, "AllowedPad", "a non-negative value"); 353*0b57cec5SDimitry Andric } 354*0b57cec5SDimitry Andric 355*0b57cec5SDimitry Andric bool ento::shouldRegisterPaddingChecker(const LangOptions &LO) { 356*0b57cec5SDimitry Andric return true; 357*0b57cec5SDimitry Andric } 358