Skip to content

Commit 3f46baa

Browse files
ghehglanza
authored andcommitted
[CIR][CIRGen][Lowering] Add support for attribute annotate (#804)
The main purpose of this PR is to add support for C/C++ attribute annotate. The PR involves both CIR generation and Lowering Prepare. In the rest of this description, we first introduce the concept of attribute annotate, then talk about expectations of LLVM regarding annotation, after it, we describe how ClangIR handles it in this PR. Finally, we list trivial differences between LLVM code generated by clang codegen and ClangIR codegen. **The concept of attribute annotate. and expected LLVM IR** the following is C code example of annotation. say in example.c `int *b __attribute__((annotate("withargs", "21", 12 ))); int *a __attribute__((annotate("oneargs", "21", ))); int *c __attribute__((annotate("noargs"))); ` here "withargs" is the annotation string, "21" and 12 are arguments for this annotation named "withargs". LLVM-based compiler is expected keep these information and build a global variable capturing all annotations used in the translation unit when emitting into LLVM IR. This global variable itself is **not** constant, but will be initialized with constants that are related to annotation representation, e.g. "withargs" should be literal string variable in IR. This global variable has a fixed name "llvm.global.annotations", and its of array of struct type, and should be initialized with a const array of const structs, each const struct is a representation of an annotation site, which has 5-field. [ptr to global var/func annotated, ptr to translation unit string const, line_no, annotation_name, ptr to arguments const] annotation name string and args constants, as well as this global var should be in section "llvm.metadata". e.g. In the above example, We shall have following in the generated LLVM IR like the following ``` @b = global ptr null, align 8 @.str = private unnamed_addr constant [9 x i8] c"withargs\00", section "llvm.metadata" @.str.1 = private unnamed_addr constant [10 x i8] c"example.c\00", section "llvm.metadata" @.str.2 = private unnamed_addr constant [3 x i8] c"21\00", align 1 @.args = private unnamed_addr constant { ptr, i32 } { ptr @.str.2, i32 12 }, section "llvm.metadata" @A = global ptr null, align 8 @.str.3 = private unnamed_addr constant [8 x i8] c"oneargs\00", section "llvm.metadata" @.args.4 = private unnamed_addr constant { ptr } { ptr @.str.2 }, section "llvm.metadata" @c = global ptr null, align 8 @.str.5 = private unnamed_addr constant [7 x i8] c"noargs\00", section "llvm.metadata" @llvm.global.annotations = appending global [3 x { ptr, ptr, ptr, i32, ptr }] [{ ptr, ptr, ptr, i32, ptr } { ptr @b, ptr @.str, ptr @.str.1, i32 1, ptr @.args }, { ptr, ptr, ptr, i32, ptr } { ptr @A, ptr @.str.3, ptr @.str.1, i32 2, ptr @.args.4 }, { ptr, ptr, ptr, i32, ptr } { ptr @c, ptr @.str.5, ptr @.str.1, i32 3, ptr null }], section "llvm.metadata" ``` notice that since variable c's annotation has no arg, the last field of its corresponding annotation entry is a nullptr. **ClangIR's handling of annotations** In CIR, we introduce AnnotationAttr to GlobalOp and FuncOp to record its annotations. That way, we are able to make fast query about annotation if in future a CIR pass is interested in them. We leave the work of generating const variables as well as global annotations' var to LLVM lowering. But at LoweringPrepare we collect all annotations and create a module attribute "cir.global_annotations" so to facilitate LLVM lowering. **Some implementation details and trivial differences between clangir generated LLVM code and vanilla LLVM code** 1. I suffix names of constants generated for annotation purpose with ".annotation" to avoid redefinition, but clang codegen doesn't do it. 3. clang codegen seems to visit FuncDecls in slightly different orders than CIR, thus, sometimes the order of elements of the initial value const array for llvm.global.annotations var is different from clang generated LLVMIR, it should be trivial, as I don't expect consumer of this var is assuming a fixed order of collecting annotations. Otherwise, clang codegen and clangir pretty much generate same LLVM IR for annotations!
1 parent fe5b59f commit 3f46baa

File tree

11 files changed

+654
-8
lines changed

11 files changed

+654
-8
lines changed

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,77 @@ def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
11011101
];
11021102
}
11031103

1104+
//===----------------------------------------------------------------------===//
1105+
// AnnotationAttr
1106+
//===----------------------------------------------------------------------===//
1107+
1108+
def AnnotationAttr : CIR_Attr<"Annotation", "annotation"> {
1109+
let summary = "Annotation attribute for global variables and functions";
1110+
let description = [{
1111+
Represent C/C++ attribute of annotate in CIR.
1112+
Example C code:
1113+
```
1114+
int *a __attribute__((annotate("testptr", "21", 12 )));
1115+
```
1116+
In this example code, the `AnnotationAttr` has annotation name "testptr",
1117+
and arguments "21" and 12 constitutes an `ArrayAttr` type parameter `args`
1118+
for global variable `a`.
1119+
In CIR, the attribute for above annotation looks like:
1120+
```
1121+
[#cir.annotation<name = "withargs", args = ["21", 12 : i32]>]
1122+
```
1123+
}];
1124+
1125+
// The parameter args is empty when there is no arg.
1126+
let parameters = (ins "StringAttr":$name,
1127+
"ArrayAttr":$args);
1128+
1129+
let assemblyFormat = "`<` struct($name, $args) `>`";
1130+
1131+
let extraClassDeclaration = [{
1132+
bool isNoArgs() const { return getArgs().empty(); };
1133+
}];
1134+
}
1135+
1136+
//===----------------------------------------------------------------------===//
1137+
// GlobalAnnotationValuesAttr
1138+
//===----------------------------------------------------------------------===//
1139+
1140+
def GlobalAnnotationValuesAttr : CIR_Attr<"GlobalAnnotationValues",
1141+
"global_annotations"> {
1142+
let summary = "Array of annotations, each element consists of name of"
1143+
"a global var or func and one of its annotations";
1144+
let description = [{
1145+
This is annotation value array, which holds the annotation
1146+
values for all global variables and functions in a module.
1147+
This array is used to create the initial value of a global annotation
1148+
metadata variable in LLVM IR.
1149+
Example C code:
1150+
```
1151+
double *a __attribute__((annotate("withargs", "21", 12 )));
1152+
int *b __attribute__((annotate("withargs", "21", 12 )));
1153+
void *c __attribute__((annotate("noargvar")));
1154+
void foo(int i) __attribute__((annotate("noargfunc"))) {}
1155+
```
1156+
After CIR lowering prepare pass, compiler generates a
1157+
`GlobalAnnotationValuesAttr` like the following:
1158+
```
1159+
#cir<global_annotations [
1160+
["a", #cir.annotation<name = "withargs", args = ["21", 12 : i32]>],
1161+
["b", #cir.annotation<name = "withargs", args = ["21", 12 : i32]>],
1162+
["c", #cir.annotation<name = "noargvar", args = []>],
1163+
["foo", #cir.annotation<name = "noargfunc", args = []>]]>
1164+
```
1165+
}];
1166+
1167+
let parameters = (ins "ArrayAttr":$annotations);
1168+
1169+
let assemblyFormat = [{ $annotations }];
1170+
1171+
// Enable verifier.
1172+
let genVerifyDecl = 1;
1173+
}
1174+
11041175
include "clang/CIR/Dialect/IR/CIROpenCLAttrs.td"
11051176

11061177
#endif // MLIR_CIR_DIALECT_CIR_ATTRS

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,7 +2205,7 @@ def TLSModel : I32EnumAttr<
22052205
[TLS_GeneralDynamic, TLS_LocalDynamic, TLS_InitialExec, TLS_LocalExec]> {
22062206
let cppNamespace = "::mlir::cir";
22072207
}
2208-
2208+
22092209
def GlobalOp : CIR_Op<"global",
22102210
[DeclareOpInterfaceMethods<RegionBranchOpInterface>,
22112211
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
@@ -2255,7 +2255,8 @@ def GlobalOp : CIR_Op<"global",
22552255
UnitAttr:$dsolocal,
22562256
OptionalAttr<I64Attr>:$alignment,
22572257
OptionalAttr<ASTVarDeclInterface>:$ast,
2258-
OptionalAttr<StrAttr>:$section);
2258+
OptionalAttr<StrAttr>:$section,
2259+
OptionalAttr<ArrayAttr>:$annotations);
22592260
let regions = (region AnyRegion:$ctorRegion, AnyRegion:$dtorRegion);
22602261
let assemblyFormat = [{
22612262
($sym_visibility^)?
@@ -2268,6 +2269,7 @@ def GlobalOp : CIR_Op<"global",
22682269
(`addrspace` `(` custom<GlobalOpAddrSpace>($addr_space)^ `)`)?
22692270
$sym_name
22702271
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, $ctorRegion, $dtorRegion)
2272+
($annotations^)?
22712273
attr-dict
22722274
}];
22732275

@@ -3100,6 +3102,7 @@ def FuncOp : CIR_Op<"func", [
31003102
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
31013103
OptionalAttr<GlobalCtorAttr>:$global_ctor,
31023104
OptionalAttr<GlobalDtorAttr>:$global_dtor,
3105+
OptionalAttr<ArrayAttr>:$annotations,
31033106
OptionalAttr<AnyASTFunctionDeclAttr>:$ast);
31043107
let regions = (region AnyRegion:$body);
31053108
let skipDefaultBuilders = 1;

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,13 @@ void CIRGenModule::buildGlobal(GlobalDecl GD) {
484484

485485
// Ignore declarations, they will be emitted on their first use.
486486
if (const auto *FD = dyn_cast<FunctionDecl>(Global)) {
487+
// Update deferred annotations with the latest declaration if the function
488+
// was already used or defined.
489+
if (FD->hasAttr<AnnotateAttr>()) {
490+
StringRef MangledName = getMangledName(GD);
491+
if (getGlobalValue(MangledName))
492+
deferredAnnotations[MangledName] = FD;
493+
}
487494
// Forward declarations are emitted lazily on first use.
488495
if (!FD->doesThisDeclarationHaveABody()) {
489496
if (!FD->doesDeclarationForceExternallyVisibleDefinition())
@@ -595,7 +602,8 @@ void CIRGenModule::buildGlobalFunctionDefinition(GlobalDecl GD,
595602
if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
596603
AddGlobalDtor(Fn, DA->getPriority(), true);
597604

598-
assert(!D->getAttr<AnnotateAttr>() && "NYI");
605+
if (D->getAttr<AnnotateAttr>())
606+
deferredAnnotations[getMangledName(GD)] = cast<ValueDecl>(D);
599607
}
600608

601609
/// Track functions to be called before main() runs.
@@ -1232,7 +1240,7 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D,
12321240
maybeHandleStaticInExternC(D, GV);
12331241

12341242
if (D->hasAttr<AnnotateAttr>())
1235-
assert(0 && "not implemented");
1243+
addGlobalAnnotations(D, GV);
12361244

12371245
// Set CIR's linkage type as appropriate.
12381246
mlir::cir::GlobalLinkageKind Linkage =
@@ -2834,7 +2842,7 @@ void CIRGenModule::Release() {
28342842
// TODO: PGOReader
28352843
// TODO: buildCtorList(GlobalCtors);
28362844
// TODO: builtCtorList(GlobalDtors);
2837-
// TODO: buildGlobalAnnotations();
2845+
buildGlobalAnnotations();
28382846
// TODO: buildDeferredUnusedCoverageMappings();
28392847
// TODO: CIRGenPGO
28402848
// TODO: CoverageMapping
@@ -3188,3 +3196,68 @@ LangAS CIRGenModule::getGlobalVarAddressSpace(const VarDecl *D) {
31883196

31893197
return getTargetCIRGenInfo().getGlobalVarAddressSpace(*this, D);
31903198
}
3199+
3200+
mlir::ArrayAttr CIRGenModule::buildAnnotationArgs(AnnotateAttr *attr) {
3201+
ArrayRef<Expr *> exprs = {attr->args_begin(), attr->args_size()};
3202+
if (exprs.empty()) {
3203+
return mlir::ArrayAttr::get(builder.getContext(), {});
3204+
}
3205+
llvm::FoldingSetNodeID id;
3206+
for (Expr *e : exprs) {
3207+
id.Add(cast<clang::ConstantExpr>(e)->getAPValueResult());
3208+
}
3209+
mlir::ArrayAttr &lookup = annotationArgs[id.ComputeHash()];
3210+
if (lookup)
3211+
return lookup;
3212+
3213+
llvm::SmallVector<mlir::Attribute, 4> args;
3214+
args.reserve(exprs.size());
3215+
for (Expr *e : exprs) {
3216+
if (auto *const strE =
3217+
::clang::dyn_cast<clang::StringLiteral>(e->IgnoreParenCasts())) {
3218+
// Add trailing null character as StringLiteral->getString() does not
3219+
args.push_back(builder.getStringAttr(strE->getString()));
3220+
} else if (auto *const intE = ::clang::dyn_cast<clang::IntegerLiteral>(
3221+
e->IgnoreParenCasts())) {
3222+
args.push_back(mlir::IntegerAttr::get(
3223+
mlir::IntegerType::get(builder.getContext(),
3224+
intE->getValue().getBitWidth()),
3225+
intE->getValue()));
3226+
} else {
3227+
llvm_unreachable("NYI");
3228+
}
3229+
}
3230+
3231+
lookup = builder.getArrayAttr(args);
3232+
return lookup;
3233+
}
3234+
3235+
mlir::cir::AnnotationAttr
3236+
CIRGenModule::buildAnnotateAttr(clang::AnnotateAttr *aa) {
3237+
mlir::StringAttr annoGV = builder.getStringAttr(aa->getAnnotation());
3238+
mlir::ArrayAttr args = buildAnnotationArgs(aa);
3239+
return mlir::cir::AnnotationAttr::get(builder.getContext(), annoGV, args);
3240+
}
3241+
3242+
void CIRGenModule::addGlobalAnnotations(const ValueDecl *d,
3243+
mlir::Operation *gv) {
3244+
assert(d->hasAttr<AnnotateAttr>() && "no annotate attribute");
3245+
assert((isa<GlobalOp>(gv) || isa<FuncOp>(gv)) &&
3246+
"annotation only on globals");
3247+
llvm::SmallVector<mlir::Attribute, 4> annotations;
3248+
for (auto *i : d->specific_attrs<AnnotateAttr>())
3249+
annotations.push_back(buildAnnotateAttr(i));
3250+
if (auto global = dyn_cast<mlir::cir::GlobalOp>(gv))
3251+
global.setAnnotationsAttr(builder.getArrayAttr(annotations));
3252+
else if (auto func = dyn_cast<mlir::cir::FuncOp>(gv))
3253+
func.setAnnotationsAttr(builder.getArrayAttr(annotations));
3254+
}
3255+
3256+
void CIRGenModule::buildGlobalAnnotations() {
3257+
for (const auto &[mangledName, vd] : deferredAnnotations) {
3258+
mlir::Operation *gv = getGlobalValue(mangledName);
3259+
if (gv)
3260+
addGlobalAnnotations(vd, gv);
3261+
}
3262+
deferredAnnotations.clear();
3263+
}

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ class CIRGenModule : public CIRGenTypeCache {
127127
/// for the same decl.
128128
llvm::DenseSet<clang::GlobalDecl> DiagnosedConflictingDefinitions;
129129

130+
/// -------
131+
/// Annotations
132+
/// -------
133+
134+
/// We do not store global annotations in the module here, instead, we store
135+
/// each annotation as attribute of GlobalOp and FuncOp.
136+
/// We defer creation of global annotation variable to LoweringPrepare
137+
/// as CIR passes do not need to have a global view of all annotations.
138+
139+
/// Used for uniquing of annotation arguments.
140+
llvm::DenseMap<unsigned, mlir::ArrayAttr> annotationArgs;
141+
142+
/// Store deferred function annotations so they can be emitted at the end with
143+
/// most up to date ValueDecl that will have all the inherited annotations.
144+
llvm::DenseMap<StringRef, const ValueDecl *> deferredAnnotations;
145+
130146
public:
131147
mlir::ModuleOp getModule() const { return theModule; }
132148
CIRGenBuilderTy &getBuilder() { return builder; }
@@ -761,6 +777,24 @@ class CIRGenModule : public CIRGenTypeCache {
761777
void setNonAliasAttributes(GlobalDecl GD, mlir::Operation *GV);
762778
/// Map source language used to a CIR attribute.
763779
mlir::cir::SourceLanguage getCIRSourceLanguage();
780+
781+
/// Emit all the global annotations.
782+
/// This actually only emits annotations for deffered declarations of
783+
/// functions, because global variables need no deffred emission.
784+
void buildGlobalAnnotations();
785+
786+
/// Emit additional args of the annotation.
787+
mlir::ArrayAttr buildAnnotationArgs(clang::AnnotateAttr *attr);
788+
789+
/// Create cir::AnnotationAttr which contains the annotation
790+
/// information for a given GlobalValue. Notice that a GlobalValue could
791+
/// have multiple annotations, and this function creates attribute for
792+
/// one of them.
793+
mlir::cir::AnnotationAttr buildAnnotateAttr(clang::AnnotateAttr *aa);
794+
795+
/// Add global annotations for a global value.
796+
/// Those annotations are emitted during lowering to the LLVM code.
797+
void addGlobalAnnotations(const ValueDecl *d, mlir::Operation *gv);
764798
};
765799
} // namespace cir
766800

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,46 @@ void MethodAttr::print(AsmPrinter &printer) const {
547547
printer << '>';
548548
}
549549

550+
//===----------------------------------------------------------------------===//
551+
// GlobalAnnotationValuesAttr definitions
552+
//===----------------------------------------------------------------------===//
553+
554+
LogicalResult GlobalAnnotationValuesAttr::verify(
555+
function_ref<::mlir::InFlightDiagnostic()> emitError,
556+
mlir::ArrayAttr annotations) {
557+
if (annotations.empty()) {
558+
emitError()
559+
<< "GlobalAnnotationValuesAttr should at least have one annotation";
560+
return failure();
561+
}
562+
for (auto &entry : annotations) {
563+
auto annoEntry = ::mlir::dyn_cast<mlir::ArrayAttr>(entry);
564+
if (!annoEntry) {
565+
emitError() << "Element of GlobalAnnotationValuesAttr annotations array"
566+
" must be an array";
567+
return failure();
568+
} else if (annoEntry.size() != 2) {
569+
emitError() << "Element of GlobalAnnotationValuesAttr annotations array"
570+
<< " must be a 2-element array and you have "
571+
<< annoEntry.size();
572+
return failure();
573+
} else if (!::mlir::isa<mlir::StringAttr>(annoEntry[0])) {
574+
emitError() << "Element of GlobalAnnotationValuesAttr annotations"
575+
"array must start with a string, which is the name of "
576+
"global op or func it annotates";
577+
return failure();
578+
}
579+
auto annoPart = ::mlir::dyn_cast<mlir::cir::AnnotationAttr>(annoEntry[1]);
580+
if (!annoPart) {
581+
emitError() << "The second element of GlobalAnnotationValuesAttr"
582+
"annotations array element must be of "
583+
"type AnnotationValueAttr";
584+
return failure();
585+
}
586+
}
587+
return success();
588+
}
589+
550590
//===----------------------------------------------------------------------===//
551591
// DynamicCastInfoAtttr definitions
552592
//===----------------------------------------------------------------------===//

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ bool isIntOrBoolCast(mlir::cir::CastOp op) {
717717
Value tryFoldCastChain(CastOp op) {
718718
CastOp head = op, tail = op;
719719

720-
while(op) {
720+
while (op) {
721721
if (!isIntOrBoolCast(op))
722722
break;
723723
head = op;
@@ -2259,6 +2259,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
22592259
auto noProtoNameAttr = getNoProtoAttrName(state.name);
22602260
auto visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
22612261
auto dsolocalNameAttr = getDsolocalAttrName(state.name);
2262+
auto annotationsNameAttr = getAnnotationsAttrName(state.name);
22622263
if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
22632264
state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
22642265
if (::mlir::succeeded(
@@ -2290,6 +2291,9 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
22902291
if (parser.parseOptionalKeyword(dsolocalNameAttr).succeeded())
22912292
state.addAttribute(dsolocalNameAttr, parser.getBuilder().getUnitAttr());
22922293

2294+
if (parser.parseOptionalKeyword(annotationsNameAttr).succeeded())
2295+
state.addAttribute(annotationsNameAttr, parser.getBuilder().getUnitAttr());
2296+
22932297
StringAttr nameAttr;
22942298
SmallVector<OpAsmParser::Argument, 8> arguments;
22952299
SmallVector<DictionaryAttr, 1> resultAttrs;
@@ -2508,6 +2512,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
25082512
else
25092513
function_interface_impl::printFunctionSignature(
25102514
p, *this, fnType.getInputs(), fnType.isVarArg(), {});
2515+
2516+
if (mlir::ArrayAttr annotations = getAnnotationsAttr()) {
2517+
p << " ";
2518+
p.printAttribute(annotations);
2519+
}
2520+
25112521
function_interface_impl::printFunctionAttributes(
25122522
p, *this,
25132523
// These are all omitted since they are custom printed already.
@@ -2517,7 +2527,8 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
25172527
getGlobalDtorAttrName(), getLambdaAttrName(), getLinkageAttrName(),
25182528
getCallingConvAttrName(), getNoProtoAttrName(),
25192529
getSymVisibilityAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
2520-
getComdatAttrName(), getGlobalVisibilityAttrName()});
2530+
getComdatAttrName(), getGlobalVisibilityAttrName(),
2531+
getAnnotationsAttrName()});
25212532

25222533
if (auto aliaseeName = getAliasee()) {
25232534
p << " alias(";

0 commit comments

Comments
 (0)