//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This program tries to reduce an IR test case for a given interesting-ness // test. It runs multiple delta debugging passes in order to minimize the input // file. It's worth noting that this is a part of the bugpoint redesign // proposal, and thus a *temporary* tool that will eventually be integrated // into the bugpoint tool itself. // //===----------------------------------------------------------------------===// #include "DeltaManager.h" #include "ReducerWorkItem.h" #include "TestRunner.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; cl::OptionCategory LLVMReduceOptions("llvm-reduce options"); static cl::opt Help("h", cl::desc("Alias for -help"), cl::Hidden, cl::cat(LLVMReduceOptions)); static cl::opt Version("v", cl::desc("Alias for -version"), cl::Hidden, cl::cat(LLVMReduceOptions)); static cl::opt PrintDeltaPasses("print-delta-passes", cl::desc("Print list of delta passes, passable to " "--delta-passes as a comma separated list"), cl::cat(LLVMReduceOptions)); static cl::opt InputFilename(cl::Positional, cl::Required, cl::desc(""), cl::cat(LLVMReduceOptions)); static cl::opt TestFilename("test", cl::Required, cl::desc("Name of the interesting-ness test to be run"), cl::cat(LLVMReduceOptions)); static cl::list TestArguments("test-arg", cl::desc("Arguments passed onto the interesting-ness test"), cl::cat(LLVMReduceOptions)); static cl::opt OutputFilename( "output", cl::desc("Specify the output file. default: reduced.ll|.bc|.mir")); static cl::alias OutputFileAlias("o", cl::desc("Alias for -output"), cl::aliasopt(OutputFilename), cl::cat(LLVMReduceOptions)); static cl::opt ReplaceInput("in-place", cl::desc("WARNING: This option will replace your input file " "with the reduced version!"), cl::cat(LLVMReduceOptions)); enum class InputLanguages { None, IR, MIR }; static cl::opt InputLanguage("x", cl::ValueOptional, cl::desc("Input language ('ir' or 'mir')"), cl::init(InputLanguages::None), cl::values(clEnumValN(InputLanguages::IR, "ir", ""), clEnumValN(InputLanguages::MIR, "mir", "")), cl::cat(LLVMReduceOptions)); static cl::opt ForceOutputBitcode( "output-bitcode", cl::desc("Emit final result as bitcode instead of text IR"), cl::Hidden, cl::cat(LLVMReduceOptions)); static cl::opt MaxPassIterations("max-pass-iterations", cl::desc("Maximum number of times to run the full set " "of delta passes (default=5)"), cl::init(5), cl::cat(LLVMReduceOptions)); static codegen::RegisterCodeGenFlags CGF; bool isReduced(ReducerWorkItem &M, TestRunner &Test); static std::pair determineOutputType(bool IsMIR, bool InputIsBitcode) { bool OutputBitcode = ForceOutputBitcode || InputIsBitcode; if (ReplaceInput) { // In-place OutputFilename = InputFilename.c_str(); } else if (OutputFilename.empty()) { // Default to producing bitcode if the input was bitcode, if not explicitly // requested. OutputFilename = IsMIR ? "reduced.mir" : (OutputBitcode ? "reduced.bc" : "reduced.ll"); } return {OutputFilename, OutputBitcode}; } void readBitcode(ReducerWorkItem &M, MemoryBufferRef Data, LLVMContext &Ctx, const char *ToolName) { Expected IF = llvm::getBitcodeFileContents(Data); if (!IF) { WithColor::error(errs(), ToolName) << IF.takeError(); exit(1); } BitcodeModule BM = IF->Mods[0]; Expected LI = BM.getLTOInfo(); Expected> MOrErr = BM.parseModule(Ctx); if (!LI || !MOrErr) { WithColor::error(errs(), ToolName) << IF.takeError(); exit(1); } M.LTOInfo = std::make_unique(*LI); M.M = std::move(MOrErr.get()); } int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); cl::HideUnrelatedOptions({&LLVMReduceOptions, &getColorCategory()}); cl::ParseCommandLineOptions(Argc, Argv, "LLVM automatic testcase reducer.\n"); bool ReduceModeMIR = false; if (InputLanguage != InputLanguages::None) { if (InputLanguage == InputLanguages::MIR) ReduceModeMIR = true; } else if (StringRef(InputFilename).endswith(".mir")) { ReduceModeMIR = true; } if (PrintDeltaPasses) { printDeltaPasses(errs()); return 0; } LLVMContext Context; std::unique_ptr TM; auto [OriginalProgram, InputIsBitcode] = parseReducerWorkItem(Argv[0], InputFilename, Context, TM, ReduceModeMIR); if (!OriginalProgram) { return 1; } StringRef OutputFilename; bool OutputBitcode; std::tie(OutputFilename, OutputBitcode) = determineOutputType(ReduceModeMIR, InputIsBitcode); // Initialize test environment TestRunner Tester(TestFilename, TestArguments, std::move(OriginalProgram), std::move(TM), Argv[0], OutputFilename, OutputBitcode); // This parses and writes out the testcase into a temporary file copy for the // test, rather than evaluating the source IR directly. This is for the // convenience of lit tests; the stripped out comments may have broken the // interestingness checks. if (!isReduced(Tester.getProgram(), Tester)) { errs() << "\nInput isn't interesting! Verify interesting-ness test\n"; return 1; } // Try to reduce code runDeltaPasses(Tester, MaxPassIterations); // Print reduced file to STDOUT if (OutputFilename == "-") Tester.getProgram().print(outs(), nullptr); else Tester.writeOutput("Done reducing! Reduced testcase: "); return 0; }