| 1 | //===-- reduce-chunk-list.cpp - Reduce a chunks list to its minimal size --===// |
| 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 | // See the llvm-project/llvm/docs/ProgrammersManual.rst to see how to use this |
| 10 | // tool |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "llvm/Support/CommandLine.h" |
| 15 | #include "llvm/Support/IntegerInclusiveInterval.h" |
| 16 | #include "llvm/Support/Program.h" |
| 17 | |
| 18 | using namespace llvm; |
| 19 | |
| 20 | static cl::opt<std::string> ReproductionCmd(cl::Positional, cl::Required); |
| 21 | |
| 22 | static cl::opt<std::string> StartChunks(cl::Positional, cl::Required); |
| 23 | |
| 24 | static cl::opt<bool> Pessimist("pessimist" , cl::init(Val: false)); |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | bool isStillInteresting(ArrayRef<IntegerInclusiveInterval> Chunks) { |
| 29 | IntegerInclusiveIntervalUtils::IntervalList SimpleChunks = |
| 30 | IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Intervals: Chunks); |
| 31 | |
| 32 | std::string ChunkStr; |
| 33 | { |
| 34 | raw_string_ostream OS(ChunkStr); |
| 35 | IntegerInclusiveIntervalUtils::printIntervals(OS, Intervals: SimpleChunks); |
| 36 | } |
| 37 | |
| 38 | errs() << "Checking with: " << ChunkStr << "\n" ; |
| 39 | |
| 40 | std::vector<StringRef> Argv; |
| 41 | Argv.push_back(x: ReproductionCmd); |
| 42 | Argv.push_back(x: ChunkStr); |
| 43 | |
| 44 | std::string ErrMsg; |
| 45 | bool ExecutionFailed; |
| 46 | int Result = sys::ExecuteAndWait(Program: Argv[0], Args: Argv, Env: std::nullopt, Redirects: {}, SecondsToWait: 0, MemoryLimit: 0, |
| 47 | ErrMsg: &ErrMsg, ExecutionFailed: &ExecutionFailed); |
| 48 | if (ExecutionFailed) { |
| 49 | errs() << "failed to execute : " << Argv[0] << " : " << ErrMsg << "\n" ; |
| 50 | exit(status: 1); |
| 51 | } |
| 52 | |
| 53 | bool Res = Result != 0; |
| 54 | if (Res) { |
| 55 | errs() << "SUCCESS : Still Interesting\n" ; |
| 56 | } else { |
| 57 | errs() << "FAILURE : Not Interesting\n" ; |
| 58 | } |
| 59 | return Res; |
| 60 | } |
| 61 | |
| 62 | bool increaseGranularity(IntegerInclusiveIntervalUtils::IntervalList &Chunks) { |
| 63 | errs() << "Increasing granularity\n" ; |
| 64 | IntegerInclusiveIntervalUtils::IntervalList NewChunks; |
| 65 | bool SplitOne = false; |
| 66 | |
| 67 | for (auto &C : Chunks) { |
| 68 | if (C.getBegin() == C.getEnd()) { |
| 69 | NewChunks.push_back(Elt: C); |
| 70 | } else { |
| 71 | int64_t Half = (C.getBegin() + C.getEnd()) / 2; |
| 72 | NewChunks.push_back(Elt: IntegerInclusiveInterval(C.getBegin(), Half)); |
| 73 | NewChunks.push_back(Elt: IntegerInclusiveInterval(Half + 1, C.getEnd())); |
| 74 | SplitOne = true; |
| 75 | } |
| 76 | } |
| 77 | if (SplitOne) { |
| 78 | Chunks = std::move(NewChunks); |
| 79 | } |
| 80 | return SplitOne; |
| 81 | } |
| 82 | |
| 83 | } // namespace |
| 84 | |
| 85 | int main(int argc, char **argv) { |
| 86 | cl::ParseCommandLineOptions(argc, argv); |
| 87 | |
| 88 | auto ExpectedChunks = |
| 89 | IntegerInclusiveIntervalUtils::parseIntervals(IntervalStr: StartChunks, Separator: ','); |
| 90 | if (!ExpectedChunks) { |
| 91 | handleAllErrors(E: ExpectedChunks.takeError(), Handlers: [](const StringError &E) { |
| 92 | errs() << "Error parsing chunks: " << E.getMessage() << "\n" ; |
| 93 | }); |
| 94 | return 1; |
| 95 | } |
| 96 | IntegerInclusiveIntervalUtils::IntervalList CurrChunks = |
| 97 | std::move(*ExpectedChunks); |
| 98 | |
| 99 | auto Program = sys::findProgramByName(Name: ReproductionCmd); |
| 100 | if (!Program) { |
| 101 | errs() << "failed to find command : " << ReproductionCmd << "\n" ; |
| 102 | return 1; |
| 103 | } |
| 104 | ReproductionCmd.setValue(V: Program.get()); |
| 105 | |
| 106 | errs() << "Input Checking:\n" ; |
| 107 | if (!isStillInteresting(Chunks: CurrChunks)) { |
| 108 | errs() << "starting chunks are not interesting\n" ; |
| 109 | return 1; |
| 110 | } |
| 111 | if (CurrChunks.size() == 1) |
| 112 | increaseGranularity(Chunks&: CurrChunks); |
| 113 | if (Pessimist) |
| 114 | while (increaseGranularity(Chunks&: CurrChunks)) |
| 115 | /* empty body */; |
| 116 | while (1) { |
| 117 | for (int Idx = (CurrChunks.size() - 1); Idx >= 0; Idx--) { |
| 118 | if (CurrChunks.size() == 1) |
| 119 | break; |
| 120 | |
| 121 | IntegerInclusiveInterval Testing = CurrChunks[Idx]; |
| 122 | errs() << "Trying to remove : " ; |
| 123 | Testing.print(OS&: errs()); |
| 124 | errs() << "\n" ; |
| 125 | |
| 126 | CurrChunks.erase(CI: CurrChunks.begin() + Idx); |
| 127 | |
| 128 | if (!isStillInteresting(Chunks: CurrChunks)) |
| 129 | CurrChunks.insert(I: CurrChunks.begin() + Idx, Elt: Testing); |
| 130 | } |
| 131 | bool HasSplit = increaseGranularity(Chunks&: CurrChunks); |
| 132 | if (!HasSplit) |
| 133 | break; |
| 134 | } |
| 135 | |
| 136 | errs() << "Minimal Chunks = " ; |
| 137 | IntegerInclusiveIntervalUtils::printIntervals( |
| 138 | OS&: llvm::errs(), |
| 139 | Intervals: IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Intervals: CurrChunks)); |
| 140 | errs() << "\n" ; |
| 141 | } |
| 142 | |