https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/153044
This change introduces the #cir.global_view attribute and adds support for using that attribute to handle initializing a global variable with the address of another global variable. This does not yet include support for the optional list of indices to get an offset from the base address. Those will be added in a follow-up patch. >From e848ef44870e8bfcd74b3f4181697a4e042c904d Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Fri, 8 Aug 2025 16:26:32 -0700 Subject: [PATCH] [CIR] Introduce the CIR global_view attribute This change introduces the #cir.global_view attribute and adds support for using that attribute to handle initializing a global variable with the address of another global variable. This does not yet include support for the optional list of indices to get an offset from the base address. Those will be added in a follow-up patch. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 7 ++ .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 47 +++++++++++ clang/include/clang/CIR/MissingFeatures.h | 3 +- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 69 +++++++++++++--- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 10 +++ clang/lib/CIR/CodeGen/CIRGenModule.h | 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 3 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 82 +++++++++++++++++-- clang/test/CIR/CodeGen/globals.cpp | 20 +++++ 9 files changed, 226 insertions(+), 18 deletions(-) create mode 100644 clang/test/CIR/CodeGen/globals.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index b26e5581014d5..986c8c3d133ac 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -212,6 +212,13 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::AllocaOp>(loc, addrType, type, name, alignment); } + /// Get constant address of a global variable as an MLIR attribute. + cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type, + cir::GlobalOp globalOp) { + auto symbol = mlir::FlatSymbolRefAttr::get(globalOp.getSymNameAttr()); + return cir::GlobalViewAttr::get(type, symbol); + } + mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) { assert(!cir::MissingFeatures::addressSpace()); return create<cir::GetGlobalOp>(loc, getPointerTo(global.getSymType()), diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 3d34d77184a4d..9db236eb5580e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -370,6 +370,53 @@ def CIR_ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> { }]; } +//===----------------------------------------------------------------------===// +// GlobalViewAttr +//===----------------------------------------------------------------------===// + +def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [ + TypedAttrInterface +]> { + let summary = "Provides constant access to a global address"; + let description = [{ + Get constant address of global `symbol`. It provides a way to access globals + from other global and always produces a pointer. + + The type of the input symbol can be different from `#cir.global_view` + output type, since a given view of the global might require a static + cast for initializing other globals. + + The result type of this attribute may be an integer type. In such a case, + the pointer to the referenced global is casted to an integer and this + attribute represents the casted result. + + Example: + + ``` + cir.global external @s = @".str2": !cir.ptr<i8> + cir.global external @x = #cir.global_view<@s> : !cir.ptr<i8> + cir.global external @s_addr = #cir.global_view<@s> : !s64i + ``` + }]; + + let parameters = (ins AttributeSelfTypeParameter<"">:$type, + "mlir::FlatSymbolRefAttr":$symbol); + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + "mlir::FlatSymbolRefAttr":$symbol), [{ + return $_get(type.getContext(), type, symbol); + }]> + ]; + + // let genVerifyDecl = 1; + let assemblyFormat = [{ + `<` + $symbol + `>` + }]; +} + //===----------------------------------------------------------------------===// // ConstComplexAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index fcc8ce7caf111..61dff2e4ef9ee 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -210,6 +210,8 @@ struct MissingFeatures { static bool fastMathFlags() { return false; } static bool fpConstraints() { return false; } static bool generateDebugInfo() { return false; } + static bool globalViewIndices() { return false; } + static bool globalViewIntLowering() { return false; } static bool hip() { return false; } static bool implicitConstructorArgs() { return false; } static bool incrementProfileCounter() { return false; } @@ -233,7 +235,6 @@ struct MissingFeatures { static bool objCGC() { return false; } static bool objCLifetime() { return false; } static bool openMP() { return false; } - static bool opGlobalViewAttr() { return false; } static bool opTBAA() { return false; } static bool peepholeProtection() { return false; } static bool pgoUse() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 5b3bf85cefbb0..1f1e9c8121ddc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -340,7 +340,11 @@ struct ConstantLValue { llvm::PointerUnion<mlir::Value, mlir::Attribute> value; bool hasOffsetApplied; - ConstantLValue(std::nullptr_t) : value(nullptr), hasOffsetApplied(false) {} + /*implicit*/ ConstantLValue(std::nullptr_t) + : value(nullptr), hasOffsetApplied(false) {} + /*implicit*/ ConstantLValue(cir::GlobalViewAttr address) + : value(address), hasOffsetApplied(false) {} + ConstantLValue() : value(nullptr), hasOffsetApplied(false) {} }; @@ -380,6 +384,33 @@ class ConstantLValueEmitter ConstantLValue VisitCXXTypeidExpr(const CXXTypeidExpr *e); ConstantLValue VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e); + + /// Return GEP-like value offset + mlir::ArrayAttr getOffset(mlir::Type ty) { + int64_t offset = value.getLValueOffset().getQuantity(); + if (offset == 0) + return {}; + + cgm.errorNYI("ConstantLValueEmitter: global view with offset"); + return {}; + } + + /// Apply the value offset to the given constant. + ConstantLValue applyOffset(ConstantLValue &c) { + // Handle attribute constant LValues. + if (auto attr = mlir::dyn_cast<mlir::Attribute>(c.value)) { + if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(attr)) { + if (value.getLValueOffset().getQuantity() == 0) + return gv; + cgm.errorNYI("ConstantLValue: global view with offset"); + return {}; + } + llvm_unreachable("Unsupported attribute type to offset"); + } + + cgm.errorNYI("ConstantLValue: non-attribute offset"); + return {}; + } }; } // namespace @@ -411,10 +442,8 @@ mlir::Attribute ConstantLValueEmitter::tryEmit() { return {}; // Apply the offset if necessary and not already done. - if (!result.hasOffsetApplied) { - cgm.errorNYI("ConstantLValueEmitter: apply offset"); - return {}; - } + if (!result.hasOffsetApplied) + value = applyOffset(result).value; // Convert to the appropriate type; this could be an lvalue for // an integer. FIXME: performAddrSpaceCast @@ -453,15 +482,35 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { } if (auto *fd = dyn_cast<FunctionDecl>(d)) { - cgm.errorNYI(fd->getSourceRange(), - "ConstantLValueEmitter: function decl"); - return {}; + cir::FuncOp fop = cgm.getAddrOfFunction(fd); + CIRGenBuilderTy &builder = cgm.getBuilder(); + mlir::MLIRContext *mlirContext = builder.getContext(); + return cir::GlobalViewAttr::get( + builder.getPointerTo(fop.getFunctionType()), + mlir::FlatSymbolRefAttr::get(mlirContext, fop.getSymNameAttr())); } if (auto *vd = dyn_cast<VarDecl>(d)) { - cgm.errorNYI(vd->getSourceRange(), "ConstantLValueEmitter: var decl"); - return {}; + // We can never refer to a variable with local storage. + if (!vd->hasLocalStorage()) { + if (vd->isFileVarDecl() || vd->hasExternalStorage()) + return cgm.getAddrOfGlobalVarAttr(vd); + + if (vd->isLocalVarDecl()) { + cgm.errorNYI(vd->getSourceRange(), + "ConstantLValueEmitter: local var decl"); + return {}; + } + } } + + // Classic codegen handles MSGuidDecl,UnnamedGlobalConstantDecl, and + // TemplateParamObjectDecl, but it can also fall through from VarDecl, + // in which case it silently returns nullptr. For now, let's emit an + // error to see what cases we need to handle. + cgm.errorNYI(d->getSourceRange(), + "ConstantLValueEmitter: unhandled value decl"); + return {}; } // Handle typeid(T). diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index ff6d293aae229..16cbbe2a5c98a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -654,6 +654,16 @@ mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty, g.getSymName()); } +cir::GlobalViewAttr CIRGenModule::getAddrOfGlobalVarAttr(const VarDecl *d) { + assert(d->hasGlobalStorage() && "Not a global variable"); + mlir::Type ty = getTypes().convertTypeForMem(d->getType()); + + cir::GlobalOp globalOp = getOrCreateCIRGlobal(d, ty, NotForDefinition); + assert(!cir::MissingFeatures::addressSpace()); + cir::PointerType ptrTy = builder.getPointerTo(globalOp.getSymType()); + return builder.getGlobalViewAttr(ptrTy, globalOp); +} + void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { if (getLangOpts().OpenCL || getLangOpts().OpenMPIsTargetDevice) { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 163a0fc925e7e..c376ecd832018 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -157,6 +157,9 @@ class CIRGenModule : public CIRGenTypeCache { getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {}, ForDefinition_t isForDefinition = NotForDefinition); + /// Return the mlir::GlobalViewAttr for the address of the given global. + cir::GlobalViewAttr getAddrOfGlobalVarAttr(const VarDecl *d); + CharUnits computeNonVirtualBaseClassOffset( const CXXRecordDecl *derivedClass, llvm::iterator_range<CastExpr::path_const_iterator> path); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 53ab04eb85385..7c8429432b0f9 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -339,7 +339,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, } if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr, - cir::ConstComplexAttr, cir::PoisonAttr>(attrType)) + cir::ConstComplexAttr, cir::GlobalViewAttr, cir::PoisonAttr>( + attrType)) return success(); assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?"); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 43a1b512a6d2c..ee6b5e89f48a7 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -202,7 +202,8 @@ class CIRAttrToValue { return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr) .Case<cir::IntAttr, cir::FPAttr, cir::ConstComplexAttr, cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr, - cir::ZeroAttr>([&](auto attrT) { return visitCirAttr(attrT); }) + cir::GlobalViewAttr, cir::ZeroAttr>( + [&](auto attrT) { return visitCirAttr(attrT); }) .Default([&](auto attrT) { return mlir::Value(); }); } @@ -212,6 +213,7 @@ class CIRAttrToValue { mlir::Value visitCirAttr(cir::ConstPtrAttr ptrAttr); mlir::Value visitCirAttr(cir::ConstArrayAttr attr); mlir::Value visitCirAttr(cir::ConstVectorAttr attr); + mlir::Value visitCirAttr(cir::GlobalViewAttr attr); mlir::Value visitCirAttr(cir::ZeroAttr attr); private: @@ -391,6 +393,62 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstVectorAttr attr) { mlirValues)); } +// GlobalViewAttr visitor. +mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) { + auto moduleOp = parentOp->getParentOfType<mlir::ModuleOp>(); + mlir::DataLayout dataLayout(moduleOp); + mlir::Type sourceType; + assert(!cir::MissingFeatures::addressSpace()); + llvm::StringRef symName; + mlir::Operation *sourceSymbol = + mlir::SymbolTable::lookupSymbolIn(moduleOp, globalAttr.getSymbol()); + if (auto llvmSymbol = dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) { + sourceType = llvmSymbol.getType(); + symName = llvmSymbol.getSymName(); + } else if (auto cirSymbol = dyn_cast<cir::GlobalOp>(sourceSymbol)) { + sourceType = + convertTypeForMemory(*converter, dataLayout, cirSymbol.getSymType()); + symName = cirSymbol.getSymName(); + } else if (auto llvmFun = dyn_cast<mlir::LLVM::LLVMFuncOp>(sourceSymbol)) { + sourceType = llvmFun.getFunctionType(); + symName = llvmFun.getSymName(); + } else if (auto fun = dyn_cast<cir::FuncOp>(sourceSymbol)) { + sourceType = converter->convertType(fun.getFunctionType()); + symName = fun.getSymName(); + } else if (auto alias = dyn_cast<mlir::LLVM::AliasOp>(sourceSymbol)) { + sourceType = alias.getType(); + symName = alias.getSymName(); + } else { + llvm_unreachable("Unexpected GlobalOp type"); + } + + mlir::Location loc = parentOp->getLoc(); + mlir::Value addrOp = rewriter.create<mlir::LLVM::AddressOfOp>( + loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symName); + + assert(!cir::MissingFeatures::globalViewIndices()); + + // The incubator has handling here for the attribute having integer type, but + // the only test case I could find that reaches it is a direct CIR-to-LLVM IR + // lowering with no clear indication of how the CIR might have been generated. + // We'll hit the unreachable below if this happens. + assert(!cir::MissingFeatures::globalViewIntLowering()); + + if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(globalAttr.getType())) { + mlir::Type llvmEltTy = + convertTypeForMemory(*converter, dataLayout, ptrTy.getPointee()); + + if (llvmEltTy == sourceType) + return addrOp; + + mlir::Type llvmDstTy = converter->convertType(globalAttr.getType()); + return rewriter.create<mlir::LLVM::BitcastOp>(parentOp->getLoc(), llvmDstTy, + addrOp); + } + + llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr"); +} + /// ZeroAttr visitor. mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) { mlir::Location loc = parentOp->getLoc(); @@ -1101,7 +1159,13 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( attr = rewriter.getIntegerAttr(typeConverter->convertType(op.getType()), value); } else if (mlir::isa<cir::IntType>(op.getType())) { - assert(!cir::MissingFeatures::opGlobalViewAttr()); + // Lower GlobalViewAttr to llvm.mlir.addressof + llvm.mlir.ptrtoint + if (auto ga = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) { + // See the comment in visitCirAttr for why this isn't implemented. + assert(!cir::MissingFeatures::globalViewIntLowering()); + op.emitError() << "global view with integer type"; + return mlir::failure(); + } attr = rewriter.getIntegerAttr( typeConverter->convertType(op.getType()), @@ -1119,7 +1183,12 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( return mlir::success(); } } - assert(!cir::MissingFeatures::opGlobalViewAttr()); + // Lower GlobalViewAttr to llvm.mlir.addressof + if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) { + auto newOp = lowerCirAttrAsValue(op, gv, rewriter, getTypeConverter()); + rewriter.replaceOp(op, newOp); + return mlir::success(); + } attr = op.getValue(); } else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) { const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue()); @@ -1374,8 +1443,9 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( cir::GlobalOp op, mlir::Attribute init, mlir::ConversionPatternRewriter &rewriter) const { // TODO: Generalize this handling when more types are needed here. - assert((isa<cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr, - cir::ConstComplexAttr, cir::ZeroAttr>(init))); + assert( + (isa<cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr, + cir::ConstComplexAttr, cir::GlobalViewAttr, cir::ZeroAttr>(init))); // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals @@ -1429,7 +1499,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( } } else if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr, cir::ConstComplexAttr, - cir::ZeroAttr>(init.value())) { + cir::GlobalViewAttr, cir::ZeroAttr>(init.value())) { // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals // to the appropriate value. diff --git a/clang/test/CIR/CodeGen/globals.cpp b/clang/test/CIR/CodeGen/globals.cpp new file mode 100644 index 0000000000000..7a08a4824276e --- /dev/null +++ b/clang/test/CIR/CodeGen/globals.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM + +// Should constant initialize global with constant address. +int var = 1; +int *constAddr = &var; + +// CIR: cir.global external @constAddr = #cir.global_view<@var> : !cir.ptr<!s32i> + +// LLVM: @constAddr = global ptr @var, align 8 + +// Should constant initialize global with constant address. +int f(); +int (*constFnAddr)() = f; + +// CIR: cir.global external @constFnAddr = #cir.global_view<@_Z1fv> : !cir.ptr<!cir.func<() -> !s32i>> + +// LLVM: @constFnAddr = global ptr @_Z1fv, align 8 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits