Skip to content

Commit a1529cd

Browse files
authored
[CIR] Add index support for global_view (#153254)
The #cir.global_view attribute was initially added without support for the optional index list. This change adds index list support. This is used when the address of an array or structure member is used as an initializer. This patch does not include support for taking the address of a structure or class member. That will be added later.
1 parent df15c0d commit a1529cd

File tree

9 files changed

+215
-17
lines changed

9 files changed

+215
-17
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
214214

215215
/// Get constant address of a global variable as an MLIR attribute.
216216
cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type,
217-
cir::GlobalOp globalOp) {
217+
cir::GlobalOp globalOp,
218+
mlir::ArrayAttr indices = {}) {
218219
auto symbol = mlir::FlatSymbolRefAttr::get(globalOp.getSymNameAttr());
219-
return cir::GlobalViewAttr::get(type, symbol);
220+
return cir::GlobalViewAttr::get(type, symbol, indices);
220221
}
221222

222223
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) {

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

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,13 +379,20 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
379379
]> {
380380
let summary = "Provides constant access to a global address";
381381
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.
382+
Get constant address of global `symbol` and optionally apply offsets to
383+
access existing subelements. It provides a way to access globals from other
384+
global and always produces a pointer.
384385

385386
The type of the input symbol can be different from `#cir.global_view`
386387
output type, since a given view of the global might require a static
387388
cast for initializing other globals.
388389

390+
A list of indices can be optionally passed and each element subsequently
391+
indexes underlying types. For `symbol` types like `!cir.array`
392+
and `!cir.record`, it leads to the constant address of sub-elements, while
393+
for `!cir.ptr`, an offset is applied. The first index is relative to the
394+
original symbol type, not the produced one.
395+
389396
The result type of this attribute may be an integer type. In such a case,
390397
the pointer to the referenced global is casted to an integer and this
391398
attribute represents the casted result.
@@ -396,23 +403,57 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
396403
cir.global external @s = @".str2": !cir.ptr<i8>
397404
cir.global external @x = #cir.global_view<@s> : !cir.ptr<i8>
398405
cir.global external @s_addr = #cir.global_view<@s> : !s64i
406+
407+
cir.global external @rgb = #cir.const_array<[0 : i8, -23 : i8, 33 : i8]
408+
: !cir.array<i8 x 3>>
409+
cir.global external @elt_ptr = #cir.global_view<@rgb, [1]> : !cir.ptr<i8>
410+
```
411+
412+
Note, that unlike LLVM IR's gep instruction, CIR doesn't add the leading
413+
zero index when it's known to be constant zero, e.g. for pointers, i.e. we
414+
use indexes exactly to access sub elements or for the offset. The leading
415+
zero index is added later in the lowering.
416+
417+
Example:
418+
```
419+
struct A {
420+
int a;
421+
};
422+
423+
struct B: virtual A {
424+
int b;
425+
};
426+
```
427+
VTT for B in CIR:
428+
```
429+
cir.global linkonce_odr @_ZTT1B = #cir.const_array<[
430+
#cir.global_view<@_ZTV1B, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>]>
431+
: !cir.array<!cir.ptr<!u8i> x 1>
432+
```
433+
VTT for B in LLVM IR:
434+
```
435+
@_ZTT1B = linkonce_odr global [1 x ptr] [ptr getelementptr inbounds
436+
({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3)], align 8
399437
```
400438
}];
401439

402440
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
403-
"mlir::FlatSymbolRefAttr":$symbol);
441+
"mlir::FlatSymbolRefAttr":$symbol,
442+
OptionalParameter<"mlir::ArrayAttr">:$indices);
404443

405444
let builders = [
406445
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
407-
"mlir::FlatSymbolRefAttr":$symbol), [{
408-
return $_get(type.getContext(), type, symbol);
446+
"mlir::FlatSymbolRefAttr":$symbol,
447+
CArg<"mlir::ArrayAttr", "{}">:$indices), [{
448+
return $_get(type.getContext(), type, symbol, indices);
409449
}]>
410450
];
411451

412452
// let genVerifyDecl = 1;
413453
let assemblyFormat = [{
414454
`<`
415455
$symbol
456+
(`,` $indices^)?
416457
`>`
417458
}];
418459
}

clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class CIRDataLayout {
3535

3636
bool isBigEndian() const { return bigEndian; }
3737

38+
/// Internal helper method that returns requested alignment for type.
39+
llvm::Align getAlignment(mlir::Type ty, bool abiOrPref) const;
40+
41+
llvm::Align getABITypeAlign(mlir::Type ty) const {
42+
return getAlignment(ty, true);
43+
}
44+
3845
/// Returns the maximum number of bytes that may be overwritten by
3946
/// storing the specified type.
4047
///
@@ -48,6 +55,19 @@ class CIRDataLayout {
4855
baseSize.isScalable()};
4956
}
5057

58+
/// Returns the offset in bytes between successive objects of the
59+
/// specified type, including alignment padding.
60+
///
61+
/// If Ty is a scalable vector type, the scalable property will be set and
62+
/// the runtime size will be a positive integer multiple of the base size.
63+
///
64+
/// This is the amount that alloca reserves for this type. For example,
65+
/// returns 12 or 16 for x86_fp80, depending on alignment.
66+
llvm::TypeSize getTypeAllocSize(mlir::Type ty) const {
67+
// Round up to the next alignment boundary.
68+
return llvm::alignTo(getTypeStoreSize(ty), getABITypeAlign(ty).value());
69+
}
70+
5171
llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const;
5272
};
5373

clang/lib/CIR/CodeGen/CIRGenBuilder.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "CIRGenBuilder.h"
10+
#include "llvm/ADT/TypeSwitch.h"
1011

1112
using namespace clang::CIRGen;
1213

@@ -66,6 +67,69 @@ clang::CIRGen::CIRGenBuilderTy::getConstFP(mlir::Location loc, mlir::Type t,
6667
return create<cir::ConstantOp>(loc, cir::FPAttr::get(t, fpVal));
6768
}
6869

70+
void CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
71+
int64_t offset, mlir::Type ty, cir::CIRDataLayout layout,
72+
llvm::SmallVectorImpl<int64_t> &indices) {
73+
if (!offset)
74+
return;
75+
76+
auto getIndexAndNewOffset =
77+
[](int64_t offset, int64_t eltSize) -> std::pair<int64_t, int64_t> {
78+
int64_t divRet = offset / eltSize;
79+
if (divRet < 0)
80+
divRet -= 1; // make sure offset is positive
81+
int64_t modRet = offset - (divRet * eltSize);
82+
return {divRet, modRet};
83+
};
84+
85+
mlir::Type subType =
86+
llvm::TypeSwitch<mlir::Type, mlir::Type>(ty)
87+
.Case<cir::ArrayType>([&](auto arrayTy) {
88+
int64_t eltSize = layout.getTypeAllocSize(arrayTy.getElementType());
89+
const auto [index, newOffset] =
90+
getIndexAndNewOffset(offset, eltSize);
91+
indices.push_back(index);
92+
offset = newOffset;
93+
return arrayTy.getElementType();
94+
})
95+
.Case<cir::RecordType>([&](auto recordTy) {
96+
ArrayRef<mlir::Type> elts = recordTy.getMembers();
97+
int64_t pos = 0;
98+
for (size_t i = 0; i < elts.size(); ++i) {
99+
int64_t eltSize =
100+
(int64_t)layout.getTypeAllocSize(elts[i]).getFixedValue();
101+
unsigned alignMask = layout.getABITypeAlign(elts[i]).value() - 1;
102+
if (recordTy.getPacked())
103+
alignMask = 0;
104+
// Union's fields have the same offset, so no need to change pos
105+
// here, we just need to find eltSize that is greater then the
106+
// required offset. The same is true for the similar union type
107+
// check below
108+
if (!recordTy.isUnion())
109+
pos = (pos + alignMask) & ~alignMask;
110+
assert(offset >= 0);
111+
if (offset < pos + eltSize) {
112+
indices.push_back(i);
113+
offset -= pos;
114+
return elts[i];
115+
}
116+
// No need to update pos here, see the comment above.
117+
if (!recordTy.isUnion())
118+
pos += eltSize;
119+
}
120+
llvm_unreachable("offset was not found within the record");
121+
})
122+
.Default([](mlir::Type otherTy) {
123+
llvm_unreachable("unexpected type");
124+
return otherTy; // Even though this is unreachable, we need to
125+
// return a type to satisfy the return type of the
126+
// lambda.
127+
});
128+
129+
assert(subType);
130+
computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices);
131+
}
132+
69133
// This can't be defined in Address.h because that file is included by
70134
// CIRGenBuilder.h
71135
Address Address::withElementType(CIRGenBuilderTy &builder,

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "Address.h"
1313
#include "CIRGenRecordLayout.h"
1414
#include "CIRGenTypeCache.h"
15+
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
1516
#include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
1617
#include "clang/CIR/MissingFeatures.h"
1718

@@ -401,6 +402,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
401402
mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
402403
mlir::Type eltTy);
403404

405+
// Convert byte offset to sequence of high-level indices suitable for
406+
// GlobalViewAttr. Ideally we shouldn't deal with low-level offsets at all
407+
// but currently some parts of Clang AST, which we don't want to touch just
408+
// yet, return them.
409+
void computeGlobalViewIndicesFromFlatOffset(
410+
int64_t offset, mlir::Type ty, cir::CIRDataLayout layout,
411+
llvm::SmallVectorImpl<int64_t> &indices);
412+
404413
/// Creates a versioned global variable. If the symbol is already taken, an ID
405414
/// will be appended to the symbol. The returned global must always be queried
406415
/// for its name so it can be referenced correctly.

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -388,22 +388,32 @@ class ConstantLValueEmitter
388388
/// Return GEP-like value offset
389389
mlir::ArrayAttr getOffset(mlir::Type ty) {
390390
int64_t offset = value.getLValueOffset().getQuantity();
391-
if (offset == 0)
392-
return {};
391+
cir::CIRDataLayout layout(cgm.getModule());
392+
SmallVector<int64_t, 3> idxVec;
393+
cgm.getBuilder().computeGlobalViewIndicesFromFlatOffset(offset, ty, layout,
394+
idxVec);
395+
396+
llvm::SmallVector<mlir::Attribute, 3> indices;
397+
for (int64_t i : idxVec) {
398+
mlir::IntegerAttr intAttr = cgm.getBuilder().getI32IntegerAttr(i);
399+
indices.push_back(intAttr);
400+
}
393401

394-
cgm.errorNYI("ConstantLValueEmitter: global view with offset");
395-
return {};
402+
if (indices.empty())
403+
return {};
404+
return cgm.getBuilder().getArrayAttr(indices);
396405
}
397406

398407
/// Apply the value offset to the given constant.
399408
ConstantLValue applyOffset(ConstantLValue &c) {
400409
// Handle attribute constant LValues.
401410
if (auto attr = mlir::dyn_cast<mlir::Attribute>(c.value)) {
402411
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 {};
412+
auto baseTy = mlir::cast<cir::PointerType>(gv.getType()).getPointee();
413+
mlir::Type destTy = cgm.getTypes().convertTypeForMem(destType);
414+
assert(!gv.getIndices() && "Global view is already indexed");
415+
return cir::GlobalViewAttr::get(destTy, gv.getSymbol(),
416+
getOffset(baseTy));
407417
}
408418
llvm_unreachable("Unsupported attribute type to offset");
409419
}

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,30 @@ void CIRDataLayout::reset(mlir::DataLayoutSpecInterface spec) {
2323
}
2424
}
2525

26+
llvm::Align CIRDataLayout::getAlignment(mlir::Type ty, bool useABIAlign) const {
27+
if (auto recTy = llvm::dyn_cast<cir::RecordType>(ty)) {
28+
// Packed record types always have an ABI alignment of one.
29+
if (recTy && recTy.getPacked() && useABIAlign)
30+
return llvm::Align(1);
31+
32+
// Get the layout annotation... which is lazily created on demand.
33+
llvm_unreachable("getAlignment()) for record type is not implemented");
34+
}
35+
36+
// FIXME(cir): This does not account for differnt address spaces, and relies
37+
// on CIR's data layout to give the proper alignment.
38+
assert(!cir::MissingFeatures::addressSpace());
39+
40+
// Fetch type alignment from MLIR's data layout.
41+
unsigned align = useABIAlign ? layout.getTypeABIAlignment(ty)
42+
: layout.getTypePreferredAlignment(ty);
43+
return llvm::Align(align);
44+
}
45+
2646
// The implementation of this method is provided inline as it is particularly
2747
// well suited to constant folding when called on a specific Type subclass.
2848
llvm::TypeSize CIRDataLayout::getTypeSizeInBits(mlir::Type ty) const {
29-
assert(!cir::MissingFeatures::dataLayoutTypeIsSized());
49+
assert(cir::isSized(ty) && "Cannot getTypeInfo() on a type that is unsized!");
3050

3151
if (auto recordTy = llvm::dyn_cast<cir::RecordType>(ty)) {
3252
// FIXME(cir): CIR record's data layout implementation doesn't do a good job
@@ -38,5 +58,6 @@ llvm::TypeSize CIRDataLayout::getTypeSizeInBits(mlir::Type ty) const {
3858
// on CIR's data layout to give the proper ABI-specific type width.
3959
assert(!cir::MissingFeatures::addressSpace());
4060

61+
// This is calling mlir::DataLayout::getTypeSizeInBits().
4162
return llvm::TypeSize::getFixed(layout.getTypeSizeInBits(ty));
4263
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,22 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
426426
mlir::Value addrOp = rewriter.create<mlir::LLVM::AddressOfOp>(
427427
loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symName);
428428

429-
assert(!cir::MissingFeatures::globalViewIndices());
429+
if (globalAttr.getIndices()) {
430+
llvm::SmallVector<mlir::LLVM::GEPArg> indices;
431+
432+
if (mlir::isa<mlir::LLVM::LLVMArrayType, mlir::LLVM::LLVMStructType>(
433+
sourceType))
434+
indices.push_back(0);
435+
436+
for (mlir::Attribute idx : globalAttr.getIndices()) {
437+
auto intAttr = mlir::cast<mlir::IntegerAttr>(idx);
438+
indices.push_back(intAttr.getValue().getSExtValue());
439+
}
440+
mlir::Type resTy = addrOp.getType();
441+
mlir::Type eltTy = converter->convertType(sourceType);
442+
addrOp = rewriter.create<mlir::LLVM::GEPOp>(
443+
loc, resTy, eltTy, addrOp, indices, mlir::LLVM::GEPNoWrapFlags::none);
444+
}
430445

431446
// The incubator has handling here for the attribute having integer type, but
432447
// the only test case I could find that reaches it is a direct CIR-to-LLVM IR

clang/test/CIR/CodeGen/globals.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
33
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
44
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
57

68
// Should constant initialize global with constant address.
79
int var = 1;
@@ -11,10 +13,25 @@ int *constAddr = &var;
1113

1214
// LLVM: @constAddr = global ptr @var, align 8
1315

16+
// OGCG: @constAddr = global ptr @var, align 8
17+
1418
// Should constant initialize global with constant address.
1519
int f();
1620
int (*constFnAddr)() = f;
1721

1822
// CIR: cir.global external @constFnAddr = #cir.global_view<@_Z1fv> : !cir.ptr<!cir.func<() -> !s32i>>
1923

2024
// LLVM: @constFnAddr = global ptr @_Z1fv, align 8
25+
26+
// OGCG: @constFnAddr = global ptr @_Z1fv, align 8
27+
28+
int arr[4][16];
29+
int *constArrAddr = &arr[2][1];
30+
31+
// CIR: cir.global external @constArrAddr = #cir.global_view<@arr, [2 : i32, 1 : i32]> : !cir.ptr<!s32i>
32+
33+
// The 'inbounds' and 'nuw' flags are inferred by LLVM's constant folder. The
34+
// same flags show up at -O1 in OGCG.
35+
// LLVM: @constArrAddr = global ptr getelementptr inbounds nuw (i8, ptr @arr, i64 132), align 8
36+
37+
// OGCG: @constArrAddr = global ptr getelementptr (i8, ptr @arr, i64 132), align 8

0 commit comments

Comments
 (0)