Skip to content

Commit de13703

Browse files
authored
[CIR][CIRGen][TBAA] Add CIR_TBAAStructAttr (#1365)
This patch introduces `CIR_TBAAStructAttr`, which encodes the type and offset of each field in a struct, although it may lead to some duplication in `CIR`. If we manage `cir::TBAAStructAttr` by adding a new method to `ASTRecordDeclInterface`, it will also introduce duplication between `CIRGen` and LLVM lowering.
1 parent e6165c8 commit de13703

File tree

5 files changed

+436
-4
lines changed

5 files changed

+436
-4
lines changed

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

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,82 @@ def CIR_TBAATagAttr : CIR_Attr<"TBAATag", "tbaa_tag", [], "TBAAAttr"> {
5050
let assemblyFormat = "`<` struct(params) `>`";
5151
}
5252

53+
def CIR_TBAAMemberAttr : CIR_Attr<"TBAAMember", "tbaa_member", []> {
54+
let summary = "Attribute representing a member of a TBAA structured type.";
55+
let parameters = (ins "TBAAAttr":$type_desc,
56+
"int64_t":$offset);
57+
let description = [{
58+
Define a TBAA struct attribute.
59+
60+
Example:
61+
```mlir
62+
!ty_StructS = !cir.struct<struct "StructS" {!u16i, !u32i} #cir.record.decl.ast>
63+
#tbaa_scalar = #cir.tbaa_scalar<id = "int", type = !s32i>
64+
#tbaa_scalar1 = #cir.tbaa_scalar<id = "short", type = !s16i>
65+
#tbaa_struct = #cir.tbaa_struct<id = "_ZTS7StructS", members = {<#tbaa_scalar1, 0>, <#tbaa_scalar, 4>}>
66+
```
67+
68+
See the following link for more details:
69+
https://llvm.org/docs/LangRef.html#tbaa-metadata
70+
}];
71+
72+
let assemblyFormat = "`<` params `>`";
73+
}
74+
75+
def CIR_TBAAMemberAttrArray : ArrayRefParameter<"TBAAMemberAttr"> {
76+
let summary = "Array of TBAAMemberAttr attributes.";
77+
let printer = [{
78+
$_printer << '{';
79+
llvm::interleaveComma($_self, $_printer, [&](TBAAMemberAttr attr) {
80+
$_printer.printStrippedAttrOrType(attr);
81+
});
82+
$_printer << '}';
83+
}];
84+
let parser = [{
85+
[&]() -> llvm::FailureOr<llvm::SmallVector<TBAAMemberAttr>> {
86+
using Result = llvm::SmallVector<TBAAMemberAttr>;
87+
if ($_parser.parseLBrace())
88+
return mlir::failure();
89+
llvm::FailureOr<Result> result = mlir::FieldParser<Result>::parse($_parser);
90+
if (failed(result))
91+
return mlir::failure();
92+
if ($_parser.parseRBrace())
93+
return mlir::failure();
94+
return result;
95+
}()
96+
}];
97+
}
98+
99+
def CIR_TBAAStructAttr : CIR_Attr<"TBAAStruct",
100+
"tbaa_struct", [], "TBAAAttr"> {
101+
let summary = "Describes a struct type in TBAA";
102+
103+
let parameters = (ins StringRefParameter<> : $id,
104+
CIR_TBAAMemberAttrArray:$members);
105+
106+
let description = [{
107+
Define a TBAA struct attribute.
108+
109+
Example:
110+
```mlir
111+
!ty_StructS = !cir.struct<struct "StructS" {!u16i, !u32i} #cir.record.decl.ast>
112+
#tbaa_scalar = #cir.tbaa_scalar<id = "int", type = !s32i>
113+
#tbaa_scalar1 = #cir.tbaa_scalar<id = "short", type = !s16i>
114+
// CIR_TBAAStructAttr
115+
#tbaa_struct = #cir.tbaa_struct<id = "_ZTS7StructS", members = {<#tbaa_scalar1, 0>, <#tbaa_scalar, 4>}>
116+
```
117+
118+
See the following link for more details:
119+
https://llvm.org/docs/LangRef.html#tbaa-metadata
120+
}];
121+
122+
let assemblyFormat = "`<` struct(params) `>`";
123+
}
124+
53125
def CIR_AnyTBAAAttr : AnyAttrOf<[
54126
CIR_TBAAAttr,
55127
CIR_TBAAOmnipotentChar,
56-
CIR_TBAAScalarAttr,
128+
CIR_TBAAScalarAttr,
129+
CIR_TBAAStructAttr,
57130
CIR_TBAATagAttr
58131
]>;

clang/lib/CIR/CodeGen/CIRGenTBAA.cpp

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "CIRGenTBAA.h"
2+
#include "CIRGenCXXABI.h"
23
#include "CIRGenTypes.h"
34
#include "mlir/IR/BuiltinAttributes.h"
45
#include "mlir/IR/MLIRContext.h"
@@ -204,7 +205,7 @@ cir::TBAAAttr CIRGenTBAA::getTypeInfo(clang::QualType qty) {
204205
// function.
205206
if (isValidBaseType(qty)) {
206207
assert(!cir::MissingFeatures::tbaaTagForStruct());
207-
return tbaa_NYI(mlirContext);
208+
return getValidBaseTypeInfo(qty);
208209
}
209210

210211
const clang::Type *ty = astContext.getCanonicalType(qty).getTypePtr();
@@ -248,9 +249,101 @@ mlir::ArrayAttr CIRGenTBAA::getTBAAStructInfo(clang::QualType qty) {
248249
}
249250

250251
cir::TBAAAttr CIRGenTBAA::getBaseTypeInfo(clang::QualType qty) {
251-
return tbaa_NYI(mlirContext);
252+
return isValidBaseType(qty) ? getValidBaseTypeInfo(qty) : nullptr;
253+
}
254+
255+
cir::TBAAAttr CIRGenTBAA::getValidBaseTypeInfo(clang::QualType qty) {
256+
assert(isValidBaseType(qty) && "Must be a valid base type");
257+
258+
const clang::Type *ty = astContext.getCanonicalType(qty).getTypePtr();
259+
260+
// nullptr is a valid value in the cache, so use find rather than []
261+
auto iter = baseTypeMetadataCache.find(ty);
262+
if (iter != baseTypeMetadataCache.end())
263+
return iter->second;
264+
265+
// First calculate the metadata, before recomputinyg the insertion point, as
266+
// the helper can recursively call us.
267+
auto typeNode = getBaseTypeInfoHelper(ty);
268+
LLVM_ATTRIBUTE_UNUSED auto inserted =
269+
baseTypeMetadataCache.insert({ty, typeNode});
270+
assert(inserted.second && "BaseType metadata was already inserted");
271+
272+
return typeNode;
252273
}
274+
cir::TBAAAttr CIRGenTBAA::getBaseTypeInfoHelper(const clang::Type *ty) {
275+
using namespace clang;
276+
if (auto *tty = mlir::dyn_cast<clang::RecordType>(ty)) {
277+
const clang::RecordDecl *rd = tty->getDecl()->getDefinition();
278+
const ASTRecordLayout &layout = astContext.getASTRecordLayout(rd);
279+
SmallVector<cir::TBAAMemberAttr, 4> fields;
280+
if (const CXXRecordDecl *cxxrd = dyn_cast<CXXRecordDecl>(rd)) {
281+
// Handle C++ base classes. Non-virtual bases can treated a kind of
282+
// field. Virtual bases are more complex and omitted, but avoid an
283+
// incomplete view for NewStructPathTBAA.
284+
if (codeGenOpts.NewStructPathTBAA && cxxrd->getNumVBases() != 0)
285+
return nullptr;
286+
for (const CXXBaseSpecifier &cxxBaseSpecifier : cxxrd->bases()) {
287+
if (cxxBaseSpecifier.isVirtual())
288+
continue;
289+
QualType baseQTy = cxxBaseSpecifier.getType();
290+
const CXXRecordDecl *baseRD = baseQTy->getAsCXXRecordDecl();
291+
if (baseRD->isEmpty())
292+
continue;
293+
auto typeNode = isValidBaseType(baseQTy) ? getValidBaseTypeInfo(baseQTy)
294+
: getTypeInfo(baseQTy);
295+
if (!typeNode)
296+
return nullptr;
297+
uint64_t offset = layout.getBaseClassOffset(baseRD).getQuantity();
298+
[[maybe_unused]] uint64_t size =
299+
astContext.getASTRecordLayout(baseRD).getDataSize().getQuantity();
300+
fields.push_back(
301+
cir::TBAAMemberAttr::get(mlirContext, typeNode, offset));
302+
}
303+
// The order in which base class subobjects are allocated is
304+
// unspecified, so may differ from declaration order. In particular,
305+
// Itanium ABI will allocate a primary base first. Since we exclude
306+
// empty subobjects, the objects are not overlapping and their offsets
307+
// are unique.
308+
llvm::sort(fields, [](const cir::TBAAMemberAttr &lhs,
309+
const cir::TBAAMemberAttr &rhs) {
310+
return lhs.getOffset() < rhs.getOffset();
311+
});
312+
}
313+
for (FieldDecl *field : rd->fields()) {
314+
if (field->isZeroSize(astContext) || field->isUnnamedBitField())
315+
continue;
316+
QualType fieldQTy = field->getType();
317+
auto typeNode = isValidBaseType(fieldQTy) ? getValidBaseTypeInfo(fieldQTy)
318+
: getTypeInfo(fieldQTy);
319+
if (!typeNode)
320+
return nullptr;
321+
322+
uint64_t bitOffset = layout.getFieldOffset(field->getFieldIndex());
323+
uint64_t offset = astContext.toCharUnitsFromBits(bitOffset).getQuantity();
324+
[[maybe_unused]] uint64_t size =
325+
astContext.getTypeSizeInChars(fieldQTy).getQuantity();
326+
fields.push_back(cir::TBAAMemberAttr::get(mlirContext, typeNode, offset));
327+
}
328+
329+
SmallString<256> outName;
330+
if (features.CPlusPlus) {
331+
// Don't use the mangler for C code.
332+
llvm::raw_svector_ostream out(outName);
333+
types.getCXXABI().getMangleContext().mangleCanonicalTypeName(
334+
QualType(ty, 0), out);
335+
} else {
336+
outName = rd->getName();
337+
}
253338

339+
if (codeGenOpts.NewStructPathTBAA) {
340+
assert(!cir::MissingFeatures::tbaaNewStructPath());
341+
return nullptr;
342+
}
343+
return cir::TBAAStructAttr::get(mlirContext, outName, fields);
344+
}
345+
return nullptr;
346+
}
254347
cir::TBAAAttr CIRGenTBAA::getAccessTagInfo(TBAAAccessInfo tbaaInfo) {
255348
assert(!tbaaInfo.isIncomplete() &&
256349
"Access to an object of an incomplete type!");

clang/lib/CIR/CodeGen/CIRGenTBAA.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class CIRGenTBAA {
105105
[[maybe_unused]] const clang::LangOptions &features;
106106

107107
llvm::DenseMap<const Type *, cir::TBAAAttr> metadataCache;
108+
llvm::DenseMap<const Type *, cir::TBAAAttr> baseTypeMetadataCache;
108109

109110
cir::TBAAAttr getChar();
110111

@@ -113,6 +114,9 @@ class CIRGenTBAA {
113114
cir::TBAAAttr getTypeInfoHelper(clang::QualType qty);
114115
cir::TBAAAttr getScalarTypeInfo(clang::QualType qty);
115116

117+
cir::TBAAAttr getValidBaseTypeInfo(clang::QualType qty);
118+
cir::TBAAAttr getBaseTypeInfoHelper(const clang::Type *ty);
119+
116120
public:
117121
CIRGenTBAA(mlir::MLIRContext *mlirContext, clang::ASTContext &astContext,
118122
CIRGenTypes &types, mlir::ModuleOp moduleOp,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
109109
}
110110
return TypeSwitch<Attribute, AliasResult>(attr)
111111
.Case<cir::TBAAAttr, cir::TBAAOmnipotentCharAttr, cir::TBAAScalarAttr,
112-
cir::TBAATagAttr>([&](auto attr) {
112+
cir::TBAAStructAttr, cir::TBAATagAttr>([&](auto attr) {
113113
os << decltype(attr)::getMnemonic();
114114
return AliasResult::OverridableAlias;
115115
})

0 commit comments

Comments
 (0)