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(C: ':')) { |
55 | auto KV = StringRef(S).split(Separator: ':'); |
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(AttrName: 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(Filename: CSVFilePath); |
94 | if (!BufferOrError) |
95 | report_fatal_error(reason: "Cannot open CSV file." ); |
96 | StringRef Buffer = BufferOrError.get()->getBuffer(); |
97 | auto MemoryBuffer = MemoryBuffer::getMemBuffer(InputData: Buffer); |
98 | line_iterator It(*MemoryBuffer); |
99 | for (; !It.is_at_end(); ++It) { |
100 | auto SplitPair = It->split(Separator: ','); |
101 | if (SplitPair.second.empty()) |
102 | continue; |
103 | Function *Func = M.getFunction(Name: SplitPair.first); |
104 | if (Func) { |
105 | if (Func->isDeclaration()) |
106 | continue; |
107 | auto SecondSplitPair = SplitPair.second.split(Separator: '='); |
108 | if (!SecondSplitPair.second.empty()) { |
109 | Func->addFnAttr(Kind: SecondSplitPair.first, Val: SecondSplitPair.second); |
110 | Changed = true; |
111 | } else { |
112 | auto AttrKind = Attribute::getAttrKindFromName(AttrName: SplitPair.second); |
113 | if (AttrKind != Attribute::None && |
114 | Attribute::canUseAsFnAttr(Kind: AttrKind)) { |
115 | // TODO: There could be string attributes without a value, we should |
116 | // support those, too. |
117 | Func->addFnAttr(Kind: 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 | |