From 46f4cc803b40d5dfd2d4c271e5a9aab3de3ce875 Mon Sep 17 00:00:00 2001 From: ghehg <166402688+ghehg@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:58:00 -0400 Subject: [PATCH] [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! --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 71 +++++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 7 +- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 79 +++++- clang/lib/CIR/CodeGen/CIRGenModule.h | 34 +++ clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 40 +++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 15 +- .../Dialect/Transforms/LoweringPrepare.cpp | 37 +++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 245 +++++++++++++++++- .../CodeGen/attribute-annotate-multiple.cpp | 71 +++++ clang/test/CIR/IR/annotations.cir | 31 +++ clang/test/CIR/IR/invalid-annotations.cir | 32 +++ 11 files changed, 654 insertions(+), 8 deletions(-) create mode 100644 clang/test/CIR/CodeGen/attribute-annotate-multiple.cpp create mode 100644 clang/test/CIR/IR/annotations.cir create mode 100644 clang/test/CIR/IR/invalid-annotations.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 75180131adb3..59343514b645 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1101,6 +1101,77 @@ def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> { ]; } +//===----------------------------------------------------------------------===// +// AnnotationAttr +//===----------------------------------------------------------------------===// + +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] + ``` + }]; + + // 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 +//===----------------------------------------------------------------------===// + +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. + 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], + ["b", #cir.annotation], + ["c", #cir.annotation], + ["foo", #cir.annotation]]> + ``` + }]; + + let parameters = (ins "ArrayAttr":$annotations); + + let assemblyFormat = [{ $annotations }]; + + // Enable verifier. + let genVerifyDecl = 1; +} + include "clang/CIR/Dialect/IR/CIROpenCLAttrs.td" #endif // MLIR_CIR_DIALECT_CIR_ATTRS diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4eb86f8a1b82..2b88304a14cf 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -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, DeclareOpInterfaceMethods, @@ -2255,7 +2255,8 @@ def GlobalOp : CIR_Op<"global", UnitAttr:$dsolocal, OptionalAttr:$alignment, OptionalAttr:$ast, - OptionalAttr:$section); + OptionalAttr:$section, + OptionalAttr:$annotations); let regions = (region AnyRegion:$ctorRegion, AnyRegion:$dtorRegion); let assemblyFormat = [{ ($sym_visibility^)? @@ -2268,6 +2269,7 @@ def GlobalOp : CIR_Op<"global", (`addrspace` `(` custom($addr_space)^ `)`)? $sym_name custom($sym_type, $initial_value, $ctorRegion, $dtorRegion) + ($annotations^)? attr-dict }]; @@ -3100,6 +3102,7 @@ def FuncOp : CIR_Op<"func", [ OptionalAttr:$aliasee, OptionalAttr:$global_ctor, OptionalAttr:$global_dtor, + OptionalAttr:$annotations, OptionalAttr:$ast); let regions = (region AnyRegion:$body); let skipDefaultBuilders = 1; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2bbd5e0fe796..b5578fbd6d02 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -484,6 +484,13 @@ void CIRGenModule::buildGlobal(GlobalDecl GD) { // Ignore declarations, they will be emitted on their first use. if (const auto *FD = dyn_cast(Global)) { + // Update deferred annotations with the latest declaration if the function + // was already used or defined. + if (FD->hasAttr()) { + StringRef MangledName = getMangledName(GD); + if (getGlobalValue(MangledName)) + deferredAnnotations[MangledName] = FD; + } // Forward declarations are emitted lazily on first use. if (!FD->doesThisDeclarationHaveABody()) { if (!FD->doesDeclarationForceExternallyVisibleDefinition()) @@ -595,7 +602,8 @@ void CIRGenModule::buildGlobalFunctionDefinition(GlobalDecl GD, if (const DestructorAttr *DA = D->getAttr()) AddGlobalDtor(Fn, DA->getPriority(), true); - assert(!D->getAttr() && "NYI"); + if (D->getAttr()) + deferredAnnotations[getMangledName(GD)] = cast(D); } /// Track functions to be called before main() runs. @@ -1232,7 +1240,7 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D, maybeHandleStaticInExternC(D, GV); if (D->hasAttr()) - assert(0 && "not implemented"); + addGlobalAnnotations(D, GV); // Set CIR's linkage type as appropriate. mlir::cir::GlobalLinkageKind Linkage = @@ -2834,7 +2842,7 @@ void CIRGenModule::Release() { // TODO: PGOReader // TODO: buildCtorList(GlobalCtors); // TODO: builtCtorList(GlobalDtors); - // TODO: buildGlobalAnnotations(); + buildGlobalAnnotations(); // TODO: buildDeferredUnusedCoverageMappings(); // TODO: CIRGenPGO // TODO: CoverageMapping @@ -3188,3 +3196,68 @@ LangAS CIRGenModule::getGlobalVarAddressSpace(const VarDecl *D) { return getTargetCIRGenInfo().getGlobalVarAddressSpace(*this, D); } + +mlir::ArrayAttr CIRGenModule::buildAnnotationArgs(AnnotateAttr *attr) { + ArrayRef 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(e)->getAPValueResult()); + } + mlir::ArrayAttr &lookup = annotationArgs[id.ComputeHash()]; + if (lookup) + return lookup; + + llvm::SmallVector args; + args.reserve(exprs.size()); + for (Expr *e : exprs) { + if (auto *const strE = + ::clang::dyn_cast(e->IgnoreParenCasts())) { + // Add trailing null character as StringLiteral->getString() does not + args.push_back(builder.getStringAttr(strE->getString())); + } else if (auto *const intE = ::clang::dyn_cast( + e->IgnoreParenCasts())) { + args.push_back(mlir::IntegerAttr::get( + mlir::IntegerType::get(builder.getContext(), + intE->getValue().getBitWidth()), + intE->getValue())); + } else { + llvm_unreachable("NYI"); + } + } + + 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() && "no annotate attribute"); + assert((isa(gv) || isa(gv)) && + "annotation only on globals"); + llvm::SmallVector annotations; + for (auto *i : d->specific_attrs()) + annotations.push_back(buildAnnotateAttr(i)); + if (auto global = dyn_cast(gv)) + global.setAnnotationsAttr(builder.getArrayAttr(annotations)); + else if (auto func = dyn_cast(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(); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 55d609c93299..4ec8950a5d33 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -127,6 +127,22 @@ class CIRGenModule : public CIRGenTypeCache { /// for the same decl. llvm::DenseSet 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 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 deferredAnnotations; + public: mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } @@ -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 diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index c2a17947ea75..79577e10fca9 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -545,6 +545,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(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(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(annoEntry[1]); + if (!annoPart) { + emitError() << "The second element of GlobalAnnotationValuesAttr" + "annotations array element must be of " + "type AnnotationValueAttr"; + return failure(); + } + } + return success(); +} + //===----------------------------------------------------------------------===// // DynamicCastInfoAtttr definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 37f73c3aa3b0..4f84c96f8c6f 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -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; @@ -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( @@ -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 arguments; SmallVector resultAttrs; @@ -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. @@ -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("; diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 160c0de9b98d..ba19c6ec4069 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -85,6 +85,9 @@ struct LoweringPreparePass : public LoweringPrepareBase { void lowerArrayDtor(ArrayDtor op); void lowerArrayCtor(ArrayCtor op); + /// Collect annotations of global values in the module + void addGlobalAnnotations(mlir::Operation *op, mlir::ArrayAttr annotations); + /// Build the function that initializes the specified global FuncOp buildCXXGlobalVarDeclInitFunc(GlobalOp op); @@ -94,6 +97,9 @@ struct LoweringPreparePass : public LoweringPrepareBase { /// Materialize global ctor/dtor list void buildGlobalCtorDtorList(); + /// Build attribute of global annotation values + void buildGlobalAnnotationValues(); + FuncOp buildRuntimeFunction(mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc, mlir::cir::FuncType type, @@ -149,6 +155,8 @@ struct LoweringPreparePass : public LoweringPrepareBase { SmallVector globalCtorList; /// List of dtors to be called when unloading module. SmallVector globalDtorList; + /// List of annotations in the module + SmallVector globalAnnotations; }; } // namespace @@ -878,6 +886,11 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { "custom initialization priority NYI"); dynamicInitializers.push_back(f); } + + std::optional annotations = op.getAnnotations(); + if (annotations) { + addGlobalAnnotations(op, annotations.value()); + } } void LoweringPreparePass::buildGlobalCtorDtorList() { @@ -1061,6 +1074,27 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) { op.erase(); } +void LoweringPreparePass::addGlobalAnnotations(mlir::Operation *op, + mlir::ArrayAttr annotations) { + auto globalValue = cast(op); + mlir::StringAttr globalValueName = globalValue.getNameAttr(); + for (auto &annot : annotations) { + SmallVector entryArray = {globalValueName, annot}; + globalAnnotations.push_back( + mlir::ArrayAttr::get(theModule.getContext(), entryArray)); + } +} + +void LoweringPreparePass::buildGlobalAnnotationValues() { + if (globalAnnotations.empty()) + return; + mlir::ArrayAttr annotationValueArray = + mlir::ArrayAttr::get(theModule.getContext(), globalAnnotations); + theModule->setAttr("cir.global_annotations", + mlir::cir::GlobalAnnotationValuesAttr::get( + theModule.getContext(), annotationValueArray)); +} + void LoweringPreparePass::runOnOp(Operation *op) { if (auto unary = dyn_cast(op)) { lowerUnaryOp(unary); @@ -1094,6 +1128,8 @@ void LoweringPreparePass::runOnOp(Operation *op) { } else if (auto globalDtor = fnOp.getGlobalDtorAttr()) { globalDtorList.push_back(globalDtor); } + if (std::optional annotations = fnOp.getAnnotations()) + addGlobalAnnotations(fnOp, annotations.value()); } } @@ -1118,6 +1154,7 @@ void LoweringPreparePass::runOnOperation() { buildCXXGlobalInitFunc(); buildGlobalCtorDtorList(); + buildGlobalAnnotationValues(); } std::unique_ptr mlir::createLoweringPreparePass() { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index c245f4307ab6..c0df80c7e947 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -863,7 +863,10 @@ struct ConvertCIRToLLVMPass } void runOnOperation() final; + void buildGlobalAnnotationsVar(); + virtual StringRef getArgument() const override { return "cir-flat-to-llvm"; } + static constexpr StringRef annotationSection = "llvm.metadata"; }; mlir::LogicalResult @@ -2041,7 +2044,7 @@ class CIRGlobalOpLowering // Rewrite op. auto llvmGlobalOp = rewriter.replaceOpWithNewOp( op, llvmType, isConst, linkage, symbol, init.value(), - /*alignment*/op.getAlignment().value_or(0), + /*alignment*/ op.getAlignment().value_or(0), /*addrSpace*/ getGlobalOpTargetAddrSpace(op), /*dsoLocal*/ false, /*threadLocal*/ (bool)op.getTlsModelAttr(), /*comdat*/ mlir::SymbolRefAttr(), attributes); @@ -4057,6 +4060,245 @@ void collect_unreachable(mlir::Operation *parent, } } +// Create a string global for annotation related string. +mlir::LLVM::GlobalOp +getAnnotationStringGlobal(mlir::StringAttr strAttr, mlir::ModuleOp &module, + llvm::StringMap &globalsMap, + mlir::OpBuilder &globalVarBuilder, + mlir::Location &loc, bool isArg = false) { + llvm::StringRef str = strAttr.getValue(); + if (!globalsMap.contains(str)) { + auto llvmStrTy = mlir::LLVM::LLVMArrayType::get( + mlir::IntegerType::get(module.getContext(), 8), str.size() + 1); + auto strGlobalOp = globalVarBuilder.create( + loc, llvmStrTy, + /*isConstant=*/true, mlir::LLVM::Linkage::Private, + ".str" + + (globalsMap.empty() ? "" + : "." + std::to_string(globalsMap.size())) + + ".annotation" + (isArg ? ".arg" : ""), + mlir::StringAttr::get(module.getContext(), std::string(str) + '\0'), + /*alignment=*/isArg ? 1 : 0); + if (!isArg) + strGlobalOp.setSection(ConvertCIRToLLVMPass::annotationSection); + strGlobalOp.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global); + strGlobalOp.setDsoLocal(true); + globalsMap[str] = strGlobalOp; + } + return globalsMap[str]; +} + +mlir::Value lowerAnnotationValue( + mlir::ArrayAttr annotValue, mlir::ModuleOp &module, + mlir::OpBuilder &varInitBuilder, mlir::OpBuilder &globalVarBuilder, + llvm::StringMap &stringGlobalsMap, + llvm::StringMap &argStringGlobalsMap, + llvm::MapVector &argsVarMap, + llvm::SmallVector &annoStructFields, + mlir::LLVM::LLVMStructType &annoStructTy, + mlir::LLVM::LLVMPointerType &annoPtrTy, mlir::Location &loc) { + mlir::Value valueEntry = + varInitBuilder.create(loc, annoStructTy); + auto globalValueName = mlir::cast(annotValue[0]); + mlir::Operation *globalValue = + mlir::SymbolTable::lookupSymbolIn(module, globalValueName); + // The first field is ptr to the global value + auto globalValueFld = varInitBuilder.create( + loc, annoPtrTy, globalValueName); + + valueEntry = varInitBuilder.create( + loc, valueEntry, globalValueFld, 0); + mlir::cir::AnnotationAttr annotation = + mlir::cast(annotValue[1]); + + // The second field is ptr to the annotation name + mlir::StringAttr annotationName = annotation.getName(); + auto annotationNameFld = varInitBuilder.create( + loc, annoPtrTy, + getAnnotationStringGlobal(annotationName, module, stringGlobalsMap, + globalVarBuilder, loc) + .getSymName()); + + valueEntry = varInitBuilder.create( + loc, valueEntry, annotationNameFld, 1); + + // The third field is ptr to the translation unit name, + // and the fourth field is the line number + auto annotLoc = globalValue->getLoc(); + if (mlir::isa(annotLoc)) { + auto FusedLoc = mlir::cast(annotLoc); + annotLoc = FusedLoc.getLocations()[0]; + } + auto annotFileLoc = mlir::cast(annotLoc); + assert(annotFileLoc && "annotation value has to be FileLineColLoc"); + // To be consistent with clang code gen, we add trailing null char + auto fileName = mlir::StringAttr::get( + module.getContext(), std::string(annotFileLoc.getFilename().getValue())); + auto fileNameFld = varInitBuilder.create( + loc, annoPtrTy, + getAnnotationStringGlobal(fileName, module, stringGlobalsMap, + globalVarBuilder, loc) + .getSymName()); + valueEntry = varInitBuilder.create(loc, valueEntry, + fileNameFld, 2); + unsigned int lineNo = annotFileLoc.getLine(); + auto lineNoFld = varInitBuilder.create( + loc, annoStructFields[3], lineNo); + valueEntry = varInitBuilder.create(loc, valueEntry, + lineNoFld, 3); + // The fifth field is ptr to the annotation args var, it could be null + if (annotation.isNoArgs()) { + auto nullPtrFld = varInitBuilder.create(loc, annoPtrTy); + valueEntry = varInitBuilder.create( + loc, valueEntry, nullPtrFld, 4); + } else { + mlir::ArrayAttr argsAttr = annotation.getArgs(); + // First time we see this argsAttr, create a global for it + // and build its initializer + if (!argsVarMap.contains(argsAttr)) { + llvm::SmallVector argStrutFldTypes; + llvm::SmallVector argStrutFields; + for (mlir::Attribute arg : annotation.getArgs()) { + if (auto strArgAttr = mlir::dyn_cast(arg)) { + // Call getAnnotationStringGlobal here to make sure + // have a global for this string before + // creation of the args var. + getAnnotationStringGlobal(strArgAttr, module, argStringGlobalsMap, + globalVarBuilder, loc, true); + // This will become a ptr to the global string + argStrutFldTypes.push_back(annoPtrTy); + } else if (auto intArgAttr = mlir::dyn_cast(arg)) { + argStrutFldTypes.push_back(intArgAttr.getType()); + } else { + llvm_unreachable("Unsupported annotation arg type"); + } + } + + mlir::LLVM::LLVMStructType argsStructTy = + mlir::LLVM::LLVMStructType::getLiteral(globalVarBuilder.getContext(), + argStrutFldTypes); + auto argsGlobalOp = globalVarBuilder.create( + loc, argsStructTy, true, mlir::LLVM::Linkage::Private, + ".args" + + (argsVarMap.empty() ? "" + : "." + std::to_string(argsVarMap.size())) + + ".annotation", + mlir::Attribute()); + argsGlobalOp.setSection(ConvertCIRToLLVMPass::annotationSection); + argsGlobalOp.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global); + argsGlobalOp.setDsoLocal(true); + + // Create the initializer for this args global + argsGlobalOp.getRegion().push_back(new mlir::Block()); + mlir::OpBuilder argsInitBuilder(module.getContext()); + argsInitBuilder.setInsertionPointToEnd( + argsGlobalOp.getInitializerBlock()); + + mlir::Value argsStructInit = + argsInitBuilder.create(loc, argsStructTy); + int idx = 0; + for (mlir::Attribute arg : annotation.getArgs()) { + if (auto strArgAttr = mlir::dyn_cast(arg)) { + // This would be simply return with existing map entry value + // from argStringGlobalsMap as string global is already + // created in the previous loop. + mlir::LLVM::GlobalOp argStrVar = + getAnnotationStringGlobal(strArgAttr, module, argStringGlobalsMap, + globalVarBuilder, loc, true); + auto argStrVarAddr = argsInitBuilder.create( + loc, annoPtrTy, argStrVar.getSymName()); + argsStructInit = argsInitBuilder.create( + loc, argsStructInit, argStrVarAddr, idx++); + } else if (auto intArgAttr = mlir::dyn_cast(arg)) { + auto intArgFld = argsInitBuilder.create( + loc, intArgAttr.getType(), intArgAttr.getValue()); + argsStructInit = argsInitBuilder.create( + loc, argsStructInit, intArgFld, idx++); + } else { + llvm_unreachable("Unsupported annotation arg type"); + } + } + argsInitBuilder.create(loc, argsStructInit); + argsVarMap[argsAttr] = argsGlobalOp; + } + auto argsVarView = varInitBuilder.create( + loc, annoPtrTy, argsVarMap[argsAttr].getSymName()); + valueEntry = varInitBuilder.create( + loc, valueEntry, argsVarView, 4); + } + return valueEntry; +} + +void ConvertCIRToLLVMPass::buildGlobalAnnotationsVar() { + mlir::ModuleOp module = getOperation(); + mlir::Attribute attr = module->getAttr("cir.global_annotations"); + if (!attr) + return; + if (auto globalAnnotValues = + mlir::dyn_cast(attr)) { + auto annotationValuesArray = + mlir::dyn_cast(globalAnnotValues.getAnnotations()); + if (!annotationValuesArray || annotationValuesArray.empty()) + return; + mlir::OpBuilder globalVarBuilder(module.getContext()); + globalVarBuilder.setInsertionPointToEnd(&module.getBodyRegion().front()); + + // Create a global array for annotation values with element type of + // struct { ptr, ptr, ptr, i32, ptr } + mlir::LLVM::LLVMPointerType annoPtrTy = + mlir::LLVM::LLVMPointerType::get(globalVarBuilder.getContext()); + llvm::SmallVector annoStructFields; + annoStructFields.push_back(annoPtrTy); + annoStructFields.push_back(annoPtrTy); + annoStructFields.push_back(annoPtrTy); + annoStructFields.push_back(globalVarBuilder.getI32Type()); + annoStructFields.push_back(annoPtrTy); + + mlir::LLVM::LLVMStructType annoStructTy = + mlir::LLVM::LLVMStructType::getLiteral(globalVarBuilder.getContext(), + annoStructFields); + mlir::LLVM::LLVMArrayType annoStructArrayTy = + mlir::LLVM::LLVMArrayType::get(annoStructTy, + annotationValuesArray.size()); + mlir::Location loc = module.getLoc(); + auto annotationGlobalOp = globalVarBuilder.create( + loc, annoStructArrayTy, false, mlir::LLVM::Linkage::Appending, + "llvm.global.annotations", mlir::Attribute()); + annotationGlobalOp.setSection("llvm.metadata"); + annotationGlobalOp.getRegion().push_back(new mlir::Block()); + mlir::OpBuilder varInitBuilder(module.getContext()); + varInitBuilder.setInsertionPointToEnd( + annotationGlobalOp.getInitializerBlock()); + // Globals created for annotation strings and args to be + // placed before the var llvm.global.annotations. + // This is consistent with clang code gen. + globalVarBuilder.setInsertionPoint(annotationGlobalOp); + + mlir::Value result = + varInitBuilder.create(loc, annoStructArrayTy); + // Track globals created for annotation related strings + llvm::StringMap stringGlobalsMap; + // Track globals created for annotation arg related strings. + // They are different from annotation strings, as strings used in args + // are not in annotationSection, and also has aligment 1. + llvm::StringMap argStringGlobalsMap; + // Track globals created for annotation args. + llvm::MapVector argsVarMap; + + int idx = 0; + for (mlir::Attribute entry : annotationValuesArray) { + auto annotValue = cast(entry); + mlir::Value init = lowerAnnotationValue( + annotValue, module, varInitBuilder, globalVarBuilder, + stringGlobalsMap, argStringGlobalsMap, argsVarMap, annoStructFields, + annoStructTy, annoPtrTy, loc); + result = varInitBuilder.create(loc, result, + init, idx++); + } + varInitBuilder.create(loc, result); + } +} + void ConvertCIRToLLVMPass::runOnOperation() { auto module = getOperation(); mlir::DataLayout dataLayout(module); @@ -4123,6 +4365,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { auto dtorAttr = mlir::cast(attr); return std::make_pair(dtorAttr.getName(), dtorAttr.getPriority()); }); + buildGlobalAnnotationsVar(); } std::unique_ptr createConvertCIRToLLVMPass() { diff --git a/clang/test/CIR/CodeGen/attribute-annotate-multiple.cpp b/clang/test/CIR/CodeGen/attribute-annotate-multiple.cpp new file mode 100644 index 000000000000..e67975bb9858 --- /dev/null +++ b/clang/test/CIR/CodeGen/attribute-annotate-multiple.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +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"))) { +} +// redeclare with more annotate +void foo(int i) __attribute__((annotate("withargfunc", "os", 23 ))); +void bar() __attribute__((annotate("withargfunc", "os", 22))) { +} + +// BEFORE: module @{{.*}}attribute-annotate-multiple.cpp" attributes {cir.lang = + +// BEFORE: cir.global external @a = #cir.ptr : !cir.ptr +// BEFORE-SAME: [#cir.annotation] +// BEFORE: cir.global external @b = #cir.ptr : !cir.ptr +// BEFORE-SAME: [#cir.annotation] +// BEFORE: cir.global external @c = #cir.ptr : !cir.ptr +// BEFORE-SAME: [#cir.annotation] + +// BEFORE: cir.func @_Z3fooi(%arg0: !s32i) [#cir.annotation, +// BEFORE-SAME: #cir.annotation] +// BEFORE: cir.func @_Z3barv() [#cir.annotation] + + +// AFTER: module {{.*}}attribute-annotate-multiple.cpp" attributes +// AFTER-SAME: {cir.global_annotations = #cir], +// AFTER-SAME: ["b", #cir.annotation], +// AFTER-SAME: ["c", #cir.annotation], +// AFTER-SAME: ["_Z3fooi", #cir.annotation], +// AFTER-SAME: ["_Z3fooi", #cir.annotation], +// AFTER-SAME: ["_Z3barv", #cir.annotation]]>, + + +// LLVM: @a = global ptr null +// LLVM: @b = global ptr null +// LLVM: @c = global ptr null +// LLVM: @.str.annotation = private unnamed_addr constant [9 x i8] c"withargs\00", section "llvm.metadata" +// LLVM: @.str.1.annotation = private unnamed_addr constant [{{[0-9]+}} x i8] c"{{.*}}attribute-annotate-multiple.cpp\00", section "llvm.metadata" +// LLVM: @.str.annotation.arg = private unnamed_addr constant [3 x i8] c"21\00", align 1 +// LLVM: @.args.annotation = private unnamed_addr constant { ptr, i32 } { ptr @.str.annotation.arg, i32 12 }, section "llvm.metadata" +// LLVM: @.str.2.annotation = private unnamed_addr constant [9 x i8] c"noargvar\00", section "llvm.metadata" +// LLVM: @.str.3.annotation = private unnamed_addr constant [10 x i8] c"noargfunc\00", section "llvm.metadata" +// LLVM: @.str.4.annotation = private unnamed_addr constant [12 x i8] c"withargfunc\00", section "llvm.metadata" +// LLVM: @.str.1.annotation.arg = private unnamed_addr constant [3 x i8] c"os\00", align 1 +// LLVM: @.args.1.annotation = private unnamed_addr constant { ptr, i32 } +// LLVM-SAME: { ptr @.str.1.annotation.arg, i32 23 }, section "llvm.metadata" +// LLVM: @.args.2.annotation = private unnamed_addr constant { ptr, i32 } +// LLVM-SAME: { ptr @.str.1.annotation.arg, i32 22 }, section "llvm.metadata" + +// LLVM: @llvm.global.annotations = appending global [6 x { ptr, ptr, ptr, i32, ptr }] +// LLVM-SAME: [{ ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @a, ptr @.str.annotation, ptr @.str.1.annotation, i32 5, ptr @.args.annotation }, +// LLVM-SAME: { ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @b, ptr @.str.annotation, ptr @.str.1.annotation, i32 6, ptr @.args.annotation }, +// LLVM-SAME: { ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @c, ptr @.str.2.annotation, ptr @.str.1.annotation, i32 7, ptr null }, +// LLVM-SAME: { ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @_Z3fooi, ptr @.str.3.annotation, ptr @.str.1.annotation, i32 8, ptr null }, +// LLVM-SAME: { ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @_Z3fooi, ptr @.str.4.annotation, ptr @.str.1.annotation, i32 8, ptr @.args.1.annotation }, +// LLVM-SAME: { ptr, ptr, ptr, i32, ptr } +// LLVM-SAME: { ptr @_Z3barv, ptr @.str.4.annotation, ptr @.str.1.annotation, i32 12, ptr @.args.2.annotation }], +// LLVM-SAME: section "llvm.metadata" + +// LLVM: define dso_local void @_Z3fooi(i32 %0) +// LLVM: define dso_local void @_Z3barv() diff --git a/clang/test/CIR/IR/annotations.cir b/clang/test/CIR/IR/annotations.cir new file mode 100644 index 000000000000..c1486e35aa71 --- /dev/null +++ b/clang/test/CIR/IR/annotations.cir @@ -0,0 +1,31 @@ +// RUN: cir-opt %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +!s32i = !cir.int +module attributes {cir.global_annotations = +#cir], +["foo", #cir.annotation], +["bar", #cir.annotation], +["bar", #cir.annotation]]>} +{ +cir.global external @a = #cir.int<0> : !s32i [#cir.annotation] +cir.func @foo() attributes {annotations = [#cir.annotation]} { + cir.return +} +cir.func @bar() attributes {annotations = [#cir.annotation, #cir.annotation]} { + cir.return +} +} + +// CHECK: module attributes {cir.global_annotations = #cir], +// CHECK-SAME: ["foo", #cir.annotation], +// CHECK-SAME: ["bar", #cir.annotation], +// CHECK-SAME: ["bar", #cir.annotation]]>} +// CHECK: cir.global external @a = #cir.int<0> : !s32i +// CHECK-SAME: [#cir.annotation] +// CHECK: cir.func @foo() +// CHECK-SAME: [#cir.annotation] +// CHECK: cir.func @bar() +// CHECK-SAME: [#cir.annotation, +// CHECK-SAME: #cir.annotation] diff --git a/clang/test/CIR/IR/invalid-annotations.cir b/clang/test/CIR/IR/invalid-annotations.cir new file mode 100644 index 000000000000..d7de2d5c5602 --- /dev/null +++ b/clang/test/CIR/IR/invalid-annotations.cir @@ -0,0 +1,32 @@ +// Test attempt to construct ill-formed global annotations +// RUN: cir-opt %s -verify-diagnostics -split-input-file + + +// expected-error @below {{invalid kind of attribute specified}} +// expected-error @below {{failed to parse AnnotationAttr parameter 'name' which is to be a `StringAttr`}} +cir.global external @a = #cir.ptr : !cir.ptr [#cir.annotation] + +// ----- + +// expected-error @below {{GlobalAnnotationValuesAttr should at least have one annotation}} +module attributes {cir.global_annotations = #cir} {} + +// ----- + +// expected-error @below {{Element of GlobalAnnotationValuesAttr annotations array must be an array}} +module attributes {cir.global_annotations = #cir} {} + +// ----- + +// expected-error @below {{Element of GlobalAnnotationValuesAttr annotations array must be a 2-element array}} +module attributes {cir.global_annotations = #cir} {} + +// ----- + +// expected-error @below {{Element of GlobalAnnotationValuesAttr annotationsarray must start with a string}} +module attributes {cir.global_annotations = #cir} {} + +// ----- + +// expected-error @below {{The second element of GlobalAnnotationValuesAttrannotations array element must be of type AnnotationValueAttr}} +module attributes {cir.global_annotations = #cir} {}