https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/184686

This change upstreams the CIR implementation of global variable replacement. 
When we get a call to get or create a global variable using a type that does 
not match the previous type for a variable of the same name, we need to replace 
the old definition with the new one. In classic codegen that was as simple as 
replaceAllUses+eraseFromParent, but in CIR because we have typed pointers, we 
need to visit the uses and update them with bitcasts to reflect the new type.

>From 05d11486371f98150db78540774e7f9e3dabb038 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 3 Mar 2026 17:05:14 -0800
Subject: [PATCH] [CIR] Upstream global variable replacement

This change upstreams the CIR implementation of global variable replacement.
When we get a call to get or create a global variable using a type that
does not match the previous type for a variable of the same name, we need
to replace the old definition with the new one. In classic codegen that
was as simple as replaceAllUses+eraseFromParent, but in CIR because we
have typed pointers, we need to visit the uses and update them with bitcasts
to reflect the new type.
---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp    | 48 +++++++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenModule.h      |  4 ++
 clang/test/CIR/CodeGen/replace-global.cpp | 37 +++++++++++++++++
 3 files changed, 89 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/replace-global.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 3ef487465ab80..0653fee57428c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -674,6 +674,49 @@ static void setLinkageForGV(cir::GlobalOp &gv, const 
NamedDecl *nd) {
     gv.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage);
 }
 
+// We want to replace a global value, but because of CIR's typed pointers,
+// we need to update the existing uses to reflect the new type, not just 
replace
+// them directly.
+void CIRGenModule::replaceGlobal(cir::GlobalOp oldGV, cir::GlobalOp newGV) {
+  assert(oldGV.getSymName() == newGV.getSymName() && "symbol names must 
match");
+
+  mlir::Type oldTy = oldGV.getSymType();
+  mlir::Type newTy = newGV.getSymType();
+
+  assert(!cir::MissingFeatures::addressSpace());
+
+  // If the type didn't change, why are we here?
+  assert(oldTy != newTy && "expected type change in replaceGlobal");
+
+  // Otherwise, visit all uses and add handling to fix up the types.
+  std::optional<mlir::SymbolTable::UseRange> oldSymUses =
+      oldGV.getSymbolUses(theModule);
+  if (oldSymUses) {
+    for (mlir::SymbolTable::SymbolUse use : *oldSymUses) {
+      mlir::Operation *userOp = use.getUser();
+      assert((mlir::isa<cir::GetGlobalOp, cir::GlobalOp, cir::ConstantOp>(
+                 userOp)) &&
+             "Unexpected user for global op");
+
+      if (auto getGlobalOp = dyn_cast<cir::GetGlobalOp>(use.getUser())) {
+        mlir::Value useOpResultValue = getGlobalOp.getAddr();
+        useOpResultValue.setType(cir::PointerType::get(newTy));
+
+        mlir::OpBuilder::InsertionGuard guard(builder);
+        builder.setInsertionPointAfter(getGlobalOp);
+        mlir::Type ptrTy = builder.getPointerTo(oldTy);
+        mlir::Value cast = builder.createBitcast(getGlobalOp->getLoc(),
+                                                 useOpResultValue, ptrTy);
+        useOpResultValue.replaceAllUsesExcept(cast, cast.getDefiningOp());
+      } else {
+        errorNYI(userOp->getLoc(), "Replace global op use in global view 
attr");
+      }
+    }
+  }
+
+  oldGV.erase();
+}
+
 /// If the specified mangled name is not in the module,
 /// create and return an mlir GlobalOp with the specified type (TODO(cir):
 /// address space).
@@ -748,6 +791,11 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, 
mlir::Type ty,
       CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, isConstant,
                                    /*insertPoint=*/entry.getOperation());
 
+  // If we already created a global with the same mangled name (but different
+  // type) before, remove it from its parent.
+  if (entry)
+    replaceGlobal(entry, gv);
+
   // This is the first use or definition of a mangled name.  If there is a
   // deferred decl with this name, remember that we need to emit it at the end
   // of the file.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 0f456f1f39ceb..48bb1f2ae20d9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -613,6 +613,10 @@ class CIRGenModule : public CIRGenTypeCache {
   // related attributes.
   bool shouldEmitCUDAGlobalVar(const VarDecl *global) const;
 
+  /// Replace all uses of the old global with the new global, updating types
+  /// and references as needed. Erases the old global when done.
+  void replaceGlobal(cir::GlobalOp oldGV, cir::GlobalOp newGV);
+
   void replaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *old,
                                                  cir::FuncOp newFn);
 
diff --git a/clang/test/CIR/CodeGen/replace-global.cpp 
b/clang/test/CIR/CodeGen/replace-global.cpp
new file mode 100644
index 0000000000000..b6c22d5562801
--- /dev/null
+++ b/clang/test/CIR/CodeGen/replace-global.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -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 -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 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct S {
+  char arr[28];
+};
+
+void use(void*);
+
+static S gS = {{0x50, 0x4B, 0x03, 0x04}};
+
+struct R {
+  R() { use(&gS); }
+};
+static R gR;
+
+// CIR: cir.func {{.*}} @_ZN1RC2Ev
+// CIR:   %[[GS_PTR:.*]] = cir.get_global @_ZL2gS : !cir.ptr<!rec_anon_struct1>
+// CIR:   %[[GS_AS_S:.*]] = cir.cast bitcast %[[GS_PTR]] : 
!cir.ptr<!rec_anon_struct1> -> !cir.ptr<!rec_S>
+// CIR:   %[[GS_AS_VOID:.*]] = cir.cast bitcast %[[GS_AS_S]] : 
!cir.ptr<!rec_S> -> !cir.ptr<!void>
+// CIR:   cir.call @_Z3usePv(%[[GS_AS_VOID]]) : (!cir.ptr<!void> {{.*}}) -> ()
+
+// CIR: cir.global {{.*}} @_ZL2gS = 
#cir.const_record<{#cir.const_record<{#cir.int<80> : !s8i, #cir.int<75> : !s8i, 
#cir.int<3> : !s8i, #cir.int<4> : !s8i, #cir.zero : !cir.array<!s8i x 24>}> : 
!rec_anon_struct}> : !rec_anon_struct1
+
+// LLVM: @_ZL2gS = internal global { <{ i8, i8, i8, i8, [24 x i8] }> } { <{ 
i8, i8, i8, i8, [24 x i8] }> <{ i8 80, i8 75, i8 3, i8 4, [24 x i8] 
zeroinitializer }> }, align 1
+
+// LLVM: define {{.*}} void @_ZN1RC2Ev
+// LLVM:   call void @_Z3usePv(ptr noundef @_ZL2gS)
+
+// OGCG: @_ZL2gS = internal global { <{ i8, i8, i8, i8, [24 x i8] }> } { <{ 
i8, i8, i8, i8, [24 x i8] }> <{ i8 80, i8 75, i8 3, i8 4, [24 x i8] 
zeroinitializer }> }, align 1
+
+// OGCG: define {{.*}} void @_ZN1RC2Ev
+// OGCG:   call void @_Z3usePv(ptr noundef @_ZL2gS)

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

Reply via email to