riccibruno created this revision.
riccibruno added a reviewer: rjmccall.
riccibruno added a project: clang.
Herald added a subscriber: cfe-commits.
riccibruno added a dependency: D53716: [AST] Only store data for the NRVO 
candidate in ReturnStmt if needed..

Don't store the init statement and condition variable if not needed.
This cuts the size of `ForStmt` by up to 2 pointers, and 1 pointer in
the common case. The condition and increment are stored unconditionally
since for statements without a condition and increment are quite uncommon.
The order of the children is kept the same.


Repository:
  rC Clang

https://reviews.llvm.org/D53717

Files:
  include/clang/AST/Stmt.h
  lib/AST/ASTImporter.cpp
  lib/AST/Stmt.cpp
  lib/Sema/SemaStmt.cpp
  lib/Serialization/ASTReaderStmt.cpp
  lib/Serialization/ASTWriterStmt.cpp
  test/Import/for-stmt/test.cpp

Index: test/Import/for-stmt/test.cpp
===================================================================
--- test/Import/for-stmt/test.cpp
+++ test/Import/for-stmt/test.cpp
@@ -3,21 +3,17 @@
 // CHECK: ForStmt
 // CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: <<NULL>>
-// CHECK-NEXT: <<NULL>>
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: NullStmt
 
 // CHECK: ForStmt
 // CHECK-NEXT: DeclStmt
 // CHECK-NEXT: VarDecl
 // CHECK-NEXT: IntegerLiteral
 // CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: <<NULL>>
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: ContinueStmt
 
 // CHECK: ForStmt
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: DeclStmt
 // CHECK-NEXT: VarDecl
 // CHECK-NEXT: CXXBoolLiteralExpr
@@ -32,7 +28,6 @@
 // CHECK-NEXT: DeclStmt
 // CHECK-NEXT: VarDecl
 // CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: <<NULL>>
 
 // CHECK-NEXT: BinaryOperator
 // CHECK-NEXT: ImplicitCastExpr
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -208,11 +208,20 @@
 
 void ASTStmtWriter::VisitForStmt(ForStmt *S) {
   VisitStmt(S);
-  Record.AddStmt(S->getInit());
+
+  bool HasInit = !!S->getInit();
+  bool HasVar = !!S->getConditionVariableDeclStmt();
+  Record.push_back(HasInit);
+  Record.push_back(HasVar);
+
   Record.AddStmt(S->getCond());
-  Record.AddDeclRef(S->getConditionVariable());
   Record.AddStmt(S->getInc());
   Record.AddStmt(S->getBody());
+  if (HasInit)
+    Record.AddStmt(S->getInit());
+  if (HasVar)
+    Record.AddDeclRef(S->getConditionVariable());
+
   Record.AddSourceLocation(S->getForLoc());
   Record.AddSourceLocation(S->getLParenLoc());
   Record.AddSourceLocation(S->getRParenLoc());
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -292,11 +292,18 @@
 
 void ASTStmtReader::VisitForStmt(ForStmt *S) {
   VisitStmt(S);
-  S->setInit(Record.readSubStmt());
+
+  bool HasInit = Record.readInt();
+  bool HasVar = Record.readInt();
+
   S->setCond(Record.readSubExpr());
-  S->setConditionVariable(Record.getContext(), ReadDeclAs<VarDecl>());
   S->setInc(Record.readSubExpr());
   S->setBody(Record.readSubStmt());
+  if (HasInit)
+    S->setInit(Record.readSubStmt());
+  if (HasVar)
+    S->setConditionVariable(Record.getContext(), ReadDeclAs<VarDecl>());
+
   S->setForLoc(ReadSourceLocation());
   S->setLParenLoc(ReadSourceLocation());
   S->setRParenLoc(ReadSourceLocation());
@@ -2342,7 +2349,10 @@
       break;
 
     case STMT_FOR:
-      S = new (Context) ForStmt(Empty);
+      S = ForStmt::CreateEmpty(
+          Context,
+          /* HasInit=*/Record[ASTStmtReader::NumStmtFields + 0],
+          /* HasVar=*/Record[ASTStmtReader::NumStmtFields + 1]);
       break;
 
     case STMT_GOTO:
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -1780,9 +1780,9 @@
   if (isa<NullStmt>(Body))
     getCurCompoundScope().setHasEmptyLoopBodies();
 
-  return new (Context)
-      ForStmt(Context, First, Second.get().second, Second.get().first, Third,
-              Body, ForLoc, LParenLoc, RParenLoc);
+  return ForStmt::Create(Context, First, Second.get().second,
+                         Second.get().first, Third, Body, ForLoc, LParenLoc,
+                         RParenLoc);
 }
 
 /// In an Objective C collection iteration statement:
Index: lib/AST/Stmt.cpp
===================================================================
--- lib/AST/Stmt.cpp
+++ lib/AST/Stmt.cpp
@@ -881,36 +881,70 @@
   return isa<ObjCAvailabilityCheckExpr>(getCond());
 }
 
-ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar,
-                 Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP,
-                 SourceLocation RP)
-  : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP)
-{
-  SubExprs[INIT] = Init;
-  setConditionVariable(C, condVar);
-  SubExprs[COND] = Cond;
-  SubExprs[INC] = Inc;
-  SubExprs[BODY] = Body;
-  ForStmtBits.ForLoc = FL;
-}
-
-VarDecl *ForStmt::getConditionVariable() const {
-  if (!SubExprs[CONDVAR])
-    return nullptr;
+ForStmt::ForStmt(const ASTContext &Ctx, Stmt *Init, Expr *Cond,
+                 VarDecl *CondVar, Expr *Inc, Stmt *Body, SourceLocation FL,
+                 SourceLocation LP, SourceLocation RP)
+    : Stmt(ForStmtClass), LParenLoc(LP), RParenLoc(RP) {
+  bool HasInit = !!Init;
+  bool HasVar = !!CondVar;
+  ForStmtBits.HasInit = HasInit;
+  ForStmtBits.HasVar = HasVar;
+
+  setCond(Cond);
+  setBody(Body);
+  setInc(Inc);
+  if (HasInit)
+    setInit(Init);
+  if (HasVar)
+    setConditionVariable(Ctx, CondVar);
+
+  setForLoc(FL);
+}
+
+ForStmt::ForStmt(EmptyShell Empty, bool HasInit, bool HasVar)
+    : Stmt(ForStmtClass, Empty) {
+  ForStmtBits.HasInit = HasInit;
+  ForStmtBits.HasVar = HasVar;
+}
+
+ForStmt *ForStmt::Create(const ASTContext &Ctx, Stmt *Init, Expr *Cond,
+                         VarDecl *CondVar, Expr *Inc, Stmt *Body,
+                         SourceLocation FL, SourceLocation LP,
+                         SourceLocation RP) {
+  bool HasInit = !!Init;
+  bool HasVar = !!CondVar;
+  void *Mem = Ctx.Allocate(
+      totalSizeToAlloc<Stmt *>(NumMandatoryStmtPtr + HasInit + HasVar),
+      alignof(ForStmt));
+  return new (Mem) ForStmt(Ctx, Init, Cond, CondVar, Inc, Body, FL, LP, RP);
+}
+
+ForStmt *ForStmt::CreateEmpty(const ASTContext &Ctx, bool HasInit,
+                              bool HasVar) {
+  void *Mem = Ctx.Allocate(
+      totalSizeToAlloc<Stmt *>(NumMandatoryStmtPtr + HasInit + HasVar),
+      alignof(ForStmt));
+  return new (Mem) ForStmt(EmptyShell(), HasInit, HasVar);
+}
 
-  auto *DS = cast<DeclStmt>(SubExprs[CONDVAR]);
+VarDecl *ForStmt::getConditionVariable() {
+  auto *DS = getConditionVariableDeclStmt();
+  if (!DS)
+    return nullptr;
   return cast<VarDecl>(DS->getSingleDecl());
 }
 
-void ForStmt::setConditionVariable(const ASTContext &C, VarDecl *V) {
+void ForStmt::setConditionVariable(const ASTContext &Ctx, VarDecl *V) {
+  assert(hasVar() && "no storage for the condition variable in this ForStmt!");
+
   if (!V) {
-    SubExprs[CONDVAR] = nullptr;
+    getTrailingObjects<Stmt *>()[varOffset()] = nullptr;
     return;
   }
 
   SourceRange VarRange = V->getSourceRange();
-  SubExprs[CONDVAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(),
-                                       VarRange.getEnd());
+  getTrailingObjects<Stmt *>()[varOffset()] = new (Ctx)
+      DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd());
 }
 
 SwitchStmt::SwitchStmt(const ASTContext &Ctx, Stmt *init, VarDecl *Var,
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -5860,10 +5860,9 @@
       ToInit, ToCond, ToConditionVariable,  ToInc, ToBody, ToForLoc,
       ToLParenLoc, ToRParenLoc) = *Imp;
 
-  return new (Importer.getToContext()) ForStmt(
-      Importer.getToContext(),
-      ToInit, ToCond, ToConditionVariable, ToInc, ToBody, ToForLoc, ToLParenLoc,
-      ToRParenLoc);
+  return ForStmt::Create(Importer.getToContext(), ToInit, ToCond,
+                         ToConditionVariable, ToInc, ToBody, ToForLoc,
+                         ToLParenLoc, ToRParenLoc);
 }
 
 ExpectedStmt ASTNodeImporter::VisitGotoStmt(GotoStmt *S) {
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -219,6 +219,12 @@
 
     unsigned : NumStmtBits;
 
+    /// True if the ForStmt has storage for an init statement.
+    unsigned HasInit : 1;
+
+    /// True if the ForStmt has storage for a condition variable.
+    unsigned HasVar : 1;
+
     /// The location of the "for".
     SourceLocation ForLoc;
   };
@@ -1784,20 +1790,121 @@
 /// ForStmt - This represents a 'for (init;cond;inc)' stmt.  Note that any of
 /// the init/cond/inc parts of the ForStmt will be null if they were not
 /// specified in the source.
-class ForStmt : public Stmt {
-  enum { INIT, CONDVAR, COND, INC, BODY, END_EXPR };
-  Stmt* SubExprs[END_EXPR]; // SubExprs[INIT] is an expression or declstmt.
+class ForStmt final : public Stmt,
+                      private llvm::TrailingObjects<ForStmt, Stmt *> {
+  friend TrailingObjects;
+
+  /// The location of the left and right parentheses.
   SourceLocation LParenLoc, RParenLoc;
 
-public:
-  ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar,
+  // ForStmt is followed by several trailing objects, some of which optional.
+  // Note that it would be more convenient to put the optional trailing objects
+  // at the end but this would affect children(). Note also that even though
+  // it is possible to have a for statement without a condition or increment,
+  // the condition and increment are unconditionally stored since these kind
+  // of for statement are quite uncommon.
+  // The trailing objects are in order:
+  //
+  // * A "Stmt *" for the init statement.
+  //    Present if and only if hasInit(). This is in fact either an "Expr *"
+  //    or a "DeclStmt *" depending on whether the init statement is an
+  //    expression statement or a simple declaration.
+  //
+  // * A "Stmt *" for the condition variable.
+  //    Present if and only if hasVar(). This is in fact a "DeclStmt *".
+  //
+  // * A "Stmt *" for the condition.
+  //    Always present. This is in fact an "Expr *".
+  //
+  // * A "Stmt *" for the increment.
+  //    Always present. This is in fact an "Expr *".
+  //
+  // * A "Stmt *" for the body.
+  //    Always present.
+  enum { InitOffset = 0, IncOffsetFromCond = 1, BodyOffsetFromCond = 2 };
+  enum { NumMandatoryStmtPtr = 3 };
+
+  unsigned numTrailingObjects(OverloadToken<Stmt *>) const {
+    return NumMandatoryStmtPtr + hasInit() + hasVar();
+  }
+
+  unsigned initOffset() const { return InitOffset; }
+  unsigned varOffset() const { return InitOffset + hasInit(); }
+  unsigned condOffset() const { return InitOffset + hasInit() + hasVar(); }
+  unsigned incOffset() const { return condOffset() + IncOffsetFromCond; }
+  unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; }
+
+  /// True if this ForStmt has storage for an init statement.
+  bool hasInit() const { return ForStmtBits.HasInit; }
+
+  /// True if this ForStmt has storage for a condition variable.
+  bool hasVar() const { return ForStmtBits.HasVar; }
+
+  /// Build a for statement.
+  ForStmt(const ASTContext &Ctx, Stmt *Init, Expr *Cond, VarDecl *CondVar,
           Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP,
           SourceLocation RP);
 
   /// Build an empty for statement.
-  explicit ForStmt(EmptyShell Empty) : Stmt(ForStmtClass, Empty) {}
+  explicit ForStmt(EmptyShell Empty, bool HasInit, bool HasVar);
+
+public:
+  /// Create a for statement.
+  static ForStmt *Create(const ASTContext &Ctx, Stmt *Init, Expr *Cond,
+                         VarDecl *CondVar, Expr *Inc, Stmt *Body,
+                         SourceLocation FL, SourceLocation LP,
+                         SourceLocation RP);
+
+  /// Create an empty for statement optionally with storage for an init
+  /// statement and a condition variable.
+  static ForStmt *CreateEmpty(const ASTContext &Ctx, bool HasInit, bool HasVar);
+
+  Expr *getCond() {
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[condOffset()]);
+  }
+
+  const Expr *getCond() const {
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[condOffset()]);
+  }
+
+  void setCond(Expr *Cond) {
+    getTrailingObjects<Stmt *>()[condOffset()] = reinterpret_cast<Stmt *>(Cond);
+  }
+
+  Expr *getInc() {
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[incOffset()]);
+  }
+
+  const Expr *getInc() const {
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[incOffset()]);
+  }
 
-  Stmt *getInit() { return SubExprs[INIT]; }
+  void setInc(Expr *Inc) {
+    getTrailingObjects<Stmt *>()[incOffset()] = reinterpret_cast<Stmt *>(Inc);
+  }
+
+  Stmt *getBody() { return getTrailingObjects<Stmt *>()[bodyOffset()]; }
+  const Stmt *getBody() const {
+    return getTrailingObjects<Stmt *>()[bodyOffset()];
+  }
+
+  void setBody(Stmt *Body) {
+    getTrailingObjects<Stmt *>()[bodyOffset()] = Body;
+  }
+
+  Stmt *getInit() {
+    return hasInit() ? getTrailingObjects<Stmt *>()[initOffset()] : nullptr;
+  }
+
+  const Stmt *getInit() const {
+    return hasInit() ? getTrailingObjects<Stmt *>()[initOffset()] : nullptr;
+  }
+
+  void setInit(Stmt *Init) {
+    assert(hasInit() &&
+           "This for statement has no storage for an init statement!");
+    getTrailingObjects<Stmt *>()[initOffset()] = Init;
+  }
 
   /// Retrieve the variable declared in this "for" statement, if any.
   ///
@@ -1807,28 +1914,28 @@
   ///   // ...
   /// }
   /// \endcode
-  VarDecl *getConditionVariable() const;
+  VarDecl *getConditionVariable();
+  const VarDecl *getConditionVariable() const {
+    return const_cast<ForStmt *>(this)->getConditionVariable();
+  }
+
+  /// Set the condition variable declared in this for statement.
+  /// The for statement must have storage for this variable.
   void setConditionVariable(const ASTContext &C, VarDecl *V);
 
   /// If this ForStmt has a condition variable, return the faux DeclStmt
   /// associated with the creation of that condition variable.
-  const DeclStmt *getConditionVariableDeclStmt() const {
-    return reinterpret_cast<DeclStmt*>(SubExprs[CONDVAR]);
+  DeclStmt *getConditionVariableDeclStmt() {
+    return hasVar() ? static_cast<DeclStmt *>(
+                          getTrailingObjects<Stmt *>()[varOffset()])
+                    : nullptr;
   }
 
-  Expr *getCond() { return reinterpret_cast<Expr*>(SubExprs[COND]); }
-  Expr *getInc()  { return reinterpret_cast<Expr*>(SubExprs[INC]); }
-  Stmt *getBody() { return SubExprs[BODY]; }
-
-  const Stmt *getInit() const { return SubExprs[INIT]; }
-  const Expr *getCond() const { return reinterpret_cast<Expr*>(SubExprs[COND]);}
-  const Expr *getInc()  const { return reinterpret_cast<Expr*>(SubExprs[INC]); }
-  const Stmt *getBody() const { return SubExprs[BODY]; }
-
-  void setInit(Stmt *S) { SubExprs[INIT] = S; }
-  void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast<Stmt*>(E); }
-  void setInc(Expr *E) { SubExprs[INC] = reinterpret_cast<Stmt*>(E); }
-  void setBody(Stmt *S) { SubExprs[BODY] = S; }
+  const DeclStmt *getConditionVariableDeclStmt() const {
+    return hasVar() ? static_cast<DeclStmt *>(
+                          getTrailingObjects<Stmt *>()[varOffset()])
+                    : nullptr;
+  }
 
   SourceLocation getForLoc() const { return ForStmtBits.ForLoc; }
   void setForLoc(SourceLocation L) { ForStmtBits.ForLoc = L; }
@@ -1838,15 +1945,19 @@
   void setRParenLoc(SourceLocation L) { RParenLoc = L; }
 
   SourceLocation getBeginLoc() const { return getForLoc(); }
-  SourceLocation getEndLoc() const { return getBody()->getEndLoc(); }
+  SourceLocation getEndLoc() const LLVM_READONLY {
+    return getBody()->getEndLoc();
+  }
 
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == ForStmtClass;
   }
 
   // Iterators
   child_range children() {
-    return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR);
+    return child_range(getTrailingObjects<Stmt *>(),
+                       getTrailingObjects<Stmt *>() +
+                           numTrailingObjects(OverloadToken<Stmt *>()));
   }
 };
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D53717: [... Bruno Ricci via Phabricator via cfe-commits

Reply via email to