[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.

llvm-svn: 255545
This commit is contained in:
Ben Craig 2015-12-14 21:38:59 +00:00
parent 5e6f2bd0cb
commit 97c191c473
6 changed files with 959 additions and 0 deletions

View File

@ -57,6 +57,7 @@ add_clang_library(clangStaticAnalyzerCheckers
ObjCMissingSuperCallChecker.cpp
ObjCSelfInitChecker.cpp
ObjCUnusedIVarsChecker.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
PointerSubChecker.cpp
PthreadLockChecker.cpp

View File

@ -46,6 +46,8 @@ def CplusplusAlpha : Package<"cplusplus">, InPackage<Alpha>, Hidden;
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;
@ -272,6 +274,18 @@ def UnreachableCodeChecker : Checker<"UnreachableCode">,
} // 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.
//===----------------------------------------------------------------------===//

View File

@ -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>();
}

View File

@ -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@+1{{Excessive padding in 'struct FieldAttrAlign' (6 padding}}
struct FieldAttrAlign {
char c1;
alignas(4) int i;
char c2;
};
// expected-warning@+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@+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@+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@+1{{Excessive padding in 'struct SmallArrayShortSandwich'}}
struct SmallArrayShortSandwich {
char c1;
short s;
char c2;
} ShortArray[20];
// expected-warning@+1{{Excessive padding in 'struct SmallArrayInFunc'}}
struct SmallArrayInFunc {
char c1;
short s;
char c2;
};
void arrayHolder() {
struct SmallArrayInFunc Arr[15];
}
// xxxexpected-warning@+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];
}

View File

@ -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@+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@+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@+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@+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

View File

@ -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@+1{{Excessive padding in 'struct IntSandwich' (6 padding bytes, where 2 is optimal)}}
struct IntSandwich {
char c1;
int i;
char c2;
};
// expected-warning@+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@+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@+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@+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@+1{{Excessive padding in 'TypedefSandwich' (6 padding bytes, where 2 is optimal)}}
typedef struct {
char c1;
int i;
char c2;
} TypedefSandwich;
// expected-warning@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+1{{Excessive padding in 'class LotsOfSpace' (6 padding bytes, where 2 is optimal)}}
class LotsOfSpace {
Empty e1;
int i;
Empty e2;
};
// expected-warning@+1{{Excessive padding in 'TypedefSandwich2' (6 padding bytes, where 2 is optimal)}}
typedef struct {
char c1;
// expected-warning@+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@+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;
};