https://github.com/Lancern created 
https://github.com/llvm/llvm-project/pull/212284

This patch folds non-volatile non-atomic `cir.load` operation that loads from a 
constant alloca slot into the initial value stored into that slot, if the 
initialization dominates the load.

This effectively enables "constant folding" at the C/C++ language level. 
Consider the following C/C++ source program:

```cpp
int g();
void use(int);

void h(const int *);   // <-- The body of h is external.
void f() {
  const int x = g();
  h(&x);
  use(x);
}
```

Since `h` is external, the LLVM optimizer cannot eliminate the load of `x` 
before the call to the `use` function. This patch enables this optimization by 
folding the load of `x` early on CIR.

PR #175037 has some prior discussions on this topic.

Assisted-by: codex / gpt-5.6 sol

>From 19e34c5d09dfc5239ec713e98b8009559514e194 Mon Sep 17 00:00:00 2001
From: Sirui Mu <[email protected]>
Date: Mon, 27 Jul 2026 23:35:30 +0800
Subject: [PATCH] [CIR] Fold load from constant alloca slots

This patch folds `cir.load` operation that loads from a constant alloca slot
into the initial value stored into that slot, if the initialization dominates
the load.

This effectively enables "constant folding" at the C/C++ language level.
Consider the following C/C++ source program:

```cpp
int g();
void use(int);

void h(const int *);   // <-- The body of h is external.
void f() {
  const int x = g();
  h(&x);
  use(x);
}
```

Since `h` is external, the conventional LLVM optimizer cannot eliminate the load
of `x` before the call to the `use` function. This patch enables this
optimization by folding the load of `x` priorly on ClangIR.

PR #175037 has some prior discussions on this topic.

Assisted-by: codex / gpt-5.6 sol
---
 .../CIR/Dialect/Transforms/CIRSimplify.cpp    | 48 +++++++++-
 .../CIR/Transforms/constant-load-fold.cir     | 95 +++++++++++++++++++
 .../CIR/Transforms/constant-load-fold.cpp     | 29 ++++++
 3 files changed, 171 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CIR/Transforms/constant-load-fold.cir
 create mode 100644 clang/test/CIR/Transforms/constant-load-fold.cpp

diff --git a/clang/lib/CIR/Dialect/Transforms/CIRSimplify.cpp 
b/clang/lib/CIR/Dialect/Transforms/CIRSimplify.cpp
index 45cf41052089c..b1e0d278724e3 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRSimplify.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRSimplify.cpp
@@ -9,6 +9,7 @@
 #include "PassDetail.h"
 #include "mlir/Dialect/Func/IR/FuncOps.h"
 #include "mlir/IR/Block.h"
+#include "mlir/IR/Dominance.h"
 #include "mlir/IR/Operation.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/IR/Region.h"
@@ -32,6 +33,50 @@ namespace mlir {
 
 namespace {
 
+/// Simplify `cir.load` that loads from an alloca marked as "constant".
+///
+/// For example:
+///
+///   %0 = cir.alloca "x" align(4) const : !cir.ptr<!s32i>
+///   cir.store %init, %0 : !s32i, !cir.ptr<!s32i>
+///   %1 = cir.load %0 : !cir.ptr<!s32i>
+///
+/// All uses of the load above could be replaced with the SSA value `%init`.
+struct SimplifyConstantLoad : public OpRewritePattern<LoadOp> {
+  using OpRewritePattern<LoadOp>::OpRewritePattern;
+  LogicalResult matchAndRewrite(LoadOp op,
+                                PatternRewriter &rewriter) const override {
+    // Volatile or atomic loads should not be simplified.
+    if (op.getIsVolatile() || op.getMemOrder())
+      return mlir::failure();
+
+    auto allocaOp = op.getAddr().getDefiningOp<cir::AllocaOp>();
+    if (!allocaOp || !allocaOp.getConstant())
+      return mlir::failure();
+
+    cir::StoreOp initStoreOp;
+    for (const mlir::OpOperand &use : allocaOp->getUses()) {
+      auto store = mlir::dyn_cast<cir::StoreOp>(use.getOwner());
+      if (!store || use.getOperandNumber() != cir::StoreOp::odsIndex_addr)
+        continue;
+      if (initStoreOp) {
+        // We have multiple stores to the alloca, give up
+        return mlir::failure();
+      }
+      initStoreOp = store;
+    }
+    if (!initStoreOp)
+      return mlir::failure();
+
+    mlir::DominanceInfo dominance;
+    if (!dominance.dominates(initStoreOp, op))
+      return mlir::failure();
+
+    rewriter.replaceOp(op, initStoreOp.getValue());
+    return mlir::success();
+  }
+};
+
 /// Simplify suitable ternary operations into select operations.
 ///
 /// For now we only simplify those ternary operations whose true and false
@@ -296,6 +341,7 @@ struct CIRSimplifyPass : public 
impl::CIRSimplifyBase<CIRSimplifyPass> {
 void populateMergeCleanupPatterns(RewritePatternSet &patterns) {
   // clang-format off
   patterns.add<
+    SimplifyConstantLoad,
     SimplifyTernary,
     SimplifySelect,
     SimplifySwitch,
@@ -312,7 +358,7 @@ void CIRSimplifyPass::runOnOperation() {
   // Collect operations to apply patterns.
   llvm::SmallVector<Operation *, 16> ops;
   getOperation()->walk([&](Operation *op) {
-    if (isa<TernaryOp, SelectOp, SwitchOp, VecSplatOp>(op))
+    if (isa<TernaryOp, SelectOp, SwitchOp, VecSplatOp, LoadOp>(op))
       ops.push_back(op);
   });
 
diff --git a/clang/test/CIR/Transforms/constant-load-fold.cir 
b/clang/test/CIR/Transforms/constant-load-fold.cir
new file mode 100644
index 0000000000000..28a5c76df1b42
--- /dev/null
+++ b/clang/test/CIR/Transforms/constant-load-fold.cir
@@ -0,0 +1,95 @@
+// RUN: cir-opt %s -cir-simplify | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+cir.func private @use_ptr(!cir.ptr<!s32i>)
+
+// CHECK-LABEL: cir.func @fold_constant_load
+// CHECK: %[[ALLOCA:.*]] = cir.alloca "x" {{.*}} const
+// CHECK: %[[INIT:.*]] = cir.const #cir.int<100>
+// CHECK: cir.store %[[INIT]], %[[ALLOCA]]
+// CHECK: cir.call @use_ptr(%[[ALLOCA]])
+// CHECK-NOT: cir.load
+// CHECK: cir.return %[[INIT]]
+cir.func @fold_constant_load() -> !s32i {
+  %x = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  %init = cir.const #cir.int<100> : !s32i
+  cir.store %init, %x : !s32i, !cir.ptr<!s32i>
+  cir.call @use_ptr(%x) : (!cir.ptr<!s32i>) -> ()
+  %value = cir.load %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_non_constant_alloca
+// CHECK: %[[VALUE:.*]] = cir.load
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_non_constant_alloca() -> !s32i {
+  %x = cir.alloca "x" align(4) init : !cir.ptr<!s32i>
+  %init = cir.const #cir.int<103> : !s32i
+  cir.store %init, %x : !s32i, !cir.ptr<!s32i>
+  %value = cir.load %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_without_store
+// CHECK: %[[VALUE:.*]] = cir.load
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_without_store() -> !s32i {
+  %x = cir.alloca "x" align(4) const : !cir.ptr<!s32i>
+  %value = cir.load %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_multiple_stores
+// CHECK: cir.store
+// CHECK: cir.store
+// CHECK: %[[VALUE:.*]] = cir.load
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_multiple_stores() -> !s32i {
+  %x = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  %first = cir.const #cir.int<104> : !s32i
+  %second = cir.const #cir.int<105> : !s32i
+  cir.store %first, %x : !s32i, !cir.ptr<!s32i>
+  cir.store %second, %x : !s32i, !cir.ptr<!s32i>
+  %value = cir.load %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_non_dominating_store
+// CHECK: cir.brcond
+// CHECK: cir.store
+// CHECK: %[[VALUE:.*]] = cir.load
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_non_dominating_store(%cond: !cir.bool) -> !s32i {
+  %x = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  %init = cir.const #cir.int<106> : !s32i
+  cir.brcond %cond ^bb1, ^bb2
+^bb1:
+  cir.store %init, %x : !s32i, !cir.ptr<!s32i>
+  cir.br ^bb2
+^bb2:
+  %value = cir.load %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_volatile_load
+// CHECK: %[[VALUE:.*]] = cir.load volatile
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_volatile_load() -> !s32i {
+  %x = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  %init = cir.const #cir.int<107> : !s32i
+  cir.store %init, %x : !s32i, !cir.ptr<!s32i>
+  %value = cir.load volatile %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
+
+// CHECK-LABEL: cir.func @do_not_fold_atomic_load
+// CHECK: %[[VALUE:.*]] = cir.load atomic(seq_cst)
+// CHECK: cir.return %[[VALUE]]
+cir.func @do_not_fold_atomic_load() -> !s32i {
+  %x = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  %init = cir.const #cir.int<108> : !s32i
+  cir.store %init, %x : !s32i, !cir.ptr<!s32i>
+  %value = cir.load atomic(seq_cst) %x : !cir.ptr<!s32i>, !s32i
+  cir.return %value : !s32i
+}
diff --git a/clang/test/CIR/Transforms/constant-load-fold.cpp 
b/clang/test/CIR/Transforms/constant-load-fold.cpp
new file mode 100644
index 0000000000000..0dfee9f035ab7
--- /dev/null
+++ b/clang/test/CIR/Transforms/constant-load-fold.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -Wno-unused-value 
-fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -Wno-unused-value 
-fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+
+int g();
+void use(int);
+
+void h(const int *);
+
+// CIR-LABEL: @_Z18fold_constant_loadv
+// LLVM-LABEL: @_Z18fold_constant_loadv
+void fold_constant_load() {
+  const int x = g();
+  h(&x);
+  use(x);
+
+  // CIR: %[[ALLOCA:.+]] = cir.alloca "x" align(4) init const : !cir.ptr<!s32i>
+  // CIR: %[[INIT:.+]] = cir.call @_Z1gv()
+  // CIR: cir.store align(4) %[[INIT]], %[[ALLOCA]] : !s32i, !cir.ptr<!s32i>
+  // CIR: cir.call @_Z1hPKi(%[[ALLOCA]])
+  // CIR: cir.call @_Z3usei(%[[INIT]])
+
+  // LLVM: %[[ALLOCA:.+]] = alloca i32, align 4
+  // LLVM: %[[INIT:.+]] = tail call noundef i32 @_Z1gv()
+  // LLVM: store i32 %[[INIT]], ptr %[[ALLOCA]], align 4
+  // LLVM: call void @_Z1hPKi(ptr noundef nonnull %[[ALLOCA]])
+  // LLVM: call void @_Z3usei(i32 noundef %[[INIT]])
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to