[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:
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user