[CIR] Implement global variable replacement in global view (#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.
This commit is contained in:
Andy Kaylor
2026-03-19 15:44:16 -07:00
committed by GitHub
parent 3fdc82c9ab
commit 9eb852c062
5 changed files with 186 additions and 3 deletions

View File

@@ -339,6 +339,18 @@ public:
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());

View File

@@ -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());

View File

@@ -680,6 +680,11 @@ public:
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.

View File

@@ -691,6 +691,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 = 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,
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, 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::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.
@@ -724,8 +810,19 @@ 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, init.value());
glob.setInitialValueAttr(nw);
}
} else if (auto c = dyn_cast<cir::ConstantOp>(userOp)) {
mlir::Attribute init = getNewInitValue(*this, newGV, oldTy, 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());
c.erase();
}
}

View File

@@ -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)