https://github.com/adams381 updated 
https://github.com/llvm/llvm-project/pull/171915

>From 517a1a251c8a5ec4422a2c63e6f779642dd5d160 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 11 Dec 2025 13:48:23 -0800
Subject: [PATCH 1/9] [CIR] Add emitDeclInvariant for global with constant
 storage

Implement emitDeclInvariant to emit llvm.invariant.start intrinsic for
global variables with constant storage. This enables optimizations by
marking when a global becomes read-only after initialization.

Changes:
- Add emitDeclInvariant and emitInvariantStart functions in CIRGenCXX.cpp
- Add emitInvariantStart declaration in CIRGenFunction.h
- Update emitCXXGlobalVarDeclInit to call emitDeclInvariant for constant
  storage globals after initialization
- Update getOrCreateCIRGlobal to set constant flag on globals with
  constant storage
- Add comprehensive test covering positive and negative cases

The implementation handles address spaces correctly, dynamically
constructing the intrinsic name (e.g., invariant.start.p0,
invariant.start.p10) based on the pointer's address space.
---
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp           |  72 +++++-
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |   2 +
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  25 +-
 .../CIR/CodeGen/global-constant-storage.cpp   | 244 ++++++++++++++++++
 4 files changed, 330 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/global-constant-storage.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index 71568ec87a31b..c0890f6b42663 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -16,11 +16,54 @@
 
 #include "clang/AST/GlobalDecl.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
 
+/// Emit code to cause the variable at the given address to be considered as
+/// constant from this point onwards.
+static void emitDeclInvariant(CIRGenFunction &CGF, const VarDecl *D) {
+  mlir::Value addr = CGF.cgm.getAddrOfGlobalVar(D);
+  CGF.emitInvariantStart(CGF.getContext().getTypeSizeInChars(D->getType()),
+                         addr, CGF.getLoc(D->getSourceRange()));
+}
+
+void CIRGenFunction::emitInvariantStart(CharUnits Size, mlir::Value Addr,
+                                        mlir::Location loc) {
+  // Do not emit the intrinsic if we're not optimizing.
+  if (!cgm.getCodeGenOpts().OptimizationLevel)
+    return;
+
+  CIRGenBuilderTy &builder = getBuilder();
+
+  // Create the size constant as i64
+  uint64_t width = Size.getQuantity();
+  mlir::Value sizeValue = builder.getConstInt(loc, builder.getSInt64Ty(),
+                                              static_cast<int64_t>(width));
+
+  // Determine address space for intrinsic name
+  unsigned addrSpace = 0;
+  if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(Addr.getType()))
+    addrSpace =
+        ptrTy.getAddrSpace() ? ptrTy.getAddrSpace().getValue().getUInt() : 0;
+
+  // Format intrinsic name with address space suffix (e.g.,
+  // "invariant.start.p0", "invariant.start.p10")
+  llvm::SmallString<32> intrinsicName;
+  llvm::raw_svector_ostream os(intrinsicName);
+  os << "invariant.start.p" << addrSpace;
+
+  // Create the intrinsic call. The llvm.invariant.start intrinsic returns a
+  // token, but we don't need to capture it. The return type is set to match
+  // the address type for consistency with the operation signature.
+  cir::LLVMIntrinsicCallOp::create(
+      builder, loc, builder.getStringAttr(intrinsicName), Addr.getType(),
+      mlir::ValueRange{sizeValue, Addr});
+}
+
 static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
                          cir::GlobalOp globalOp) {
   assert((varDecl->hasGlobalStorage() ||
@@ -234,13 +277,32 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl 
*varDecl,
 
     bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
                      QualType::DK_cxx_destructor;
+    bool isConstantStorage =
+        varDecl->getType().isConstantStorage(getASTContext(), true, 
!needsDtor);
     // PerformInit, constant store invariant / destroy handled below.
-    if (performInit)
+    if (performInit) {
       emitDeclInit(cgf, varDecl, addr);
-
-    if (varDecl->getType().isConstantStorage(getASTContext(), true, 
!needsDtor))
-      errorNYI(varDecl->getSourceRange(), "global with constant storage");
-    else
+      // For constant storage, emit invariant.start in the ctor region after
+      // initialization but before the yield.
+      if (isConstantStorage) {
+        CIRGenBuilderTy &builder = cgf.getBuilder();
+        mlir::OpBuilder::InsertionGuard guard(builder);
+        // Set insertion point to end of ctor region (before yield)
+        if (!addr.getCtorRegion().empty()) {
+          mlir::Block *block = &addr.getCtorRegion().back();
+          // Find the yield op and insert before it
+          mlir::Operation *yieldOp = block->getTerminator();
+          if (yieldOp) {
+            builder.setInsertionPoint(yieldOp);
+            emitDeclInvariant(cgf, varDecl);
+          }
+        }
+      }
+    } else if (isConstantStorage) {
+      emitDeclInvariant(cgf, varDecl);
+    }
+
+    if (!isConstantStorage)
       emitDeclDestroy(cgf, varDecl, addr);
     return;
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index efe0fe5fcc979..627698c5b19af 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1611,6 +1611,8 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::Value emitRuntimeCall(mlir::Location loc, cir::FuncOp callee,
                               llvm::ArrayRef<mlir::Value> args = {});
 
+  void emitInvariantStart(CharUnits Size, mlir::Value Addr, mlir::Location 
loc);
+
   /// Emit the computation of the specified expression of scalar type.
   mlir::Value emitScalarExpr(const clang::Expr *e,
                              bool ignoreResultAssign = false);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 1ad1c2fa41aa1..715e877fcca3d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -652,10 +652,19 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, 
mlir::Type ty,
 
   mlir::Location loc = getLoc(d->getSourceRange());
 
+  // Calculate constant storage flag before creating the global.
+  bool isConstant = false;
+  if (d) {
+    bool needsDtor =
+        d->needsDestruction(astContext) == QualType::DK_cxx_destructor;
+    isConstant = d->getType().isConstantStorage(
+        astContext, /*ExcludeCtor=*/true, /*ExcludeDtor=*/!needsDtor);
+  }
+
   // mlir::SymbolTable::Visibility::Public is the default, no need to 
explicitly
   // mark it as such.
   cir::GlobalOp gv =
-      CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, false,
+      CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, isConstant,
                                    /*insertPoint=*/entry.getOperation());
 
   // This is the first use or definition of a mangled name.  If there is a
@@ -675,10 +684,6 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, 
mlir::Type ty,
       errorNYI(d->getSourceRange(), "OpenMP target global variable");
 
     gv.setAlignmentAttr(getSize(astContext.getDeclAlign(d)));
-    // FIXME: This code is overly simple and should be merged with other global
-    // handling.
-    gv.setConstant(d->getType().isConstantStorage(
-        astContext, /*ExcludeCtor=*/false, /*ExcludeDtor=*/false));
 
     setLinkageForGV(gv, d);
 
@@ -870,10 +875,14 @@ void CIRGenModule::emitGlobalVarDefinition(const 
clang::VarDecl *vd,
     emitter->finalize(gv);
 
   // If it is safe to mark the global 'constant', do so now.
+  // Use the same logic as emitCXXGlobalVarDeclInit to determine constant
+  // storage.
+  bool needsDtor =
+      vd->needsDestruction(astContext) == QualType::DK_cxx_destructor;
   gv.setConstant((vd->hasAttr<CUDAConstantAttr>() && langOpts.CUDAIsDevice) ||
-                 (!needsGlobalCtor && !needsGlobalDtor &&
-                  vd->getType().isConstantStorage(
-                      astContext, /*ExcludeCtor=*/true, 
/*ExcludeDtor=*/true)));
+                 vd->getType().isConstantStorage(astContext,
+                                                 /*ExcludeCtor=*/true,
+                                                 /*ExcludeDtor=*/!needsDtor));
   assert(!cir::MissingFeatures::opGlobalSection());
 
   // Set CIR's linkage type as appropriate.
diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
new file mode 100644
index 0000000000000..8aa240cf8fc5b
--- /dev/null
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -0,0 +1,244 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm -O1 -disable-llvm-passes %s -o %t-opt.ll
+// RUN: FileCheck --check-prefix=LLVM-OPT --input-file=%t-opt.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -O1 
-disable-llvm-passes %s -o %t-opt-ogcg.ll
+// RUN: FileCheck --check-prefix=OGCG-OPT --input-file=%t-opt-ogcg.ll %s
+
+// Test for global with constant storage - const object with constructor but 
no destructor
+// Check that we add an llvm.invariant.start to mark when a global becomes 
read-only.
+
+struct A {
+  A();
+  int n;
+};
+
+// Should emit invariant.start - has constructor, no destructor, no mutable
+extern const A a = A();
+
+struct A2 {
+  A2();
+  constexpr ~A2() {}
+  int n;
+};
+
+// Should emit invariant.start - constexpr destructor doesn't prevent constant 
storage
+extern const A2 a2 = A2();
+
+struct B {
+  B();
+  mutable int n;
+};
+
+// Should NOT emit invariant.start - has mutable member
+extern const B b = B();
+
+struct CWithDtor {
+  CWithDtor();
+  ~CWithDtor();
+  int n;
+};
+
+// Should NOT emit invariant.start - has non-constexpr destructor
+extern const CWithDtor c_with_dtor = CWithDtor();
+
+// Simple case - just const C c; (no initializer) - Andy's suggestion
+class C {
+public:
+  C();
+  int a;
+  int b;
+};
+
+const C c;
+
+// CIR checks for 'a' - should have constant storage
+// CIR: cir.global constant external @a = #cir.zero : !rec_A
+// CIR: cir.func internal private @__cxx_global_var_init() {
+// CIR:   %[[OBJ:.*]] = cir.get_global @a : !cir.ptr<!rec_A>
+// CIR:   cir.call @_ZN1AC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A>) -> ()
+// CIR:   cir.return
+// CIR: }
+
+// CIR checks for 'a2' - should have constant storage (constexpr dtor)
+// CIR: cir.global constant external @a2 = #cir.zero : !rec_A2
+// CIR: cir.func internal private @__cxx_global_var_init.1() {
+// CIR:   %[[OBJ:.*]] = cir.get_global @a2 : !cir.ptr<!rec_A2>
+// CIR:   cir.call @_ZN2A2C1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A2>) -> ()
+// CIR:   cir.return
+// CIR: }
+
+// CIR checks for 'b' - should NOT have constant storage (mutable member)
+// CIR: cir.global external @b = #cir.zero : !rec_B
+// CIR: cir.func internal private @__cxx_global_var_init.2() {
+// CIR:   %[[OBJ:.*]] = cir.get_global @b : !cir.ptr<!rec_B>
+// CIR:   cir.call @_ZN1BC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_B>) -> ()
+// CIR:   cir.return
+// CIR: }
+
+// CIR checks for 'c_with_dtor' - should NOT have constant storage 
(non-constexpr dtor)
+// CIR: cir.global external @c_with_dtor = #cir.zero : !rec_CWithDtor
+// CIR: cir.func internal private @__cxx_global_var_init.3() {
+// CIR:   %[[OBJ:.*]] = cir.get_global @c_with_dtor : !cir.ptr<!rec_CWithDtor>
+// CIR:   cir.call @_ZN9CWithDtorC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_CWithDtor>) 
-> ()
+// CIR:   cir.return
+// CIR: }
+
+// CIR checks for 'c' - Andy's simple case, should have constant storage 
(internal linkage)
+// CIR: cir.global {{.*}} constant internal {{.*}} @_ZL1c = #cir.zero : !rec_C
+// CIR: cir.func internal private @__cxx_global_var_init.4() {
+// CIR:   %[[OBJ:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
+// CIR:   cir.call @_ZN1CC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_C>) -> ()
+// CIR:   cir.return
+// CIR: }
+
+// LLVM checks (no optimization)
+// Check all globals first (they appear at the top)
+// LLVM: @a ={{.*}} constant {{.*}} zeroinitializer
+// LLVM: @a2 ={{.*}} constant {{.*}} zeroinitializer
+// LLVM: @b ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @_ZL1c ={{.*}} constant {{.*}} zeroinitializer
+
+// Then check the init functions
+// LLVM: define internal void @__cxx_global_var_init() {
+// LLVM:   call void @_ZN1AC1Ev(ptr @a)
+// LLVM:   ret void
+// LLVM: }
+
+// LLVM: define internal void @__cxx_global_var_init.1() {
+// LLVM:   call void @_ZN2A2C1Ev(ptr @a2)
+// LLVM:   ret void
+// LLVM: }
+
+// LLVM: define internal void @__cxx_global_var_init.2() {
+// LLVM:   call void @_ZN1BC1Ev(ptr @b)
+// LLVM:   ret void
+// LLVM: }
+
+// LLVM: define internal void @__cxx_global_var_init.3() {
+// LLVM:   call void @_ZN9CWithDtorC1Ev(ptr @c_with_dtor)
+// LLVM:   ret void
+// LLVM: }
+
+// LLVM: define internal void @__cxx_global_var_init.4() {
+// LLVM:   call void @_ZN1CC1Ev(ptr @_ZL1c)
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG checks (no optimization)
+// Check all globals first (they appear at the top)
+// OGCG: @a ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @a2 ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @b ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
+
+// Then check the init functions
+// OGCG: define internal void @__cxx_global_var_init() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN1AC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@a)
+// OGCG:   ret void
+// OGCG: }
+
+// OGCG: define internal void @__cxx_global_var_init.1() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN2A2C1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a2)
+// OGCG:   ret void
+// OGCG: }
+
+// OGCG: define internal void @__cxx_global_var_init.2() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN1BC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@b)
+// OGCG:   ret void
+// OGCG: }
+
+// OGCG: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN9CWithDtorC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @c_with_dtor)
+// OGCG:   ret void
+// OGCG: }
+
+// OGCG: define internal void @__cxx_global_var_init.4() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 dereferenceable(8) 
@_ZL1c)
+// OGCG:   ret void
+// OGCG: }
+
+// With optimization enabled, should emit invariant.start intrinsic for 
constant storage cases
+// Check all globals first (they appear at the top)
+// LLVM-OPT: @a ={{.*}} constant {{.*}} zeroinitializer
+// LLVM-OPT: @a2 ={{.*}} constant {{.*}} zeroinitializer
+// LLVM-OPT: @b ={{.*}} global {{.*}} zeroinitializer
+// LLVM-OPT: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
+// LLVM-OPT: @_ZL1c ={{.*}} constant {{.*}} zeroinitializer
+
+// Then check the init functions with invariant.start
+// LLVM-OPT: define internal void @__cxx_global_var_init() {
+// LLVM-OPT:   call void @_ZN1AC1Ev(ptr @a)
+// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
+// LLVM-OPT: define internal void @__cxx_global_var_init.1() {
+// LLVM-OPT:   call void @_ZN2A2C1Ev(ptr @a2)
+// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
+// LLVM-OPT: define internal void @__cxx_global_var_init.2() {
+// LLVM-OPT:   call void @_ZN1BC1Ev(ptr @b)
+// LLVM-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr @b)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
+// LLVM-OPT: define internal void @__cxx_global_var_init.3() {
+// LLVM-OPT:   call void @_ZN9CWithDtorC1Ev(ptr @c_with_dtor)
+// LLVM-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr 
@c_with_dtor)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
+// LLVM-OPT: define internal void @__cxx_global_var_init.4() {
+// LLVM-OPT:   call void @_ZN1CC1Ev(ptr @_ZL1c)
+// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
+// OGCG-OPT checks (with optimization)
+// Check all globals first (they appear at the top)
+// OGCG-OPT: @a ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @b ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
+
+// Then check the init functions with invariant.start
+// OGCG-OPT: define internal void @__cxx_global_var_init() {{.*}} section 
".text.startup" {
+// OGCG-OPT:   call void @_ZN1AC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a)
+// OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a)
+// OGCG-OPT:   ret void
+// OGCG-OPT: }
+
+// OGCG-OPT: define internal void @__cxx_global_var_init.1() {{.*}} section 
".text.startup" {
+// OGCG-OPT:   call void @_ZN2A2C1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a2)
+// OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
+// OGCG-OPT:   ret void
+// OGCG-OPT: }
+
+// OGCG-OPT: define internal void @__cxx_global_var_init.2() {{.*}} section 
".text.startup" {
+// OGCG-OPT:   call void @_ZN1BC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @b)
+// OGCG-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr @b)
+// OGCG-OPT:   ret void
+// OGCG-OPT: }
+
+// OGCG-OPT: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
+// OGCG-OPT:   call void @_ZN9CWithDtorC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @c_with_dtor)
+// OGCG-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr 
@c_with_dtor)
+// OGCG-OPT:   ret void
+// OGCG-OPT: }
+
+// OGCG-OPT: define internal void @__cxx_global_var_init.4() {{.*}} section 
".text.startup" {
+// OGCG-OPT:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 
dereferenceable(8) @_ZL1c)
+// OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
+// OGCG-OPT:   ret void
+// OGCG-OPT: }
+

>From 72b9a9c22c7990e1f1cfa9966a15fb9df6814152 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Fri, 12 Dec 2025 10:46:08 -0800
Subject: [PATCH 2/9] [CIR] Fix parameter naming in emitDeclInvariant

Change parameter names from CGF/D to cgf/d to match LLVM coding
standards. Addresses review comments.
---
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index c0890f6b42663..b61e520de0e4e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -25,10 +25,10 @@ using namespace clang::CIRGen;
 
 /// Emit code to cause the variable at the given address to be considered as
 /// constant from this point onwards.
-static void emitDeclInvariant(CIRGenFunction &CGF, const VarDecl *D) {
-  mlir::Value addr = CGF.cgm.getAddrOfGlobalVar(D);
-  CGF.emitInvariantStart(CGF.getContext().getTypeSizeInChars(D->getType()),
-                         addr, CGF.getLoc(D->getSourceRange()));
+static void emitDeclInvariant(CIRGenFunction &cgf, const VarDecl *d) {
+  mlir::Value addr = cgf.cgm.getAddrOfGlobalVar(d);
+  cgf.emitInvariantStart(cgf.getContext().getTypeSizeInChars(d->getType()),
+                         addr, cgf.getLoc(d->getSourceRange()));
 }
 
 void CIRGenFunction::emitInvariantStart(CharUnits Size, mlir::Value Addr,

>From 5ecf23df1eeff7c1a4238965a2e2d6d81439f0b8 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 16 Dec 2025 10:03:16 -0800
Subject: [PATCH 3/9] [CIR] Fix parameter naming in emitInvariantStart

Change parameter names from PascalCase (Size, Addr) to lowercase
(size, addr) to match LLVM coding standards for function parameters.
---
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp    | 10 +++++-----
 clang/lib/CIR/CodeGen/CIRGenFunction.h |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index b61e520de0e4e..9b3cc40fb5a4f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -31,7 +31,7 @@ static void emitDeclInvariant(CIRGenFunction &cgf, const 
VarDecl *d) {
                          addr, cgf.getLoc(d->getSourceRange()));
 }
 
-void CIRGenFunction::emitInvariantStart(CharUnits Size, mlir::Value Addr,
+void CIRGenFunction::emitInvariantStart(CharUnits size, mlir::Value addr,
                                         mlir::Location loc) {
   // Do not emit the intrinsic if we're not optimizing.
   if (!cgm.getCodeGenOpts().OptimizationLevel)
@@ -40,13 +40,13 @@ void CIRGenFunction::emitInvariantStart(CharUnits Size, 
mlir::Value Addr,
   CIRGenBuilderTy &builder = getBuilder();
 
   // Create the size constant as i64
-  uint64_t width = Size.getQuantity();
+  uint64_t width = size.getQuantity();
   mlir::Value sizeValue = builder.getConstInt(loc, builder.getSInt64Ty(),
                                               static_cast<int64_t>(width));
 
   // Determine address space for intrinsic name
   unsigned addrSpace = 0;
-  if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(Addr.getType()))
+  if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(addr.getType()))
     addrSpace =
         ptrTy.getAddrSpace() ? ptrTy.getAddrSpace().getValue().getUInt() : 0;
 
@@ -60,8 +60,8 @@ void CIRGenFunction::emitInvariantStart(CharUnits Size, 
mlir::Value Addr,
   // token, but we don't need to capture it. The return type is set to match
   // the address type for consistency with the operation signature.
   cir::LLVMIntrinsicCallOp::create(
-      builder, loc, builder.getStringAttr(intrinsicName), Addr.getType(),
-      mlir::ValueRange{sizeValue, Addr});
+      builder, loc, builder.getStringAttr(intrinsicName), addr.getType(),
+      mlir::ValueRange{sizeValue, addr});
 }
 
 static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 627698c5b19af..fcd897ddb5c09 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1611,7 +1611,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::Value emitRuntimeCall(mlir::Location loc, cir::FuncOp callee,
                               llvm::ArrayRef<mlir::Value> args = {});
 
-  void emitInvariantStart(CharUnits Size, mlir::Value Addr, mlir::Location 
loc);
+  void emitInvariantStart(CharUnits size, mlir::Value addr, mlir::Location 
loc);
 
   /// Emit the computation of the specified expression of scalar type.
   mlir::Value emitScalarExpr(const clang::Expr *e,

>From 2adae263d0d3477b2700a24e31f7517bdcac20b7 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 16 Dec 2025 14:21:17 -0800
Subject: [PATCH 4/9] [CIR] Remove address space suffix from invariant.start
 intrinsic

Use base intrinsic name 'invariant.start' instead of constructing
address space-specific names (e.g., 'invariant.start.p0'). The address
space will be automatically handled when the intrinsic is lowered to
LLVM IR, matching classic codegen behavior.

Also add comment explaining why constant storage calculation was moved
to before global creation in CIRGenModule.cpp.
---
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp    | 20 +++-----------------
 clang/lib/CIR/CodeGen/CIRGenModule.cpp |  4 +++-
 2 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index 9b3cc40fb5a4f..f8a058b521c54 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -16,9 +16,7 @@
 
 #include "clang/AST/GlobalDecl.h"
 #include "clang/CIR/MissingFeatures.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/SaveAndRestore.h"
-#include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -44,23 +42,11 @@ void CIRGenFunction::emitInvariantStart(CharUnits size, 
mlir::Value addr,
   mlir::Value sizeValue = builder.getConstInt(loc, builder.getSInt64Ty(),
                                               static_cast<int64_t>(width));
 
-  // Determine address space for intrinsic name
-  unsigned addrSpace = 0;
-  if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(addr.getType()))
-    addrSpace =
-        ptrTy.getAddrSpace() ? ptrTy.getAddrSpace().getValue().getUInt() : 0;
-
-  // Format intrinsic name with address space suffix (e.g.,
-  // "invariant.start.p0", "invariant.start.p10")
-  llvm::SmallString<32> intrinsicName;
-  llvm::raw_svector_ostream os(intrinsicName);
-  os << "invariant.start.p" << addrSpace;
-
   // Create the intrinsic call. The llvm.invariant.start intrinsic returns a
-  // token, but we don't need to capture it. The return type is set to match
-  // the address type for consistency with the operation signature.
+  // token, but we don't need to capture it. The address space will be
+  // automatically handled when the intrinsic is lowered to LLVM IR.
   cir::LLVMIntrinsicCallOp::create(
-      builder, loc, builder.getStringAttr(intrinsicName), addr.getType(),
+      builder, loc, builder.getStringAttr("invariant.start"), addr.getType(),
       mlir::ValueRange{sizeValue, addr});
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 715e877fcca3d..29adb7d35c1f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -652,7 +652,9 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, 
mlir::Type ty,
 
   mlir::Location loc = getLoc(d->getSourceRange());
 
-  // Calculate constant storage flag before creating the global.
+  // Calculate constant storage flag before creating the global. This was moved
+  // from after the global creation to ensure the constant flag is set 
correctly
+  // at creation time, matching the logic used in emitCXXGlobalVarDeclInit.
   bool isConstant = false;
   if (d) {
     bool needsDtor =

>From 3971eee75e5c96e3acedfb4af41ab04fd74083da Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 17 Dec 2025 09:51:25 -0800
Subject: [PATCH 5/9] [CIR] Align emitGlobalVarDefinition constant logic with
 classic codegen

Match classic codegen EmitGlobalVarDefinition logic by checking
!needsGlobalCtor && !needsGlobalDtor before calling isConstantStorage.
This ensures CIR behaves consistently with classic codegen for global
variable constant flag determination.

The isConstantStorage call now uses ExcludeCtor=true and ExcludeDtor=true
to match classic codegen, instead of the previous logic that used
!needsDtor for ExcludeDtor.

Update test expectations to match correct behavior: globals that need
constructors are not marked as constant, even if they have constant
storage semantics (which still triggers invariant.start emission).
---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp         | 12 +++++-------
 .../CIR/CodeGen/global-constant-storage.cpp    | 18 +++++++++---------
 2 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 29adb7d35c1f1..e1cd4bd3271ed 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -877,14 +877,12 @@ void CIRGenModule::emitGlobalVarDefinition(const 
clang::VarDecl *vd,
     emitter->finalize(gv);
 
   // If it is safe to mark the global 'constant', do so now.
-  // Use the same logic as emitCXXGlobalVarDeclInit to determine constant
-  // storage.
-  bool needsDtor =
-      vd->needsDestruction(astContext) == QualType::DK_cxx_destructor;
+  // Use the same logic as classic codegen EmitGlobalVarDefinition.
   gv.setConstant((vd->hasAttr<CUDAConstantAttr>() && langOpts.CUDAIsDevice) ||
-                 vd->getType().isConstantStorage(astContext,
-                                                 /*ExcludeCtor=*/true,
-                                                 /*ExcludeDtor=*/!needsDtor));
+                 (!needsGlobalCtor && !needsGlobalDtor &&
+                  vd->getType().isConstantStorage(astContext,
+                                                  /*ExcludeCtor=*/true,
+                                                  /*ExcludeDtor=*/true)));
   assert(!cir::MissingFeatures::opGlobalSection());
 
   // Set CIR's linkage type as appropriate.
diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
index 8aa240cf8fc5b..b3c5737522eb7 100644
--- a/clang/test/CIR/CodeGen/global-constant-storage.cpp
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -57,7 +57,7 @@ class C {
 const C c;
 
 // CIR checks for 'a' - should have constant storage
-// CIR: cir.global constant external @a = #cir.zero : !rec_A
+// CIR: cir.global external @a = #cir.zero : !rec_A
 // CIR: cir.func internal private @__cxx_global_var_init() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @a : !cir.ptr<!rec_A>
 // CIR:   cir.call @_ZN1AC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A>) -> ()
@@ -65,7 +65,7 @@ const C c;
 // CIR: }
 
 // CIR checks for 'a2' - should have constant storage (constexpr dtor)
-// CIR: cir.global constant external @a2 = #cir.zero : !rec_A2
+// CIR: cir.global external @a2 = #cir.zero : !rec_A2
 // CIR: cir.func internal private @__cxx_global_var_init.1() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @a2 : !cir.ptr<!rec_A2>
 // CIR:   cir.call @_ZN2A2C1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A2>) -> ()
@@ -89,7 +89,7 @@ const C c;
 // CIR: }
 
 // CIR checks for 'c' - Andy's simple case, should have constant storage 
(internal linkage)
-// CIR: cir.global {{.*}} constant internal {{.*}} @_ZL1c = #cir.zero : !rec_C
+// CIR: cir.global {{.*}} internal {{.*}} @_ZL1c = #cir.zero : !rec_C
 // CIR: cir.func internal private @__cxx_global_var_init.4() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
 // CIR:   cir.call @_ZN1CC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_C>) -> ()
@@ -98,11 +98,11 @@ const C c;
 
 // LLVM checks (no optimization)
 // Check all globals first (they appear at the top)
-// LLVM: @a ={{.*}} constant {{.*}} zeroinitializer
-// LLVM: @a2 ={{.*}} constant {{.*}} zeroinitializer
+// LLVM: @a ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @a2 ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @b ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
-// LLVM: @_ZL1c ={{.*}} constant {{.*}} zeroinitializer
+// LLVM: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions
 // LLVM: define internal void @__cxx_global_var_init() {
@@ -166,11 +166,11 @@ const C c;
 
 // With optimization enabled, should emit invariant.start intrinsic for 
constant storage cases
 // Check all globals first (they appear at the top)
-// LLVM-OPT: @a ={{.*}} constant {{.*}} zeroinitializer
-// LLVM-OPT: @a2 ={{.*}} constant {{.*}} zeroinitializer
+// LLVM-OPT: @a ={{.*}} global {{.*}} zeroinitializer
+// LLVM-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @b ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
-// LLVM-OPT: @_ZL1c ={{.*}} constant {{.*}} zeroinitializer
+// LLVM-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions with invariant.start
 // LLVM-OPT: define internal void @__cxx_global_var_init() {

>From 5b706556b11c881f08496ea96b783704b0f0bb0a Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 17 Dec 2025 09:56:38 -0800
Subject: [PATCH 6/9] [CIR] Remove duplicate CWithDtor case from test

Remove CWithDtor test case as it duplicates the A case. The C case
already exists and tests the simple case with internal linkage, matching
classic codegen test patterns.

Update function numbering: C case changes from __cxx_global_var_init.4
to __cxx_global_var_init.3 after removing the duplicate case.
---
 .../CIR/CodeGen/global-constant-storage.cpp   | 45 +------------------
 1 file changed, 1 insertion(+), 44 deletions(-)

diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
index b3c5737522eb7..03e1d4ab51919 100644
--- a/clang/test/CIR/CodeGen/global-constant-storage.cpp
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -37,15 +37,6 @@ struct B {
 // Should NOT emit invariant.start - has mutable member
 extern const B b = B();
 
-struct CWithDtor {
-  CWithDtor();
-  ~CWithDtor();
-  int n;
-};
-
-// Should NOT emit invariant.start - has non-constexpr destructor
-extern const CWithDtor c_with_dtor = CWithDtor();
-
 // Simple case - just const C c; (no initializer) - Andy's suggestion
 class C {
 public:
@@ -80,17 +71,9 @@ const C c;
 // CIR:   cir.return
 // CIR: }
 
-// CIR checks for 'c_with_dtor' - should NOT have constant storage 
(non-constexpr dtor)
-// CIR: cir.global external @c_with_dtor = #cir.zero : !rec_CWithDtor
-// CIR: cir.func internal private @__cxx_global_var_init.3() {
-// CIR:   %[[OBJ:.*]] = cir.get_global @c_with_dtor : !cir.ptr<!rec_CWithDtor>
-// CIR:   cir.call @_ZN9CWithDtorC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_CWithDtor>) 
-> ()
-// CIR:   cir.return
-// CIR: }
-
 // CIR checks for 'c' - Andy's simple case, should have constant storage 
(internal linkage)
 // CIR: cir.global {{.*}} internal {{.*}} @_ZL1c = #cir.zero : !rec_C
-// CIR: cir.func internal private @__cxx_global_var_init.4() {
+// CIR: cir.func internal private @__cxx_global_var_init.3() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
 // CIR:   cir.call @_ZN1CC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_C>) -> ()
 // CIR:   cir.return
@@ -101,7 +84,6 @@ const C c;
 // LLVM: @a ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @a2 ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @b ={{.*}} global {{.*}} zeroinitializer
-// LLVM: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions
@@ -121,11 +103,6 @@ const C c;
 // LLVM: }
 
 // LLVM: define internal void @__cxx_global_var_init.3() {
-// LLVM:   call void @_ZN9CWithDtorC1Ev(ptr @c_with_dtor)
-// LLVM:   ret void
-// LLVM: }
-
-// LLVM: define internal void @__cxx_global_var_init.4() {
 // LLVM:   call void @_ZN1CC1Ev(ptr @_ZL1c)
 // LLVM:   ret void
 // LLVM: }
@@ -135,7 +112,6 @@ const C c;
 // OGCG: @a ={{.*}} global {{.*}} zeroinitializer
 // OGCG: @a2 ={{.*}} global {{.*}} zeroinitializer
 // OGCG: @b ={{.*}} global {{.*}} zeroinitializer
-// OGCG: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
 // OGCG: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions
@@ -155,11 +131,6 @@ const C c;
 // OGCG: }
 
 // OGCG: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
-// OGCG:   call void @_ZN9CWithDtorC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @c_with_dtor)
-// OGCG:   ret void
-// OGCG: }
-
-// OGCG: define internal void @__cxx_global_var_init.4() {{.*}} section 
".text.startup" {
 // OGCG:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 dereferenceable(8) 
@_ZL1c)
 // OGCG:   ret void
 // OGCG: }
@@ -169,7 +140,6 @@ const C c;
 // LLVM-OPT: @a ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @b ={{.*}} global {{.*}} zeroinitializer
-// LLVM-OPT: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions with invariant.start
@@ -192,12 +162,6 @@ const C c;
 // LLVM-OPT: }
 
 // LLVM-OPT: define internal void @__cxx_global_var_init.3() {
-// LLVM-OPT:   call void @_ZN9CWithDtorC1Ev(ptr @c_with_dtor)
-// LLVM-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr 
@c_with_dtor)
-// LLVM-OPT:   ret void
-// LLVM-OPT: }
-
-// LLVM-OPT: define internal void @__cxx_global_var_init.4() {
 // LLVM-OPT:   call void @_ZN1CC1Ev(ptr @_ZL1c)
 // LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
 // LLVM-OPT:   ret void
@@ -208,7 +172,6 @@ const C c;
 // OGCG-OPT: @a ={{.*}} global {{.*}} zeroinitializer
 // OGCG-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
 // OGCG-OPT: @b ={{.*}} global {{.*}} zeroinitializer
-// OGCG-OPT: @c_with_dtor ={{.*}} global {{.*}} zeroinitializer
 // OGCG-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
 // Then check the init functions with invariant.start
@@ -231,12 +194,6 @@ const C c;
 // OGCG-OPT: }
 
 // OGCG-OPT: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
-// OGCG-OPT:   call void @_ZN9CWithDtorC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @c_with_dtor)
-// OGCG-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr 
@c_with_dtor)
-// OGCG-OPT:   ret void
-// OGCG-OPT: }
-
-// OGCG-OPT: define internal void @__cxx_global_var_init.4() {{.*}} section 
".text.startup" {
 // OGCG-OPT:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 
dereferenceable(8) @_ZL1c)
 // OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
 // OGCG-OPT:   ret void

>From 70ab5f27364a3933e812b6fcc30b4c60548f80ca Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 17 Dec 2025 10:18:17 -0800
Subject: [PATCH 7/9] [CIR] Reorganize test checks to interleave by test case

Reorganize global-constant-storage.cpp test file to group CIR, LLVM, and
OGCG checks together for each test case, making it easier to compare
the three implementations side-by-side.

This addresses reviewer feedback requesting that checks be interleaved
so that the various implementations of each case are adjacent.
---
 .../CIR/CodeGen/global-constant-storage.cpp   | 165 +++++++++---------
 1 file changed, 86 insertions(+), 79 deletions(-)

diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
index 03e1d4ab51919..193e09aef259a 100644
--- a/clang/test/CIR/CodeGen/global-constant-storage.cpp
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -47,7 +47,19 @@ class C {
 
 const C c;
 
-// CIR checks for 'a' - should have constant storage
+// Check all globals first (they appear at the top of LLVM/OGCG output)
+// LLVM: @a ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @a2 ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @b ={{.*}} global {{.*}} zeroinitializer
+// LLVM: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
+
+// OGCG: @a ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @a2 ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @b ={{.*}} global {{.*}} zeroinitializer
+// OGCG: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
+
+// Test case 'a' - should have constant storage
+// CIR checks for 'a'
 // CIR: cir.global external @a = #cir.zero : !rec_A
 // CIR: cir.func internal private @__cxx_global_var_init() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @a : !cir.ptr<!rec_A>
@@ -55,7 +67,20 @@ const C c;
 // CIR:   cir.return
 // CIR: }
 
-// CIR checks for 'a2' - should have constant storage (constexpr dtor)
+// LLVM checks for 'a' (no optimization)
+// LLVM: define internal void @__cxx_global_var_init() {
+// LLVM:   call void @_ZN1AC1Ev(ptr @a)
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG checks for 'a' (no optimization)
+// OGCG: define internal void @__cxx_global_var_init() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN1AC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@a)
+// OGCG:   ret void
+// OGCG: }
+
+// Test case 'a2' - should have constant storage (constexpr dtor)
+// CIR checks for 'a2'
 // CIR: cir.global external @a2 = #cir.zero : !rec_A2
 // CIR: cir.func internal private @__cxx_global_var_init.1() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @a2 : !cir.ptr<!rec_A2>
@@ -63,7 +88,20 @@ const C c;
 // CIR:   cir.return
 // CIR: }
 
-// CIR checks for 'b' - should NOT have constant storage (mutable member)
+// LLVM checks for 'a2' (no optimization)
+// LLVM: define internal void @__cxx_global_var_init.1() {
+// LLVM:   call void @_ZN2A2C1Ev(ptr @a2)
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG checks for 'a2' (no optimization)
+// OGCG: define internal void @__cxx_global_var_init.1() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN2A2C1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a2)
+// OGCG:   ret void
+// OGCG: }
+
+// Test case 'b' - should NOT have constant storage (mutable member)
+// CIR checks for 'b'
 // CIR: cir.global external @b = #cir.zero : !rec_B
 // CIR: cir.func internal private @__cxx_global_var_init.2() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @b : !cir.ptr<!rec_B>
@@ -71,7 +109,20 @@ const C c;
 // CIR:   cir.return
 // CIR: }
 
-// CIR checks for 'c' - Andy's simple case, should have constant storage 
(internal linkage)
+// LLVM checks for 'b' (no optimization)
+// LLVM: define internal void @__cxx_global_var_init.2() {
+// LLVM:   call void @_ZN1BC1Ev(ptr @b)
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG checks for 'b' (no optimization)
+// OGCG: define internal void @__cxx_global_var_init.2() {{.*}} section 
".text.startup" {
+// OGCG:   call void @_ZN1BC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@b)
+// OGCG:   ret void
+// OGCG: }
+
+// Test case 'c' - Andy's simple case, should have constant storage (internal 
linkage)
+// CIR checks for 'c'
 // CIR: cir.global {{.*}} internal {{.*}} @_ZL1c = #cir.zero : !rec_C
 // CIR: cir.func internal private @__cxx_global_var_init.3() {
 // CIR:   %[[OBJ:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
@@ -79,123 +130,79 @@ const C c;
 // CIR:   cir.return
 // CIR: }
 
-// LLVM checks (no optimization)
-// Check all globals first (they appear at the top)
-// LLVM: @a ={{.*}} global {{.*}} zeroinitializer
-// LLVM: @a2 ={{.*}} global {{.*}} zeroinitializer
-// LLVM: @b ={{.*}} global {{.*}} zeroinitializer
-// LLVM: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
-
-// Then check the init functions
-// LLVM: define internal void @__cxx_global_var_init() {
-// LLVM:   call void @_ZN1AC1Ev(ptr @a)
-// LLVM:   ret void
-// LLVM: }
-
-// LLVM: define internal void @__cxx_global_var_init.1() {
-// LLVM:   call void @_ZN2A2C1Ev(ptr @a2)
-// LLVM:   ret void
-// LLVM: }
-
-// LLVM: define internal void @__cxx_global_var_init.2() {
-// LLVM:   call void @_ZN1BC1Ev(ptr @b)
-// LLVM:   ret void
-// LLVM: }
-
+// LLVM checks for 'c' (no optimization)
 // LLVM: define internal void @__cxx_global_var_init.3() {
 // LLVM:   call void @_ZN1CC1Ev(ptr @_ZL1c)
 // LLVM:   ret void
 // LLVM: }
 
-// OGCG checks (no optimization)
-// Check all globals first (they appear at the top)
-// OGCG: @a ={{.*}} global {{.*}} zeroinitializer
-// OGCG: @a2 ={{.*}} global {{.*}} zeroinitializer
-// OGCG: @b ={{.*}} global {{.*}} zeroinitializer
-// OGCG: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
-
-// Then check the init functions
-// OGCG: define internal void @__cxx_global_var_init() {{.*}} section 
".text.startup" {
-// OGCG:   call void @_ZN1AC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@a)
-// OGCG:   ret void
-// OGCG: }
-
-// OGCG: define internal void @__cxx_global_var_init.1() {{.*}} section 
".text.startup" {
-// OGCG:   call void @_ZN2A2C1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a2)
-// OGCG:   ret void
-// OGCG: }
-
-// OGCG: define internal void @__cxx_global_var_init.2() {{.*}} section 
".text.startup" {
-// OGCG:   call void @_ZN1BC1Ev(ptr noundef nonnull align 4 dereferenceable(4) 
@b)
-// OGCG:   ret void
-// OGCG: }
-
+// OGCG checks for 'c' (no optimization)
 // OGCG: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
 // OGCG:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 dereferenceable(8) 
@_ZL1c)
 // OGCG:   ret void
 // OGCG: }
 
 // With optimization enabled, should emit invariant.start intrinsic for 
constant storage cases
-// Check all globals first (they appear at the top)
+
+// Check all globals first (they appear at the top of optimized LLVM/OGCG 
output)
 // LLVM-OPT: @a ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @b ={{.*}} global {{.*}} zeroinitializer
 // LLVM-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
 
-// Then check the init functions with invariant.start
+// OGCG-OPT: @a ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @b ={{.*}} global {{.*}} zeroinitializer
+// OGCG-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
+
+// Test case 'a' - optimized checks
 // LLVM-OPT: define internal void @__cxx_global_var_init() {
 // LLVM-OPT:   call void @_ZN1AC1Ev(ptr @a)
 // LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a)
 // LLVM-OPT:   ret void
 // LLVM-OPT: }
 
-// LLVM-OPT: define internal void @__cxx_global_var_init.1() {
-// LLVM-OPT:   call void @_ZN2A2C1Ev(ptr @a2)
-// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
-// LLVM-OPT:   ret void
-// LLVM-OPT: }
-
-// LLVM-OPT: define internal void @__cxx_global_var_init.2() {
-// LLVM-OPT:   call void @_ZN1BC1Ev(ptr @b)
-// LLVM-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr @b)
-// LLVM-OPT:   ret void
-// LLVM-OPT: }
-
-// LLVM-OPT: define internal void @__cxx_global_var_init.3() {
-// LLVM-OPT:   call void @_ZN1CC1Ev(ptr @_ZL1c)
-// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
-// LLVM-OPT:   ret void
-// LLVM-OPT: }
-
-// OGCG-OPT checks (with optimization)
-// Check all globals first (they appear at the top)
-// OGCG-OPT: @a ={{.*}} global {{.*}} zeroinitializer
-// OGCG-OPT: @a2 ={{.*}} global {{.*}} zeroinitializer
-// OGCG-OPT: @b ={{.*}} global {{.*}} zeroinitializer
-// OGCG-OPT: @_ZL1c ={{.*}} global {{.*}} zeroinitializer
-
-// Then check the init functions with invariant.start
 // OGCG-OPT: define internal void @__cxx_global_var_init() {{.*}} section 
".text.startup" {
 // OGCG-OPT:   call void @_ZN1AC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a)
 // OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a)
 // OGCG-OPT:   ret void
 // OGCG-OPT: }
 
+// Test case 'a2' - optimized checks
+// LLVM-OPT: define internal void @__cxx_global_var_init.1() {
+// LLVM-OPT:   call void @_ZN2A2C1Ev(ptr @a2)
+// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
 // OGCG-OPT: define internal void @__cxx_global_var_init.1() {{.*}} section 
".text.startup" {
 // OGCG-OPT:   call void @_ZN2A2C1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @a2)
 // OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
 // OGCG-OPT:   ret void
 // OGCG-OPT: }
 
+// Test case 'b' - optimized checks (should NOT emit invariant.start)
+// LLVM-OPT: define internal void @__cxx_global_var_init.2() {
+// LLVM-OPT:   call void @_ZN1BC1Ev(ptr @b)
+// LLVM-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr @b)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
 // OGCG-OPT: define internal void @__cxx_global_var_init.2() {{.*}} section 
".text.startup" {
 // OGCG-OPT:   call void @_ZN1BC1Ev(ptr noundef nonnull align 4 
dereferenceable(4) @b)
 // OGCG-OPT-NOT: call {{.*}}@llvm.invariant.start.p0(i64 {{.*}}, ptr @b)
 // OGCG-OPT:   ret void
 // OGCG-OPT: }
 
+// Test case 'c' - optimized checks
+// LLVM-OPT: define internal void @__cxx_global_var_init.3() {
+// LLVM-OPT:   call void @_ZN1CC1Ev(ptr @_ZL1c)
+// LLVM-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
+// LLVM-OPT:   ret void
+// LLVM-OPT: }
+
 // OGCG-OPT: define internal void @__cxx_global_var_init.3() {{.*}} section 
".text.startup" {
 // OGCG-OPT:   call void @_ZN1CC1Ev(ptr noundef nonnull align 4 
dereferenceable(8) @_ZL1c)
 // OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
 // OGCG-OPT:   ret void
 // OGCG-OPT: }
-

>From be3c0dbcb330798f07ad945703c563a5282b1940 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 17 Dec 2025 10:27:04 -0800
Subject: [PATCH 8/9] [CIR] Add checks for before/after LoweringPrepare
 transformation

Add CIR-BEFORE-LPP checks to verify the initial CIR format with ctor
regions before LoweringPrepare transformation. This shows globals in
their initial state (cir.global external @a = ctor : !rec_A { ... })
before they are transformed into separate init functions.

This addresses reviewer feedback requesting checks for both the initial
CIR state and the state after LoweringPrepare transformation.
---
 .../CIR/CodeGen/global-constant-storage.cpp   | 31 ++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
index 193e09aef259a..86b21fc3df087 100644
--- a/clang/test/CIR/CodeGen/global-constant-storage.cpp
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> 
%t-before.cir
+// RUN: FileCheck --check-prefix=CIR-BEFORE-LPP --input-file=%t-before.cir %s
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
 // RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
@@ -47,6 +48,34 @@ class C {
 
 const C c;
 
+// CIR checks before LoweringPrepare transformation - globals have ctor regions
+// Test case 'a' - before LoweringPrepare
+// CIR-BEFORE-LPP: cir.global external @a = ctor : !rec_A {
+// CIR-BEFORE-LPP:   %[[OBJ:.*]] = cir.get_global @a : !cir.ptr<!rec_A>
+// CIR-BEFORE-LPP:   cir.call @_ZN1AC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A>) -> ()
+// CIR-BEFORE-LPP:   %[[OBJ2:.*]] = cir.get_global @a : !cir.ptr<!rec_A>
+// CIR-BEFORE-LPP: }
+
+// Test case 'a2' - before LoweringPrepare
+// CIR-BEFORE-LPP: cir.global external @a2 = ctor : !rec_A2 {
+// CIR-BEFORE-LPP:   %[[OBJ:.*]] = cir.get_global @a2 : !cir.ptr<!rec_A2>
+// CIR-BEFORE-LPP:   cir.call @_ZN2A2C1Ev(%[[OBJ]]) : (!cir.ptr<!rec_A2>) -> ()
+// CIR-BEFORE-LPP:   %[[OBJ2:.*]] = cir.get_global @a2 : !cir.ptr<!rec_A2>
+// CIR-BEFORE-LPP: }
+
+// Test case 'b' - before LoweringPrepare
+// CIR-BEFORE-LPP: cir.global external @b = ctor : !rec_B {
+// CIR-BEFORE-LPP:   %[[OBJ:.*]] = cir.get_global @b : !cir.ptr<!rec_B>
+// CIR-BEFORE-LPP:   cir.call @_ZN1BC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_B>) -> ()
+// CIR-BEFORE-LPP: }
+
+// Test case 'c' - before LoweringPrepare (internal linkage)
+// CIR-BEFORE-LPP: cir.global {{.*}} internal {{.*}} @_ZL1c = ctor : !rec_C {
+// CIR-BEFORE-LPP:   %[[OBJ:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
+// CIR-BEFORE-LPP:   cir.call @_ZN1CC1Ev(%[[OBJ]]) : (!cir.ptr<!rec_C>) -> ()
+// CIR-BEFORE-LPP:   %[[OBJ2:.*]] = cir.get_global @_ZL1c : !cir.ptr<!rec_C>
+// CIR-BEFORE-LPP: }
+
 // Check all globals first (they appear at the top of LLVM/OGCG output)
 // LLVM: @a ={{.*}} global {{.*}} zeroinitializer
 // LLVM: @a2 ={{.*}} global {{.*}} zeroinitializer

>From a5d549232d94d06ade5250798195ab4283820619 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 17 Dec 2025 10:31:17 -0800
Subject: [PATCH 9/9] [CIR] Add -O1 RUN line to test CIR output at optimization
 level

Add a RUN line to emit CIR at -O1 optimization level and add corresponding
FileCheck assertions to verify that invariant.start intrinsic calls are
present in the CIR representation for constant storage cases.

This addresses reviewer feedback requesting a RUN line to test CIR output
at -O1, ensuring that optimization level correctly triggers the emission
of invariant.start intrinsics in CIR.
---
 .../CIR/CodeGen/global-constant-storage.cpp   | 29 +++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/clang/test/CIR/CodeGen/global-constant-storage.cpp 
b/clang/test/CIR/CodeGen/global-constant-storage.cpp
index 86b21fc3df087..6f62823b8235f 100644
--- a/clang/test/CIR/CodeGen/global-constant-storage.cpp
+++ b/clang/test/CIR/CodeGen/global-constant-storage.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> 
%t-before.cir
 // RUN: FileCheck --check-prefix=CIR-BEFORE-LPP --input-file=%t-before.cir %s
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -O1 %s -o %t-o1.cir
+// RUN: FileCheck --check-prefix=CIR-O1 --input-file=%t-o1.cir %s
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
 // RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
@@ -235,3 +237,30 @@ const C c;
 // OGCG-OPT:   call {{.*}}@llvm.invariant.start.p0(i64 8, ptr @_ZL1c)
 // OGCG-OPT:   ret void
 // OGCG-OPT: }
+
+// CIR checks at -O1 - should include invariant.start intrinsic calls for 
constant storage cases
+// CIR-O1: module {{.*}} attributes {{.*}} cir.opt_info = #cir.opt_info<level 
= 1
+
+// Test case 'a' - CIR at -O1
+// CIR-O1: cir.func internal private @__cxx_global_var_init() {
+// CIR-O1:   cir.call @_ZN1AC1Ev(%{{.*}}) : (!cir.ptr<!rec_A>) -> ()
+// CIR-O1:   cir.call_llvm_intrinsic "invariant.start" {{.*}} : (!s64i, 
!cir.ptr<!rec_A>) -> !cir.ptr<!rec_A>
+// CIR-O1: }
+
+// Test case 'a2' - CIR at -O1
+// CIR-O1: cir.func internal private @__cxx_global_var_init.1() {
+// CIR-O1:   cir.call @_ZN2A2C1Ev(%{{.*}}) : (!cir.ptr<!rec_A2>) -> ()
+// CIR-O1:   cir.call_llvm_intrinsic "invariant.start" {{.*}} : (!s64i, 
!cir.ptr<!rec_A2>) -> !cir.ptr<!rec_A2>
+// CIR-O1: }
+
+// Test case 'b' - CIR at -O1 (should NOT emit invariant.start)
+// CIR-O1: cir.func internal private @__cxx_global_var_init.2() {
+// CIR-O1:   cir.call @_ZN1BC1Ev(%{{.*}}) : (!cir.ptr<!rec_B>) -> ()
+// CIR-O1-NOT: cir.call_llvm_intrinsic "invariant.start"
+// CIR-O1: }
+
+// Test case 'c' - CIR at -O1
+// CIR-O1: cir.func internal private @__cxx_global_var_init.3() {
+// CIR-O1:   cir.call @_ZN1CC1Ev(%{{.*}}) : (!cir.ptr<!rec_C>) -> ()
+// CIR-O1:   cir.call_llvm_intrinsic "invariant.start" {{.*}} : (!s64i, 
!cir.ptr<!rec_C>) -> !cir.ptr<!rec_C>
+// CIR-O1: }

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

Reply via email to