[clang] [clang] Optimize EmbedExpr child iterator callback (PR #155803)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 28 02:46:48 PDT 2025


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/155803

Instead of querying the bitwidth and signeness of the integer literal for every iteration, get the bitwidth directly from the `APIntStorage` and assume the signeness to be `true` since we set the type of the `EmbedExpr` to `Ctx.IntTy` and the type of the integer literal to that of the `EmbedExpr`, so it should always be signed, as long as `ASTContext::IntTy` is signed.

Before:
```
$ hyperfine -r 50 -w 3 'bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000'
Benchmark 1: bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000
  Time (mean ± σ):      1.796 s ±  0.090 s    [User: 0.961 s, System: 0.834 s]
  Range (min … max):    1.640 s …  2.150 s    50 runs
```
After:
```
$ hyperfine -r 50 -w 3 'bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000'
Benchmark 1: bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000
  Time (mean ± σ):      1.700 s ±  0.050 s    [User: 0.909 s, System: 0.789 s]
  Range (min … max):    1.637 s …  1.880 s    50 runs
```

That is roughly .1s less, or whatever, 5% or something.

The benchmark is simply:
```c++
constexpr char str[] = {
#embed "sqlite3.c" suffix(,0)
};

constexpr char str2[] = {
#embed "sqlite3.c" suffix(,0)
};

constexpr char str3[] = {
#embed "sqlite3.c" suffix(,0)
};
```
where `sqlite3.c` contains the sqlite3 amalgamation (roughly 9 million characters).



>From 6945f7777d398413cfc34b1c887d04a43e33c594 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 28 Aug 2025 11:25:34 +0200
Subject: [PATCH] [clang] Optimize EmbedExpr child iterator callback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Before:
```
$ hyperfine -r 50 -w 3 'bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000'
Benchmark 1: bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000
  Time (mean ± σ):      1.796 s ±  0.090 s    [User: 0.961 s, System: 0.834 s]
  Range (min … max):    1.640 s …  2.150 s    50 runs
```
After:
```
Benchmark 1: bin/clang -c ../../benchmarks/embed.cpp -std=c++20 -fconstexpr-steps=1000000000
  Time (mean ± σ):      1.700 s ±  0.050 s    [User: 0.909 s, System: 0.789 s]
  Range (min … max):    1.637 s …  1.880 s    50 runs
```

That is rougly .1s less, or whatever, 5% or something.
---
 clang/include/clang/AST/APNumericStorage.h | 3 ++-
 clang/include/clang/AST/Expr.h             | 4 ++--
 clang/lib/AST/Expr.cpp                     | 1 +
 clang/lib/Serialization/ASTWriterStmt.cpp  | 2 +-
 4 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/APNumericStorage.h b/clang/include/clang/AST/APNumericStorage.h
index 95eddbcd86e83..e1948a552bf7e 100644
--- a/clang/include/clang/AST/APNumericStorage.h
+++ b/clang/include/clang/AST/APNumericStorage.h
@@ -28,7 +28,6 @@ class APNumericStorage {
     uint64_t VAL;   ///< Used to store the <= 64 bits integer value.
     uint64_t *pVal; ///< Used to store the >64 bits integer value.
   };
-  unsigned BitWidth;
 
   bool hasAllocation() const { return llvm::APInt::getNumWords(BitWidth) > 1; }
 
@@ -36,6 +35,7 @@ class APNumericStorage {
   void operator=(const APNumericStorage &) = delete;
 
 protected:
+  unsigned BitWidth;
   APNumericStorage() : VAL(0), BitWidth(0) {}
 
   llvm::APInt getIntValue() const {
@@ -51,6 +51,7 @@ class APNumericStorage {
 class APIntStorage : private APNumericStorage {
 public:
   llvm::APInt getValue() const { return getIntValue(); }
+  unsigned getBitWidth() const { return BitWidth; }
   void setValue(const ASTContext &C, const llvm::APInt &Val) {
     setIntValue(C, Val);
   }
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 574ee54077ce7..23a0996f02725 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -5113,9 +5113,9 @@ class EmbedExpr final : public Expr {
              "trying to dereference an invalid iterator");
       IntegerLiteral *N = EExpr->FakeChildNode;
       N->setValue(*EExpr->Ctx,
-                  llvm::APInt(N->getValue().getBitWidth(),
+                  llvm::APInt(N->getBitWidth(),
                               EExpr->Data->BinaryData->getCodeUnit(CurOffset),
-                              N->getType()->isSignedIntegerType()));
+                              /*Signed=*/true));
       // We want to return a reference to the fake child node in the
       // EmbedExpr, not the local variable N.
       return const_cast<typename BaseTy::reference>(EExpr->FakeChildNode);
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 512f152059a01..cdff160067fed 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -2395,6 +2395,7 @@ EmbedExpr::EmbedExpr(const ASTContext &Ctx, SourceLocation Loc,
   setDependence(ExprDependence::None);
   FakeChildNode = IntegerLiteral::Create(
       Ctx, llvm::APInt::getZero(Ctx.getTypeSize(getType())), getType(), Loc);
+  assert(getType()->isSignedIntegerType() && "IntTy should be signed");
 }
 
 InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc,
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index be9bad9e96cc1..301ed9b23c206 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -730,7 +730,7 @@ void ASTStmtWriter::VisitIntegerLiteral(IntegerLiteral *E) {
   Record.AddSourceLocation(E->getLocation());
   Record.AddAPInt(E->getValue());
 
-  if (E->getValue().getBitWidth() == 32) {
+  if (E->getBitWidth() == 32) {
     AbbrevToUse = Writer.getIntegerLiteralAbbrev();
   }
 



More information about the cfe-commits mailing list