Skip to content

Commit 43f2d5e

Browse files
committed
[CIR] Upstream support for cir.get_global
This adds basic support for referencing global variables from within functions via the cir.get_global operation.
1 parent 58b91d1 commit 43f2d5e

File tree

9 files changed

+291
-2
lines changed

9 files changed

+291
-2
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,37 @@ def GlobalOp : CIR_Op<"global"> {
12791279
let hasVerifier = 1;
12801280
}
12811281

1282+
//===----------------------------------------------------------------------===//
1283+
// GetGlobalOp
1284+
//===----------------------------------------------------------------------===//
1285+
1286+
def GetGlobalOp : CIR_Op<"get_global",
1287+
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
1288+
let summary = "Get the address of a global variable";
1289+
let description = [{
1290+
The `cir.get_global` operation retrieves the address pointing to a
1291+
named global variable. If the global variable is marked constant, writing
1292+
to the resulting address (such as through a `cir.store` operation) is
1293+
undefined. Resulting type must always be a `!cir.ptr<...>` type with the
1294+
same address space as the global variable.
1295+
1296+
Example:
1297+
```mlir
1298+
%x = cir.get_global @gv : !cir.ptr<i32>
1299+
```
1300+
}];
1301+
1302+
let arguments = (ins FlatSymbolRefAttr:$name);
1303+
let results = (outs Res<CIR_PointerType, "", []>:$addr);
1304+
1305+
let assemblyFormat = [{
1306+
$name `:` qualified(type($addr)) attr-dict
1307+
}];
1308+
1309+
// `GetGlobalOp` is fully verified by its traits.
1310+
let hasVerifier = 0;
1311+
}
1312+
12821313
//===----------------------------------------------------------------------===//
12831314
// FuncOp
12841315
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct MissingFeatures {
3535
static bool opGlobalThreadLocal() { return false; }
3636
static bool opGlobalConstant() { return false; }
3737
static bool opGlobalAlignment() { return false; }
38+
static bool opGlobalWeakRef() { return false; }
3839

3940
static bool supportIFuncAttr() { return false; }
4041
static bool supportVisibility() { return false; }
@@ -113,6 +114,10 @@ struct MissingFeatures {
113114
static bool incrementProfileCounter() { return false; }
114115
static bool insertBuiltinUnpredictable() { return false; }
115116
static bool objCGC() { return false; }
117+
static bool setObjCGCLValueClass() { return false; }
118+
static bool mangledNames() { return false; }
119+
static bool setDLLStorageClass() { return false; }
120+
static bool openMP() { return false; }
116121

117122
// Missing types
118123
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,43 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
183183
emitStoreOfScalar(src.getScalarVal(), dst, isInit);
184184
}
185185

186+
static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
187+
const VarDecl *vd) {
188+
QualType T = e->getType();
189+
190+
// If it's thread_local, emit a call to its wrapper function instead.
191+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
192+
if (vd->getTLSKind() == VarDecl::TLS_Dynamic)
193+
cgf.cgm.errorNYI(e->getSourceRange(),
194+
"emitGlobalVarDeclLValue: thread_local variable");
195+
196+
// Check if the variable is marked as declare target with link clause in
197+
// device codegen.
198+
if (cgf.getLangOpts().OpenMP)
199+
cgf.cgm.errorNYI(e->getSourceRange(), "emitGlobalVarDeclLValue: OpenMP");
200+
201+
// Traditional LLVM codegen handles thread local separately, CIR handles
202+
// as part of getAddrOfGlobalVar.
203+
mlir::Value v = cgf.cgm.getAddrOfGlobalVar(vd);
204+
205+
assert(!cir::MissingFeatures::addressSpace());
206+
mlir::Type realVarTy = cgf.convertTypeForMem(vd->getType());
207+
cir::PointerType realPtrTy = cgf.getBuilder().getPointerTo(realVarTy);
208+
if (realPtrTy != v.getType())
209+
v = cgf.getBuilder().createBitcast(v.getLoc(), v, realPtrTy);
210+
211+
CharUnits alignment = cgf.getContext().getDeclAlign(vd);
212+
Address addr(v, realVarTy, alignment);
213+
LValue lv;
214+
if (vd->getType()->isReferenceType())
215+
cgf.cgm.errorNYI(e->getSourceRange(),
216+
"emitGlobalVarDeclLValue: reference type");
217+
else
218+
lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl);
219+
assert(!cir::MissingFeatures::setObjCGCLValueClass());
220+
return lv;
221+
}
222+
186223
void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
187224
bool isVolatile, QualType ty,
188225
bool isInit, bool isNontemporal) {
@@ -288,7 +325,7 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
288325

289326
// Check if this is a global variable
290327
if (vd->hasLinkage() || vd->isStaticDataMember())
291-
cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: global variable");
328+
return emitGlobalVarDeclLValue(*this, e, vd);
292329

293330
Address addr = Address::invalid();
294331

@@ -299,7 +336,7 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
299336
} else {
300337
// Otherwise, it might be static local we haven't emitted yet for some
301338
// reason; most likely, because it's in an outer function.
302-
cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: static local");
339+
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: static local");
303340
}
304341

305342
return makeAddrLValue(addr, ty, AlignmentSource::Type);

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,105 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
200200
}
201201
}
202202

203+
mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) {
204+
mlir::Operation *global = mlir::SymbolTable::lookupSymbolIn(theModule, name);
205+
if (!global)
206+
return nullptr;
207+
return global;
208+
}
209+
210+
/// If the specified mangled name is not in the module,
211+
/// create and return an mlir GlobalOp with the specified type (TODO(cir):
212+
/// address space).
213+
///
214+
/// TODO(cir):
215+
/// 1. If there is something in the module with the specified name, return
216+
/// it potentially bitcasted to the right type.
217+
///
218+
/// 2. If \p d is non-null, it specifies a decl that correspond to this. This
219+
/// is used to set the attributes on the global when it is first created.
220+
///
221+
/// 3. If \p isForDefinition is true, it is guaranteed that an actual global
222+
/// with type \p ty will be returned, not conversion of a variable with the same
223+
/// mangled name but some other type.
224+
cir::GlobalOp
225+
CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
226+
LangAS langAS, const VarDecl *d,
227+
ForDefinition_t isForDefinition) {
228+
// Lookup the entry, lazily creating it if necessary.
229+
cir::GlobalOp entry;
230+
if (mlir::Operation *v = getGlobalValue(mangledName)) {
231+
if (!isa<cir::GlobalOp>(v))
232+
errorNYI(d->getSourceRange(), "global with non-GlobalOp type");
233+
entry = cast<cir::GlobalOp>(v);
234+
}
235+
236+
if (entry) {
237+
assert(!cir::MissingFeatures::addressSpace());
238+
assert(!cir::MissingFeatures::opGlobalWeakRef());
239+
240+
assert(!cir::MissingFeatures::setDLLStorageClass());
241+
assert(!cir::MissingFeatures::openMP());
242+
243+
if (entry.getSymType() == ty)
244+
return entry;
245+
246+
// If there are two attempts to define the same mangled name, issue an
247+
// error.
248+
//
249+
// TODO(cir): look at mlir::GlobalValue::isDeclaration for all aspects of
250+
// recognizing the global as a declaration, for now only check if
251+
// initializer is present.
252+
if (isForDefinition && !entry.isDeclaration()) {
253+
errorNYI(d->getSourceRange(), "global with conflicting type");
254+
}
255+
256+
// Address space check removed because it is unnecessary because CIR records
257+
// address space info in types.
258+
259+
// (If global is requested for a definition, we always need to create a new
260+
// global, not just return a bitcast.)
261+
if (!isForDefinition)
262+
return entry;
263+
}
264+
265+
errorNYI(d->getSourceRange(), "reference of undeclared global");
266+
}
267+
268+
cir::GlobalOp
269+
CIRGenModule::getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty,
270+
ForDefinition_t isForDefinition) {
271+
assert(d->hasGlobalStorage() && "Not a global variable");
272+
QualType astTy = d->getType();
273+
if (!ty)
274+
ty = getTypes().convertTypeForMem(astTy);
275+
276+
assert(!cir::MissingFeatures::mangledNames());
277+
return getOrCreateCIRGlobal(d->getIdentifier()->getName(), ty,
278+
astTy.getAddressSpace(), d, isForDefinition);
279+
}
280+
281+
/// Return the mlir::Value for the address of the given global variable. If
282+
/// \p ty is non-null and if the global doesn't exist, then it will be created
283+
/// with the specified type instead of whatever the normal requested type would
284+
/// be. If \p isForDefinition is true, it is guaranteed that an actual global
285+
/// with type \p ty will be returned, not conversion of a variable with the same
286+
/// mangled name but some other type.
287+
mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty,
288+
ForDefinition_t isForDefinition) {
289+
assert(d->hasGlobalStorage() && "Not a global variable");
290+
QualType astTy = d->getType();
291+
if (!ty)
292+
ty = getTypes().convertTypeForMem(astTy);
293+
294+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
295+
296+
cir::GlobalOp g = getOrCreateCIRGlobal(d, ty, isForDefinition);
297+
mlir::Type ptrTy = builder.getPointerTo(g.getSymType());
298+
return builder.create<cir::GetGlobalOp>(getLoc(d->getSourceRange()), ptrTy,
299+
g.getSymName());
300+
}
301+
203302
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
204303
bool isTentative) {
205304
const QualType astTy = vd->getType();
@@ -505,6 +604,7 @@ cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd,
505604
funcType = convertType(fd->getType());
506605
}
507606

607+
assert(!cir::MissingFeatures::mangledNames());
508608
cir::FuncOp func = getOrCreateCIRFunction(
509609
cast<NamedDecl>(gd.getDecl())->getIdentifier()->getName(), funcType, gd,
510610
forVTable, dontDefer, /*isThunk=*/false, isForDefinition);

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,31 @@ class CIRGenModule : public CIRGenTypeCache {
8585
const clang::LangOptions &getLangOpts() const { return langOpts; }
8686
mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); }
8787

88+
/// -------
89+
/// Handling globals
90+
/// -------
91+
92+
mlir::Operation *getGlobalValue(llvm::StringRef ref);
93+
94+
/// If the specified mangled name is not in the module, create and return an
95+
/// mlir::GlobalOp value
96+
cir::GlobalOp getOrCreateCIRGlobal(llvm::StringRef mangledName, mlir::Type ty,
97+
LangAS langAS, const VarDecl *d,
98+
ForDefinition_t isForDefinition);
99+
100+
cir::GlobalOp getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty,
101+
ForDefinition_t isForDefinition);
102+
103+
/// Return the mlir::Value for the address of the given global variable.
104+
/// If Ty is non-null and if the global doesn't exist, then it will be created
105+
/// with the specified type instead of whatever the normal requested type
106+
/// would be. If IsForDefinition is true, it is guaranteed that an actual
107+
/// global with type Ty will be returned, not conversion of a variable with
108+
/// the same mangled name but some other type.
109+
mlir::Value
110+
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
111+
ForDefinition_t isForDefinition = NotForDefinition);
112+
88113
/// Helpers to convert the presumed location of Clang's SourceLocation to an
89114
/// MLIR Location.
90115
mlir::Location getLoc(clang::SourceLocation cLoc);

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,41 @@ parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
764764
return success();
765765
}
766766

767+
//===----------------------------------------------------------------------===//
768+
// GetGlobalOp
769+
//===----------------------------------------------------------------------===//
770+
771+
LogicalResult
772+
cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
773+
// Verify that the result type underlying pointer type matches the type of
774+
// the referenced cir.global or cir.func op.
775+
mlir::Operation *op =
776+
symbolTable.lookupNearestSymbolFrom(*this, getNameAttr());
777+
if (op == nullptr || !(isa<GlobalOp>(op) || isa<FuncOp>(op)))
778+
return emitOpError("'")
779+
<< getName()
780+
<< "' does not reference a valid cir.global or cir.func";
781+
782+
mlir::Type symTy;
783+
if (auto g = dyn_cast<GlobalOp>(op)) {
784+
symTy = g.getSymType();
785+
assert(!cir::MissingFeatures::addressSpace());
786+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
787+
} else if (auto f = dyn_cast<FuncOp>(op)) {
788+
symTy = f.getFunctionType();
789+
} else {
790+
llvm_unreachable("Unexpected operation for GetGlobalOp");
791+
}
792+
793+
auto resultType = dyn_cast<PointerType>(getAddr().getType());
794+
if (!resultType || symTy != resultType.getPointee())
795+
return emitOpError("result type pointee type '")
796+
<< resultType.getPointee() << "' does not match type " << symTy
797+
<< " of the global @" << getName();
798+
799+
return success();
800+
}
801+
767802
//===----------------------------------------------------------------------===//
768803
// FuncOp
769804
//===----------------------------------------------------------------------===//

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,26 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
828828
return mlir::LogicalResult::success();
829829
}
830830

831+
mlir::LogicalResult CIRToLLVMGetGlobalOpLowering::matchAndRewrite(
832+
cir::GetGlobalOp op, OpAdaptor adaptor,
833+
mlir::ConversionPatternRewriter &rewriter) const {
834+
// FIXME(cir): Premature DCE to avoid lowering stuff we're not using.
835+
// CIRGen should mitigate this and not emit the get_global.
836+
if (op->getUses().empty()) {
837+
rewriter.eraseOp(op);
838+
return mlir::success();
839+
}
840+
841+
mlir::Type type = getTypeConverter()->convertType(op.getType());
842+
mlir::Operation *newop =
843+
rewriter.create<mlir::LLVM::AddressOfOp>(op.getLoc(), type, op.getName());
844+
845+
assert(!cir::MissingFeatures::opGlobalThreadLocal());
846+
847+
rewriter.replaceOp(op, newop);
848+
return mlir::success();
849+
}
850+
831851
/// Replace CIR global with a region initialized LLVM global and update
832852
/// insertion point to the end of the initializer block.
833853
void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
@@ -1418,6 +1438,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
14181438
CIRToLLVMCmpOpLowering,
14191439
CIRToLLVMConstantOpLowering,
14201440
CIRToLLVMFuncOpLowering,
1441+
CIRToLLVMGetGlobalOpLowering,
14211442
CIRToLLVMTrapOpLowering,
14221443
CIRToLLVMUnaryOpLowering
14231444
// clang-format on

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
140140
mlir::ConversionPatternRewriter &) const override;
141141
};
142142

143+
class CIRToLLVMGetGlobalOpLowering
144+
: public mlir::OpConversionPattern<cir::GetGlobalOp> {
145+
public:
146+
using mlir::OpConversionPattern<cir::GetGlobalOp>::OpConversionPattern;
147+
148+
mlir::LogicalResult
149+
matchAndRewrite(cir::GetGlobalOp op, OpAdaptor,
150+
mlir::ConversionPatternRewriter &) const override;
151+
};
152+
143153
class CIRToLLVMGlobalOpLowering
144154
: public mlir::OpConversionPattern<cir::GlobalOp> {
145155
const mlir::DataLayout &dataLayout;

clang/test/CIR/CodeGen/basic.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,28 @@ void f5(void) {
144144
// OGCG: br label %[[LOOP:.*]]
145145
// OGCG: [[LOOP]]:
146146
// OGCG: br label %[[LOOP]]
147+
148+
int gv;
149+
int f6(void) {
150+
return gv;
151+
}
152+
153+
// CIR: cir.func @f6() -> !s32i
154+
// CIR-NEXT: %[[RV:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
155+
// CIR-NEXT: %[[GV_PTR:.*]] = cir.get_global @gv : !cir.ptr<!s32i>
156+
// CIR-NEXT: %[[GV:.*]] = cir.load %[[GV_PTR]] : !cir.ptr<!s32i>, !s32i
157+
// CIR-NEXT: cir.store %[[GV]], %[[RV]] : !s32i, !cir.ptr<!s32i>
158+
// CIR-NEXT: %[[R:.*]] = cir.load %[[RV]] : !cir.ptr<!s32i>, !s32i
159+
// CIR-NEXT: cir.return %[[R]] : !s32i
160+
161+
// LLVM: define i32 @f6()
162+
// LLVM-NEXT: %[[RV_PTR:.*]] = alloca i32, i64 1, align 4
163+
// LLVM-NEXT: %[[GV:.*]] = load i32, ptr @gv, align 4
164+
// LLVM-NEXT: store i32 %[[GV]], ptr %[[RV_PTR]], align 4
165+
// LLVM-NEXT: %[[RV:.*]] = load i32, ptr %[[RV_PTR]], align 4
166+
// LLVM-NEXT: ret i32 %[[RV]]
167+
168+
// OGCG: define{{.*}} i32 @f6()
169+
// OGCG-NEXT: entry:
170+
// OGCG-NEXT: %[[GV:.*]] = load i32, ptr @gv, align 4
171+
// OGCG-NEXT: ret i32 %[[GV]]

0 commit comments

Comments
 (0)