diff --git a/llvm/include/llvm/Transforms/Utils/FunctionAnnotator.h b/llvm/include/llvm/Transforms/Utils/FunctionAnnotator.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/FunctionAnnotator.h @@ -0,0 +1,26 @@ +//===-- FunctionAnnotator.h - FunctionAnnotator ---------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// This file defines the FunctionAnnotator class which is used in phase +// ordering. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_FUNCTIONANNOTATOR_H +#define LLVM_TRANSFORMS_FUNCTIONANNOTATOR_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Support/CommandLine.h" + +namespace llvm { +class FunctionAnnotator : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_FUNCTIONANNOTATOR_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -232,6 +232,7 @@ #include "llvm/Transforms/Utils/Debugify.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" #include "llvm/Transforms/Utils/FixIrreducible.h" +#include "llvm/Transforms/Utils/FunctionAnnotator.h" #include "llvm/Transforms/Utils/HelloWorld.h" #include "llvm/Transforms/Utils/InjectTLIMappings.h" #include "llvm/Transforms/Utils/InstructionNamer.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -59,6 +59,7 @@ MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass()) MODULE_PASS("extract-blocks", BlockExtractorPass({}, false)) MODULE_PASS("forceattrs", ForceFunctionAttrsPass()) +MODULE_PASS("function-annotator", FunctionAnnotator()) MODULE_PASS("function-import", FunctionImportPass()) MODULE_PASS("globaldce", GlobalDCEPass()) MODULE_PASS("globalopt", GlobalOptPass()) diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -11,6 +11,8 @@ #include "llvm/IR/Module.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -30,6 +32,15 @@ "example -force-remove-attribute=foo:noinline. This " "option can be specified multiple times.")); +static cl::opt + OptLevelAttributeName("opt-level-attribute-name", cl::init("opt-level"), + cl::Hidden, cl::desc("Optimization attribute name")); + +static cl::opt CSVFilePath( + "func-annotator-csv-file-path", cl::Hidden, + cl::desc("CSV file containing function names and optimization level as " + "attribute")); + /// If F has any forced attributes given on the command line, add them. /// If F has any forced remove attributes given on the command line, remove /// them. When both force and force-remove are given to a function, the latter @@ -68,12 +79,36 @@ } PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, - ModuleAnalysisManager &) { - if (!hasForceAttributes()) - return PreservedAnalyses::all(); + ModuleAnalysisManager &AM) { - for (Function &F : M.functions()) - forceAttributes(F); + auto BufferOrError = MemoryBuffer::getFileOrSTDIN(CSVFilePath); + if (!BufferOrError) { + if (!hasForceAttributes()) + return PreservedAnalyses::all(); + + for (Function &F : M.functions()) + forceAttributes(F); + } + + StringRef Buffer = BufferOrError.get()->getBuffer(); + auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer); + line_iterator It(*MemoryBuffer); + for (++It; !It.is_at_end(); ++It) { + auto SplitPair = It->split(','); + if (!SplitPair.second.empty()) { + Function *Func = M.getFunction(SplitPair.first); + if (Func) { + if (Func->isDeclaration()) { + continue; + } + Func->addFnAttr(OptLevelAttributeName, SplitPair.second); + } else { + errs() << "Function in CSV file at line " << It.line_number() + << " does not exist\n"; + continue; + } + } + } // Just conservatively invalidate analyses, this isn't likely to be important. return PreservedAnalyses::none(); diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -25,6 +25,7 @@ Evaluator.cpp FixIrreducible.cpp FlattenCFG.cpp + FunctionAnnotator.cpp FunctionComparator.cpp FunctionImportUtils.cpp GlobalStatus.cpp diff --git a/llvm/lib/Transforms/Utils/FunctionAnnotator.cpp b/llvm/lib/Transforms/Utils/FunctionAnnotator.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Utils/FunctionAnnotator.cpp @@ -0,0 +1,59 @@ +//===- FunctionAnnotator.cpp - Function Annotator -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Annotates functions with the appropriate optimization level attribute for +// assistance in phase ordering. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/FunctionAnnotator.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Testing/Annotations/Annotations.h" + +using namespace llvm; + +static cl::opt + OptLevelAttributeName("opt-level-attribute-names", cl::init("opt-level"), + cl::Hidden, cl::desc("Optimization attribute name")); + +static cl::opt CSVFilePath( + "func-annotator-csv-file-paths", cl::Hidden, + cl::desc("CSV file containing function names and optimization level as " + "attribute")); + +PreservedAnalyses FunctionAnnotator::run(Module &M, ModuleAnalysisManager &AM) { + + auto BufferOrError = MemoryBuffer::getFileOrSTDIN(CSVFilePath); + if (!BufferOrError) { + report_fatal_error("Cannot open CSV File"); + } + + StringRef Buffer = BufferOrError.get()->getBuffer(); + auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer); + line_iterator It(*MemoryBuffer); + for (++It; !It.is_at_end(); ++It) { + auto SplitPair = It->split(','); + if (!SplitPair.second.empty()) { + Function *Func = M.getFunction(SplitPair.first); + if (Func) { + if (Func->isDeclaration()) { + continue; + } + Func->addFnAttr(OptLevelAttributeName, SplitPair.second); + } else { + errs() << "Function in CSV file at line " << It.line_number() + << " does not exist\n"; + continue; + } + } + } + + return PreservedAnalyses::all(); +} diff --git a/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotation.csv b/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotation.csv new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotation.csv @@ -0,0 +1,6 @@ +FunctionName,OptimizationLevel +first_function,O1 +second_function,O3 +third_function,O1 +fourth_function,O2 +fifth_function,O3 diff --git a/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotator.ll b/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotator.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ForcedFunctionAttrs/FunctionAnnotator.ll @@ -0,0 +1,31 @@ +; RUN: opt -passes='function-annotator' -func-annotator-csv-file-paths="%S/FunctionAnnotation.csv" -opt-level-attribute-names="opt-level" -S < %s | FileCheck %s +define void @first_function() { +; CHECK: @first_function() #0 + ret void +} + +define void @second_function() { +; CHECK: @second_function() #1 + ret void +} + +define void @third_function() { +; CHECK: @third_function() #0 + ret void +} + +define void @fourth_function() { +; CHECK: @fourth_function() #2 + ret void +} + +define void @fifth_function() { +; CHECK: @fifth_function() #1 + ret void +} + +; CHECK-LABEL: attributes #0 = { "opt-level"="O1" } + +; CHECK-LABEL: attributes #1 = { "opt-level"="O3" } + +; CHECK-LABEL: attributes #2 = { "opt-level"="O2" }