Skip to content

Commit 54f53c9

Browse files
authored
[CIR] Introduce the CIR global_view attribute (#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.
1 parent 7f195b3 commit 54f53c9

File tree

9 files changed

+226
-18
lines changed

9 files changed

+226
-18
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
212212
return create<cir::AllocaOp>(loc, addrType, type, name, alignment);
213213
}
214214

215+
/// Get constant address of a global variable as an MLIR attribute.
216+
cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type,
217+
cir::GlobalOp globalOp) {
218+
auto symbol = mlir::FlatSymbolRefAttr::get(globalOp.getSymNameAttr());
219+
return cir::GlobalViewAttr::get(type, symbol);
220+
}
221+
215222
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) {
216223
assert(!cir::MissingFeatures::addressSpace());
217224
return create<cir::GetGlobalOp>(loc, getPointerTo(global.getSymType()),

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,53 @@ def CIR_ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
370370
}];
371371
}
372372

373+
//===----------------------------------------------------------------------===//
374+
// GlobalViewAttr
375+
//===----------------------------------------------------------------------===//
376+
377+
def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
378+
TypedAttrInterface
379+
]> {
380+
let summary = "Provides constant access to a global address";
381+
let description = [{
382+
Get constant address of global `symbol`. It provides a way to access globals
383+
from other global and always produces a pointer.
384+
385+
The type of the input symbol can be different from `#cir.global_view`
386+
output type, since a given view of the global might require a static
387+
cast for initializing other globals.
388+
389+
The result type of this attribute may be an integer type. In such a case,
390+
the pointer to the referenced global is casted to an integer and this
391+
attribute represents the casted result.
392+
393+
Example:
394+
395+
```
396+
cir.global external @s = @".str2": !cir.ptr<i8>
397+
cir.global external @x = #cir.global_view<@s> : !cir.ptr<i8>
398+
cir.global external @s_addr = #cir.global_view<@s> : !s64i
399+
```
400+
}];
401+
402+
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
403+
"mlir::FlatSymbolRefAttr":$symbol);
404+
405+
let builders = [
406+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
407+
"mlir::FlatSymbolRefAttr":$symbol), [{
408+
return $_get(type.getContext(), type, symbol);
409+
}]>
410+
];
411+
412+
// let genVerifyDecl = 1;
413+
let assemblyFormat = [{
414+
`<`
415+
$symbol
416+
`>`
417+
}];
418+
}
419+
373420
//===----------------------------------------------------------------------===//
374421
// ConstComplexAttr
375422
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ struct MissingFeatures {
213213
static bool fastMathFlags() { return false; }
214214
static bool fpConstraints() { return false; }
215215
static bool generateDebugInfo() { return false; }
216+
static bool globalViewIndices() { return false; }
217+
static bool globalViewIntLowering() { return false; }
216218
static bool hip() { return false; }
217219
static bool implicitConstructorArgs() { return false; }
218220
static bool incrementProfileCounter() { return false; }
@@ -237,7 +239,6 @@ struct MissingFeatures {
237239
static bool objCGC() { return false; }
238240
static bool objCLifetime() { return false; }
239241
static bool openMP() { return false; }
240-
static bool opGlobalViewAttr() { return false; }
241242
static bool opTBAA() { return false; }
242243
static bool peepholeProtection() { return false; }
243244
static bool pgoUse() { return false; }

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,11 @@ struct ConstantLValue {
340340
llvm::PointerUnion<mlir::Value, mlir::Attribute> value;
341341
bool hasOffsetApplied;
342342

343-
ConstantLValue(std::nullptr_t) : value(nullptr), hasOffsetApplied(false) {}
343+
/*implicit*/ ConstantLValue(std::nullptr_t)
344+
: value(nullptr), hasOffsetApplied(false) {}
345+
/*implicit*/ ConstantLValue(cir::GlobalViewAttr address)
346+
: value(address), hasOffsetApplied(false) {}
347+
344348
ConstantLValue() : value(nullptr), hasOffsetApplied(false) {}
345349
};
346350

@@ -380,6 +384,33 @@ class ConstantLValueEmitter
380384
ConstantLValue VisitCXXTypeidExpr(const CXXTypeidExpr *e);
381385
ConstantLValue
382386
VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);
387+
388+
/// Return GEP-like value offset
389+
mlir::ArrayAttr getOffset(mlir::Type ty) {
390+
int64_t offset = value.getLValueOffset().getQuantity();
391+
if (offset == 0)
392+
return {};
393+
394+
cgm.errorNYI("ConstantLValueEmitter: global view with offset");
395+
return {};
396+
}
397+
398+
/// Apply the value offset to the given constant.
399+
ConstantLValue applyOffset(ConstantLValue &c) {
400+
// Handle attribute constant LValues.
401+
if (auto attr = mlir::dyn_cast<mlir::Attribute>(c.value)) {
402+
if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(attr)) {
403+
if (value.getLValueOffset().getQuantity() == 0)
404+
return gv;
405+
cgm.errorNYI("ConstantLValue: global view with offset");
406+
return {};
407+
}
408+
llvm_unreachable("Unsupported attribute type to offset");
409+
}
410+
411+
cgm.errorNYI("ConstantLValue: non-attribute offset");
412+
return {};
413+
}
383414
};
384415

385416
} // namespace
@@ -411,10 +442,8 @@ mlir::Attribute ConstantLValueEmitter::tryEmit() {
411442
return {};
412443

413444
// Apply the offset if necessary and not already done.
414-
if (!result.hasOffsetApplied) {
415-
cgm.errorNYI("ConstantLValueEmitter: apply offset");
416-
return {};
417-
}
445+
if (!result.hasOffsetApplied)
446+
value = applyOffset(result).value;
418447

419448
// Convert to the appropriate type; this could be an lvalue for
420449
// an integer. FIXME: performAddrSpaceCast
@@ -453,15 +482,35 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
453482
}
454483

455484
if (auto *fd = dyn_cast<FunctionDecl>(d)) {
456-
cgm.errorNYI(fd->getSourceRange(),
457-
"ConstantLValueEmitter: function decl");
458-
return {};
485+
cir::FuncOp fop = cgm.getAddrOfFunction(fd);
486+
CIRGenBuilderTy &builder = cgm.getBuilder();
487+
mlir::MLIRContext *mlirContext = builder.getContext();
488+
return cir::GlobalViewAttr::get(
489+
builder.getPointerTo(fop.getFunctionType()),
490+
mlir::FlatSymbolRefAttr::get(mlirContext, fop.getSymNameAttr()));
459491
}
460492

461493
if (auto *vd = dyn_cast<VarDecl>(d)) {
462-
cgm.errorNYI(vd->getSourceRange(), "ConstantLValueEmitter: var decl");
463-
return {};
494+
// We can never refer to a variable with local storage.
495+
if (!vd->hasLocalStorage()) {
496+
if (vd->isFileVarDecl() || vd->hasExternalStorage())
497+
return cgm.getAddrOfGlobalVarAttr(vd);
498+
499+
if (vd->isLocalVarDecl()) {
500+
cgm.errorNYI(vd->getSourceRange(),
501+
"ConstantLValueEmitter: local var decl");
502+
return {};
503+
}
504+
}
464505
}
506+
507+
// Classic codegen handles MSGuidDecl,UnnamedGlobalConstantDecl, and
508+
// TemplateParamObjectDecl, but it can also fall through from VarDecl,
509+
// in which case it silently returns nullptr. For now, let's emit an
510+
// error to see what cases we need to handle.
511+
cgm.errorNYI(d->getSourceRange(),
512+
"ConstantLValueEmitter: unhandled value decl");
513+
return {};
465514
}
466515

467516
// Handle typeid(T).

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,16 @@ mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty,
655655
g.getSymName());
656656
}
657657

658+
cir::GlobalViewAttr CIRGenModule::getAddrOfGlobalVarAttr(const VarDecl *d) {
659+
assert(d->hasGlobalStorage() && "Not a global variable");
660+
mlir::Type ty = getTypes().convertTypeForMem(d->getType());
661+
662+
cir::GlobalOp globalOp = getOrCreateCIRGlobal(d, ty, NotForDefinition);
663+
assert(!cir::MissingFeatures::addressSpace());
664+
cir::PointerType ptrTy = builder.getPointerTo(globalOp.getSymType());
665+
return builder.getGlobalViewAttr(ptrTy, globalOp);
666+
}
667+
658668
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
659669
bool isTentative) {
660670
if (getLangOpts().OpenCL || getLangOpts().OpenMPIsTargetDevice) {

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class CIRGenModule : public CIRGenTypeCache {
161161
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
162162
ForDefinition_t isForDefinition = NotForDefinition);
163163

164+
/// Return the mlir::GlobalViewAttr for the address of the given global.
165+
cir::GlobalViewAttr getAddrOfGlobalVarAttr(const VarDecl *d);
166+
164167
CharUnits computeNonVirtualBaseClassOffset(
165168
const CXXRecordDecl *derivedClass,
166169
llvm::iterator_range<CastExpr::path_const_iterator> path);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
339339
}
340340

341341
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
342-
cir::ConstComplexAttr, cir::PoisonAttr>(attrType))
342+
cir::ConstComplexAttr, cir::GlobalViewAttr, cir::PoisonAttr>(
343+
attrType))
343344
return success();
344345

345346
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");

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

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ class CIRAttrToValue {
202202
return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr)
203203
.Case<cir::IntAttr, cir::FPAttr, cir::ConstComplexAttr,
204204
cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr,
205-
cir::ZeroAttr>([&](auto attrT) { return visitCirAttr(attrT); })
205+
cir::GlobalViewAttr, cir::ZeroAttr>(
206+
[&](auto attrT) { return visitCirAttr(attrT); })
206207
.Default([&](auto attrT) { return mlir::Value(); });
207208
}
208209

@@ -212,6 +213,7 @@ class CIRAttrToValue {
212213
mlir::Value visitCirAttr(cir::ConstPtrAttr ptrAttr);
213214
mlir::Value visitCirAttr(cir::ConstArrayAttr attr);
214215
mlir::Value visitCirAttr(cir::ConstVectorAttr attr);
216+
mlir::Value visitCirAttr(cir::GlobalViewAttr attr);
215217
mlir::Value visitCirAttr(cir::ZeroAttr attr);
216218

217219
private:
@@ -391,6 +393,62 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstVectorAttr attr) {
391393
mlirValues));
392394
}
393395

396+
// GlobalViewAttr visitor.
397+
mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
398+
auto moduleOp = parentOp->getParentOfType<mlir::ModuleOp>();
399+
mlir::DataLayout dataLayout(moduleOp);
400+
mlir::Type sourceType;
401+
assert(!cir::MissingFeatures::addressSpace());
402+
llvm::StringRef symName;
403+
mlir::Operation *sourceSymbol =
404+
mlir::SymbolTable::lookupSymbolIn(moduleOp, globalAttr.getSymbol());
405+
if (auto llvmSymbol = dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
406+
sourceType = llvmSymbol.getType();
407+
symName = llvmSymbol.getSymName();
408+
} else if (auto cirSymbol = dyn_cast<cir::GlobalOp>(sourceSymbol)) {
409+
sourceType =
410+
convertTypeForMemory(*converter, dataLayout, cirSymbol.getSymType());
411+
symName = cirSymbol.getSymName();
412+
} else if (auto llvmFun = dyn_cast<mlir::LLVM::LLVMFuncOp>(sourceSymbol)) {
413+
sourceType = llvmFun.getFunctionType();
414+
symName = llvmFun.getSymName();
415+
} else if (auto fun = dyn_cast<cir::FuncOp>(sourceSymbol)) {
416+
sourceType = converter->convertType(fun.getFunctionType());
417+
symName = fun.getSymName();
418+
} else if (auto alias = dyn_cast<mlir::LLVM::AliasOp>(sourceSymbol)) {
419+
sourceType = alias.getType();
420+
symName = alias.getSymName();
421+
} else {
422+
llvm_unreachable("Unexpected GlobalOp type");
423+
}
424+
425+
mlir::Location loc = parentOp->getLoc();
426+
mlir::Value addrOp = rewriter.create<mlir::LLVM::AddressOfOp>(
427+
loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symName);
428+
429+
assert(!cir::MissingFeatures::globalViewIndices());
430+
431+
// The incubator has handling here for the attribute having integer type, but
432+
// the only test case I could find that reaches it is a direct CIR-to-LLVM IR
433+
// lowering with no clear indication of how the CIR might have been generated.
434+
// We'll hit the unreachable below if this happens.
435+
assert(!cir::MissingFeatures::globalViewIntLowering());
436+
437+
if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(globalAttr.getType())) {
438+
mlir::Type llvmEltTy =
439+
convertTypeForMemory(*converter, dataLayout, ptrTy.getPointee());
440+
441+
if (llvmEltTy == sourceType)
442+
return addrOp;
443+
444+
mlir::Type llvmDstTy = converter->convertType(globalAttr.getType());
445+
return rewriter.create<mlir::LLVM::BitcastOp>(parentOp->getLoc(), llvmDstTy,
446+
addrOp);
447+
}
448+
449+
llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr");
450+
}
451+
394452
/// ZeroAttr visitor.
395453
mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
396454
mlir::Location loc = parentOp->getLoc();
@@ -1124,7 +1182,13 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
11241182
attr = rewriter.getIntegerAttr(typeConverter->convertType(op.getType()),
11251183
value);
11261184
} else if (mlir::isa<cir::IntType>(op.getType())) {
1127-
assert(!cir::MissingFeatures::opGlobalViewAttr());
1185+
// Lower GlobalViewAttr to llvm.mlir.addressof + llvm.mlir.ptrtoint
1186+
if (auto ga = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
1187+
// See the comment in visitCirAttr for why this isn't implemented.
1188+
assert(!cir::MissingFeatures::globalViewIntLowering());
1189+
op.emitError() << "global view with integer type";
1190+
return mlir::failure();
1191+
}
11281192

11291193
attr = rewriter.getIntegerAttr(
11301194
typeConverter->convertType(op.getType()),
@@ -1142,7 +1206,12 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
11421206
return mlir::success();
11431207
}
11441208
}
1145-
assert(!cir::MissingFeatures::opGlobalViewAttr());
1209+
// Lower GlobalViewAttr to llvm.mlir.addressof
1210+
if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
1211+
auto newOp = lowerCirAttrAsValue(op, gv, rewriter, getTypeConverter());
1212+
rewriter.replaceOp(op, newOp);
1213+
return mlir::success();
1214+
}
11461215
attr = op.getValue();
11471216
} else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) {
11481217
const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue());
@@ -1397,8 +1466,9 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
13971466
cir::GlobalOp op, mlir::Attribute init,
13981467
mlir::ConversionPatternRewriter &rewriter) const {
13991468
// TODO: Generalize this handling when more types are needed here.
1400-
assert((isa<cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr,
1401-
cir::ConstComplexAttr, cir::ZeroAttr>(init)));
1469+
assert(
1470+
(isa<cir::ConstArrayAttr, cir::ConstVectorAttr, cir::ConstPtrAttr,
1471+
cir::ConstComplexAttr, cir::GlobalViewAttr, cir::ZeroAttr>(init)));
14021472

14031473
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
14041474
// should be updated. For now, we use a custom op to initialize globals
@@ -1452,7 +1522,7 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
14521522
}
14531523
} else if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
14541524
cir::ConstPtrAttr, cir::ConstComplexAttr,
1455-
cir::ZeroAttr>(init.value())) {
1525+
cir::GlobalViewAttr, cir::ZeroAttr>(init.value())) {
14561526
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
14571527
// should be updated. For now, we use a custom op to initialize globals
14581528
// to the appropriate value.

clang/test/CIR/CodeGen/globals.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
6+
// Should constant initialize global with constant address.
7+
int var = 1;
8+
int *constAddr = &var;
9+
10+
// CIR: cir.global external @constAddr = #cir.global_view<@var> : !cir.ptr<!s32i>
11+
12+
// LLVM: @constAddr = global ptr @var, align 8
13+
14+
// Should constant initialize global with constant address.
15+
int f();
16+
int (*constFnAddr)() = f;
17+
18+
// CIR: cir.global external @constFnAddr = #cir.global_view<@_Z1fv> : !cir.ptr<!cir.func<() -> !s32i>>
19+
20+
// LLVM: @constFnAddr = global ptr @_Z1fv, align 8

0 commit comments

Comments
 (0)