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,12 @@ "example -force-remove-attribute=foo:noinline. This " "option can be specified multiple times.")); +static cl::opt CSVFilePath( + "forceattrs-csv-path", cl::Hidden, + cl::desc( + "Path to CSV file containing lines of function names and attributes to " + "add to them in the form of `f1,attr1` or `f2,attr2=str`.")); + /// 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 @@ -69,12 +77,52 @@ PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, ModuleAnalysisManager &) { - if (!hasForceAttributes()) - return PreservedAnalyses::all(); - - for (Function &F : M.functions()) - forceAttributes(F); - - // Just conservatively invalidate analyses, this isn't likely to be important. - return PreservedAnalyses::none(); + bool Changed = false; + if (!CSVFilePath.empty()) { + 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.is_at_end(); ++It) { + auto SplitPair = It->split(','); + if (SplitPair.second.empty()) + continue; + Function *Func = M.getFunction(SplitPair.first); + if (Func) { + if (Func->isDeclaration()) + continue; + auto SecondSplitPair = SplitPair.second.split('='); + if (!SecondSplitPair.second.empty()) { + Func->addFnAttr(SecondSplitPair.first, SecondSplitPair.second); + Changed = true; + } else { + auto AttrKind = Attribute::getAttrKindFromName(SplitPair.second); + if (AttrKind != Attribute::None && + Attribute::canUseAsFnAttr(AttrKind)) { + // TODO: There could be string attributes without a value, we should + // support those, too. + Func->addFnAttr(AttrKind); + Changed = true; + } else + errs() << "Cannot add " << SplitPair.second + << " as an attribute name.\n"; + } + } else { + errs() << "Function in CSV file at line " << It.line_number() + << " does not exist.\n"; + // TODO: `report_fatal_error at end of pass for missing functions. + continue; + } + } + } + if (hasForceAttributes()) { + for (Function &F : M.functions()) + forceAttributes(F); + Changed = true; + } + // Just conservatively invalidate analyses if we've made any changes, this + // isn't likely to be important. + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } diff --git a/llvm/test/Transforms/ForcedFunctionAttrs/DoesNotExist.csv b/llvm/test/Transforms/ForcedFunctionAttrs/DoesNotExist.csv new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ForcedFunctionAttrs/DoesNotExist.csv @@ -0,0 +1 @@ +unknown_function,hot=cold 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,5 @@ +first_function,opt-level=O1 +second_function,cold +third_function,opt-level=O1 +fourth_function,opt-level=O2 +fifth_function,foo=bar 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,36 @@ +; RUN: opt -passes='forceattrs' -forceattrs-csv-path="%S/FunctionAnnotation.csv" -S < %s | FileCheck %s +; RUN: opt -passes='forceattrs' -forceattrs-csv-path="%S/DoesNotExist.csv" -S < %s 2>&1 | FileCheck %s --check-prefix=NonexistentFunc +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() #3 + ret void +} + +; CHECK-LABEL: attributes #0 = { "opt-level"="O1" } + +; CHECK-LABEL: attributes #1 = { cold } + +; CHECK-LABEL: attributes #2 = { "opt-level"="O2" } + +; CHECK-LABEL: attributes #3 = { "foo"="bar" } + +; NonexistentFunc: Function in CSV file at line 1 does not exist.