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