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

This change upstreams the CIR implementation of global variable replacement 
handling for cases where the global was used in a cir.global_view operation, 
either as an initializer for another global or as a constant ptr.

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

This change upstreams the CIR implementation of global variable replacement
handling for cases where the global was used in a cir.global_view operation,
either as an initializer for another global or as a constant ptr.
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  12 +++
 clang/lib/CIR/CodeGen/CIRGenBuilder.cpp       |  22 ++++
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |   5 +
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 102 +++++++++++++++++-
 clang/test/CIR/CodeGen/replace-global.cpp     |  49 ++++++++-
 5 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index f51bea894d2ae..a7984175ac1ce 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -326,6 +326,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return cir::GlobalViewAttr::get(type, symbol, indices);
   }
 
+  /// Get constant address of a global variable as an MLIR attribute.
+  /// This overload converts raw int64_t indices to an ArrayAttr.
+  cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type,
+                                        cir::GlobalOp globalOp,
+                                        llvm::ArrayRef<int64_t> indices) {
+    llvm::SmallVector<mlir::Attribute> attrs;
+    for (int64_t ind : indices)
+      attrs.push_back(getI64IntegerAttr(ind));
+    mlir::ArrayAttr arAttr = mlir::ArrayAttr::get(getContext(), attrs);
+    return getGlobalViewAttr(type, globalOp, arAttr);
+  }
+
   mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global,
                               bool threadLocal = false) {
     assert(!cir::MissingFeatures::addressSpace());
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index e8298e8231f05..7953a648715b2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -147,6 +147,28 @@ void 
CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
   computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices);
 }
 
+uint64_t CIRGenBuilderTy::computeOffsetFromGlobalViewIndices(
+    const cir::CIRDataLayout &layout, mlir::Type ty,
+    llvm::ArrayRef<int64_t> indices) {
+  int64_t offset = 0;
+  for (int64_t idx : indices) {
+    if (auto recordTy = dyn_cast<cir::RecordType>(ty)) {
+      offset += recordTy.getElementOffset(layout.layout, idx);
+      const llvm::Align tyAlign = llvm::Align(
+          recordTy.getPacked() ? 1 : layout.layout.getTypeABIAlignment(ty));
+      offset = llvm::alignTo(offset, tyAlign);
+      assert(idx < (int64_t)recordTy.getMembers().size());
+      ty = recordTy.getMembers()[idx];
+    } else if (auto arrayTy = dyn_cast<cir::ArrayType>(ty)) {
+      ty = arrayTy.getElementType();
+      offset += layout.getTypeAllocSize(ty) * idx;
+    } else {
+      llvm_unreachable("unexpected type");
+    }
+  }
+  return offset;
+}
+
 cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
     mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name) {
   assert(!cir::MissingFeatures::astRecordDeclAttr());
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 7cd1bdcf491be..c08e1cacb15c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -691,6 +691,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
       int64_t offset, mlir::Type ty, cir::CIRDataLayout layout,
       llvm::SmallVectorImpl<int64_t> &indices);
 
+  // Convert high-level indices (e.g. from GlobalViewAttr) to byte offset.
+  uint64_t computeOffsetFromGlobalViewIndices(const cir::CIRDataLayout &layout,
+                                              mlir::Type ty,
+                                              llvm::ArrayRef<int64_t> indices);
+
   /// Creates a versioned global variable. If the symbol is already taken, an 
ID
   /// will be appended to the symbol. The returned global must always be 
queried
   /// for its name so it can be referenced correctly.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 25fa4c7c86a89..d48d6b9efe7c4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -687,6 +687,92 @@ static void setLinkageForGV(cir::GlobalOp &gv, const 
NamedDecl *nd) {
     gv.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage);
 }
 
+static llvm::SmallVector<int64_t> indexesOfArrayAttr(mlir::ArrayAttr indexes) {
+  llvm::SmallVector<int64_t> inds;
+  for (mlir::Attribute i : indexes) {
+    auto ind = mlir::cast<mlir::IntegerAttr>(i);
+    inds.push_back(ind.getValue().getSExtValue());
+  }
+  return inds;
+}
+
+static bool isViewOnGlobal(cir::GlobalOp glob, cir::GlobalViewAttr view) {
+  return view.getSymbol().getValue() == glob.getSymName();
+}
+
+static cir::GlobalViewAttr createNewGlobalView(CIRGenModule &cgm,
+                                               cir::GlobalOp newGlob,
+                                               cir::GlobalViewAttr attr,
+                                               mlir::Type oldTy) {
+  // If the attribute does not require indexes or it is not a global view on
+  // the global we're replacing, keep the original attribute.
+  if (!attr.getIndices() || !isViewOnGlobal(newGlob, attr))
+    return attr;
+
+  llvm::SmallVector<int64_t> oldInds = indexesOfArrayAttr(attr.getIndices());
+  llvm::SmallVector<int64_t> newInds;
+  CIRGenBuilderTy &bld = cgm.getBuilder();
+  const cir::CIRDataLayout &layout = cgm.getDataLayout();
+  mlir::Type newTy = newGlob.getSymType();
+
+  uint64_t offset =
+      bld.computeOffsetFromGlobalViewIndices(layout, oldTy, oldInds);
+  bld.computeGlobalViewIndicesFromFlatOffset(offset, newTy, layout, newInds);
+  cir::PointerType newPtrTy;
+
+  if (isa<cir::RecordType>(oldTy))
+    newPtrTy = cir::PointerType::get(newTy);
+  else if (isa<cir::ArrayType>(oldTy))
+    newPtrTy = dyn_cast<cir::PointerType>(attr.getType());
+
+  if (newPtrTy)
+    return bld.getGlobalViewAttr(newPtrTy, newGlob, newInds);
+
+  // This may be unreachable in practice, but keep it as errorNYI while CIR
+  // is still under development.
+  cgm.errorNYI("Unhandled type in createNewGlobalView");
+  return {};
+}
+
+static mlir::Attribute getNewInitValue(CIRGenModule &cgm, cir::GlobalOp 
newGlob,
+                                       mlir::Type oldTy, cir::GlobalOp user,
+                                       mlir::Attribute oldInit) {
+  if (auto oldView = mlir::dyn_cast<cir::GlobalViewAttr>(oldInit))
+    return createNewGlobalView(cgm, newGlob, oldView, oldTy);
+
+  auto getNewInitElements =
+      [&](mlir::ArrayAttr oldElements) -> mlir::ArrayAttr {
+    llvm::SmallVector<mlir::Attribute> newElements;
+    for (mlir::Attribute elt : oldElements) {
+      if (auto view = mlir::dyn_cast<cir::GlobalViewAttr>(elt))
+        newElements.push_back(createNewGlobalView(cgm, newGlob, view, oldTy));
+      else if (mlir::isa<cir::ConstArrayAttr, cir::ConstRecordAttr>(elt))
+        newElements.push_back(getNewInitValue(cgm, newGlob, oldTy, user, elt));
+      else
+        newElements.push_back(elt);
+    }
+    return mlir::ArrayAttr::get(cgm.getBuilder().getContext(), newElements);
+  };
+
+  if (auto oldArray = mlir::dyn_cast<cir::ConstArrayAttr>(oldInit)) {
+    mlir::Attribute newElements =
+        
getNewInitElements(mlir::dyn_cast<mlir::ArrayAttr>(oldArray.getElts()));
+    return cgm.getBuilder().getConstArray(
+        newElements, mlir::cast<cir::ArrayType>(oldArray.getType()));
+  }
+  if (auto oldRecord = mlir::dyn_cast<cir::ConstRecordAttr>(oldInit)) {
+    mlir::ArrayAttr newMembers = getNewInitElements(oldRecord.getMembers());
+    auto recordTy = mlir::cast<cir::RecordType>(oldRecord.getType());
+    return cgm.getBuilder().getConstRecordOrZeroAttr(
+        newMembers, recordTy.getPacked(), recordTy.getPadded(), recordTy);
+  }
+
+  // This may be unreachable in practice, but keep it as errorNYI while CIR
+  // is still under development.
+  cgm.errorNYI("Unhandled type in getNewInitValue");
+  return {};
+}
+
 // 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.
@@ -720,8 +806,20 @@ void CIRGenModule::replaceGlobal(cir::GlobalOp oldGV, 
cir::GlobalOp newGV) {
       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");
+    } else if (auto glob = dyn_cast<cir::GlobalOp>(userOp)) {
+      if (auto init = glob.getInitialValue()) {
+        mlir::Attribute nw =
+            getNewInitValue(*this, newGV, oldTy, glob, init.value());
+        glob.setInitialValueAttr(nw);
+      }
+    } else if (auto c = dyn_cast<cir::ConstantOp>(userOp)) {
+      mlir::Attribute init =
+          getNewInitValue(*this, newGV, oldTy, newGV, c.getValue());
+      auto typedAttr = mlir::cast<mlir::TypedAttr>(init);
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      builder.setInsertionPointAfter(c);
+      auto newUser = cir::ConstantOp::create(builder, c.getLoc(), typedAttr);
+      c.replaceAllUsesWith(newUser.getOperation());
     }
   }
 
diff --git a/clang/test/CIR/CodeGen/replace-global.cpp 
b/clang/test/CIR/CodeGen/replace-global.cpp
index b6c22d5562801..709956d412873 100644
--- a/clang/test/CIR/CodeGen/replace-global.cpp
+++ b/clang/test/CIR/CodeGen/replace-global.cpp
@@ -13,25 +13,72 @@ void use(void*);
 
 static S gS = {{0x50, 0x4B, 0x03, 0x04}};
 
+S* ptrToS = &gS;
+
 struct R {
   R() { use(&gS); }
 };
 static R gR;
 
+void use_as_constant() {
+  constexpr S *ptrToS = &gS;
+}
+
+// This is just here to force ptrToS to be emitted.
+S *get_ptr_to_s() {
+  return ptrToS;
+}
+
+// Multi-index case: ptrToElement = &gSMulti.arr[5] creates a GlobalViewAttr 
with
+// indices (struct member 0 = arr, array index 5). With extern gSMulti declared
+// before the use, gSMulti is created when evaluating ptrToElement's 
initializer,
+// then replaced when gSMulti is defined, so createNewGlobalView runs with 
multiple
+// indices. Definition has external linkage to match the extern declaration.
+extern S gSMulti;
+char *ptrToElement = &gSMulti.arr[5];
+S gSMulti = {{0x50, 0x4B, 0x03, 0x04}};
+
+char *get_ptr_to_element() { return ptrToElement; }
+
+// 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
+// CIR: cir.global {{.*}} @ptrToS = #cir.global_view<@_ZL2gS> : 
!cir.ptr<!rec_S>
+
 // 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
+// Multi-index case: ptrToElement = &gSMulti.arr[5] produces a global_view with
+// multiple indices, exercising createNewGlobalView.
+// CIR: cir.global {{.*}} @gSMulti = #cir.const_record<
+// CIR: cir.global {{.*}} @ptrToElement = #cir.global_view<@gSMulti, [0, 4, 
1]> : !cir.ptr<
+
+// CIR: cir.func {{.*}} @_Z15use_as_constantv()
+// CIR:   %[[PTR_TO_S:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["ptrToS", init, const]
+// CIR:   %[[GLOBAL_PTR:.*]] = cir.const #cir.global_view<@_ZL2gS> : 
!cir.ptr<!rec_S>
+// CIR:   cir.store{{.*}} %[[GLOBAL_PTR]], %[[PTR_TO_S]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
 
 // 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: @ptrToS = global ptr @_ZL2gS, align 8
+// LLVM: @gSMulti = global {{.*}} align 1
+// LLVM: @ptrToElement = global ptr getelementptr
 
 // LLVM: define {{.*}} void @_ZN1RC2Ev
 // LLVM:   call void @_Z3usePv(ptr noundef @_ZL2gS)
 
+// LLVM: define {{.*}} void @_Z15use_as_constantv()
+// LLVM:   %[[PTR_TO_S:.*]] = alloca ptr
+// LLVM:   store ptr @_ZL2gS, ptr %[[PTR_TO_S]]
+
+// OGCG: @ptrToS = global ptr @_ZL2gS, align 8
+// OGCG: @ptrToElement = global ptr {{.*}} align 8
+// OGCG: @gSMulti = global {{.*}} align 1
 // 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 @_Z15use_as_constantv()
+// OGCG:   %[[PTR_TO_S:.*]] = alloca ptr
+// OGCG:   store ptr @_ZL2gS, ptr %[[PTR_TO_S]]
+
 // 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