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