xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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       report_fatal_error("Cannot open CSV file.");
96     StringRef Buffer = BufferOrError.get()->getBuffer();
97     auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer);
98     line_iterator It(*MemoryBuffer);
99     for (; !It.is_at_end(); ++It) {
100       auto SplitPair = It->split(',');
101       if (SplitPair.second.empty())
102         continue;
103       Function *Func = M.getFunction(SplitPair.first);
104       if (Func) {
105         if (Func->isDeclaration())
106           continue;
107         auto SecondSplitPair = SplitPair.second.split('=');
108         if (!SecondSplitPair.second.empty()) {
109           Func->addFnAttr(SecondSplitPair.first, SecondSplitPair.second);
110           Changed = true;
111         } else {
112           auto AttrKind = Attribute::getAttrKindFromName(SplitPair.second);
113           if (AttrKind != Attribute::None &&
114               Attribute::canUseAsFnAttr(AttrKind)) {
115             // TODO: There could be string attributes without a value, we should
116             // support those, too.
117             Func->addFnAttr(AttrKind);
118             Changed = true;
119           } else
120             errs() << "Cannot add " << SplitPair.second
121                    << " as an attribute name.\n";
122         }
123       } else {
124         errs() << "Function in CSV file at line " << It.line_number()
125                << " does not exist.\n";
126         // TODO: `report_fatal_error at end of pass for missing functions.
127         continue;
128       }
129     }
130   }
131   if (hasForceAttributes()) {
132     for (Function &F : M.functions())
133       forceAttributes(F);
134     Changed = true;
135   }
136   // Just conservatively invalidate analyses if we've made any changes, this
137   // isn't likely to be important.
138   return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
139 }
140