Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR][CIRGen][Lowering] Add support for attribute annotate #804

Merged
merged 15 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,77 @@ def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
];
}

//===----------------------------------------------------------------------===//
// AnnotationAttr
//===----------------------------------------------------------------------===//
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved

def AnnotationAttr : CIR_Attr<"Annotation", "annotation"> {
let summary = "Annotation attribute for global variables and functions";
let description = [{
Represent C/C++ attribute of annotate in CIR.
Example C code:
```
int *a __attribute__((annotate("testptr", "21", 12 )));
```
In this example code, the `AnnotationAttr` has annotation name "testptr",
and arguments "21" and 12 constitutes an `ArrayAttr` type parameter `args`
for global variable `a`.
In CIR, the attribute for above annotation looks like:
```
[#cir.annotation<name = "withargs", args = ["21", 12 : i32]>]
```
}];

// The parameter args is empty when there is no arg.
let parameters = (ins "StringAttr":$name,
"ArrayAttr":$args);

let assemblyFormat = "`<` struct($name, $args) `>`";

let extraClassDeclaration = [{
bool isNoArgs() const { return getArgs().empty(); };
}];
}

//===----------------------------------------------------------------------===//
// GlobalAnnotationValuesAttr
//===----------------------------------------------------------------------===//
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved

def GlobalAnnotationValuesAttr : CIR_Attr<"GlobalAnnotationValues",
"global_annotations"> {
let summary = "Array of annotations, each element consists of name of"
"a global var or func and one of its annotations";
let description = [{
This is annotation value array, which holds the annotation
values for all global variables and functions in a module.
This array is used to create the initial value of a global annotation
metadata variable in LLVM IR.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an example in CIR of how this looks like.

Example C code:
```
double *a __attribute__((annotate("withargs", "21", 12 )));
int *b __attribute__((annotate("withargs", "21", 12 )));
void *c __attribute__((annotate("noargvar")));
void foo(int i) __attribute__((annotate("noargfunc"))) {}
```
After CIR lowering prepare pass, compiler generates a
`GlobalAnnotationValuesAttr` like the following:
```
#cir<global_annotations [
["a", #cir.annotation<name = "withargs", args = ["21", 12 : i32]>],
["b", #cir.annotation<name = "withargs", args = ["21", 12 : i32]>],
["c", #cir.annotation<name = "noargvar", args = []>],
["foo", #cir.annotation<name = "noargfunc", args = []>]]>
```
}];

let parameters = (ins "ArrayAttr":$annotations);

let assemblyFormat = [{ $annotations }];

bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
// Enable verifier.
let genVerifyDecl = 1;
}

include "clang/CIR/Dialect/IR/CIROpenCLAttrs.td"

#endif // MLIR_CIR_DIALECT_CIR_ATTRS
7 changes: 5 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2205,7 +2205,7 @@ def TLSModel : I32EnumAttr<
[TLS_GeneralDynamic, TLS_LocalDynamic, TLS_InitialExec, TLS_LocalExec]> {
let cppNamespace = "::mlir::cir";
}

def GlobalOp : CIR_Op<"global",
[DeclareOpInterfaceMethods<RegionBranchOpInterface>,
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
Expand Down Expand Up @@ -2255,7 +2255,8 @@ def GlobalOp : CIR_Op<"global",
UnitAttr:$dsolocal,
OptionalAttr<I64Attr>:$alignment,
OptionalAttr<ASTVarDeclInterface>:$ast,
OptionalAttr<StrAttr>:$section);
OptionalAttr<StrAttr>:$section,
OptionalAttr<ArrayAttr>:$annotations);
let regions = (region AnyRegion:$ctorRegion, AnyRegion:$dtorRegion);
let assemblyFormat = [{
($sym_visibility^)?
Expand All @@ -2268,6 +2269,7 @@ def GlobalOp : CIR_Op<"global",
(`addrspace` `(` custom<GlobalOpAddrSpace>($addr_space)^ `)`)?
$sym_name
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, $ctorRegion, $dtorRegion)
($annotations^)?
attr-dict
}];

Expand Down Expand Up @@ -3100,6 +3102,7 @@ def FuncOp : CIR_Op<"func", [
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
OptionalAttr<GlobalCtorAttr>:$global_ctor,
OptionalAttr<GlobalDtorAttr>:$global_dtor,
OptionalAttr<ArrayAttr>:$annotations,
OptionalAttr<AnyASTFunctionDeclAttr>:$ast);
let regions = (region AnyRegion:$body);
let skipDefaultBuilders = 1;
Expand Down
79 changes: 76 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,13 @@ void CIRGenModule::buildGlobal(GlobalDecl GD) {

// Ignore declarations, they will be emitted on their first use.
if (const auto *FD = dyn_cast<FunctionDecl>(Global)) {
// Update deferred annotations with the latest declaration if the function
// was already used or defined.
if (FD->hasAttr<AnnotateAttr>()) {
StringRef MangledName = getMangledName(GD);
if (getGlobalValue(MangledName))
deferredAnnotations[MangledName] = FD;
}
// Forward declarations are emitted lazily on first use.
if (!FD->doesThisDeclarationHaveABody()) {
if (!FD->doesDeclarationForceExternallyVisibleDefinition())
Expand Down Expand Up @@ -594,7 +601,8 @@ void CIRGenModule::buildGlobalFunctionDefinition(GlobalDecl GD,
if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
AddGlobalDtor(Fn, DA->getPriority(), true);

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

/// Track functions to be called before main() runs.
Expand Down Expand Up @@ -1231,7 +1239,7 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D,
maybeHandleStaticInExternC(D, GV);

if (D->hasAttr<AnnotateAttr>())
assert(0 && "not implemented");
addGlobalAnnotations(D, GV);

// Set CIR's linkage type as appropriate.
mlir::cir::GlobalLinkageKind Linkage =
Expand Down Expand Up @@ -2833,7 +2841,7 @@ void CIRGenModule::Release() {
// TODO: PGOReader
// TODO: buildCtorList(GlobalCtors);
// TODO: builtCtorList(GlobalDtors);
// TODO: buildGlobalAnnotations();
buildGlobalAnnotations();
// TODO: buildDeferredUnusedCoverageMappings();
// TODO: CIRGenPGO
// TODO: CoverageMapping
Expand Down Expand Up @@ -3187,3 +3195,68 @@ LangAS CIRGenModule::getGlobalVarAddressSpace(const VarDecl *D) {

return getTargetCIRGenInfo().getGlobalVarAddressSpace(*this, D);
}

mlir::ArrayAttr CIRGenModule::buildAnnotationArgs(AnnotateAttr *attr) {
ArrayRef<Expr *> exprs = {attr->args_begin(), attr->args_size()};
if (exprs.empty()) {
return mlir::ArrayAttr::get(builder.getContext(), {});
}
llvm::FoldingSetNodeID id;
for (Expr *e : exprs) {
id.Add(cast<clang::ConstantExpr>(e)->getAPValueResult());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While debugging some core dump in this function, I wonder what happens here when e is not a clang::ConstantExpr?
@ghehg an idea about the missing comments here? 😄
No need for some IgnoreParenCasts() here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still diving into it. Please ignore my comments since I got the inheritance wrong.

}
mlir::ArrayAttr &lookup = annotationArgs[id.ComputeHash()];
if (lookup)
return lookup;

llvm::SmallVector<mlir::Attribute, 4> args;
args.reserve(exprs.size());
for (Expr *e : exprs) {
if (auto *const strE =
::clang::dyn_cast<clang::StringLiteral>(e->IgnoreParenCasts())) {
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
// Add trailing null character as StringLiteral->getString() does not
args.push_back(builder.getStringAttr(strE->getString()));
} else if (auto *const intE = ::clang::dyn_cast<clang::IntegerLiteral>(
e->IgnoreParenCasts())) {
args.push_back(mlir::IntegerAttr::get(
mlir::IntegerType::get(builder.getContext(),
intE->getValue().getBitWidth()),
intE->getValue()));
} else {
llvm_unreachable("NYI");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It crashes on my use-case when e is a ConstantExpr which is not handled. I can handle it with a PR but I need to understand the neighborhood first.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Actually everything is a ConstantExpr here. Just that I have an enum which is not lowered to its underlying type.
Thinking about it...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for looking into this! As you might notice that the logic is actually from clang codegen.

llvm::Constant *CodeGenModule::EmitAnnotationArgs(const AnnotateAttr *Attr) {

I think the assumption is that as argument to annotation, everything should be ConstantExpr, so I guess we just trust clang front end wouldn't shoot its own foot by generating other types of AST expr for annotation arg values.

But you could find an example that breaks the assumption, what I'd do is to use clang code gen (removing -fclangir from opt list), and if you can reproduce it. That's an issue we should open at upstream, and of course, we could fix it there to be ahead of upstream :-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "normal" clang is able to evaluate the ConstantExp beyond just literals.
I have made #874 for integers.

}
}

lookup = builder.getArrayAttr(args);
return lookup;
}

mlir::cir::AnnotationAttr
CIRGenModule::buildAnnotateAttr(clang::AnnotateAttr *aa) {
mlir::StringAttr annoGV = builder.getStringAttr(aa->getAnnotation());
mlir::ArrayAttr args = buildAnnotationArgs(aa);
return mlir::cir::AnnotationAttr::get(builder.getContext(), annoGV, args);
}

void CIRGenModule::addGlobalAnnotations(const ValueDecl *d,
mlir::Operation *gv) {
assert(d->hasAttr<AnnotateAttr>() && "no annotate attribute");
assert((isa<GlobalOp>(gv) || isa<FuncOp>(gv)) &&
"annotation only on globals");
llvm::SmallVector<mlir::Attribute, 4> annotations;
for (auto *i : d->specific_attrs<AnnotateAttr>())
annotations.push_back(buildAnnotateAttr(i));
if (auto global = dyn_cast<mlir::cir::GlobalOp>(gv))
global.setAnnotationsAttr(builder.getArrayAttr(annotations));
else if (auto func = dyn_cast<mlir::cir::FuncOp>(gv))
func.setAnnotationsAttr(builder.getArrayAttr(annotations));
}

void CIRGenModule::buildGlobalAnnotations() {
for (const auto &[mangledName, vd] : deferredAnnotations) {
mlir::Operation *gv = getGlobalValue(mangledName);
if (gv)
addGlobalAnnotations(vd, gv);
}
deferredAnnotations.clear();
}
34 changes: 34 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ class CIRGenModule : public CIRGenTypeCache {
/// for the same decl.
llvm::DenseSet<clang::GlobalDecl> DiagnosedConflictingDefinitions;

/// -------
/// Annotations
/// -------

/// We do not store global annotations in the module here, instead, we store
/// each annotation as attribute of GlobalOp and FuncOp.
/// We defer creation of global annotation variable to LoweringPrepare
/// as CIR passes do not need to have a global view of all annotations.

/// Used for uniquing of annotation arguments.
llvm::DenseMap<unsigned, mlir::ArrayAttr> annotationArgs;

/// Store deferred function annotations so they can be emitted at the end with
/// most up to date ValueDecl that will have all the inherited annotations.
llvm::DenseMap<StringRef, const ValueDecl *> deferredAnnotations;

public:
mlir::ModuleOp getModule() const { return theModule; }
CIRGenBuilderTy &getBuilder() { return builder; }
Expand Down Expand Up @@ -761,6 +777,24 @@ class CIRGenModule : public CIRGenTypeCache {
void setNonAliasAttributes(GlobalDecl GD, mlir::Operation *GV);
/// Map source language used to a CIR attribute.
mlir::cir::SourceLanguage getCIRSourceLanguage();

/// Emit all the global annotations.
/// This actually only emits annotations for deffered declarations of
/// functions, because global variables need no deffred emission.
void buildGlobalAnnotations();

/// Emit additional args of the annotation.
mlir::ArrayAttr buildAnnotationArgs(clang::AnnotateAttr *attr);

/// Create cir::AnnotationAttr which contains the annotation
/// information for a given GlobalValue. Notice that a GlobalValue could
/// have multiple annotations, and this function creates attribute for
/// one of them.
mlir::cir::AnnotationAttr buildAnnotateAttr(clang::AnnotateAttr *aa);

/// Add global annotations for a global value.
/// Those annotations are emitted during lowering to the LLVM code.
void addGlobalAnnotations(const ValueDecl *d, mlir::Operation *gv);
};
} // namespace cir

Expand Down
40 changes: 40 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,46 @@ void MethodAttr::print(AsmPrinter &printer) const {
printer << '>';
}

//===----------------------------------------------------------------------===//
// GlobalAnnotationValuesAttr definitions
//===----------------------------------------------------------------------===//

LogicalResult GlobalAnnotationValuesAttr::verify(
function_ref<::mlir::InFlightDiagnostic()> emitError,
mlir::ArrayAttr annotations) {
if (annotations.empty()) {
emitError()
<< "GlobalAnnotationValuesAttr should at least have one annotation";
return failure();
}
for (auto &entry : annotations) {
auto annoEntry = ::mlir::dyn_cast<mlir::ArrayAttr>(entry);
if (!annoEntry) {
emitError() << "Element of GlobalAnnotationValuesAttr annotations array"
" must be an array";
return failure();
} else if (annoEntry.size() != 2) {
emitError() << "Element of GlobalAnnotationValuesAttr annotations array"
<< " must be a 2-element array and you have "
<< annoEntry.size();
return failure();
} else if (!::mlir::isa<mlir::StringAttr>(annoEntry[0])) {
emitError() << "Element of GlobalAnnotationValuesAttr annotations"
"array must start with a string, which is the name of "
"global op or func it annotates";
return failure();
}
auto annoPart = ::mlir::dyn_cast<mlir::cir::AnnotationAttr>(annoEntry[1]);
if (!annoPart) {
emitError() << "The second element of GlobalAnnotationValuesAttr"
"annotations array element must be of "
"type AnnotationValueAttr";
return failure();
}
}
return success();
}

//===----------------------------------------------------------------------===//
// DynamicCastInfoAtttr definitions
//===----------------------------------------------------------------------===//
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ bool isIntOrBoolCast(mlir::cir::CastOp op) {
Value tryFoldCastChain(CastOp op) {
CastOp head = op, tail = op;

while(op) {
while (op) {
if (!isIntOrBoolCast(op))
break;
head = op;
Expand Down Expand Up @@ -2259,6 +2259,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
auto noProtoNameAttr = getNoProtoAttrName(state.name);
auto visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
auto dsolocalNameAttr = getDsolocalAttrName(state.name);
auto annotationsNameAttr = getAnnotationsAttrName(state.name);
if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
if (::mlir::succeeded(
Expand Down Expand Up @@ -2290,6 +2291,9 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
if (parser.parseOptionalKeyword(dsolocalNameAttr).succeeded())
state.addAttribute(dsolocalNameAttr, parser.getBuilder().getUnitAttr());

if (parser.parseOptionalKeyword(annotationsNameAttr).succeeded())
state.addAttribute(annotationsNameAttr, parser.getBuilder().getUnitAttr());

StringAttr nameAttr;
SmallVector<OpAsmParser::Argument, 8> arguments;
SmallVector<DictionaryAttr, 1> resultAttrs;
Expand Down Expand Up @@ -2508,6 +2512,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
else
function_interface_impl::printFunctionSignature(
p, *this, fnType.getInputs(), fnType.isVarArg(), {});

if (mlir::ArrayAttr annotations = getAnnotationsAttr()) {
p << " ";
p.printAttribute(annotations);
}

function_interface_impl::printFunctionAttributes(
p, *this,
// These are all omitted since they are custom printed already.
Expand All @@ -2517,7 +2527,8 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
getGlobalDtorAttrName(), getLambdaAttrName(), getLinkageAttrName(),
getCallingConvAttrName(), getNoProtoAttrName(),
getSymVisibilityAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
getComdatAttrName(), getGlobalVisibilityAttrName()});
getComdatAttrName(), getGlobalVisibilityAttrName(),
getAnnotationsAttrName()});

if (auto aliaseeName = getAliasee()) {
p << " alias(";
Expand Down
Loading