diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -340,6 +340,8 @@ } /// @} + void addAnnotationMetadata(StringRef Name); + /// Sets the metadata on this instruction from the AAMDNodes structure. void setAAMetadata(const AAMDNodes &N); diff --git a/llvm/include/llvm/IR/MDBuilder.h b/llvm/include/llvm/IR/MDBuilder.h --- a/llvm/include/llvm/IR/MDBuilder.h +++ b/llvm/include/llvm/IR/MDBuilder.h @@ -109,6 +109,9 @@ /// Merge the new callback encoding \p NewCB into \p ExistingCallbacks. MDNode *mergeCallbackEncodings(MDNode *ExistingCallbacks, MDNode *NewCB); + MDNode *createAnnotation(ArrayRef ExistingNames); + MDNode *createAnnotation(StringRef Name); + //===------------------------------------------------------------------===// // AA metadata. //===------------------------------------------------------------------===// diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -62,6 +62,8 @@ void initializeAAEvalLegacyPassPass(PassRegistry&); void initializeAAResultsWrapperPassPass(PassRegistry&); +void initializeAnnotation2MetadataPass(PassRegistry &); +void initializeAnnotationRemarksPass(PassRegistry &); void initializeADCELegacyPassPass(PassRegistry&); void initializeAddDiscriminatorsLegacyPassPass(PassRegistry&); void initializeModuleAddressSanitizerLegacyPassPass(PassRegistry &); diff --git a/llvm/include/llvm/Transforms/IPO.h b/llvm/include/llvm/Transforms/IPO.h --- a/llvm/include/llvm/Transforms/IPO.h +++ b/llvm/include/llvm/Transforms/IPO.h @@ -29,6 +29,11 @@ class GlobalValue; class raw_ostream; +//===----------------------------------------------------------------------===// +// +// +ModulePass *createAnnotation2MetadataPass(); + //===----------------------------------------------------------------------===// // // These functions removes symbols from functions and modules. If OnlyDebugInfo diff --git a/llvm/include/llvm/Transforms/Scalar.h b/llvm/include/llvm/Transforms/Scalar.h --- a/llvm/include/llvm/Transforms/Scalar.h +++ b/llvm/include/llvm/Transforms/Scalar.h @@ -31,6 +31,10 @@ // FunctionPass *createAlignmentFromAssumptionsPass(); +//===----------------------------------------------------------------------===// +// +FunctionPass *createAnnotationRemarksPass(); + //===----------------------------------------------------------------------===// // // SCCP - Sparse conditional constant propagation. diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -116,6 +116,23 @@ return Order < Other->Order; } +void Instruction::addAnnotationMetadata(StringRef Name) { + MDBuilder MDB(getContext()); + + auto *Existing = getMetadata("annotation"); + MDNode *MD; + if (Existing) { + SmallVector Ops; + auto *Tuple = cast(Existing->getOperand(0)); + for (auto &N : make_range(Tuple->op_begin(), Tuple->op_end())) + Ops.push_back(N.get()); + Ops.push_back(MDB.createString(Name)); + MD = MDB.createAnnotation(Ops); + } else + MD = MDB.createAnnotation(Name); + setMetadata("annotation", MD); +} + void Instruction::setHasNoUnsignedWrap(bool b) { cast(this)->setHasNoUnsignedWrap(b); } diff --git a/llvm/lib/IR/MDBuilder.cpp b/llvm/lib/IR/MDBuilder.cpp --- a/llvm/lib/IR/MDBuilder.cpp +++ b/llvm/lib/IR/MDBuilder.cpp @@ -150,6 +150,16 @@ return MDNode::get(Context, Ops); } +MDNode *MDBuilder::createAnnotation(ArrayRef ExistingNames) { + return MDNode::get(Context, MDTuple::get(Context, ExistingNames)); +} + +MDNode *MDBuilder::createAnnotation(StringRef Name) { + SmallVector Ops; + Ops.push_back(createString(Name)); + return createAnnotation(Ops); +} + MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) { SmallVector Args(1, nullptr); if (Extra) @@ -185,7 +195,7 @@ } MDNode *MDBuilder::createAliasScopeDomain(StringRef Name) { - return MDNode::get(Context, createString(Name)); + return MDNode::get(Context, {createString(Name)}); } MDNode *MDBuilder::createAliasScope(StringRef Name, MDNode *Domain) { diff --git a/llvm/lib/Transforms/IPO/Annotation2Metadata.cpp b/llvm/lib/Transforms/IPO/Annotation2Metadata.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/IPO/Annotation2Metadata.cpp @@ -0,0 +1,89 @@ +//===-- Annotation2Metadata.cpp - Add !annotation metadata. ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Add !annotation metadata for entries in @llvm.global.anotations, generated +// using __attribute__((annotate("_name"))) on functions in Clang. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/IPO.h" + +using namespace llvm; + +#define DEBUG_TYPE "annotation2md" + +static bool convertAnnotations2Metadata(Module &M) { + auto *Annotations = M.getGlobalVariable("llvm.global.annotations"); + auto *C = dyn_cast_or_null(Annotations); + if (!C) + return false; + + C = cast(C->getOperand(0)); + for (auto &Op : C->operands()) { + auto *OpC = dyn_cast(&Op); + if (!OpC) + continue; + + auto *StrGEP = dyn_cast(OpC->getOperand(1)); + if (!StrGEP || StrGEP->getNumOperands() < 2) + continue; + auto *StrC = dyn_cast(StrGEP->getOperand(0)); + if (!StrC) + continue; + auto *StrData = dyn_cast(StrC->getOperand(0)); + if (!StrData) + continue; + // Look through bitcast. + auto *Fn = + dyn_cast(cast(OpC->getOperand(0))->getOperand(0)); + if (!Fn) + continue; + for (auto &I : instructions(Fn)) + I.addAnnotationMetadata(StrData->getAsCString()); + } + return true; +} + +namespace { + +class Annotation2Metadata : public ModulePass { +public: + static char ID; + + Annotation2Metadata() : ModulePass(ID) { + initializeAnnotation2MetadataPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override { + return convertAnnotations2Metadata(M); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } +}; + +} // end anonymous namespace + +char Annotation2Metadata::ID = 0; + +INITIALIZE_PASS_BEGIN(Annotation2Metadata, "annotation2md", + "Annotation2Metadata", false, false) +INITIALIZE_PASS_END(Annotation2Metadata, "annotation2md", "Annotation2Metadata", + false, false) + +ModulePass *llvm::createAnnotation2MetadataPass() { + return new Annotation2Metadata(); +} diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt --- a/llvm/lib/Transforms/IPO/CMakeLists.txt +++ b/llvm/lib/Transforms/IPO/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMipo AlwaysInliner.cpp + Annotation2Metadata.cpp ArgumentPromotion.cpp Attributor.cpp AttributorAttributes.cpp diff --git a/llvm/lib/Transforms/IPO/IPO.cpp b/llvm/lib/Transforms/IPO/IPO.cpp --- a/llvm/lib/Transforms/IPO/IPO.cpp +++ b/llvm/lib/Transforms/IPO/IPO.cpp @@ -25,6 +25,7 @@ void llvm::initializeIPO(PassRegistry &Registry) { initializeOpenMPOptLegacyPassPass(Registry); initializeArgPromotionPass(Registry); + initializeAnnotation2MetadataPass(Registry); initializeCalledValuePropagationLegacyPassPass(Registry); initializeConstantMergeLegacyPassPass(Registry); initializeCrossDSOCFIPass(Registry); diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -512,6 +512,8 @@ // is handled separately, so just check this is not the ThinLTO post-link. bool DefaultOrPreLinkPipeline = !PerformThinLTO; + MPM.add(createAnnotation2MetadataPass()); + if (!PGOSampleUse.empty()) { MPM.add(createPruneEHPass()); // In ThinLTO mode, when flattened profile is used, all the available @@ -902,6 +904,8 @@ // Rename anon globals to be able to handle them in the summary MPM.add(createNameAnonGlobalPass()); } + + MPM.add(createAnnotationRemarksPass()); } void PassManagerBuilder::addLTOOptimizationPasses(legacy::PassManagerBase &PM) { diff --git a/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/AnnotationRemarks.cpp @@ -0,0 +1,92 @@ +//===-- AnnotationRemarks.cpp - Generate remarks for annotated instrs. ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Generate remarks for instructions marked with !annotation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/MapVector.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/Scalar.h" + +using namespace llvm; +using namespace llvm::ore; + +#define DEBUG_TYPE "annotation-remarks" +#define REMARK_PASS "annotation-remarks" + +static void runImpl(Function &F, OptimizationRemarkEmitter &ORE, + const TargetLibraryInfo &TLI) { + if (!ORE.allowExtraAnalysis(REMARK_PASS)) + return; + + // For now, just generate a summary of the annotated instructions. + MapVector Mapping; + for (Instruction &I : instructions(F)) { + if (!I.hasMetadata("annotation")) + continue; + MDTuple *Strs = cast(I.getMetadata("annotation")->getOperand(0)); + for (const MDOperand &Op : make_range(Strs->op_begin(), Strs->op_end())) { + auto Iter = Mapping.insert({cast(Op.get())->getString(), 0}); + Iter.first->second++; + } + } + + for (auto &KV : Mapping) + ORE.emit(OptimizationRemarkMissed(REMARK_PASS, "AnnotationSummary", + &*F.begin()->begin()) + << "Annotated " << NV("count", KV.second) << " instructions with " + << NV("type", KV.first)); +} + +namespace { + +class AnnotationRemarks : public FunctionPass { +public: + static char ID; + + AnnotationRemarks() : FunctionPass(ID) { + initializeAnnotationRemarksPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + OptimizationRemarkEmitter &ORE = + getAnalysis().getORE(); + const TargetLibraryInfo &TLI = + getAnalysis().getTLI(F); + runImpl(F, ORE, TLI); + return false; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired(); + AU.addRequired(); + } +}; + +} // end anonymous namespace + +char AnnotationRemarks::ID = 0; + +INITIALIZE_PASS_BEGIN(AnnotationRemarks, "annotation-remarks", + "Annotation Remarks", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass) +INITIALIZE_PASS_END(AnnotationRemarks, "annotation-remarks", + "Annotation Remarks", false, false) + +FunctionPass *llvm::createAnnotationRemarksPass() { + return new AnnotationRemarks(); +} diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_component_library(LLVMScalarOpts ADCE.cpp AlignmentFromAssumptions.cpp + AnnotationRemarks.cpp BDCE.cpp CallSiteSplitting.cpp ConstantHoisting.cpp diff --git a/llvm/lib/Transforms/Scalar/Scalar.cpp b/llvm/lib/Transforms/Scalar/Scalar.cpp --- a/llvm/lib/Transforms/Scalar/Scalar.cpp +++ b/llvm/lib/Transforms/Scalar/Scalar.cpp @@ -34,6 +34,7 @@ /// ScalarOpts library. void llvm::initializeScalarOpts(PassRegistry &Registry) { initializeADCELegacyPassPass(Registry); + initializeAnnotationRemarksPass(Registry); initializeBDCELegacyPassPass(Registry); initializeAlignmentFromAssumptionsPass(Registry); initializeCallSiteSplittingLegacyPassPass(Registry); diff --git a/llvm/test/Transforms/Util/annotation-remarks.ll b/llvm/test/Transforms/Util/annotation-remarks.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Util/annotation-remarks.ll @@ -0,0 +1,52 @@ +; RUN: opt -annotation-remarks -pass-remarks-missed='annotation-remarks' -disable-output -pass-remarks-output=%t.opt.yaml %s +; RUN: FileCheck --input-file=%t.opt.yaml %s + +; CHECK: --- !Missed +; CHECK-NEXT: Pass: annotation-remarks +; CHECK-NEXT: Name: AnnotationSummary +; CHECK-NEXT: Function: test1 +; CHECK-NEXT: Args: +; CHECK-NEXT: - String: 'Annotated ' +; CHECK-NEXT: - count: '4' +; CHECK-NEXT: - String: ' instructions with ' +; CHECK-NEXT: - type: _remarks1 +; CHECK-NEXT: ... +; CHECK-NEXT: --- !Missed +; CHECK-NEXT: Pass: annotation-remarks +; CHECK-NEXT: Name: AnnotationSummary +; CHECK-NEXT: Function: test1 +; CHECK-NEXT: Args: +; CHECK-NEXT: - String: 'Annotated ' +; CHECK-NEXT: - count: '3' +; CHECK-NEXT: - String: ' instructions with ' +; CHECK-NEXT: - type: _remarks2 +; CHECK-NEXT: ... +; CHECK-NEXT: --- !Missed +; CHECK-NEXT: Pass: annotation-remarks +; CHECK-NEXT: Name: AnnotationSummary +; CHECK-NEXT: Function: test2 +; CHECK-NEXT: Args: +; CHECK-NEXT: - String: 'Annotated ' +; CHECK-NEXT: - count: '2' +; CHECK-NEXT: - String: ' instructions with ' +; CHECK-NEXT: - type: _remarks1 +; CHECK-NEXT: ... + +define void @test1(float* %a) { +entry: + %a.addr = alloca float*, align 8, !annotation !0 + store float* null, float** %a.addr, align 8, !annotation !2 + store float* %a, float** %a.addr, align 8, !annotation !0 + ret void, !annotation !0 +} + +define void @test2(float* %a) { +entry: + %a.addr = alloca float*, align 8, !annotation !2 + ret void, !annotation !2 +} + +!0 = !{!1} +!1 = !{!"_remarks1", !"_remarks2"} +!2 = !{!3} +!3 = !{!"_remarks1"} diff --git a/llvm/test/Transforms/Util/annotations2metadata.ll b/llvm/test/Transforms/Util/annotations2metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Util/annotations2metadata.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -annotation2md -S %s | FileCheck %s + +@.str = private unnamed_addr constant [10 x i8] c"_remarks1\00", section "llvm.metadata" +@.str.1 = private unnamed_addr constant [6 x i8] c"ann.c\00", section "llvm.metadata" +@.str.2 = private unnamed_addr constant [10 x i8] c"_remarks2\00", section "llvm.metadata" +@llvm.global.annotations = appending global [4 x { i8*, i8*, i8*, i32 }] [ + { i8*, i8*, i8*, i32 } { i8* bitcast (void (float*)* @test1 to i8*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i32 2 }, + { i8*, i8*, i8*, i32 } { i8* bitcast (void (float*)* @test1 to i8*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i32 2 }, + { i8*, i8*, i8*, i32 } { i8* bitcast (void (float*)* @test3 to i8*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0), i32 4 }, + { i8*, i8*, i8*, i32 } { i8* undef, i8* undef, i8* undef, i32 300 } ; Invalid entry, make sure we do not crash. + ], section "llvm.metadata" + + + +define void @test1(float* %a) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca float*, align 8, !annotation [[GROUP1:!.+]] +; CHECK-NEXT: store float* [[A:%.*]], float** [[A_ADDR]], align 8, !annotation [[GROUP1]] +; CHECK-NEXT: ret void, !annotation [[GROUP1]] +; +entry: + %a.addr = alloca float*, align 8 + store float* %a, float** %a.addr, align 8 + ret void +} + +define void @test2(float* %a) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca float*, align 8 +; CHECK-NEXT: store float* [[A:%.*]], float** [[A_ADDR]], align 8 +; CHECK-NEXT: ret void +; +entry: + %a.addr = alloca float*, align 8 + store float* %a, float** %a.addr, align 8 + ret void +} + +define void @test3(float* %a) { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca float*, align 8, !annotation [[GROUP2:!.+]] +; CHECK-NEXT: store float* [[A:%.*]], float** [[A_ADDR]], align 8, !annotation [[GROUP2]] +; CHECK-NEXT: ret void, !annotation [[GROUP2]] +; +entry: + %a.addr = alloca float*, align 8 + store float* %a, float** %a.addr, align 8 + ret void +} + +; CHECK: [[GROUP1]] = !{[[NAMES1:!.+]]} +; CHECK-NEXT: [[NAMES1]] = !{!"_remarks1", !"_remarks2"} +; CHECK-NEXT: [[GROUP2]] = !{[[NAMES2:!.+]]} +; CHECK-NEXT: [[NAMES2]] = !{!"_remarks1"}