r255545 - [PATCH] Adding checker to detect excess padding in records

Ben Craig via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 14 13:38:59 PST 2015


Author: bcraig
Date: Mon Dec 14 15:38:59 2015
New Revision: 255545

URL: http://llvm.org/viewvc/llvm-project?rev=255545&view=rev
Log:
[PATCH] Adding checker to detect excess padding in records

The intent of this checker is to generate a report for any class / structure
that could reduce its padding by reordering the fields.  This results in a very
noisy checker.  To reduce the noise, this checker will currently only warn when
the number of bytes over "optimal" is more than 24.  This value is configurable
with -analyzer-config performance.Padding:AllowedPad=N.  Small values of
AllowedPad have the potential to generate hundreds of reports, and gigabytes
of HTML reports.

The checker searches for padding violations in two main ways.  First, it goes
record by record.  A report is generated if the fields could be reordered in a
way that reduces the padding by more than AllowedPad bytes.  Second, the
checker will generate a report if an array will cause more than AllowedPad
padding bytes to be generated.

The record checker currently skips many ABI specific cases.  Classes with base
classes are skipped because base class tail padding is ABI specific.  Bitfields
are just plain hard, and duplicating that code seems like a bad idea.  VLAs are
both uncommon and non-trivial to fix.

The array checker isn't very thorough right now.  It only checks to see if the
element type's fields could be reordered, and it doesn't recursively check to
see if any of the fields' fields could be reordered.  At some point in the
future, it would be nice if "arrays" could also look at array new usages and
malloc patterns that appear to be creating arrays.

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
    cfe/trunk/test/Analysis/padding_c.c
    cfe/trunk/test/Analysis/padding_cpp.cpp
    cfe/trunk/test/Analysis/padding_message.cpp
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=255545&r1=255544&r2=255545&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Mon Dec 14 15:38:59 2015
@@ -57,6 +57,7 @@ add_clang_library(clangStaticAnalyzerChe
   ObjCMissingSuperCallChecker.cpp
   ObjCSelfInitChecker.cpp
   ObjCUnusedIVarsChecker.cpp
+  PaddingChecker.cpp
   PointerArithChecker.cpp
   PointerSubChecker.cpp
   PthreadLockChecker.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=255545&r1=255544&r2=255545&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Mon Dec 14 15:38:59 2015
@@ -46,6 +46,8 @@ def CplusplusAlpha : Package<"cplusplus"
 def DeadCode : Package<"deadcode">;
 def DeadCodeAlpha : Package<"deadcode">, InPackage<Alpha>, Hidden;
 
+def Performance : Package<"performance">, InPackage<OptIn>;
+
 def Security : Package <"security">;
 def InsecureAPI : Package<"insecureAPI">, InPackage<Security>;
 def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden;
@@ -273,6 +275,18 @@ def UnreachableCodeChecker : Checker<"Un
 } // end "alpha.deadcode"
 
 //===----------------------------------------------------------------------===//
+// Performance checkers.
+//===----------------------------------------------------------------------===//
+
+let ParentPackage = Performance in {
+
+def PaddingChecker : Checker<"Padding">,
+  HelpText<"Check for excessively padded structs.">,
+  DescFile<"PaddingChecker.cpp">;
+
+} // end: "padding"
+
+//===----------------------------------------------------------------------===//
 // Security checkers.
 //===----------------------------------------------------------------------===//
 

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

Added: cfe/trunk/test/Analysis/padding_c.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/padding_c.c?rev=255545&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/padding_c.c (added)
+++ cfe/trunk/test/Analysis/padding_c.c Mon Dec 14 15:38:59 2015
@@ -0,0 +1,243 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=2 -verify %s
+
+#if __has_include(<stdalign.h>)
+#include <stdalign.h>
+#endif
+
+#if __has_include(<stdalign.h>) || defined(__cplusplus)
+// expected-warning at +1{{Excessive padding in 'struct FieldAttrAlign' (6 padding}}
+struct FieldAttrAlign {
+  char c1;
+  alignas(4) int i;
+  char c2;
+};
+
+// expected-warning at +1{{Excessive padding in 'struct FieldAttrOverAlign' (10 padding}}
+struct FieldAttrOverAlign {
+  char c1;
+  alignas(8) int i;
+  char c2;
+};
+
+#endif // __has_include(<stdalign.h>) || defined(__cplusplus)
+
+// Re-ordering members of these structs won't reduce padding, so don't warn
+struct LeadingChar { // no-warning
+  char c;
+  int i;
+};
+
+struct TrailingChar { // no-warning
+  int i;
+  char c;
+};
+
+struct Helpless { // no-warning
+  struct TrailingChar i1;
+  struct LeadingChar i2;
+  char c;
+};
+
+#pragma pack(push)
+#pragma pack(1)
+struct SquishedIntSandwich { // no-warning
+  char c1;
+  int i;
+  char c2;
+};
+#pragma pack(pop)
+
+// Re-ordering members of these structs will reduce padding, so warn
+struct IntSandwich { // expected-warning{{Excessive padding in 'struct IntSandwich'}}
+  char c1;
+  int i;
+  char c2;
+};
+
+struct TurDuckHen { // expected-warning{{Excessive padding in 'struct TurDuckHen'}}
+  char c1;
+  struct IntSandwich i;
+  char c2;
+};
+
+#pragma pack(push)
+#pragma pack(2)
+struct SmallIntSandwich { // expected-warning{{Excessive padding in 'struct SmallIntSandwich'}}
+  char c1;
+  int i1;
+  char c2;
+  int i2;
+  char c3;
+  int i3;
+  char c4;
+};
+#pragma pack(pop)
+
+union SomeUnion { // no-warning
+  char c;
+  short s;
+  int i;
+};
+
+struct HoldsAUnion { // expected-warning{{Excessive padding in 'struct HoldsAUnion'}}
+  char c1;
+  union SomeUnion u;
+  char c2;
+};
+
+struct BigCharArray { // no-warning
+  char c[129];
+};
+
+struct SmallCharArray { // no-warning
+  char c[5];
+};
+
+struct MediumIntArray { // no-warning
+  int i[5];
+};
+
+struct LargeSizeToSmallSize { // expected-warning{{Excessive padding in 'struct LargeSizeToSmallSize'}}
+  struct BigCharArray b;
+  struct MediumIntArray m;
+  struct SmallCharArray s;
+};
+
+struct LargeAlignToSmallAlign { // no-warning
+  struct MediumIntArray m;
+  struct BigCharArray b;
+  struct SmallCharArray s;
+};
+
+// Currently ignoring VLA padding problems.  Still need to make sure we don't
+// choke on VLAs though
+struct HoldsVLA { // no-warning
+  char c1;
+  int x;
+  char c2;
+  int vla[];
+};
+
+// Currently ignoring bitfield padding problems.  Still need to make sure we
+// don't choke on bitfields though
+struct HoldsBitfield { // no-warning
+  char c1;
+  int x;
+  char c2;
+  unsigned char b1 : 3;
+  unsigned char b2 : 3;
+  unsigned char b3 : 2;
+};
+
+typedef struct { // expected-warning{{Excessive padding in 'TypedefSandwich'}}
+  char c1;
+  int i;
+  char c2;
+} TypedefSandwich;
+
+// expected-warning at +1{{Excessive padding in 'struct StructAttrAlign' (10 padding}}
+struct StructAttrAlign {
+  char c1;
+  int i;
+  char c2;
+} __attribute__((aligned(8)));
+
+struct CorrectOverlyAlignedChar { // no-warning
+  char c __attribute__((aligned(4096)));
+  char c1;
+  int x1;
+  char c2;
+  int x2;
+  char c3;
+};
+
+struct OverlyAlignedChar { // expected-warning{{Excessive padding in 'struct OverlyAlignedChar'}}
+  char c1;
+  int x;
+  char c2;
+  char c __attribute__((aligned(4096)));
+};
+
+struct HoldsOverlyAlignedChar { // expected-warning{{Excessive padding in 'struct HoldsOverlyAlignedChar'}}
+  char c1;
+  struct OverlyAlignedChar o;
+  char c2;
+};
+
+void internalStructFunc() {
+  struct X { // expected-warning{{Excessive padding in 'struct X'}}
+    char c1;
+    int t;
+    char c2;
+  };
+  struct X obj;
+}
+
+void typedefStructFunc() {
+  typedef struct { // expected-warning{{Excessive padding in 'S'}}
+    char c1;
+    int t;
+    char c2;
+  } S;
+  S obj;
+}
+
+void anonStructFunc() {
+  struct { // expected-warning{{Excessive padding in 'struct (anonymous}}
+    char c1;
+    int t;
+    char c2;
+  } obj;
+}
+
+// expected-warning at +1{{Excessive padding in 'struct DefaultAttrAlign'}}
+struct DefaultAttrAlign {
+  char c1;
+  long long i;
+  char c2;
+} __attribute__((aligned));
+
+struct CorrectDefaultAttrAlign { // no-warning
+  long long i;
+  char c1;
+  char c2;
+} __attribute__((aligned));
+
+struct TooSmallShortSandwich { // no-warning
+  char c1;
+  short s;
+  char c2;
+};
+
+// expected-warning at +1{{Excessive padding in 'struct SmallArrayShortSandwich'}}
+struct SmallArrayShortSandwich {
+  char c1;
+  short s;
+  char c2;
+} ShortArray[20];
+
+// expected-warning at +1{{Excessive padding in 'struct SmallArrayInFunc'}}
+struct SmallArrayInFunc {
+  char c1;
+  short s;
+  char c2;
+};
+
+void arrayHolder() {
+  struct SmallArrayInFunc Arr[15];
+}
+
+// xxxexpected-warning at +1{{Excessive padding in 'struct SmallArrayInStruct'}}
+struct SmallArrayInStruct {
+  char c1;
+  short s;
+  char c2;
+};
+
+struct HoldsSmallArray {
+  struct SmallArrayInStruct Field[20];
+} HoldsSmallArrayElt;
+
+void nestedPadding() {
+  struct HoldsSmallArray Arr[15];
+}

Added: cfe/trunk/test/Analysis/padding_cpp.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/padding_cpp.cpp?rev=255545&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/padding_cpp.cpp (added)
+++ cfe/trunk/test/Analysis/padding_cpp.cpp Mon Dec 14 15:38:59 2015
@@ -0,0 +1,202 @@
+// RUN: %clang_cc1 -std=c++14 -analyze -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=2 -verify %s
+
+// Make sure that the C cases still work fine, even when compiled as C++.
+#include "padding_c.c"
+
+struct BigCharArray2 { // no-warning
+  char c[129];
+};
+
+// xxxexpected-warning at +1{{Excessive padding in 'struct LowAlignmentBase'}}
+struct LowAlignmentBase : public BigCharArray2 {
+  int i;
+  char c;
+};
+
+struct CorrectLowAlignmentBase : public BigCharArray2 { // no-warning
+  char c;
+  int i;
+};
+
+// xxxexpected-warning at +1{{Excessive padding in 'struct LowAlignmentBase2'}}
+struct LowAlignmentBase2 : public BigCharArray2 {
+  char c1;
+  int i;
+  char c2;
+};
+
+class PaddedA { // expected-warning{{Excessive padding in 'class PaddedA'}}
+  char c1;
+  int i;
+  char c2;
+};
+
+class VirtualPaddedA : public PaddedA { // no-warning
+  virtual void foo() {}
+};
+
+class VirtualIntSandwich { // expected-warning{{Excessive padding in 'class VirtualIntSandwich'}}
+  virtual void foo() {}
+  char c1;
+  int i;
+  char c2;
+};
+
+// constructed so as not to have tail padding
+class InnerPaddedB { // expected-warning{{Excessive padding in 'class InnerPaddedB'}}
+  char c1;
+  int i1;
+  char c2;
+  int i2;
+};
+
+class TailPaddedB { // expected-warning{{Excessive padding in 'class TailPaddedB'}}
+  char c1;
+  int i1;
+  char c2;
+};
+
+class SI : public PaddedA { // no-warning
+  char c;
+};
+
+class SI2 : public PaddedA { // xxxexpected-warning{{Excessive padding in 'class SI2'}}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class VirtualSI : virtual public PaddedA { // no-warning
+  char c;
+};
+
+// currently not checked for
+class VirtualSI2 : virtual public PaddedA { // no-warning
+  char c10;
+  int i10;
+  char c11;
+};
+
+class VtblSI : public PaddedA { // no-warning
+  virtual void foo() {}
+  char c;
+};
+
+class VtblSI2 : public PaddedA { // xxxexpected-warning{{Excessive padding in 'class VtblSI2'}}
+  virtual void foo() {}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class VtblSI3 : public VirtualPaddedA { // xxxexpected-warning{{Excessive padding in 'class VtblSI3'}}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class MI : public PaddedA, public InnerPaddedB { // no-warning
+  char c;
+};
+
+class MI2 : public PaddedA, public InnerPaddedB { // xxxexpected-warning{{Excessive padding in 'class MI2'}}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class VtblMI : public PaddedA, public InnerPaddedB { // xxxexpected-warning{{Excessive padding in 'class VtblMI'}}
+  virtual void foo() {}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class VtblMI2 : public VirtualPaddedA, public InnerPaddedB { // xxxexpected-warning{{Excessive padding in 'class VtblMI2'}}
+  char c10;
+  int i10;
+  char c11;
+};
+
+class Empty {}; // no-warning
+
+class LotsOfSpace { // expected-warning{{Excessive padding in 'class LotsOfSpace'}}
+  Empty e1;
+  int i;
+  Empty e2;
+};
+
+class EBO1 : public Empty { // xxxexpected-warning{{Excessive padding in 'class EBO1'}}
+  char c1;
+  int i;
+  char c2;
+};
+
+class EBO2 : public Empty { // xxxexpected-warning{{Excessive padding in 'class EBO2'}}
+  Empty c1;
+  int i;
+  Empty c2;
+};
+
+template <typename T>
+class TemplateSandwich { // expected-warning{{Excessive padding in 'class TemplateSandwich<int>' instantiated here}}
+  char c1;
+  T t;
+  char c2;
+};
+
+template <typename T>
+class TemplateSandwich<T *> { // expected-warning{{Excessive padding in 'class TemplateSandwich<void *>' instantiated here}}
+  char c1;
+  T *t;
+  char c2;
+};
+
+template <>
+class TemplateSandwich<long long> { // expected-warning{{Excessive padding in 'class TemplateSandwich<long long>' (}}
+  char c1;
+  long long t;
+  char c2;
+};
+
+class Holder1 { // no-warning
+  TemplateSandwich<int> t1;
+  TemplateSandwich<char> t2;
+  TemplateSandwich<void *> t3;
+};
+
+typedef struct { // expected-warning{{Excessive padding in 'TypedefSandwich2'}}
+  char c1;
+  typedef struct { // expected-warning{{Excessive padding in 'TypedefSandwich2::NestedTypedef'}}
+    char c1;
+    int i;
+    char c2;
+  } NestedTypedef;
+  NestedTypedef t;
+  char c2;
+} TypedefSandwich2;
+
+template <typename T>
+struct Foo {
+  // expected-warning at +1{{Excessive padding in 'struct Foo<int>::Nested'}}
+  struct Nested {
+    char c1;
+    T t;
+    char c2;
+  };
+};
+
+struct Holder { // no-warning
+  Foo<int>::Nested t1;
+  Foo<char>::Nested t2;
+};
+
+struct GlobalsForLambda { // no-warning
+  int i;
+  char c1;
+  char c2;
+} G;
+
+// expected-warning at +1{{Excessive padding in 'class (lambda}}
+auto lambda1 = [ c1 = G.c1, i = G.i, c2 = G.c2 ]{};
+auto lambda2 = [ i = G.i, c1 = G.c1, c2 = G.c2 ]{}; // no-warning

Added: cfe/trunk/test/Analysis/padding_message.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/padding_message.cpp?rev=255545&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/padding_message.cpp (added)
+++ cfe/trunk/test/Analysis/padding_message.cpp Mon Dec 14 15:38:59 2015
@@ -0,0 +1,185 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++14 -analyze -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=2 -verify %s
+
+// expected-warning at +1{{Excessive padding in 'struct IntSandwich' (6 padding bytes, where 2 is optimal)}}
+struct IntSandwich {
+  char c1;
+  int i;
+  char c2;
+};
+
+// expected-warning at +1{{Excessive padding in 'struct TurDuckHen' (6 padding bytes, where 2 is optimal)}}
+struct TurDuckHen {
+  char c1;
+  struct IntSandwich i;
+  char c2;
+};
+
+#pragma pack(push)
+#pragma pack(2)
+// expected-warning at +1{{Excessive padding in 'struct SmallIntSandwich' (4 padding bytes, where 0 is optimal)}}
+struct SmallIntSandwich {
+  char c1;
+  int i1;
+  char c2;
+  int i2;
+  char c3;
+  int i3;
+  char c4;
+};
+#pragma pack(pop)
+
+union SomeUnion { // no-warning
+  char c;
+  short s;
+  int i;
+};
+
+// expected-warning at +1{{Excessive padding in 'struct HoldsAUnion' (6 padding bytes, where 2 is optimal)}}
+struct HoldsAUnion {
+  char c1;
+  union SomeUnion u;
+  char c2;
+};
+
+struct SmallCharArray { // no-warning
+  char c[5];
+};
+
+struct MediumIntArray { // no-warning
+  int i[5];
+};
+
+// expected-warning at +1{{Excessive padding in 'struct StructSandwich' (6 padding bytes, where 2 is optimal)}}
+struct StructSandwich {
+  struct SmallCharArray s;
+  struct MediumIntArray m;
+  struct SmallCharArray s2;
+};
+
+// expected-warning at +1{{Excessive padding in 'TypedefSandwich' (6 padding bytes, where 2 is optimal)}}
+typedef struct {
+  char c1;
+  int i;
+  char c2;
+} TypedefSandwich;
+
+// expected-warning at +1{{Excessive padding in 'struct StructAttrAlign' (10 padding bytes, where 2 is optimal)}}
+struct StructAttrAlign {
+  char c1;
+  int i;
+  char c2;
+} __attribute__((aligned(8)));
+
+// expected-warning at +1{{Excessive padding in 'struct OverlyAlignedChar' (8185 padding bytes, where 4089 is optimal)}}
+struct OverlyAlignedChar {
+  char c1;
+  int x;
+  char c2;
+  char c __attribute__((aligned(4096)));
+};
+
+// expected-warning at +1{{Excessive padding in 'struct HoldsOverlyAlignedChar' (8190 padding bytes, where 4094 is optimal)}}
+struct HoldsOverlyAlignedChar {
+  char c1;
+  struct OverlyAlignedChar o;
+  char c2;
+};
+
+void internalStructFunc() {
+  // expected-warning at +1{{Excessive padding in 'struct X' (6 padding bytes, where 2 is optimal)}}
+  struct X {
+    char c1;
+    int t;
+    char c2;
+  };
+  struct X obj;
+}
+
+void typedefStructFunc() {
+  // expected-warning at +1{{Excessive padding in 'S' (6 padding bytes, where 2 is optimal)}}
+  typedef struct {
+    char c1;
+    int t;
+    char c2;
+  } S;
+  S obj;
+}
+
+// expected-warning at +1{{Excessive padding in 'struct DefaultAttrAlign' (22 padding bytes, where 6 is optimal)}}
+struct DefaultAttrAlign {
+  char c1;
+  long long i;
+  char c2;
+} __attribute__((aligned));
+
+// expected-warning at +1{{Excessive padding in 'struct SmallArrayShortSandwich' (2 padding bytes, where 0 is optimal)}}
+struct SmallArrayShortSandwich {
+  char c1;
+  short s;
+  char c2;
+} ShortArray[20];
+
+// expected-warning at +1{{Excessive padding in 'struct SmallArrayInFunc' (2 padding bytes, where 0 is optimal)}}
+struct SmallArrayInFunc {
+  char c1;
+  short s;
+  char c2;
+};
+
+void arrayHolder() {
+  struct SmallArrayInFunc Arr[15];
+}
+
+// expected-warning at +1{{Excessive padding in 'class VirtualIntSandwich' (10 padding bytes, where 2 is optimal)}}
+class VirtualIntSandwich {
+  virtual void foo() {}
+  char c1;
+  int i;
+  char c2;
+};
+
+// constructed so as not to have tail padding
+// expected-warning at +1{{Excessive padding in 'class InnerPaddedB' (6 padding bytes, where 2 is optimal)}}
+class InnerPaddedB {
+  char c1;
+  int i1;
+  char c2;
+  int i2;
+};
+
+class Empty {}; // no-warning
+
+// expected-warning at +1{{Excessive padding in 'class LotsOfSpace' (6 padding bytes, where 2 is optimal)}}
+class LotsOfSpace {
+  Empty e1;
+  int i;
+  Empty e2;
+};
+
+// expected-warning at +1{{Excessive padding in 'TypedefSandwich2' (6 padding bytes, where 2 is optimal)}}
+typedef struct {
+  char c1;
+  // expected-warning at +1{{Excessive padding in 'TypedefSandwich2::NestedTypedef' (6 padding bytes, where 2 is optimal)}}
+  typedef struct {
+    char c1;
+    int i;
+    char c2;
+  } NestedTypedef;
+  NestedTypedef t;
+  char c2;
+} TypedefSandwich2;
+
+template <typename T>
+struct Foo {
+  // expected-warning at +1{{Excessive padding in 'struct Foo<int>::Nested' (6 padding bytes, where 2 is optimal)}}
+  struct Nested {
+    char c1;
+    T t;
+    char c2;
+  };
+};
+
+struct Holder { // no-warning
+  Foo<int>::Nested t1;
+  Foo<char>::Nested t2;
+};




More information about the cfe-commits mailing list