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 "TestRunner.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/IR/LLVMContext.h"
21 #include "llvm/IR/Verifier.h"
22 #include "llvm/IRReader/IRReader.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/SourceMgr.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include <system_error>
28 #include <vector>
29 
30 using namespace llvm;
31 
32 static cl::OptionCategory Options("llvm-reduce options");
33 
34 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden,
35                           cl::cat(Options));
36 static cl::opt<bool> Version("v", cl::desc("Alias for -version"), cl::Hidden,
37                              cl::cat(Options));
38 
39 static cl::opt<bool>
40     PrintDeltaPasses("print-delta-passes",
41                      cl::desc("Print list of delta passes, passable to "
42                               "--delta-passes as a comma separated list"));
43 
44 static cl::opt<std::string> InputFilename(cl::Positional, cl::Required,
45                                           cl::desc("<input llvm ll/bc file>"),
46                                           cl::cat(Options));
47 
48 static cl::opt<std::string>
49     TestFilename("test", cl::Required,
50                  cl::desc("Name of the interesting-ness test to be run"),
51                  cl::cat(Options));
52 
53 static cl::list<std::string>
54     TestArguments("test-arg", cl::ZeroOrMore,
55                   cl::desc("Arguments passed onto the interesting-ness test"),
56                   cl::cat(Options));
57 
58 static cl::opt<std::string>
59     OutputFilename("output",
60                    cl::desc("Specify the output file. default: reduced.ll"));
61 static cl::alias OutputFileAlias("o", cl::desc("Alias for -output"),
62                                  cl::aliasopt(OutputFilename),
63                                  cl::cat(Options));
64 
65 static cl::opt<bool>
66     ReplaceInput("in-place",
67                  cl::desc("WARNING: This option will replace your input file "
68                           "with the reduced version!"),
69                  cl::cat(Options));
70 
71 // Parses IR into a Module and verifies it
72 static std::unique_ptr<Module> parseInputFile(StringRef Filename,
73                                               LLVMContext &Ctxt) {
74   SMDiagnostic Err;
75   std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
76   if (!Result) {
77     Err.print("llvm-reduce", errs());
78     return Result;
79   }
80 
81   if (verifyModule(*Result, &errs())) {
82     errs() << "Error: " << Filename << " - input module is broken!\n";
83     return std::unique_ptr<Module>();
84   }
85 
86   return Result;
87 }
88 
89 void writeOutput(Module *M, StringRef Message) {
90   if (ReplaceInput) // In-place
91     OutputFilename = InputFilename.c_str();
92   else if (OutputFilename.empty() || OutputFilename == "-")
93     OutputFilename = "reduced.ll";
94 
95   std::error_code EC;
96   raw_fd_ostream Out(OutputFilename, EC);
97   if (EC) {
98     errs() << "Error opening output file: " << EC.message() << "!\n";
99     exit(1);
100   }
101   M->print(Out, /*AnnotationWriter=*/nullptr);
102   errs() << Message << OutputFilename << "\n";
103 }
104 
105 int main(int Argc, char **Argv) {
106   InitLLVM X(Argc, Argv);
107 
108   cl::ParseCommandLineOptions(Argc, Argv, "LLVM automatic testcase reducer.\n");
109 
110   if (PrintDeltaPasses) {
111     printDeltaPasses(errs());
112     return 0;
113   }
114 
115   LLVMContext Context;
116   std::unique_ptr<Module> OriginalProgram =
117       parseInputFile(InputFilename, Context);
118 
119   // Initialize test environment
120   TestRunner Tester(TestFilename, TestArguments);
121   Tester.setProgram(std::move(OriginalProgram));
122 
123   // Try to reduce code
124   runDeltaPasses(Tester);
125 
126   if (!Tester.getProgram()) {
127     errs() << "\nCouldnt reduce input :/\n";
128   } else {
129     // Print reduced file to STDOUT
130     if (OutputFilename == "-")
131       Tester.getProgram()->print(outs(), nullptr);
132     else
133       writeOutput(Tester.getProgram(), "\nDone reducing! Reduced testcase: ");
134   }
135 
136   return 0;
137 }
138