xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
1 //===- ForceFunctionAttrs.cpp - Force function attrs for debugging --------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Transforms/IPO/ForceFunctionAttrs.h"
10 #include "llvm/IR/Function.h"
11 #include "llvm/IR/Module.h"
12 #include "llvm/Support/CommandLine.h"
13 #include "llvm/Support/Debug.h"
14 #include "llvm/Support/LineIterator.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/raw_ostream.h"
17 using namespace llvm;
18 
19 #define DEBUG_TYPE "forceattrs"
20 
21 static cl::list<std::string> ForceAttributes(
22     "force-attribute", cl::Hidden,
23     cl::desc(
24         "Add an attribute to a function. This can be a "
25         "pair of 'function-name:attribute-name', to apply an attribute to a "
26         "specific function. For "
27         "example -force-attribute=foo:noinline. Specifying only an attribute "
28         "will apply the attribute to every function in the module. This "
29         "option can be specified multiple times."));
30 
31 static cl::list<std::string> ForceRemoveAttributes(
32     "force-remove-attribute", cl::Hidden,
33     cl::desc("Remove an attribute from a function. This can be a "
34              "pair of 'function-name:attribute-name' to remove an attribute "
35              "from a specific function. For "
36              "example -force-remove-attribute=foo:noinline. Specifying only an "
37              "attribute will remove the attribute from all functions in the "
38              "module. This "
39              "option can be specified multiple times."));
40 
41 static cl::opt<std::string> CSVFilePath(
42     "forceattrs-csv-path", cl::Hidden,
43     cl::desc(
44         "Path to CSV file containing lines of function names and attributes to "
45         "add to them in the form of `f1,attr1` or `f2,attr2=str`."));
46 
47 /// If F has any forced attributes given on the command line, add them.
48 /// If F has any forced remove attributes given on the command line, remove
49 /// them. When both force and force-remove are given to a function, the latter
50 /// takes precedence.
51 static void forceAttributes(Function &F) {
52   auto ParseFunctionAndAttr = [&](StringRef S) {
53     StringRef AttributeText;
54     if (S.contains(':')) {
55       auto KV = StringRef(S).split(':');
56       if (KV.first != F.getName())
57         return Attribute::None;
58       AttributeText = KV.second;
59     } else {
60       AttributeText = S;
61     }
62     auto Kind = Attribute::getAttrKindFromName(AttributeText);
63     if (Kind == Attribute::None || !Attribute::canUseAsFnAttr(Kind)) {
64       LLVM_DEBUG(dbgs() << "ForcedAttribute: " << AttributeText
65                         << " unknown or not a function attribute!\n");
66     }
67     return Kind;
68   };
69 
70   for (const auto &S : ForceAttributes) {
71     auto Kind = ParseFunctionAndAttr(S);
72     if (Kind == Attribute::None || F.hasFnAttribute(Kind))
73       continue;
74     F.addFnAttr(Kind);
75   }
76 
77   for (const auto &S : ForceRemoveAttributes) {
78     auto Kind = ParseFunctionAndAttr(S);
79     if (Kind == Attribute::None || !F.hasFnAttribute(Kind))
80       continue;
81     F.removeFnAttr(Kind);
82   }
83 }
84 
85 static bool hasForceAttributes() {
86   return !ForceAttributes.empty() || !ForceRemoveAttributes.empty();
87 }
88 
89 PreservedAnalyses ForceFunctionAttrsPass::run(Module &M,
90                                               ModuleAnalysisManager &) {
91   bool Changed = false;
92   if (!CSVFilePath.empty()) {
93     auto BufferOrError = MemoryBuffer::getFileOrSTDIN(CSVFilePath);
94     if (!BufferOrError) {
95       std::error_code EC = BufferOrError.getError();
96       M.getContext().emitError("cannot open CSV file: " + EC.message());
97       return PreservedAnalyses::all();
98     }
99 
100     StringRef Buffer = BufferOrError.get()->getBuffer();
101     auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer);
102     line_iterator It(*MemoryBuffer);
103     for (; !It.is_at_end(); ++It) {
104       auto SplitPair = It->split(',');
105       if (SplitPair.second.empty())
106         continue;
107       Function *Func = M.getFunction(SplitPair.first);
108       if (Func) {
109         if (Func->isDeclaration())
110           continue;
111         auto SecondSplitPair = SplitPair.second.split('=');
112         if (!SecondSplitPair.second.empty()) {
113           Func->addFnAttr(SecondSplitPair.first, SecondSplitPair.second);
114           Changed = true;
115         } else {
116           auto AttrKind = Attribute::getAttrKindFromName(SplitPair.second);
117           if (AttrKind != Attribute::None &&
118               Attribute::canUseAsFnAttr(AttrKind)) {
119             // TODO: There could be string attributes without a value, we should
120             // support those, too.
121             Func->addFnAttr(AttrKind);
122             Changed = true;
123           } else
124             errs() << "Cannot add " << SplitPair.second
125                    << " as an attribute name.\n";
126         }
127       } else {
128         errs() << "Function in CSV file at line " << It.line_number()
129                << " does not exist.\n";
130         // TODO: `report_fatal_error at end of pass for missing functions.
131         continue;
132       }
133     }
134   }
135   if (hasForceAttributes()) {
136     for (Function &F : M.functions())
137       forceAttributes(F);
138     Changed = true;
139   }
140   // Just conservatively invalidate analyses if we've made any changes, this
141   // isn't likely to be important.
142   return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
143 }
144