[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
diff --git a/clang/test/Analysis/padding_c.c b/clang/test/Analysis/padding_c.c
new file mode 100644
index 0000000..ff532c6
--- /dev/null
+++ b/clang/test/Analysis/padding_c.c
@@ -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];
+}
diff --git a/clang/test/Analysis/padding_cpp.cpp b/clang/test/Analysis/padding_cpp.cpp
new file mode 100644
index 0000000..df2f2a8
--- /dev/null
+++ b/clang/test/Analysis/padding_cpp.cpp
@@ -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
diff --git a/clang/test/Analysis/padding_message.cpp b/clang/test/Analysis/padding_message.cpp
new file mode 100644
index 0000000..f73a11a
--- /dev/null
+++ b/clang/test/Analysis/padding_message.cpp
@@ -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;
+};