[clang] [CIR] Implement global variable replacement in global view (PR #186168)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 09:24:17 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clangir
Author: Andy Kaylor (andykaylor)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/186168.diff
5 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+12)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.cpp (+22)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+5)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+100-2)
- (modified) clang/test/CIR/CodeGen/replace-global.cpp (+48-1)
``````````diff
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)
``````````
</details>
https://github.com/llvm/llvm-project/pull/186168
More information about the cfe-commits
mailing list