Skip to content

[Clang] Optimize some getBeginLoc implementations #141058

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

cor3ntin
Copy link
Contributor

The bulk of the changes are in CallExpr

We cache Begin/End source locs in the trailing objects, in the space left by making the offset to the trailing objects static.
We also set a flag to indicate that we are calling an explicit object member function, further reducing the cost of getBeginLoc.

Fixes #140876

@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 May 22, 2025
@llvmbot
Copy link
Member

llvmbot commented May 22, 2025

@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

The bulk of the changes are in CallExpr

We cache Begin/End source locs in the trailing objects, in the space left by making the offset to the trailing objects static.
We also set a flag to indicate that we are calling an explicit object member function, further reducing the cost of getBeginLoc.

Fixes #140876


Patch is 27.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/141058.diff

10 Files Affected:

  • (modified) clang/include/clang/AST/Expr.h (+98-19)
  • (modified) clang/include/clang/AST/NestedNameSpecifier.h (+11-5)
  • (modified) clang/include/clang/AST/Stmt.h (+11-7)
  • (modified) clang/lib/AST/Expr.cpp (+48-76)
  • (modified) clang/lib/AST/ExprCXX.cpp (+28-14)
  • (modified) clang/lib/AST/NestedNameSpecifier.cpp (-12)
  • (modified) clang/lib/Sema/SemaOpenCL.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaOverload.cpp (+2)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+5)
  • (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+2)
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index e9c3c16c87598..2a1b5a838d794 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1344,7 +1344,13 @@ class DeclRefExpr final
 
   SourceLocation getLocation() const { return DeclRefExprBits.Loc; }
   void setLocation(SourceLocation L) { DeclRefExprBits.Loc = L; }
-  SourceLocation getBeginLoc() const LLVM_READONLY;
+
+  SourceLocation getBeginLoc() const {
+    if (hasQualifier())
+      return getQualifierLoc().getBeginLoc();
+    return DeclRefExprBits.Loc;
+  }
+
   SourceLocation getEndLoc() const LLVM_READONLY;
 
   /// Determine whether this declaration reference was preceded by a
@@ -2901,34 +2907,44 @@ class CallExpr : public Expr {
   //
   // * An optional of type FPOptionsOverride.
   //
-  // Note that we store the offset in bytes from the this pointer to the start
-  // of the trailing objects. It would be perfectly possible to compute it
-  // based on the dynamic kind of the CallExpr. However 1.) we have plenty of
-  // space in the bit-fields of Stmt. 2.) It was benchmarked to be faster to
-  // compute this once and then load the offset from the bit-fields of Stmt,
-  // instead of re-computing the offset each time the trailing objects are
-  // accessed.
+  // CallExpr subclasses are asssumed to be 32 bytes or less, and CallExpr
+  // itself is 24 bytes. To avoid having to recompute or store the offset of the
+  // trailing objects, we put it at 32 bytes (such that it is suitable for all
+  // subclasses) We use the 8 bytes gap left for instances of CallExpr to store
+  // the begin and end source locations. Caching the begin source location in
+  // particular as a significant impact on perf as getBeginLoc is assumed to be
+  // cheap.
+  // The layourt is as follow:
+  // CallExpr | Begin | End | Trailing Objects
+  // CXXMemberCallExpr | Trailing Objects
+  // A bit in CallExprBitfields indicates if source locations are presents.
 
+protected:
+  static constexpr unsigned offsetToTrailingObjects = 32;
+  template <typename T>
+  static constexpr unsigned
+  sizeToAllocateForCallExprSubclass(unsigned SizeOfTrailingObjects) {
+    static_assert(sizeof(T) <= CallExpr::offsetToTrailingObjects);
+    return SizeOfTrailingObjects + CallExpr::offsetToTrailingObjects;
+  }
+
+private:
   /// Return a pointer to the start of the trailing array of "Stmt *".
   Stmt **getTrailingStmts() {
     return reinterpret_cast<Stmt **>(reinterpret_cast<char *>(this) +
-                                     CallExprBits.OffsetToTrailingObjects);
+                                     offsetToTrailingObjects);
   }
   Stmt *const *getTrailingStmts() const {
     return const_cast<CallExpr *>(this)->getTrailingStmts();
   }
 
-  /// Map a statement class to the appropriate offset in bytes from the
-  /// this pointer to the trailing objects.
-  static unsigned offsetToTrailingObjects(StmtClass SC);
-
   unsigned getSizeOfTrailingStmts() const {
     return (1 + getNumPreArgs() + getNumArgs()) * sizeof(Stmt *);
   }
 
   size_t getOffsetOfTrailingFPFeatures() const {
     assert(hasStoredFPFeatures());
-    return CallExprBits.OffsetToTrailingObjects + getSizeOfTrailingStmts();
+    return offsetToTrailingObjects + getSizeOfTrailingStmts();
   }
 
 public:
@@ -2975,14 +2991,14 @@ class CallExpr : public Expr {
   FPOptionsOverride *getTrailingFPFeatures() {
     assert(hasStoredFPFeatures());
     return reinterpret_cast<FPOptionsOverride *>(
-        reinterpret_cast<char *>(this) + CallExprBits.OffsetToTrailingObjects +
+        reinterpret_cast<char *>(this) + offsetToTrailingObjects +
         getSizeOfTrailingStmts());
   }
   const FPOptionsOverride *getTrailingFPFeatures() const {
     assert(hasStoredFPFeatures());
     return reinterpret_cast<const FPOptionsOverride *>(
-        reinterpret_cast<const char *>(this) +
-        CallExprBits.OffsetToTrailingObjects + getSizeOfTrailingStmts());
+        reinterpret_cast<const char *>(this) + offsetToTrailingObjects +
+        getSizeOfTrailingStmts());
   }
 
 public:
@@ -3028,6 +3044,19 @@ class CallExpr : public Expr {
 
   bool hasStoredFPFeatures() const { return CallExprBits.HasFPFeatures; }
 
+  bool usesMemberSyntax() const {
+    return CallExprBits.ExplicitObjectMemFunUsingMemberSyntax;
+  }
+  void setUsesMemberSyntax(bool V = true) {
+    CallExprBits.ExplicitObjectMemFunUsingMemberSyntax = V;
+    // Because the source location may be different for explicit
+    // member, we reset the cached values.
+    if (CallExprBits.HasTrailingSourceLocs) {
+      CallExprBits.HasTrailingSourceLocs = false;
+      updateTrailingSourceLocs();
+    }
+  }
+
   bool isCoroElideSafe() const { return CallExprBits.IsCoroElideSafe; }
   void setCoroElideSafe(bool V = true) { CallExprBits.IsCoroElideSafe = V; }
 
@@ -3187,9 +3216,59 @@ class CallExpr : public Expr {
   SourceLocation getRParenLoc() const { return RParenLoc; }
   void setRParenLoc(SourceLocation L) { RParenLoc = L; }
 
-  SourceLocation getBeginLoc() const LLVM_READONLY;
-  SourceLocation getEndLoc() const LLVM_READONLY;
+  template <unsigned N> SourceLocation getTrailingSourceLoc() const {
+    static_assert(N <= 1);
+    assert(CallExprBits.HasTrailingSourceLocs && "No trailing source loc");
+    static_assert(sizeof(CallExpr) <=
+                  offsetToTrailingObjects + 2 * sizeof(SourceLocation));
+    return *reinterpret_cast<const SourceLocation *>(
+        reinterpret_cast<const char *>(this) + sizeof(CallExpr) +
+        sizeof(SourceLocation) * N);
+  }
+
+  SourceLocation getBeginLoc() const {
+    if (CallExprBits.HasTrailingSourceLocs)
+      return getTrailingSourceLoc<0>();
+
+    if (usesMemberSyntax()) {
+      if (auto FirstArgLoc = getArg(0)->getBeginLoc(); FirstArgLoc.isValid()) {
+        return FirstArgLoc;
+      }
+    }
+    return getCallee()->getBeginLoc();
+  }
+
+  SourceLocation getEndLoc() const {
+    if (CallExprBits.HasTrailingSourceLocs)
+      return getTrailingSourceLoc<0>();
+
+    SourceLocation end = getRParenLoc();
+    if (end.isInvalid() && getNumArgs() > 0 && getArg(getNumArgs() - 1))
+      end = getArg(getNumArgs() - 1)->getEndLoc();
+    return end;
+  }
+
+private:
+  bool hasTrailingSourceLoc() const {
+    return CallExprBits.HasTrailingSourceLocs;
+  }
 
+  void updateTrailingSourceLocs() {
+    assert(!CallExprBits.HasTrailingSourceLocs &&
+           "Trailing source loc already set?");
+    assert(getStmtClass() == CallExprClass &&
+           "Calling setTrailingSourceLocs on a subclass of CallExpr");
+    static_assert(sizeof(CallExpr) <=
+                  offsetToTrailingObjects + 2 * sizeof(SourceLocation));
+
+    SourceLocation *Locs = reinterpret_cast<SourceLocation *>(
+        reinterpret_cast<char *>(this) + sizeof(CallExpr));
+    new (Locs) SourceLocation(getBeginLoc());
+    new (Locs + 1) SourceLocation(getEndLoc());
+    CallExprBits.HasTrailingSourceLocs = true;
+  }
+
+public:
   /// Return true if this is a call to __assume() or __builtin_assume() with
   /// a non-value-dependent constant parameter evaluating as false.
   bool isBuiltinAssumeFalse(const ASTContext &Ctx) const;
diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h
index d7da3272d0943..952c79753d10a 100644
--- a/clang/include/clang/AST/NestedNameSpecifier.h
+++ b/clang/include/clang/AST/NestedNameSpecifier.h
@@ -283,7 +283,9 @@ class NestedNameSpecifierLoc {
   /// For example, if this instance refers to a nested-name-specifier
   /// \c \::std::vector<int>::, the returned source range would cover
   /// from the initial '::' to the last '::'.
-  SourceRange getSourceRange() const LLVM_READONLY;
+  SourceRange getSourceRange() const LLVM_READONLY {
+    return SourceRange(getBeginLoc(), getEndLoc());
+  }
 
   /// Retrieve the source range covering just the last part of
   /// this nested-name-specifier, not including the prefix.
@@ -296,14 +298,18 @@ class NestedNameSpecifierLoc {
   /// Retrieve the location of the beginning of this
   /// nested-name-specifier.
   SourceLocation getBeginLoc() const {
-    return getSourceRange().getBegin();
+    if (!Qualifier)
+      return SourceLocation();
+
+    NestedNameSpecifierLoc First = *this;
+    while (NestedNameSpecifierLoc Prefix = First.getPrefix())
+      First = Prefix;
+    return First.getLocalSourceRange().getBegin();
   }
 
   /// Retrieve the location of the end of this
   /// nested-name-specifier.
-  SourceLocation getEndLoc() const {
-    return getSourceRange().getEnd();
-  }
+  SourceLocation getEndLoc() const { return getLocalSourceRange().getEnd(); }
 
   /// Retrieve the location of the beginning of this
   /// component of the nested-name-specifier.
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 336eb6d3df7e1..e233f04b2405d 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -563,17 +563,21 @@ class alignas(void *) Stmt {
     unsigned HasFPFeatures : 1;
 
     /// True if the call expression is a must-elide call to a coroutine.
+    LLVM_PREFERRED_TYPE(bool)
     unsigned IsCoroElideSafe : 1;
 
-    /// Padding used to align OffsetToTrailingObjects to a byte multiple.
-    unsigned : 24 - 4 - NumExprBits;
+    /// Tracks When CallExpr is used to represent an explicit object
+    /// member function, in order to adjust the begin location.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned ExplicitObjectMemFunUsingMemberSyntax : 1;
 
-    /// The offset in bytes from the this pointer to the start of the
-    /// trailing objects belonging to CallExpr. Intentionally byte sized
-    /// for faster access.
-    unsigned OffsetToTrailingObjects : 8;
+    /// Indicates that SourceLocations are cached as
+    /// Trailing objects. See the definition of CallExpr.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned HasTrailingSourceLocs : 1;
   };
-  enum { NumCallExprBits = 32 };
+
+  enum { NumCallExprBits = 25 };
 
   class MemberExprBitfields {
     friend class ASTStmtReader;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index fe874ccd7b60f..7f768665b3157 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -547,11 +547,6 @@ void DeclRefExpr::setDecl(ValueDecl *NewD) {
   setDependence(computeDependence(this, NewD->getASTContext()));
 }
 
-SourceLocation DeclRefExpr::getBeginLoc() const {
-  if (hasQualifier())
-    return getQualifierLoc().getBeginLoc();
-  return getNameInfo().getBeginLoc();
-}
 SourceLocation DeclRefExpr::getEndLoc() const {
   if (hasExplicitTemplateArgs())
     return getRAngleLoc();
@@ -1455,6 +1450,23 @@ OverloadedOperatorKind UnaryOperator::getOverloadedOperator(Opcode Opc) {
 // Postfix Operators.
 //===----------------------------------------------------------------------===//
 
+static unsigned SizeOfCallExprInstance(Expr::StmtClass SC) {
+  switch (SC) {
+  case Expr::CallExprClass:
+    return sizeof(CallExpr);
+  case Expr::CXXOperatorCallExprClass:
+    return sizeof(CXXOperatorCallExpr);
+  case Expr::CXXMemberCallExprClass:
+    return sizeof(CXXMemberCallExpr);
+  case Expr::UserDefinedLiteralClass:
+    return sizeof(UserDefinedLiteral);
+  case Expr::CUDAKernelCallExprClass:
+    return sizeof(CUDAKernelCallExpr);
+  default:
+    llvm_unreachable("unexpected class deriving from CallExpr!");
+  }
+}
+
 CallExpr::CallExpr(StmtClass SC, Expr *Fn, ArrayRef<Expr *> PreArgs,
                    ArrayRef<Expr *> Args, QualType Ty, ExprValueKind VK,
                    SourceLocation RParenLoc, FPOptionsOverride FPFeatures,
@@ -1464,11 +1476,8 @@ CallExpr::CallExpr(StmtClass SC, Expr *Fn, ArrayRef<Expr *> PreArgs,
   unsigned NumPreArgs = PreArgs.size();
   CallExprBits.NumPreArgs = NumPreArgs;
   assert((NumPreArgs == getNumPreArgs()) && "NumPreArgs overflow!");
-
-  unsigned OffsetToTrailingObjects = offsetToTrailingObjects(SC);
-  CallExprBits.OffsetToTrailingObjects = OffsetToTrailingObjects;
-  assert((CallExprBits.OffsetToTrailingObjects == OffsetToTrailingObjects) &&
-         "OffsetToTrailingObjects overflow!");
+  assert(SizeOfCallExprInstance(SC) <= offsetToTrailingObjects &&
+         "This CallExpr subclass is too big or unsupported");
 
   CallExprBits.UsesADL = static_cast<bool>(UsesADL);
 
@@ -1484,6 +1493,9 @@ CallExpr::CallExpr(StmtClass SC, Expr *Fn, ArrayRef<Expr *> PreArgs,
 
   CallExprBits.HasFPFeatures = FPFeatures.requiresTrailingStorage();
   CallExprBits.IsCoroElideSafe = false;
+  CallExprBits.ExplicitObjectMemFunUsingMemberSyntax = false;
+  CallExprBits.HasTrailingSourceLocs = false;
+
   if (hasStoredFPFeatures())
     setStoredFPFeatures(FPFeatures);
 }
@@ -1493,13 +1505,10 @@ CallExpr::CallExpr(StmtClass SC, unsigned NumPreArgs, unsigned NumArgs,
     : Expr(SC, Empty), NumArgs(NumArgs) {
   CallExprBits.NumPreArgs = NumPreArgs;
   assert((NumPreArgs == getNumPreArgs()) && "NumPreArgs overflow!");
-
-  unsigned OffsetToTrailingObjects = offsetToTrailingObjects(SC);
-  CallExprBits.OffsetToTrailingObjects = OffsetToTrailingObjects;
-  assert((CallExprBits.OffsetToTrailingObjects == OffsetToTrailingObjects) &&
-         "OffsetToTrailingObjects overflow!");
   CallExprBits.HasFPFeatures = HasFPFeatures;
   CallExprBits.IsCoroElideSafe = false;
+  CallExprBits.ExplicitObjectMemFunUsingMemberSyntax = false;
+  CallExprBits.HasTrailingSourceLocs = false;
 }
 
 CallExpr *CallExpr::Create(const ASTContext &Ctx, Expr *Fn,
@@ -1510,41 +1519,41 @@ CallExpr *CallExpr::Create(const ASTContext &Ctx, Expr *Fn,
   unsigned NumArgs = std::max<unsigned>(Args.size(), MinNumArgs);
   unsigned SizeOfTrailingObjects = CallExpr::sizeOfTrailingObjects(
       /*NumPreArgs=*/0, NumArgs, FPFeatures.requiresTrailingStorage());
-  void *Mem =
-      Ctx.Allocate(sizeof(CallExpr) + SizeOfTrailingObjects, alignof(CallExpr));
-  return new (Mem) CallExpr(CallExprClass, Fn, /*PreArgs=*/{}, Args, Ty, VK,
-                            RParenLoc, FPFeatures, MinNumArgs, UsesADL);
+  void *Mem = Ctx.Allocate(
+      sizeToAllocateForCallExprSubclass<CallExpr>(SizeOfTrailingObjects),
+      alignof(CallExpr));
+  CallExpr *E =
+      new (Mem) CallExpr(CallExprClass, Fn, /*PreArgs=*/{}, Args, Ty, VK,
+                         RParenLoc, FPFeatures, MinNumArgs, UsesADL);
+  E->updateTrailingSourceLocs();
+  return E;
 }
 
 CallExpr *CallExpr::CreateEmpty(const ASTContext &Ctx, unsigned NumArgs,
                                 bool HasFPFeatures, EmptyShell Empty) {
   unsigned SizeOfTrailingObjects =
       CallExpr::sizeOfTrailingObjects(/*NumPreArgs=*/0, NumArgs, HasFPFeatures);
-  void *Mem =
-      Ctx.Allocate(sizeof(CallExpr) + SizeOfTrailingObjects, alignof(CallExpr));
+  void *Mem = Ctx.Allocate(
+      sizeToAllocateForCallExprSubclass<CallExpr>(SizeOfTrailingObjects),
+      alignof(CallExpr));
   return new (Mem)
       CallExpr(CallExprClass, /*NumPreArgs=*/0, NumArgs, HasFPFeatures, Empty);
 }
 
-unsigned CallExpr::offsetToTrailingObjects(StmtClass SC) {
-  switch (SC) {
-  case CallExprClass:
-    return sizeof(CallExpr);
-  case CXXOperatorCallExprClass:
-    return sizeof(CXXOperatorCallExpr);
-  case CXXMemberCallExprClass:
-    return sizeof(CXXMemberCallExpr);
-  case UserDefinedLiteralClass:
-    return sizeof(UserDefinedLiteral);
-  case CUDAKernelCallExprClass:
-    return sizeof(CUDAKernelCallExpr);
-  default:
-    llvm_unreachable("unexpected class deriving from CallExpr!");
-  }
-}
-
 Decl *Expr::getReferencedDeclOfCallee() {
-  Expr *CEE = IgnoreParenImpCasts();
+
+  // Optimize for the common case first
+  // (simple function or member function call)
+  // then try more exotic possibilities
+  Expr *CEE = IgnoreImpCasts();
+
+  if (auto *DRE = dyn_cast<DeclRefExpr>(CEE))
+    return DRE->getDecl();
+
+  if (auto *ME = dyn_cast<MemberExpr>(CEE))
+    return ME->getMemberDecl();
+
+  CEE = CEE->IgnoreParens();
 
   while (auto *NTTP = dyn_cast<SubstNonTypeTemplateParmExpr>(CEE))
     CEE = NTTP->getReplacement()->IgnoreParenImpCasts();
@@ -1638,43 +1647,6 @@ CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const {
   return {nullptr, nullptr};
 }
 
-SourceLocation CallExpr::getBeginLoc() const {
-  if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(this))
-    return OCE->getBeginLoc();
-
-  // A non-dependent call to a member function with an explicit object parameter
-  // is modelled with the object expression being the first argument, e.g. in
-  // `o.f(x)`, the callee will be just `f`, and `o` will be the first argument.
-  // Since the first argument is written before the callee, the expression's
-  // begin location should come from the first argument.
-  // This does not apply to dependent calls, which are modelled with `o.f`
-  // being the callee.
-  if (!isTypeDependent()) {
-    if (const auto *Method =
-            dyn_cast_if_present<const CXXMethodDecl>(getCalleeDecl());
-        Method && Method->isExplicitObjectMemberFunction()) {
-      if (auto FirstArgLoc = getArg(0)->getBeginLoc(); FirstArgLoc.isValid()) {
-        return FirstArgLoc;
-      }
-    }
-  }
-
-  SourceLocation begin = getCallee()->getBeginLoc();
-  if (begin.isInvalid() && getNumArgs() > 0 && getArg(0))
-    begin = getArg(0)->getBeginLoc();
-  return begin;
-}
-
-SourceLocation CallExpr::getEndLoc() const {
-  if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(this))
-    return OCE->getEndLoc();
-
-  SourceLocation end = getRParenLoc();
-  if (end.isInvalid() && getNumArgs() > 0 && getArg(getNumArgs() - 1))
-    end = getArg(getNumArgs() - 1)->getEndLoc();
-  return end;
-}
-
 OffsetOfExpr *OffsetOfExpr::Create(const ASTContext &C, QualType type,
                                    SourceLocation OperatorLoc,
                                    TypeSourceInfo *tsi,
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 00bddce3a1ee2..5c712e146e5a8 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -619,8 +619,10 @@ CXXOperatorCallExpr::Create(const ASTContext &Ctx,
   unsigned NumArgs = Args.size();
   unsigned SizeOfTrailingObjects = CallExpr::sizeOfTrailingObjects(
       /*NumPreArgs=*/0, NumArgs, FPFeatures.requiresTrailingStorage());
-  void *Mem = Ctx.Allocate(sizeof(CXXOperatorCallExpr) + SizeOfTrailingObjects,
-                           alignof(CXXOperatorCallExpr));
+  void *Mem =
+      Ctx.Allocate(sizeToAllocateForCallExprSubclass<CXXOperatorCallExpr>(
+                       SizeOfTrailingObjects),
+                   alignof(CXXOperatorCallExpr));
   return new (Mem) CXXOperatorCallExpr(OpKind, Fn, Args, Ty, VK, OperatorLoc,
                                        FPFeatures, UsesADL);
 }
@@ -632,8 +634,10 @@ CXXOperatorCallExpr *CXXOperatorCallExpr::CreateEmpty(const ASTContext &Ctx,
   // Allocate storage for the trailing objects of CallExpr.
   unsigned SizeOfTrailingObjects =
       CallExpr::sizeOfTrailingObjects(/*NumPreArgs=*/0, NumArgs, HasFPFeatures);
-  void *Mem = Ctx.Allocate(sizeof(CXXOperatorCallExpr) + SizeOfTrailingObjects,
-                           alignof(CXXOperatorCallExpr));
+  void *Mem =
+      Ctx.Allocate(sizeToAllocateForCallExprSubclass<CXXOperatorCallExpr>(
+                       SizeOfTrailingObjects),
+                   alignof(CXXOperatorCallExpr));
   return new (Mem) CXXOperatorCallExpr(NumArgs, HasFPFeatures, Empty);
 }
 
@@ -684,7 +688,8 @@ CXXMemberCallExpr *CXXMemberCallExpr::Create(const ASTContext &Ctx, Expr *Fn,
   unsigned NumArgs = std::max<unsigned>(Args.size(), MinNumArgs);
   unsigned SizeOfTrailingObjects = CallExpr::sizeOfTrailingObjects(
       /*NumPreArgs=*/0, NumArgs, FPFeatures.requiresTrailingStorage());
-  void *Mem = Ctx.Allocate(sizeof(CXXMemberCallExpr) + SizeOfTrailingObjects,
+  void *Mem = Ctx.Allocate(sizeToAllocateForCallExprSubclass<CXXMemberCallExpr>(
+                               SizeOfTrailingObjects),
                            alignof(CXXMemberCallExpr));
   return new (Mem)
       CXXMemberCallExpr(Fn, Args, Ty, VK, RP, FPFeatures, MinNumArgs);
@@ -697,7 +702,8 @@ CXXMemberCallExpr *CXXMemberCallExpr::CreateEmpty(const ASTContext &Ctx,
   // Allocate storage for the trailing objects of CallExpr.
   unsigned SizeOfTrailingObjects =
       CallExpr::sizeOfTrailingObjects(/*NumPreArgs=*/0, NumArgs, HasFPFeatures);
-  void *Mem = Ctx.Allocate(sizeof(CXXMemberCallExpr) + SizeOfTrailingObjects,
+  void *Mem = Ctx.Allocate(sizeToAllocateForCallExprSubclass<...
[truncated]

@cor3ntin
Copy link
Contributor Author

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.

Thank you for working on this!

// CallExpr subclasses are asssumed to be 32 bytes or less, and CallExpr
// itself is 24 bytes. To avoid having to recompute or store the offset of the
// trailing objects, we put it at 32 bytes (such that it is suitable for all
// subclasses) We use the 8 bytes gap left for instances of CallExpr to store
Copy link
Collaborator

Choose a reason for hiding this comment

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

This works today, but we keep talking about moving to 64-bit source locations (and that's getting more important thanks to modules), what's the idea for in that situation? Grow the size of CallExpr?

Copy link
Contributor Author

@cor3ntin cor3ntin May 22, 2025

Choose a reason for hiding this comment

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

We have 4 bytes left now. But yeah, if we need more space, we would need a different strategy


protected:
static constexpr unsigned offsetToTrailingObjects = 32;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
static constexpr unsigned offsetToTrailingObjects = 32;
static constexpr unsigned OffsetToTrailingObjects = 32;

if (CallExprBits.HasTrailingSourceLocs)
return getTrailingSourceLoc<0>();

SourceLocation end = getRParenLoc();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
SourceLocation end = getRParenLoc();
SourceLocation End = getRParenLoc();

offsetToTrailingObjects + 2 * sizeof(SourceLocation));

SourceLocation *Locs = reinterpret_cast<SourceLocation *>(
reinterpret_cast<char *>(this) + sizeof(CallExpr));
Copy link
Collaborator

Choose a reason for hiding this comment

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

reinterpret_cast<SourceLocation *>(this + 1) ?


// Optimize for the common case first
// (simple function or member function call)
// then try more exotic possibilities
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// then try more exotic possibilities
// then try more exotic possibilities.

if (auto *ME = dyn_cast<MemberExpr>(CEE))
return ME->getMemberDecl();

CEE = CEE->IgnoreParens();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Warning bells going off!

From IgnoreParenImpCasts():

  /// Skip past any parentheses and implicit casts which might surround this
  /// expression until reaching a fixed point.
  /// FIXME: IgnoreParenImpCasts really ought to be equivalent to
  /// IgnoreParens() + IgnoreImpCasts() until reaching a fixed point. However
  /// this is currently not the case. Instead IgnoreParenImpCasts() skips:

Are we subtly changing behavior here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not in so far as the tests pass (we already handle SubstNonTypeTemplateParmExpr, and MaterializeTemporaryExpr is a prvalue), so... it should be fine. If not, we will add more tests!

Copy link

github-actions bot commented May 22, 2025

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

// The layourt is as follow:
// CallExpr | Begin | 4 bytes left | Trailing Objects
// CXXMemberCallExpr | Trailing Objects
// A bit in CallExprBitfields indicates if source locations are presents.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// A bit in CallExprBitfields indicates if source locations are presents.
// A bit in CallExprBitfields indicates if source locations are present.

SourceLocation getEndLoc() const LLVM_READONLY;
SourceLocation getBeginLoc() const {
if (CallExprBits.HasTrailingSourceLoc) {
assert(CallExprBits.HasTrailingSourceLoc && "No trailing source loc");
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the point of this assert? Right after you just did the same thing in the 'if' above?

reinterpret_cast<const char *>(this) + sizeof(CallExpr));
}

if (usesMemberSyntax()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

no curleys on single liners?

// then try more exotic possibilities
Expr *CEE = IgnoreImpCasts();

if (auto *DRE = dyn_cast<DeclRefExpr>(CEE))
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 if this function would benefit SEVERELY from a llvm::typeSwitch.

Comment on lines +301 to +307
if (!Qualifier)
return SourceLocation();

NestedNameSpecifierLoc First = *this;
while (NestedNameSpecifierLoc Prefix = First.getPrefix())
First = Prefix;
return First.getLocalSourceRange().getBegin();
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't see the differences of changes on these functions... Are they related?

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.

Expr::getExprLoc() is slow
6 participants