Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Reduce the size of Decl and classes derived from it #87361

Merged
merged 5 commits into from
Apr 14, 2024

Conversation

philnik777
Copy link
Contributor

Class Old size (in bytes) New size (in bytes)
Decl 40 32
AccessSpecDecl 40 40
BlockDecl 128 120
CapturedDecl 88 80
EmptyDecl 40 32
ExportDecl 80 72
ExternCContextDecl 72 64
FileScopeAsmDecl 56 48
FriendDecl 64 56
FriendTemplateDecl 64 64
ImplicitConceptSpecializationDecl 40 40
ImportDecl 56 48
LifetimeExtendedTemporaryDecl 72 64
LinkageSpecDecl 80 72
NamedDecl 48 40
ObjCPropertyImplDecl 96 88
PragmaCommentDecl 40 40
PragmaDetectMismatchDecl 48 40
RequiresExprBodyDecl 72 64
StaticAssertDecl 64 56
TopLevelStmtDecl 88 80
TranslationUnitDecl 104 96
BaseUsingDecl 56 48
UsingDecl 88 80
UsingEnumDecl 72 64
HLSLBufferDecl 96 88
LabelDecl 80 72
NamespaceAliasDecl 96 88
NamespaceDecl 112 104
ObjCCompatibleAliasDecl 56 48
ObjCContainerDecl 88 80
ObjCMethodDecl 136 128
ObjCPropertyDecl 128 120
TemplateDecl 64 56
BuiltinTemplateDecl 72 64
TypeDecl 64 56
UnresolvedUsingIfExistsDecl 48 40
UsingDirectiveDecl 88 80
UsingPackDecl 64 56
UsingShadowDecl 80 72
ValueDecl 56 48

When parsing libc++'s <string> header the used memory is reduced from 42.8MB to 42.5MB.

Copy link

github-actions bot commented Apr 2, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@philnik777 philnik777 marked this pull request as ready for review April 3, 2024 10:53
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Good catch on this really nice space-saving change!

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! I'd love it to be a little simplified (see suggestions), but am glad this could be done!

@@ -268,17 +268,34 @@ class alignas(8) Decl {
/// }
/// void A::f(); // SemanticDC == namespace 'A'
/// // LexicalDC == global namespace
llvm::PointerUnion<DeclContext*, MultipleDC*> DeclCtx;
llvm::PointerIntPair<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find myself wondering a touch whether the two PointerIntPair vs an enum-bitmask-thing of two bits is worth the extra complexity here.

Also, a comment above it explaining what this is doing would be REALLY helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK. I'm personally not a huge fan of manual bit twiddling, since it's way to easy to get things wrong. I much rather let the compiler do it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it ends up being 'manual bit twiddling' so much as 3 values in an enum, and I also found the getPointer().getPointer() pretty mind-bending, but at least it is somewhat well hidden in stuff we won't touch for a while, so I won't insist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for more comments. I spent more time to understand the structure here. Also maybe we can make the code self explained by:

Suggested change
llvm::PointerIntPair<
using DeclCtxTy = llvm::PointerUnion<DeclContext*, MultipleDC*>;
using DeclCtxAndHasAttrTy = llvm::PointerIntPair<llvm::PointerUnion<DeclContext *, MultipleDC *>, 1,
bool>;
using DeclCtxAndHasAttrTyOrInvalidDeclTy = ...

clang/include/clang/AST/DeclBase.h Show resolved Hide resolved
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels Apr 8, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Apr 8, 2024

@llvm/pr-subscribers-clang

Author: Nikolas Klauser (philnik777)

Changes
Class Old size (in bytes) New size (in bytes)
Decl 40 32
AccessSpecDecl 40 40
BlockDecl 128 120
CapturedDecl 88 80
EmptyDecl 40 32
ExportDecl 80 72
ExternCContextDecl 72 64
FileScopeAsmDecl 56 48
FriendDecl 64 56
FriendTemplateDecl 64 64
ImplicitConceptSpecializationDecl 40 40
ImportDecl 56 48
LifetimeExtendedTemporaryDecl 72 64
LinkageSpecDecl 80 72
NamedDecl 48 40
ObjCPropertyImplDecl 96 88
PragmaCommentDecl 40 40
PragmaDetectMismatchDecl 48 40
RequiresExprBodyDecl 72 64
StaticAssertDecl 64 56
TopLevelStmtDecl 88 80
TranslationUnitDecl 104 96
BaseUsingDecl 56 48
UsingDecl 88 80
UsingEnumDecl 72 64
HLSLBufferDecl 96 88
LabelDecl 80 72
NamespaceAliasDecl 96 88
NamespaceDecl 112 104
ObjCCompatibleAliasDecl 56 48
ObjCContainerDecl 88 80
ObjCMethodDecl 136 128
ObjCPropertyDecl 128 120
TemplateDecl 64 56
BuiltinTemplateDecl 72 64
TypeDecl 64 56
UnresolvedUsingIfExistsDecl 48 40
UsingDirectiveDecl 88 80
UsingPackDecl 64 56
UsingShadowDecl 80 72
ValueDecl 56 48

When parsing libc++'s &lt;string&gt; header the used memory is reduced from 42.8MB to 42.5MB.


Full diff: https://github.com/llvm/llvm-project/pull/87361.diff

3 Files Affected:

  • (modified) clang/include/clang/AST/DeclBase.h (+44-25)
  • (modified) clang/lib/AST/DeclBase.cpp (+22-9)
  • (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+1-1)
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 47ed6d0d1db0df..9a3f8c41de387e 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -268,17 +268,37 @@ class alignas(8) Decl {
   ///   }
   ///   void A::f(); // SemanticDC == namespace 'A'
   ///                // LexicalDC == global namespace
-  llvm::PointerUnion<DeclContext*, MultipleDC*> DeclCtx;
 
-  bool isInSemaDC() const { return DeclCtx.is<DeclContext*>(); }
-  bool isOutOfSemaDC() const { return DeclCtx.is<MultipleDC*>(); }
+  // Compress the InvalidDecl and HasAttrs bits into DeclCtx to keep Decl below
+  // 32 bytes in size
+  llvm::PointerIntPair<
+      llvm::PointerIntPair<llvm::PointerUnion<DeclContext *, MultipleDC *>, 1,
+                           bool>,
+      1, bool>
+      DeclCtxWithInvalidDeclAndHasAttrs;
+
+  bool isInSemaDC() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .is<DeclContext *>();
+  }
+
+  bool isOutOfSemaDC() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .is<MultipleDC *>();
+  }
 
   MultipleDC *getMultipleDC() const {
-    return DeclCtx.get<MultipleDC*>();
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .get<MultipleDC *>();
   }
 
   DeclContext *getSemanticDC() const {
-    return DeclCtx.get<DeclContext*>();
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .get<DeclContext *>();
   }
 
   /// Loc - The location of this decl.
@@ -288,14 +308,6 @@ class alignas(8) Decl {
   LLVM_PREFERRED_TYPE(Kind)
   unsigned DeclKind : 7;
 
-  /// InvalidDecl - This indicates a semantic error occurred.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned InvalidDecl :  1;
-
-  /// HasAttrs - This indicates whether the decl has attributes or not.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned HasAttrs : 1;
-
   /// Implicit - Whether this declaration was implicitly generated by
   /// the implementation rather than explicitly written by the user.
   LLVM_PREFERRED_TYPE(bool)
@@ -393,21 +405,22 @@ class alignas(8) Decl {
 protected:
   Decl(Kind DK, DeclContext *DC, SourceLocation L)
       : NextInContextAndBits(nullptr, getModuleOwnershipKindForChildOf(DC)),
-        DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(false), HasAttrs(false),
-        Implicit(false), Used(false), Referenced(false),
+        DeclCtxWithInvalidDeclAndHasAttrs({DC, false}, false), Loc(L),
+        DeclKind(DK), Implicit(false), Used(false), Referenced(false),
         TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
         CacheValidAndLinkage(llvm::to_underlying(Linkage::Invalid)) {
-    if (StatisticsEnabled) add(DK);
+    if (StatisticsEnabled)
+      add(DK);
   }
 
   Decl(Kind DK, EmptyShell Empty)
-      : DeclKind(DK), InvalidDecl(false), HasAttrs(false), Implicit(false),
-        Used(false), Referenced(false), TopLevelDeclInObjCContainer(false),
-        Access(AS_none), FromASTFile(0),
+      : DeclKind(DK), Implicit(false), Used(false), Referenced(false),
+        TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
         CacheValidAndLinkage(llvm::to_underlying(Linkage::Invalid)) {
-    if (StatisticsEnabled) add(DK);
+    if (StatisticsEnabled)
+      add(DK);
   }
 
   virtual ~Decl();
@@ -520,7 +533,7 @@ class alignas(8) Decl {
     return AccessSpecifier(Access);
   }
 
-  bool hasAttrs() const { return HasAttrs; }
+  bool hasAttrs() const { return DeclCtxWithInvalidDeclAndHasAttrs.getPointer().getInt(); }
 
   void setAttrs(const AttrVec& Attrs) {
     return setAttrsImpl(Attrs, getASTContext());
@@ -549,13 +562,16 @@ class alignas(8) Decl {
   }
 
   template <typename... Ts> void dropAttrs() {
-    if (!HasAttrs) return;
+    if (!hasAttrs()) return;
 
     AttrVec &Vec = getAttrs();
     llvm::erase_if(Vec, [](Attr *A) { return isa<Ts...>(A); });
 
-    if (Vec.empty())
-      HasAttrs = false;
+    if (Vec.empty()) {
+      auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+      InnerPtr.setInt(false);
+      DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
+    }
   }
 
   template <typename T> void dropAttr() { dropAttrs<T>(); }
@@ -590,7 +606,10 @@ class alignas(8) Decl {
   /// setInvalidDecl - Indicates the Decl had a semantic error. This
   /// allows for graceful error recovery.
   void setInvalidDecl(bool Invalid = true);
-  bool isInvalidDecl() const { return (bool) InvalidDecl; }
+
+  bool isInvalidDecl() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getInt();
+  }
 
   /// isImplicit - Indicates whether the declaration was implicitly
   /// generated by the implementation. If false, this declaration
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 04bbc49ab2f319..c06b41d6b29f89 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -52,6 +52,8 @@
 
 using namespace clang;
 
+static_assert(sizeof(Decl) <= 32, "Decl grew beyond 32 bytes!");
+
 //===----------------------------------------------------------------------===//
 //  Statistics
 //===----------------------------------------------------------------------===//
@@ -130,7 +132,7 @@ const char *Decl::getDeclKindName() const {
 }
 
 void Decl::setInvalidDecl(bool Invalid) {
-  InvalidDecl = Invalid;
+  DeclCtxWithInvalidDeclAndHasAttrs.setInt(Invalid);
   assert(!isa<TagDecl>(this) || !cast<TagDecl>(this)->isCompleteDefinition());
   if (!Invalid) {
     return;
@@ -334,7 +336,9 @@ void PrettyStackTraceDecl::print(raw_ostream &OS) const {
 Decl::~Decl() = default;
 
 void Decl::setDeclContext(DeclContext *DC) {
-  DeclCtx = DC;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setPointer(DC);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
 }
 
 void Decl::setLexicalDeclContext(DeclContext *DC) {
@@ -364,12 +368,16 @@ void Decl::setLexicalDeclContext(DeclContext *DC) {
 void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
                                ASTContext &Ctx) {
   if (SemaDC == LexicalDC) {
-    DeclCtx = SemaDC;
+    auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+    InnerPtr.setPointer(SemaDC);
+    DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   } else {
     auto *MDC = new (Ctx) Decl::MultipleDC();
     MDC->SemanticDC = SemaDC;
     MDC->LexicalDC = LexicalDC;
-    DeclCtx = MDC;
+    auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+    InnerPtr.setPointer(MDC);
+    DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   }
 }
 
@@ -956,19 +964,24 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 }
 
 void Decl::setAttrsImpl(const AttrVec &attrs, ASTContext &Ctx) {
-  assert(!HasAttrs && "Decl already contains attrs.");
+  assert(!hasAttrs() && "Decl already contains attrs.");
 
   AttrVec &AttrBlank = Ctx.getDeclAttrs(this);
   assert(AttrBlank.empty() && "HasAttrs was wrong?");
 
   AttrBlank = attrs;
-  HasAttrs = true;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setInt(true);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
 }
 
 void Decl::dropAttrs() {
-  if (!HasAttrs) return;
+  if (!hasAttrs())
+    return;
 
-  HasAttrs = false;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setInt(false);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   getASTContext().eraseDeclAttrs(this);
 }
 
@@ -996,7 +1009,7 @@ void Decl::addAttr(Attr *A) {
 }
 
 const AttrVec &Decl::getAttrs() const {
-  assert(HasAttrs && "No attrs to get!");
+  assert(hasAttrs() && "No attrs to get!");
   return getASTContext().getDeclAttrs(this);
 }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index a22f760408c634..2e186109327fb2 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -594,7 +594,7 @@ void ASTDeclReader::VisitDecl(Decl *D) {
   bool HasStandaloneLexicalDC = DeclBits.getNextBit();
   bool HasAttrs = DeclBits.getNextBit();
   D->setTopLevelDeclInObjCContainer(DeclBits.getNextBit());
-  D->InvalidDecl = DeclBits.getNextBit();
+  D->DeclCtxWithInvalidDeclAndHasAttrs.setInt(DeclBits.getNextBit());
   D->FromASTFile = true;
 
   if (D->isTemplateParameter() || D->isTemplateParameterPack() ||

@llvmbot
Copy link
Collaborator

llvmbot commented Apr 8, 2024

@llvm/pr-subscribers-clang-modules

Author: Nikolas Klauser (philnik777)

Changes
Class Old size (in bytes) New size (in bytes)
Decl 40 32
AccessSpecDecl 40 40
BlockDecl 128 120
CapturedDecl 88 80
EmptyDecl 40 32
ExportDecl 80 72
ExternCContextDecl 72 64
FileScopeAsmDecl 56 48
FriendDecl 64 56
FriendTemplateDecl 64 64
ImplicitConceptSpecializationDecl 40 40
ImportDecl 56 48
LifetimeExtendedTemporaryDecl 72 64
LinkageSpecDecl 80 72
NamedDecl 48 40
ObjCPropertyImplDecl 96 88
PragmaCommentDecl 40 40
PragmaDetectMismatchDecl 48 40
RequiresExprBodyDecl 72 64
StaticAssertDecl 64 56
TopLevelStmtDecl 88 80
TranslationUnitDecl 104 96
BaseUsingDecl 56 48
UsingDecl 88 80
UsingEnumDecl 72 64
HLSLBufferDecl 96 88
LabelDecl 80 72
NamespaceAliasDecl 96 88
NamespaceDecl 112 104
ObjCCompatibleAliasDecl 56 48
ObjCContainerDecl 88 80
ObjCMethodDecl 136 128
ObjCPropertyDecl 128 120
TemplateDecl 64 56
BuiltinTemplateDecl 72 64
TypeDecl 64 56
UnresolvedUsingIfExistsDecl 48 40
UsingDirectiveDecl 88 80
UsingPackDecl 64 56
UsingShadowDecl 80 72
ValueDecl 56 48

When parsing libc++'s &lt;string&gt; header the used memory is reduced from 42.8MB to 42.5MB.


Full diff: https://github.com/llvm/llvm-project/pull/87361.diff

3 Files Affected:

  • (modified) clang/include/clang/AST/DeclBase.h (+44-25)
  • (modified) clang/lib/AST/DeclBase.cpp (+22-9)
  • (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+1-1)
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 47ed6d0d1db0df..9a3f8c41de387e 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -268,17 +268,37 @@ class alignas(8) Decl {
   ///   }
   ///   void A::f(); // SemanticDC == namespace 'A'
   ///                // LexicalDC == global namespace
-  llvm::PointerUnion<DeclContext*, MultipleDC*> DeclCtx;
 
-  bool isInSemaDC() const { return DeclCtx.is<DeclContext*>(); }
-  bool isOutOfSemaDC() const { return DeclCtx.is<MultipleDC*>(); }
+  // Compress the InvalidDecl and HasAttrs bits into DeclCtx to keep Decl below
+  // 32 bytes in size
+  llvm::PointerIntPair<
+      llvm::PointerIntPair<llvm::PointerUnion<DeclContext *, MultipleDC *>, 1,
+                           bool>,
+      1, bool>
+      DeclCtxWithInvalidDeclAndHasAttrs;
+
+  bool isInSemaDC() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .is<DeclContext *>();
+  }
+
+  bool isOutOfSemaDC() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .is<MultipleDC *>();
+  }
 
   MultipleDC *getMultipleDC() const {
-    return DeclCtx.get<MultipleDC*>();
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .get<MultipleDC *>();
   }
 
   DeclContext *getSemanticDC() const {
-    return DeclCtx.get<DeclContext*>();
+    return DeclCtxWithInvalidDeclAndHasAttrs.getPointer()
+        .getPointer()
+        .get<DeclContext *>();
   }
 
   /// Loc - The location of this decl.
@@ -288,14 +308,6 @@ class alignas(8) Decl {
   LLVM_PREFERRED_TYPE(Kind)
   unsigned DeclKind : 7;
 
-  /// InvalidDecl - This indicates a semantic error occurred.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned InvalidDecl :  1;
-
-  /// HasAttrs - This indicates whether the decl has attributes or not.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned HasAttrs : 1;
-
   /// Implicit - Whether this declaration was implicitly generated by
   /// the implementation rather than explicitly written by the user.
   LLVM_PREFERRED_TYPE(bool)
@@ -393,21 +405,22 @@ class alignas(8) Decl {
 protected:
   Decl(Kind DK, DeclContext *DC, SourceLocation L)
       : NextInContextAndBits(nullptr, getModuleOwnershipKindForChildOf(DC)),
-        DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(false), HasAttrs(false),
-        Implicit(false), Used(false), Referenced(false),
+        DeclCtxWithInvalidDeclAndHasAttrs({DC, false}, false), Loc(L),
+        DeclKind(DK), Implicit(false), Used(false), Referenced(false),
         TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
         CacheValidAndLinkage(llvm::to_underlying(Linkage::Invalid)) {
-    if (StatisticsEnabled) add(DK);
+    if (StatisticsEnabled)
+      add(DK);
   }
 
   Decl(Kind DK, EmptyShell Empty)
-      : DeclKind(DK), InvalidDecl(false), HasAttrs(false), Implicit(false),
-        Used(false), Referenced(false), TopLevelDeclInObjCContainer(false),
-        Access(AS_none), FromASTFile(0),
+      : DeclKind(DK), Implicit(false), Used(false), Referenced(false),
+        TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
         IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
         CacheValidAndLinkage(llvm::to_underlying(Linkage::Invalid)) {
-    if (StatisticsEnabled) add(DK);
+    if (StatisticsEnabled)
+      add(DK);
   }
 
   virtual ~Decl();
@@ -520,7 +533,7 @@ class alignas(8) Decl {
     return AccessSpecifier(Access);
   }
 
-  bool hasAttrs() const { return HasAttrs; }
+  bool hasAttrs() const { return DeclCtxWithInvalidDeclAndHasAttrs.getPointer().getInt(); }
 
   void setAttrs(const AttrVec& Attrs) {
     return setAttrsImpl(Attrs, getASTContext());
@@ -549,13 +562,16 @@ class alignas(8) Decl {
   }
 
   template <typename... Ts> void dropAttrs() {
-    if (!HasAttrs) return;
+    if (!hasAttrs()) return;
 
     AttrVec &Vec = getAttrs();
     llvm::erase_if(Vec, [](Attr *A) { return isa<Ts...>(A); });
 
-    if (Vec.empty())
-      HasAttrs = false;
+    if (Vec.empty()) {
+      auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+      InnerPtr.setInt(false);
+      DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
+    }
   }
 
   template <typename T> void dropAttr() { dropAttrs<T>(); }
@@ -590,7 +606,10 @@ class alignas(8) Decl {
   /// setInvalidDecl - Indicates the Decl had a semantic error. This
   /// allows for graceful error recovery.
   void setInvalidDecl(bool Invalid = true);
-  bool isInvalidDecl() const { return (bool) InvalidDecl; }
+
+  bool isInvalidDecl() const {
+    return DeclCtxWithInvalidDeclAndHasAttrs.getInt();
+  }
 
   /// isImplicit - Indicates whether the declaration was implicitly
   /// generated by the implementation. If false, this declaration
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 04bbc49ab2f319..c06b41d6b29f89 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -52,6 +52,8 @@
 
 using namespace clang;
 
+static_assert(sizeof(Decl) <= 32, "Decl grew beyond 32 bytes!");
+
 //===----------------------------------------------------------------------===//
 //  Statistics
 //===----------------------------------------------------------------------===//
@@ -130,7 +132,7 @@ const char *Decl::getDeclKindName() const {
 }
 
 void Decl::setInvalidDecl(bool Invalid) {
-  InvalidDecl = Invalid;
+  DeclCtxWithInvalidDeclAndHasAttrs.setInt(Invalid);
   assert(!isa<TagDecl>(this) || !cast<TagDecl>(this)->isCompleteDefinition());
   if (!Invalid) {
     return;
@@ -334,7 +336,9 @@ void PrettyStackTraceDecl::print(raw_ostream &OS) const {
 Decl::~Decl() = default;
 
 void Decl::setDeclContext(DeclContext *DC) {
-  DeclCtx = DC;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setPointer(DC);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
 }
 
 void Decl::setLexicalDeclContext(DeclContext *DC) {
@@ -364,12 +368,16 @@ void Decl::setLexicalDeclContext(DeclContext *DC) {
 void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
                                ASTContext &Ctx) {
   if (SemaDC == LexicalDC) {
-    DeclCtx = SemaDC;
+    auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+    InnerPtr.setPointer(SemaDC);
+    DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   } else {
     auto *MDC = new (Ctx) Decl::MultipleDC();
     MDC->SemanticDC = SemaDC;
     MDC->LexicalDC = LexicalDC;
-    DeclCtx = MDC;
+    auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+    InnerPtr.setPointer(MDC);
+    DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   }
 }
 
@@ -956,19 +964,24 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 }
 
 void Decl::setAttrsImpl(const AttrVec &attrs, ASTContext &Ctx) {
-  assert(!HasAttrs && "Decl already contains attrs.");
+  assert(!hasAttrs() && "Decl already contains attrs.");
 
   AttrVec &AttrBlank = Ctx.getDeclAttrs(this);
   assert(AttrBlank.empty() && "HasAttrs was wrong?");
 
   AttrBlank = attrs;
-  HasAttrs = true;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setInt(true);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
 }
 
 void Decl::dropAttrs() {
-  if (!HasAttrs) return;
+  if (!hasAttrs())
+    return;
 
-  HasAttrs = false;
+  auto InnerPtr = DeclCtxWithInvalidDeclAndHasAttrs.getPointer();
+  InnerPtr.setInt(false);
+  DeclCtxWithInvalidDeclAndHasAttrs.setPointer(InnerPtr);
   getASTContext().eraseDeclAttrs(this);
 }
 
@@ -996,7 +1009,7 @@ void Decl::addAttr(Attr *A) {
 }
 
 const AttrVec &Decl::getAttrs() const {
-  assert(HasAttrs && "No attrs to get!");
+  assert(hasAttrs() && "No attrs to get!");
   return getASTContext().getDeclAttrs(this);
 }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index a22f760408c634..2e186109327fb2 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -594,7 +594,7 @@ void ASTDeclReader::VisitDecl(Decl *D) {
   bool HasStandaloneLexicalDC = DeclBits.getNextBit();
   bool HasAttrs = DeclBits.getNextBit();
   D->setTopLevelDeclInObjCContainer(DeclBits.getNextBit());
-  D->InvalidDecl = DeclBits.getNextBit();
+  D->DeclCtxWithInvalidDeclAndHasAttrs.setInt(DeclBits.getNextBit());
   D->FromASTFile = true;
 
   if (D->isTemplateParameter() || D->isTemplateParameterPack() ||

@@ -268,17 +268,34 @@ class alignas(8) Decl {
/// }
/// void A::f(); // SemanticDC == namespace 'A'
/// // LexicalDC == global namespace
llvm::PointerUnion<DeclContext*, MultipleDC*> DeclCtx;
llvm::PointerIntPair<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it ends up being 'manual bit twiddling' so much as 3 values in an enum, and I also found the getPointer().getPointer() pretty mind-bending, but at least it is somewhat well hidden in stuff we won't touch for a while, so I won't insist.

@philnik777 philnik777 merged commit c6f9c84 into llvm:main Apr 14, 2024
4 checks passed
@philnik777 philnik777 deleted the reduce_decl_size branch April 14, 2024 10:08
philnik777 added a commit that referenced this pull request Apr 14, 2024
philnik777 added a commit that referenced this pull request Apr 14, 2024
…88654)

Reverts #87361

On 32 bit platforms there is only a single bit available in the
`DeclCtx`, resulting in an assertion failure.
bazuzi pushed a commit to bazuzi/llvm-project that referenced this pull request Apr 15, 2024
Class | Old size (in bytes) | New size (in bytes)

----------------------------------|---------------------|--------------------
Decl                              | 40                  | 32
AccessSpecDecl                    | 40                  | 40
BlockDecl                         | 128                 | 120
CapturedDecl                      | 88                  | 80
EmptyDecl                         | 40                  | 32
ExportDecl                        | 80                  | 72
ExternCContextDecl                | 72                  | 64
FileScopeAsmDecl                  | 56                  | 48
FriendDecl                        | 64                  | 56
FriendTemplateDecl                | 64                  | 64
ImplicitConceptSpecializationDecl | 40                  | 40
ImportDecl                        | 56                  | 48
LifetimeExtendedTemporaryDecl     | 72                  | 64
LinkageSpecDecl                   | 80                  | 72
NamedDecl                         | 48                  | 40
ObjCPropertyImplDecl              | 96                  | 88
PragmaCommentDecl                 | 40                  | 40
PragmaDetectMismatchDecl          | 48                  | 40
RequiresExprBodyDecl              | 72                  | 64
StaticAssertDecl                  | 64                  | 56
TopLevelStmtDecl                  | 88                  | 80
TranslationUnitDecl               | 104                 | 96
BaseUsingDecl                     | 56                  | 48
UsingDecl                         | 88                  | 80
UsingEnumDecl                     | 72                  | 64
HLSLBufferDecl                    | 96                  | 88
LabelDecl                         | 80                  | 72
NamespaceAliasDecl                | 96                  | 88
NamespaceDecl                     | 112                 | 104
ObjCCompatibleAliasDecl           | 56                  | 48
ObjCContainerDecl                 | 88                  | 80
ObjCMethodDecl                    | 136                 | 128
ObjCPropertyDecl                  | 128                 | 120
TemplateDecl                      | 64                  | 56
BuiltinTemplateDecl               | 72                  | 64
TypeDecl                          | 64                  | 56
UnresolvedUsingIfExistsDecl       | 48                  | 40
UsingDirectiveDecl                | 88                  | 80
UsingPackDecl                     | 64                  | 56
UsingShadowDecl                   | 80                  | 72
ValueDecl                         | 56                  | 48

When parsing libc++'s `<string>` header the used memory is reduced from
42.8MB to 42.5MB.
bazuzi pushed a commit to bazuzi/llvm-project that referenced this pull request Apr 15, 2024
…lvm#88654)

Reverts llvm#87361

On 32 bit platforms there is only a single bit available in the
`DeclCtx`, resulting in an assertion failure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants