Author: Andy Kaylor
Date: 2026-03-04T13:50:54-08:00
New Revision: 954e5e7aa2e23d315a13fc052807d6600b182341

URL: 
https://github.com/llvm/llvm-project/commit/954e5e7aa2e23d315a13fc052807d6600b182341
DIFF: 
https://github.com/llvm/llvm-project/commit/954e5e7aa2e23d315a13fc052807d6600b182341.diff

LOG: [CIR] Upstream global variable replacement (#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.

Added: 
    clang/test/CIR/CodeGen/replace-global.cpp

Modified: 
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 3ef487465ab80..c9df1499ea453 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -674,6 +674,47 @@ 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");
+
+  // Visit all uses and add handling to fix up the types.
+  std::optional<mlir::SymbolTable::UseRange> oldSymUses =
+      oldGV.getSymbolUses(theModule);
+  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 +789,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 
diff erent
+  // 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