[clang] db22909 - [CIR] Upstream support for cir.get_global (#135095)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 10 14:15:14 PDT 2025
Author: Andy Kaylor
Date: 2025-04-10T14:15:10-07:00
New Revision: db22909089dbc28d072e8cba6183f5a51f418bf0
URL: https://github.com/llvm/llvm-project/commit/db22909089dbc28d072e8cba6183f5a51f418bf0
DIFF: https://github.com/llvm/llvm-project/commit/db22909089dbc28d072e8cba6183f5a51f418bf0.diff
LOG: [CIR] Upstream support for cir.get_global (#135095)
This adds basic support for referencing global variables from within
functions via the cir.get_global operation.
Added:
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenModule.h
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
clang/test/CIR/CodeGen/basic.c
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c6b509fe50cbc..0d3c2065cd58c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1279,6 +1279,37 @@ def GlobalOp : CIR_Op<"global"> {
let hasVerifier = 1;
}
+//===----------------------------------------------------------------------===//
+// GetGlobalOp
+//===----------------------------------------------------------------------===//
+
+def GetGlobalOp : CIR_Op<"get_global",
+ [Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+ let summary = "Get the address of a global variable";
+ let description = [{
+ The `cir.get_global` operation retrieves the address pointing to a
+ named global variable. If the global variable is marked constant, writing
+ to the resulting address (such as through a `cir.store` operation) is
+ undefined. The resulting type must always be a `!cir.ptr<...>` type with the
+ same address space as the global variable.
+
+ Example:
+ ```mlir
+ %x = cir.get_global @gv : !cir.ptr<i32>
+ ```
+ }];
+
+ let arguments = (ins FlatSymbolRefAttr:$name);
+ let results = (outs Res<CIR_PointerType, "", []>:$addr);
+
+ let assemblyFormat = [{
+ $name `:` qualified(type($addr)) attr-dict
+ }];
+
+ // `GetGlobalOp` is fully verified by its traits.
+ let hasVerifier = 0;
+}
+
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 1b61c32cf4f38..bacb7879a5527 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -35,6 +35,7 @@ struct MissingFeatures {
static bool opGlobalThreadLocal() { return false; }
static bool opGlobalConstant() { return false; }
static bool opGlobalAlignment() { return false; }
+ static bool opGlobalWeakRef() { return false; }
static bool supportIFuncAttr() { return false; }
static bool supportVisibility() { return false; }
@@ -136,6 +137,10 @@ struct MissingFeatures {
static bool objCGC() { return false; }
static bool weakRefReference() { return false; }
static bool hip() { return false; }
+ static bool setObjCGCLValueClass() { return false; }
+ static bool mangledNames() { return false; }
+ static bool setDLLStorageClass() { return false; }
+ static bool openMP() { return false; }
// Missing types
static bool dataMemberType() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index bb5588c0ff525..4c20170d75131 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -183,6 +183,43 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
emitStoreOfScalar(src.getScalarVal(), dst, isInit);
}
+static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
+ const VarDecl *vd) {
+ QualType T = e->getType();
+
+ // If it's thread_local, emit a call to its wrapper function instead.
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+ if (vd->getTLSKind() == VarDecl::TLS_Dynamic)
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "emitGlobalVarDeclLValue: thread_local variable");
+
+ // Check if the variable is marked as declare target with link clause in
+ // device codegen.
+ if (cgf.getLangOpts().OpenMP)
+ cgf.cgm.errorNYI(e->getSourceRange(), "emitGlobalVarDeclLValue: OpenMP");
+
+ // Traditional LLVM codegen handles thread local separately, CIR handles
+ // as part of getAddrOfGlobalVar.
+ mlir::Value v = cgf.cgm.getAddrOfGlobalVar(vd);
+
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Type realVarTy = cgf.convertTypeForMem(vd->getType());
+ cir::PointerType realPtrTy = cgf.getBuilder().getPointerTo(realVarTy);
+ if (realPtrTy != v.getType())
+ v = cgf.getBuilder().createBitcast(v.getLoc(), v, realPtrTy);
+
+ CharUnits alignment = cgf.getContext().getDeclAlign(vd);
+ Address addr(v, realVarTy, alignment);
+ LValue lv;
+ if (vd->getType()->isReferenceType())
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "emitGlobalVarDeclLValue: reference type");
+ else
+ lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl);
+ assert(!cir::MissingFeatures::setObjCGCLValueClass());
+ return lv;
+}
+
void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
bool isVolatile, QualType ty,
bool isInit, bool isNontemporal) {
@@ -288,7 +325,7 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
// Check if this is a global variable
if (vd->hasLinkage() || vd->isStaticDataMember())
- cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: global variable");
+ return emitGlobalVarDeclLValue(*this, e, vd);
Address addr = Address::invalid();
@@ -299,7 +336,7 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
} else {
// Otherwise, it might be static local we haven't emitted yet for some
// reason; most likely, because it's in an outer function.
- cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: static local");
+ cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: static local");
}
return makeAddrLValue(addr, ty, AlignmentSource::Type);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 2f90141b2d158..4b5acb36a9319 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -202,6 +202,102 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
curCGF = nullptr;
}
+mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) {
+ return mlir::SymbolTable::lookupSymbolIn(theModule, name);
+}
+
+/// If the specified mangled name is not in the module,
+/// create and return an mlir GlobalOp with the specified type (TODO(cir):
+/// address space).
+///
+/// TODO(cir):
+/// 1. If there is something in the module with the specified name, return
+/// it potentially bitcasted to the right type.
+///
+/// 2. If \p d is non-null, it specifies a decl that correspond to this. This
+/// is used to set the attributes on the global when it is first created.
+///
+/// 3. If \p isForDefinition is true, it is guaranteed that an actual global
+/// with type \p ty will be returned, not conversion of a variable with the same
+/// mangled name but some other type.
+cir::GlobalOp
+CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
+ LangAS langAS, const VarDecl *d,
+ ForDefinition_t isForDefinition) {
+ // Lookup the entry, lazily creating it if necessary.
+ cir::GlobalOp entry;
+ if (mlir::Operation *v = getGlobalValue(mangledName)) {
+ if (!isa<cir::GlobalOp>(v))
+ errorNYI(d->getSourceRange(), "global with non-GlobalOp type");
+ entry = cast<cir::GlobalOp>(v);
+ }
+
+ if (entry) {
+ assert(!cir::MissingFeatures::addressSpace());
+ assert(!cir::MissingFeatures::opGlobalWeakRef());
+
+ assert(!cir::MissingFeatures::setDLLStorageClass());
+ assert(!cir::MissingFeatures::openMP());
+
+ if (entry.getSymType() == ty)
+ return entry;
+
+ // If there are two attempts to define the same mangled name, issue an
+ // error.
+ //
+ // TODO(cir): look at mlir::GlobalValue::isDeclaration for all aspects of
+ // recognizing the global as a declaration, for now only check if
+ // initializer is present.
+ if (isForDefinition && !entry.isDeclaration()) {
+ errorNYI(d->getSourceRange(), "global with conflicting type");
+ }
+
+ // Address space check removed because it is unnecessary because CIR records
+ // address space info in types.
+
+ // (If global is requested for a definition, we always need to create a new
+ // global, not just return a bitcast.)
+ if (!isForDefinition)
+ return entry;
+ }
+
+ errorNYI(d->getSourceRange(), "reference of undeclared global");
+}
+
+cir::GlobalOp
+CIRGenModule::getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty,
+ ForDefinition_t isForDefinition) {
+ assert(d->hasGlobalStorage() && "Not a global variable");
+ QualType astTy = d->getType();
+ if (!ty)
+ ty = getTypes().convertTypeForMem(astTy);
+
+ assert(!cir::MissingFeatures::mangledNames());
+ return getOrCreateCIRGlobal(d->getIdentifier()->getName(), ty,
+ astTy.getAddressSpace(), d, isForDefinition);
+}
+
+/// Return the mlir::Value for the address of the given global variable. If
+/// \p ty is non-null and if the global doesn't exist, then it will be created
+/// with the specified type instead of whatever the normal requested type would
+/// be. If \p isForDefinition is true, it is guaranteed that an actual global
+/// with type \p ty will be returned, not conversion of a variable with the same
+/// mangled name but some other type.
+mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty,
+ ForDefinition_t isForDefinition) {
+ assert(d->hasGlobalStorage() && "Not a global variable");
+ QualType astTy = d->getType();
+ if (!ty)
+ ty = getTypes().convertTypeForMem(astTy);
+
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+
+ cir::GlobalOp g = getOrCreateCIRGlobal(d, ty, isForDefinition);
+ mlir::Type ptrTy = builder.getPointerTo(g.getSymType());
+ return builder.create<cir::GetGlobalOp>(getLoc(d->getSourceRange()), ptrTy,
+ g.getSymName());
+}
+
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
bool isTentative) {
const QualType astTy = vd->getType();
@@ -507,6 +603,7 @@ cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd,
funcType = convertType(fd->getType());
}
+ assert(!cir::MissingFeatures::mangledNames());
cir::FuncOp func = getOrCreateCIRFunction(
cast<NamedDecl>(gd.getDecl())->getIdentifier()->getName(), funcType, gd,
forVTable, dontDefer, /*isThunk=*/false, isForDefinition);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 174094bafabad..764ad1d7592aa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -91,6 +91,31 @@ class CIRGenModule : public CIRGenTypeCache {
const clang::LangOptions &getLangOpts() const { return langOpts; }
mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); }
+ /// -------
+ /// Handling globals
+ /// -------
+
+ mlir::Operation *getGlobalValue(llvm::StringRef ref);
+
+ /// If the specified mangled name is not in the module, create and return an
+ /// mlir::GlobalOp value
+ cir::GlobalOp getOrCreateCIRGlobal(llvm::StringRef mangledName, mlir::Type ty,
+ LangAS langAS, const VarDecl *d,
+ ForDefinition_t isForDefinition);
+
+ cir::GlobalOp getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty,
+ ForDefinition_t isForDefinition);
+
+ /// Return the mlir::Value for the address of the given global variable.
+ /// If Ty is non-null and if the global doesn't exist, then it will be created
+ /// with the specified type instead of whatever the normal requested type
+ /// would be. If IsForDefinition is true, it is guaranteed that an actual
+ /// global with type Ty will be returned, not conversion of a variable with
+ /// the same mangled name but some other type.
+ mlir::Value
+ getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
+ ForDefinition_t isForDefinition = NotForDefinition);
+
/// Helpers to convert the presumed location of Clang's SourceLocation to an
/// MLIR Location.
mlir::Location getLoc(clang::SourceLocation cLoc);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index c798c14508f56..f3e5e572653da 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -848,6 +848,41 @@ parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
return success();
}
+//===----------------------------------------------------------------------===//
+// GetGlobalOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ // Verify that the result type underlying pointer type matches the type of
+ // the referenced cir.global or cir.func op.
+ mlir::Operation *op =
+ symbolTable.lookupNearestSymbolFrom(*this, getNameAttr());
+ if (op == nullptr || !(isa<GlobalOp>(op) || isa<FuncOp>(op)))
+ return emitOpError("'")
+ << getName()
+ << "' does not reference a valid cir.global or cir.func";
+
+ mlir::Type symTy;
+ if (auto g = dyn_cast<GlobalOp>(op)) {
+ symTy = g.getSymType();
+ assert(!cir::MissingFeatures::addressSpace());
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+ } else if (auto f = dyn_cast<FuncOp>(op)) {
+ symTy = f.getFunctionType();
+ } else {
+ llvm_unreachable("Unexpected operation for GetGlobalOp");
+ }
+
+ auto resultType = dyn_cast<PointerType>(getAddr().getType());
+ if (!resultType || symTy != resultType.getPointee())
+ return emitOpError("result type pointee type '")
+ << resultType.getPointee() << "' does not match type " << symTy
+ << " of the global @" << getName();
+
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 7ca36409c9cac..7159f89c93a53 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -828,6 +828,26 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
return mlir::LogicalResult::success();
}
+mlir::LogicalResult CIRToLLVMGetGlobalOpLowering::matchAndRewrite(
+ cir::GetGlobalOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ // FIXME(cir): Premature DCE to avoid lowering stuff we're not using.
+ // CIRGen should mitigate this and not emit the get_global.
+ if (op->getUses().empty()) {
+ rewriter.eraseOp(op);
+ return mlir::success();
+ }
+
+ mlir::Type type = getTypeConverter()->convertType(op.getType());
+ mlir::Operation *newop =
+ rewriter.create<mlir::LLVM::AddressOfOp>(op.getLoc(), type, op.getName());
+
+ assert(!cir::MissingFeatures::opGlobalThreadLocal());
+
+ rewriter.replaceOp(op, newop);
+ return mlir::success();
+}
+
/// Replace CIR global with a region initialized LLVM global and update
/// insertion point to the end of the initializer block.
void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
@@ -1418,6 +1438,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMCmpOpLowering,
CIRToLLVMConstantOpLowering,
CIRToLLVMFuncOpLowering,
+ CIRToLLVMGetGlobalOpLowering,
CIRToLLVMTrapOpLowering,
CIRToLLVMUnaryOpLowering
// clang-format on
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d53c4b31682bb..1de6c9c56b485 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -140,6 +140,16 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMGetGlobalOpLowering
+ : public mlir::OpConversionPattern<cir::GetGlobalOp> {
+public:
+ using mlir::OpConversionPattern<cir::GetGlobalOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::GetGlobalOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMGlobalOpLowering
: public mlir::OpConversionPattern<cir::GlobalOp> {
const mlir::DataLayout &dataLayout;
diff --git a/clang/test/CIR/CodeGen/basic.c b/clang/test/CIR/CodeGen/basic.c
index 7184e395ce386..cf687e44044c4 100644
--- a/clang/test/CIR/CodeGen/basic.c
+++ b/clang/test/CIR/CodeGen/basic.c
@@ -144,3 +144,28 @@ void f5(void) {
// OGCG: br label %[[LOOP:.*]]
// OGCG: [[LOOP]]:
// OGCG: br label %[[LOOP]]
+
+int gv;
+int f6(void) {
+ return gv;
+}
+
+// CIR: cir.func @f6() -> !s32i
+// CIR-NEXT: %[[RV:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
+// CIR-NEXT: %[[GV_PTR:.*]] = cir.get_global @gv : !cir.ptr<!s32i>
+// CIR-NEXT: %[[GV:.*]] = cir.load %[[GV_PTR]] : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT: cir.store %[[GV]], %[[RV]] : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT: %[[R:.*]] = cir.load %[[RV]] : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT: cir.return %[[R]] : !s32i
+
+// LLVM: define i32 @f6()
+// LLVM-NEXT: %[[RV_PTR:.*]] = alloca i32, i64 1, align 4
+// LLVM-NEXT: %[[GV:.*]] = load i32, ptr @gv, align 4
+// LLVM-NEXT: store i32 %[[GV]], ptr %[[RV_PTR]], align 4
+// LLVM-NEXT: %[[RV:.*]] = load i32, ptr %[[RV_PTR]], align 4
+// LLVM-NEXT: ret i32 %[[RV]]
+
+// OGCG: define{{.*}} i32 @f6()
+// OGCG-NEXT: entry:
+// OGCG-NEXT: %[[GV:.*]] = load i32, ptr @gv, align 4
+// OGCG-NEXT: ret i32 %[[GV]]
More information about the cfe-commits
mailing list