1 | //===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// |
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 | // This program tries to reduce an IR test case for a given interesting-ness |
10 | // test. It runs multiple delta debugging passes in order to minimize the input |
11 | // file. It's worth noting that this is a part of the bugpoint redesign |
12 | // proposal, and thus a *temporary* tool that will eventually be integrated |
13 | // into the bugpoint tool itself. |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "DeltaManager.h" |
18 | #include "ReducerWorkItem.h" |
19 | #include "TestRunner.h" |
20 | #include "llvm/Bitcode/BitcodeReader.h" |
21 | #include "llvm/CodeGen/CommandFlags.h" |
22 | #include "llvm/Support/CommandLine.h" |
23 | #include "llvm/Support/InitLLVM.h" |
24 | #include "llvm/Support/Process.h" |
25 | #include "llvm/Support/WithColor.h" |
26 | #include "llvm/Support/raw_ostream.h" |
27 | |
28 | #ifdef _WIN32 |
29 | #include <windows.h> |
30 | #endif |
31 | |
32 | using namespace llvm; |
33 | |
34 | cl::OptionCategory LLVMReduceOptions("llvm-reduce options" ); |
35 | |
36 | static cl::opt<bool> Help("h" , cl::desc("Alias for -help" ), cl::Hidden, |
37 | cl::cat(LLVMReduceOptions)); |
38 | static cl::opt<bool> Version("v" , cl::desc("Alias for -version" ), cl::Hidden, |
39 | cl::cat(LLVMReduceOptions)); |
40 | |
41 | static cl::opt<bool> PreserveDebugEnvironment( |
42 | "preserve-debug-environment" , |
43 | cl::desc("Don't disable features used for crash " |
44 | "debugging (crash reports, llvm-symbolizer and core dumps)" ), |
45 | cl::cat(LLVMReduceOptions)); |
46 | |
47 | static cl::opt<bool> |
48 | PrintDeltaPasses("print-delta-passes" , |
49 | cl::desc("Print list of delta passes, passable to " |
50 | "--delta-passes as a comma separated list" ), |
51 | cl::cat(LLVMReduceOptions)); |
52 | |
53 | static cl::opt<std::string> InputFilename(cl::Positional, |
54 | cl::desc("<input llvm ll/bc file>" ), |
55 | cl::cat(LLVMReduceOptions)); |
56 | |
57 | static cl::opt<std::string> |
58 | TestFilename("test" , |
59 | cl::desc("Name of the interesting-ness test to be run" ), |
60 | cl::cat(LLVMReduceOptions)); |
61 | |
62 | static cl::list<std::string> |
63 | TestArguments("test-arg" , |
64 | cl::desc("Arguments passed onto the interesting-ness test" ), |
65 | cl::cat(LLVMReduceOptions)); |
66 | |
67 | static cl::opt<std::string> OutputFilename( |
68 | "output" , |
69 | cl::desc("Specify the output file. default: reduced.ll|.bc|.mir" )); |
70 | static cl::alias OutputFileAlias("o" , cl::desc("Alias for -output" ), |
71 | cl::aliasopt(OutputFilename), |
72 | cl::cat(LLVMReduceOptions)); |
73 | |
74 | static cl::opt<bool> |
75 | ReplaceInput("in-place" , |
76 | cl::desc("WARNING: This option will replace your input file " |
77 | "with the reduced version!" ), |
78 | cl::cat(LLVMReduceOptions)); |
79 | |
80 | enum class InputLanguages { None, IR, MIR }; |
81 | |
82 | static cl::opt<InputLanguages> |
83 | InputLanguage("x" , cl::ValueOptional, |
84 | cl::desc("Input language ('ir' or 'mir')" ), |
85 | cl::init(Val: InputLanguages::None), |
86 | cl::values(clEnumValN(InputLanguages::IR, "ir" , "" ), |
87 | clEnumValN(InputLanguages::MIR, "mir" , "" )), |
88 | cl::cat(LLVMReduceOptions)); |
89 | |
90 | static cl::opt<bool> ForceOutputBitcode( |
91 | "output-bitcode" , |
92 | cl::desc("Emit final result as bitcode instead of text IR" ), cl::Hidden, |
93 | cl::cat(LLVMReduceOptions)); |
94 | |
95 | static cl::opt<int> |
96 | MaxPassIterations("max-pass-iterations" , |
97 | cl::desc("Maximum number of times to run the full set " |
98 | "of delta passes (default=5)" ), |
99 | cl::init(Val: 5), cl::cat(LLVMReduceOptions)); |
100 | |
101 | static codegen::RegisterCodeGenFlags CGF; |
102 | |
103 | /// Turn off crash debugging features |
104 | /// |
105 | /// Crash is expected, so disable crash reports and symbolization to reduce |
106 | /// output clutter and avoid potentially slow symbolization. |
107 | static void disableEnvironmentDebugFeatures() { |
108 | sys::Process::PreventCoreFiles(); |
109 | |
110 | // TODO: Copied from not. Should have a wrapper around setenv. |
111 | #ifdef _WIN32 |
112 | SetEnvironmentVariableA("LLVM_DISABLE_CRASH_REPORT" , "1" ); |
113 | SetEnvironmentVariableA("LLVM_DISABLE_SYMBOLIZATION" , "1" ); |
114 | #else |
115 | setenv(name: "LLVM_DISABLE_CRASH_REPORT" , value: "1" , /*overwrite=*/replace: 1); |
116 | setenv(name: "LLVM_DISABLE_SYMBOLIZATION" , value: "1" , /*overwrite=*/replace: 1); |
117 | #endif |
118 | } |
119 | |
120 | static std::pair<StringRef, bool> determineOutputType(bool IsMIR, |
121 | bool InputIsBitcode) { |
122 | bool OutputBitcode = ForceOutputBitcode || InputIsBitcode; |
123 | |
124 | if (ReplaceInput) { // In-place |
125 | OutputFilename = InputFilename.c_str(); |
126 | } else if (OutputFilename.empty()) { |
127 | // Default to producing bitcode if the input was bitcode, if not explicitly |
128 | // requested. |
129 | |
130 | OutputFilename = |
131 | IsMIR ? "reduced.mir" : (OutputBitcode ? "reduced.bc" : "reduced.ll" ); |
132 | } |
133 | |
134 | return {OutputFilename, OutputBitcode}; |
135 | } |
136 | |
137 | int main(int Argc, char **Argv) { |
138 | InitLLVM X(Argc, Argv); |
139 | const StringRef ToolName(Argv[0]); |
140 | |
141 | cl::HideUnrelatedOptions(Categories: {&LLVMReduceOptions, &getColorCategory()}); |
142 | cl::ParseCommandLineOptions( |
143 | argc: Argc, argv: Argv, |
144 | Overview: "LLVM automatic testcase reducer.\n" |
145 | "See https://llvm.org/docs/CommandGuide/llvm-reduce.html for more " |
146 | "information.\n" ); |
147 | |
148 | if (Argc == 1) { |
149 | cl::PrintHelpMessage(); |
150 | return 0; |
151 | } |
152 | |
153 | if (PrintDeltaPasses) { |
154 | printDeltaPasses(OS&: outs()); |
155 | return 0; |
156 | } |
157 | |
158 | bool ReduceModeMIR = false; |
159 | if (InputLanguage != InputLanguages::None) { |
160 | if (InputLanguage == InputLanguages::MIR) |
161 | ReduceModeMIR = true; |
162 | } else if (StringRef(InputFilename).ends_with(Suffix: ".mir" )) { |
163 | ReduceModeMIR = true; |
164 | } |
165 | |
166 | if (InputFilename.empty()) { |
167 | WithColor::error(OS&: errs(), Prefix: ToolName) |
168 | << "reduction testcase positional argument must be specified\n" ; |
169 | return 1; |
170 | } |
171 | |
172 | if (TestFilename.empty()) { |
173 | WithColor::error(OS&: errs(), Prefix: ToolName) << "--test option must be specified\n" ; |
174 | return 1; |
175 | } |
176 | |
177 | if (!PreserveDebugEnvironment) |
178 | disableEnvironmentDebugFeatures(); |
179 | |
180 | LLVMContext Context; |
181 | std::unique_ptr<TargetMachine> TM; |
182 | |
183 | auto [OriginalProgram, InputIsBitcode] = |
184 | parseReducerWorkItem(ToolName, Filename: InputFilename, Ctxt&: Context, TM, IsMIR: ReduceModeMIR); |
185 | if (!OriginalProgram) { |
186 | return 1; |
187 | } |
188 | |
189 | StringRef OutputFilename; |
190 | bool OutputBitcode; |
191 | std::tie(args&: OutputFilename, args&: OutputBitcode) = |
192 | determineOutputType(IsMIR: ReduceModeMIR, InputIsBitcode); |
193 | |
194 | // Initialize test environment |
195 | TestRunner Tester(TestFilename, TestArguments, std::move(OriginalProgram), |
196 | std::move(TM), ToolName, OutputFilename, InputIsBitcode, |
197 | OutputBitcode); |
198 | |
199 | // This parses and writes out the testcase into a temporary file copy for the |
200 | // test, rather than evaluating the source IR directly. This is for the |
201 | // convenience of lit tests; the stripped out comments may have broken the |
202 | // interestingness checks. |
203 | if (!Tester.getProgram().isReduced(Test: Tester)) { |
204 | errs() << "\nInput isn't interesting! Verify interesting-ness test\n" ; |
205 | return 2; |
206 | } |
207 | |
208 | // Try to reduce code |
209 | runDeltaPasses(Tester, MaxPassIterations); |
210 | |
211 | // Print reduced file to STDOUT |
212 | if (OutputFilename == "-" ) |
213 | Tester.getProgram().print(ROS&: outs(), p: nullptr); |
214 | else |
215 | Tester.writeOutput(Message: "Done reducing! Reduced testcase: " ); |
216 | |
217 | return 0; |
218 | } |
219 | |