From 4b26d6e0b9ce8ab622631ae70e6bd8e39f666ec8 Mon Sep 17 00:00:00 2001 From: Tyker Date: Mon, 20 May 2024 20:06:48 +0200 Subject: [PATCH] [DebugCounter] Add support for non-continous ranges. --- llvm/docs/ProgrammersManual.rst | 38 +++- llvm/include/llvm/Support/DebugCounter.h | 88 ++++---- llvm/lib/Support/DebugCounter.cpp | 194 +++++++++++++----- llvm/lib/Transforms/Scalar/NewGVN.cpp | 6 +- .../ValueTracking/assume-queries-counter.ll | 6 +- .../AArch64/GlobalISel/counter-fallback.ll | 4 +- llvm/test/CodeGen/AMDGPU/waitcnt-debug.mir | 8 +- llvm/test/CodeGen/X86/dag-combine-counter.ll | 2 +- .../Other/X86/debugcounter-divrempairs.ll | 2 +- .../debugcounter-partiallyinlinelibcalls.ll | 2 +- llvm/test/Other/debugcounter-dce.ll | 2 +- llvm/test/Other/debugcounter-earlycse.ll | 2 +- llvm/test/Other/debugcounter-newgvn.ll | 2 +- llvm/test/Other/debugcounter-predicateinfo.ll | 2 +- llvm/test/Other/print-debug-counter.ll | 10 +- .../DeadStoreElimination/debug-counter.ll | 8 +- .../Transforms/Util/assume-builder-counter.ll | 6 +- llvm/tools/reduce-chunk-list/CMakeLists.txt | 9 + .../reduce-chunk-list/reduce-chunk-list.cpp | 147 +++++++++++++ llvm/unittests/Support/DebugCounterTest.cpp | 28 +-- 20 files changed, 426 insertions(+), 140 deletions(-) create mode 100644 llvm/tools/reduce-chunk-list/CMakeLists.txt create mode 100644 llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst index 491e6b1dd2498..f4a16c43d4499 100644 --- a/llvm/docs/ProgrammersManual.rst +++ b/llvm/docs/ProgrammersManual.rst @@ -1362,16 +1362,14 @@ Whatever code you want that control, use ``DebugCounter::shouldExecute`` to cont if (DebugCounter::shouldExecute(DeleteAnInstruction)) I->eraseFromParent(); -That's all you have to do. Now, using opt, you can control when this code triggers using -the '``--debug-counter``' option. There are two counters provided, ``skip`` and ``count``. -``skip`` is the number of times to skip execution of the codepath. ``count`` is the number -of times, once we are done skipping, to execute the codepath. +That's all you have to do. Now, using opt, you can control when this code triggers using +the '``--debug-counter``' Options.To specify when to execute the codepath. .. code-block:: none - $ opt --debug-counter=passname-delete-instruction-skip=1,passname-delete-instruction-count=2 -passname + $ opt --debug-counter=passname-delete-instruction=2-3 -passname -This will skip the above code the first time we hit it, then execute it twice, then skip the rest of the executions. +This will skip the above code the first two times we hit it, then execute it 2 times, then skip the rest of the executions. So if executed on the following code: @@ -1385,8 +1383,32 @@ So if executed on the following code: It would delete number ``%2`` and ``%3``. A utility is provided in `utils/bisect-skip-count` to binary search -skip and count arguments. It can be used to automatically minimize the -skip and count for a debug-counter variable. +the begin and end of the range argument. It can be used to automatically minimize the +range for a debug-counter variable. + +A more general utility is provided in `llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp` to minimize debug counter chunks lists. + +How to use reduce-chunk-list: +First, Figure out the number of calls to the debug counter you want to minimize. +To do so, run the compilation command causing you want to minimize with `-print-debug-counter` adding a `-mllvm` if needed. +Than find the line with the counter of interest. it should look like: +.. code-block:: none + + my-counter : {5678,empty} + +The number of calls to `my-counter` is 5678 + +Than Find the minimum set of chunks that is interesting, with `reduce-chunk-list`. +Build a reproducer script like: +.. code-block:: bash + + #! /bin/bash + opt -debug-counter=my-counter=$1 + # ... Test result of the command. Failure of the script is considered interesting + +Than run `reduce-chunk-list my-script.sh 0-5678 2>&1 | tee dump_bisect` +This command may take some time. +but when it is done, it will print the result like: `Minimal Chunks = 0:1:5:11-12:33-34` .. _ViewGraph: diff --git a/llvm/include/llvm/Support/DebugCounter.h b/llvm/include/llvm/Support/DebugCounter.h index 9fa4620ade3c8..e4345e5739e99 100644 --- a/llvm/include/llvm/Support/DebugCounter.h +++ b/llvm/include/llvm/Support/DebugCounter.h @@ -17,14 +17,13 @@ /// debug. That is where debug counting steps in. You can instrument the pass /// with a debug counter before it does a certain thing, and depending on the /// counts, it will either execute that thing or not. The debug counter itself -/// consists of a skip and a count. Skip is the number of times shouldExecute -/// needs to be called before it returns true. Count is the number of times to -/// return true once Skip is 0. So a skip=47, count=2 ,would skip the first 47 -/// executions by returning false from shouldExecute, then execute twice, and -/// then return false again. -/// Note that a counter set to a negative number will always execute. -/// For a concrete example, during predicateinfo creation, the renaming pass -/// replaces each use with a renamed use. +/// consists of a list of chunks (inclusive numeric ranges). `shouldExecute` +/// returns true iff the list is empty or the current count is in one of the +/// chunks. +/// +/// Note that a counter set to a negative number will always execute. For a +/// concrete example, during predicateinfo creation, the renaming pass replaces +/// each use with a renamed use. //// /// If I use DEBUG_COUNTER to create a counter called "predicateinfo", and /// variable name RenameCounter, and then instrument this renaming with a debug @@ -34,15 +33,16 @@ /// /// /// Now I can, from the command line, make it rename or not rename certain uses -/// by setting the skip and count. +/// by setting the chunk list. /// So for example -/// bin/opt -debug-counter=predicateinfo-skip=47,predicateinfo-count=1 +/// bin/opt -debug-counter=predicateinfo=47 /// will skip renaming the first 47 uses, then rename one, then skip the rest. //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_DEBUGCOUNTER_H #define LLVM_SUPPORT_DEBUGCOUNTER_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/UniqueVector.h" @@ -55,6 +55,19 @@ class raw_ostream; class DebugCounter { public: + struct Chunk { + int64_t Begin; + int64_t End; + void print(llvm::raw_ostream &OS); + bool contains(int64_t Idx) { return Idx >= Begin && Idx <= End; } + }; + + static void printChunks(raw_ostream &OS, ArrayRef); + + /// Return true on parsing error and print the error message on the + /// llvm::errs() + static bool parseChunks(StringRef Str, SmallVector &Res); + /// Returns a reference to the singleton instance. static DebugCounter &instance(); @@ -69,29 +82,12 @@ class DebugCounter { static unsigned registerCounter(StringRef Name, StringRef Desc) { return instance().addCounter(std::string(Name), std::string(Desc)); } + static bool shouldExecuteImpl(unsigned CounterName); + inline static bool shouldExecute(unsigned CounterName) { if (!isCountingEnabled()) return true; - - auto &Us = instance(); - auto Result = Us.Counters.find(CounterName); - if (Result != Us.Counters.end()) { - auto &CounterInfo = Result->second; - ++CounterInfo.Count; - - // We only execute while the Skip is not smaller than Count, - // and the StopAfter + Skip is larger than Count. - // Negative counters always execute. - if (CounterInfo.Skip < 0) - return true; - if (CounterInfo.Skip >= CounterInfo.Count) - return false; - if (CounterInfo.StopAfter < 0) - return true; - return CounterInfo.StopAfter + CounterInfo.Skip >= CounterInfo.Count; - } - // Didn't find the counter, should we warn? - return true; + return shouldExecuteImpl(CounterName); } // Return true if a given counter had values set (either programatically or on @@ -101,18 +97,25 @@ class DebugCounter { return instance().Counters[ID].IsSet; } - // Return the Count for a counter. This only works for set counters. - static int64_t getCounterValue(unsigned ID) { + struct CounterState { + int64_t Count; + uint64_t ChunkIdx; + }; + + // Return the state of a counter. This only works for set counters. + static CounterState getCounterState(unsigned ID) { auto &Us = instance(); auto Result = Us.Counters.find(ID); assert(Result != Us.Counters.end() && "Asking about a non-set counter"); - return Result->second.Count; + return {Result->second.Count, Result->second.CurrChunkIdx}; } - // Set a registered counter to a given Count value. - static void setCounterValue(unsigned ID, int64_t Count) { + // Set a registered counter to a given state. + static void setCounterState(unsigned ID, CounterState State) { auto &Us = instance(); - Us.Counters[ID].Count = Count; + auto &Counter = Us.Counters[ID]; + Counter.Count = State.Count; + Counter.CurrChunkIdx = State.ChunkIdx; } // Dump or print the current counter set into llvm::dbgs(). @@ -152,11 +155,11 @@ class DebugCounter { #ifdef NDEBUG return false; #else - return instance().Enabled; + return instance().Enabled || instance().ShouldPrintCounter; #endif } -private: +protected: unsigned addCounter(const std::string &Name, const std::string &Desc) { unsigned Result = RegisteredCounters.insert(Name); Counters[Result] = {}; @@ -166,17 +169,22 @@ class DebugCounter { // Struct to store counter info. struct CounterInfo { int64_t Count = 0; - int64_t Skip = 0; - int64_t StopAfter = -1; + uint64_t CurrChunkIdx = 0; bool IsSet = false; std::string Desc; + SmallVector Chunks; }; + DenseMap Counters; CounterVector RegisteredCounters; // Whether we should do DebugCounting at all. DebugCounters aren't // thread-safe, so this should always be false in multithreaded scenarios. bool Enabled = false; + + bool ShouldPrintCounter = false; + + bool BreakOnLast = false; }; #define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \ diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp index 502665d2a8348..a5d8a704bdd21 100644 --- a/llvm/lib/Support/DebugCounter.cpp +++ b/llvm/lib/Support/DebugCounter.cpp @@ -7,6 +7,83 @@ using namespace llvm; +namespace llvm { + +void DebugCounter::Chunk::print(llvm::raw_ostream &OS) { + if (Begin == End) + OS << Begin; + else + OS << Begin << "-" << End; +} + +void DebugCounter::printChunks(raw_ostream &OS, ArrayRef Chunks) { + if (Chunks.empty()) { + OS << "empty"; + } else { + bool IsFirst = true; + for (auto E : Chunks) { + if (!IsFirst) + OS << ':'; + else + IsFirst = false; + E.print(OS); + } + } +} + +bool DebugCounter::parseChunks(StringRef Str, SmallVector &Chunks) { + StringRef Remaining = Str; + + auto ConsumeInt = [&]() -> int64_t { + StringRef Number = + Remaining.take_until([](char c) { return c < '0' || c > '9'; }); + int64_t Res; + if (Number.getAsInteger(10, Res)) { + errs() << "Failed to parse int at : " << Remaining << "\n"; + return -1; + } + Remaining = Remaining.drop_front(Number.size()); + return Res; + }; + + while (1) { + int64_t Num = ConsumeInt(); + if (Num == -1) + return true; + if (!Chunks.empty() && Num <= Chunks[Chunks.size() - 1].End) { + errs() << "Expected Chunks to be in increasing order " << Num + << " <= " << Chunks[Chunks.size() - 1].End << "\n"; + return true; + } + if (Remaining.starts_with("-")) { + Remaining = Remaining.drop_front(); + int64_t Num2 = ConsumeInt(); + if (Num2 == -1) + return true; + if (Num >= Num2) { + errs() << "Expected " << Num << " < " << Num2 << " in " << Num << "-" + << Num2 << "\n"; + return true; + } + + Chunks.push_back({Num, Num2}); + } else { + Chunks.push_back({Num, Num}); + } + if (Remaining.starts_with(":")) { + Remaining = Remaining.drop_front(); + continue; + } + if (Remaining.empty()) + break; + errs() << "Failed to parse at : " << Remaining; + return true; + } + return false; +} + +} // namespace llvm + namespace { // This class overrides the default list implementation of printing so we // can pretty print the list of debug counter options. This type of @@ -47,15 +124,26 @@ class DebugCounterList : public cl::list { // itself, are owned by a single global instance of the DebugCounterOwner // struct. This makes it easier to control the order in which constructors and // destructors are run. -struct DebugCounterOwner { - DebugCounter DC; +struct DebugCounterOwner : DebugCounter { DebugCounterList DebugCounterOption{ "debug-counter", cl::Hidden, cl::desc("Comma separated list of debug counter skip and count"), - cl::CommaSeparated, cl::location(DC)}; - cl::opt PrintDebugCounter{ - "print-debug-counter", cl::Hidden, cl::init(false), cl::Optional, + cl::CommaSeparated, cl::location(*this)}; + cl::opt PrintDebugCounter{ + "print-debug-counter", + cl::Hidden, + cl::Optional, + cl::location(this->ShouldPrintCounter), + cl::init(false), cl::desc("Print out debug counter info after all counters accumulated")}; + cl::opt BreakOnLastCount{ + "debug-counter-break-on-last", + cl::Hidden, + cl::Optional, + cl::location(this->BreakOnLast), + cl::init(false), + cl::desc("Insert a break point on the last enabled count of a " + "chunks list")}; DebugCounterOwner() { // Our destructor uses the debug stream. By referencing it here, we @@ -65,8 +153,8 @@ struct DebugCounterOwner { // Print information when destroyed, iff command line option is specified. ~DebugCounterOwner() { - if (DC.isCountingEnabled() && PrintDebugCounter) - DC.print(dbgs()); + if (ShouldPrintCounter) + print(dbgs()); } }; @@ -76,7 +164,7 @@ void llvm::initDebugCounterOptions() { (void)DebugCounter::instance(); } DebugCounter &DebugCounter::instance() { static DebugCounterOwner O; - return O.DC; + return O; } // This is called by the command line parser when it sees a value for the @@ -84,52 +172,31 @@ DebugCounter &DebugCounter::instance() { void DebugCounter::push_back(const std::string &Val) { if (Val.empty()) return; - // The strings should come in as counter=value + + // The strings should come in as counter=chunk_list auto CounterPair = StringRef(Val).split('='); if (CounterPair.second.empty()) { errs() << "DebugCounter Error: " << Val << " does not have an = in it\n"; return; } - // Now we have counter=value. - // First, process value. - int64_t CounterVal; - if (CounterPair.second.getAsInteger(0, CounterVal)) { - errs() << "DebugCounter Error: " << CounterPair.second - << " is not a number\n"; + StringRef CounterName = CounterPair.first; + SmallVector Chunks; + + if (parseChunks(CounterPair.second, Chunks)) { return; } - // Now we need to see if this is the skip or the count, remove the suffix, and - // add it to the counter values. - if (CounterPair.first.ends_with("-skip")) { - auto CounterName = CounterPair.first.drop_back(5); - unsigned CounterID = getCounterId(std::string(CounterName)); - if (!CounterID) { - errs() << "DebugCounter Error: " << CounterName - << " is not a registered counter\n"; - return; - } - enableAllCounters(); - CounterInfo &Counter = Counters[CounterID]; - Counter.Skip = CounterVal; - Counter.IsSet = true; - } else if (CounterPair.first.ends_with("-count")) { - auto CounterName = CounterPair.first.drop_back(6); - unsigned CounterID = getCounterId(std::string(CounterName)); - if (!CounterID) { - errs() << "DebugCounter Error: " << CounterName - << " is not a registered counter\n"; - return; - } - enableAllCounters(); - - CounterInfo &Counter = Counters[CounterID]; - Counter.StopAfter = CounterVal; - Counter.IsSet = true; - } else { - errs() << "DebugCounter Error: " << CounterPair.first - << " does not end with -skip or -count\n"; + unsigned CounterID = getCounterId(std::string(CounterName)); + if (!CounterID) { + errs() << "DebugCounter Error: " << CounterName + << " is not a registered counter\n"; + return; } + enableAllCounters(); + + CounterInfo &Counter = Counters[CounterID]; + Counter.IsSet = true; + Counter.Chunks = std::move(Chunks); } void DebugCounter::print(raw_ostream &OS) const { @@ -142,9 +209,42 @@ void DebugCounter::print(raw_ostream &OS) const { for (auto &CounterName : CounterNames) { unsigned CounterID = getCounterId(std::string(CounterName)); OS << left_justify(RegisteredCounters[CounterID], 32) << ": {" - << Us.Counters[CounterID].Count << "," << Us.Counters[CounterID].Skip - << "," << Us.Counters[CounterID].StopAfter << "}\n"; + << Us.Counters[CounterID].Count << ","; + printChunks(OS, Us.Counters[CounterID].Chunks); + OS << "}\n"; + } +} + +bool DebugCounter::shouldExecuteImpl(unsigned CounterName) { + auto &Us = instance(); + auto Result = Us.Counters.find(CounterName); + if (Result != Us.Counters.end()) { + auto &CounterInfo = Result->second; + int64_t CurrCount = CounterInfo.Count++; + uint64_t CurrIdx = CounterInfo.CurrChunkIdx; + + if (CounterInfo.Chunks.empty()) + return true; + if (CurrIdx >= CounterInfo.Chunks.size()) + return false; + + bool Res = CounterInfo.Chunks[CurrIdx].contains(CurrCount); + if (Us.BreakOnLast && CurrIdx == (CounterInfo.Chunks.size() - 1) && + CurrCount == CounterInfo.Chunks[CurrIdx].End) { + LLVM_BUILTIN_DEBUGTRAP; + } + if (CurrCount > CounterInfo.Chunks[CurrIdx].End) { + CounterInfo.CurrChunkIdx++; + + /// Handle consecutive blocks. + if (CounterInfo.CurrChunkIdx < CounterInfo.Chunks.size() && + CurrCount == CounterInfo.Chunks[CounterInfo.CurrChunkIdx].Begin) + return true; + } + return Res; } + // Didn't find the counter, should we warn? + return true; } LLVM_DUMP_METHOD void DebugCounter::dump() const { diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp index 056be8629b961..40a6a89d23c2c 100644 --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -892,7 +892,7 @@ class NewGVN { // Debug counter info. When verifying, we have to reset the value numbering // debug counter to the same state it started in to get the same results. - int64_t StartingVNCounter = 0; + DebugCounter::CounterState StartingVNCounter; }; } // end anonymous namespace @@ -3278,7 +3278,7 @@ void NewGVN::verifyIterationSettled(Function &F) { #ifndef NDEBUG LLVM_DEBUG(dbgs() << "Beginning iteration verification\n"); if (DebugCounter::isCounterSet(VNCounter)) - DebugCounter::setCounterValue(VNCounter, StartingVNCounter); + DebugCounter::setCounterState(VNCounter, StartingVNCounter); // Note that we have to store the actual classes, as we may change existing // classes during iteration. This is because our memory iteration propagation @@ -3423,7 +3423,7 @@ void NewGVN::iterateTouchedInstructions() { // This is the main transformation entry point. bool NewGVN::runGVN() { if (DebugCounter::isCounterSet(VNCounter)) - StartingVNCounter = DebugCounter::getCounterValue(VNCounter); + StartingVNCounter = DebugCounter::getCounterState(VNCounter); bool Changed = false; NumFuncArgs = F.arg_size(); MSSAWalker = MSSA->getWalker(); diff --git a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll index 3e93b5d244421..a53b90b7b448a 100644 --- a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll +++ b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll @@ -1,9 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; REQUIRES: asserts -; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter-skip=0,assume-queries-counter-count=1 -S | FileCheck %s --check-prefixes=COUNTER1 -; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter-skip=1,assume-queries-counter-count=2 -S | FileCheck %s --check-prefixes=COUNTER2 -; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter-skip=2,assume-queries-counter-count=5 -S | FileCheck %s --check-prefixes=COUNTER3 +; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=0 -S | FileCheck %s --check-prefixes=COUNTER1 +; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=1-2 -S | FileCheck %s --check-prefixes=COUNTER2 +; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=2-6 -S | FileCheck %s --check-prefixes=COUNTER3 declare i1 @get_val() declare void @llvm.assume(i1) diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/counter-fallback.ll b/llvm/test/CodeGen/AArch64/GlobalISel/counter-fallback.ll index 9a39b5a2ad7a9..72c8103de875d 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/counter-fallback.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/counter-fallback.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 -; RUN: llc -mtriple=aarch64-- -global-isel -global-isel-abort=0 -debug-counter=globalisel-count=1 %s -o - 2>/dev/null | FileCheck %s -; RUN: llc -mtriple=aarch64-- -global-isel -global-isel-abort=0 -debug-counter=globalisel-count=1 %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG +; RUN: llc -mtriple=aarch64-- -global-isel -global-isel-abort=0 -debug-counter=globalisel=0 %s -o - 2>/dev/null | FileCheck %s +; RUN: llc -mtriple=aarch64-- -global-isel -global-isel-abort=0 -debug-counter=globalisel=0 %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=DEBUG ; REQUIRES: asserts diff --git a/llvm/test/CodeGen/AMDGPU/waitcnt-debug.mir b/llvm/test/CodeGen/AMDGPU/waitcnt-debug.mir index 641a31ab9b0f0..0e3656b498d33 100644 --- a/llvm/test/CodeGen/AMDGPU/waitcnt-debug.mir +++ b/llvm/test/CodeGen/AMDGPU/waitcnt-debug.mir @@ -1,8 +1,8 @@ # REQUIRES: asserts -# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forcelgkm-count=1 -o - %s | FileCheck -check-prefixes=GCN,LGKM %s -# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forceexp-count=2 -o - %s | FileCheck -check-prefixes=GCN,EXP %s -# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forcevm-count=3 -o - %s | FileCheck -check-prefixes=GCN,VM %s -# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -amdgpu-waitcnt-forcezero=1 -debug-counter=si-insert-waitcnts-forcevm-count=2 -o - %s | FileCheck -check-prefixes=GCN,ZERO %s +# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forcelgkm=0 -o - %s | FileCheck -check-prefixes=GCN,LGKM %s +# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forceexp=0-1 -o - %s | FileCheck -check-prefixes=GCN,EXP %s +# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -debug-counter=si-insert-waitcnts-forcevm=0-2 -o - %s | FileCheck -check-prefixes=GCN,VM %s +# RUN: llc -mtriple=amdgcn -verify-machineinstrs -run-pass si-insert-waitcnts -amdgpu-waitcnt-forcezero=1 -debug-counter=si-insert-waitcnts-forcevm=0-1 -o - %s | FileCheck -check-prefixes=GCN,ZERO %s # check that the waitcnt pass options that force insertion of waitcnt instructions are working as expected diff --git a/llvm/test/CodeGen/X86/dag-combine-counter.ll b/llvm/test/CodeGen/X86/dag-combine-counter.ll index 8568db8d840a4..4cc3c71b2328c 100644 --- a/llvm/test/CodeGen/X86/dag-combine-counter.ll +++ b/llvm/test/CodeGen/X86/dag-combine-counter.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 -; RUN: llc -mtriple=x86_64-- -debug-counter=dagcombine-count=6 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-- -debug-counter=dagcombine=0-5 < %s | FileCheck %s ; REQUIRES: asserts diff --git a/llvm/test/Other/X86/debugcounter-divrempairs.ll b/llvm/test/Other/X86/debugcounter-divrempairs.ll index ed4b47a16d212..c196dcd01eca4 100644 --- a/llvm/test/Other/X86/debugcounter-divrempairs.ll +++ b/llvm/test/Other/X86/debugcounter-divrempairs.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt < %s -passes=div-rem-pairs -debug-counter=div-rem-pairs-transform-skip=1,div-rem-pairs-transform-count=1 \ +; RUN: opt < %s -passes=div-rem-pairs -debug-counter=div-rem-pairs-transform=1 \ ; RUN: -S -mtriple=x86_64-unknown-unknown | FileCheck %s ;; Test that, with debug counters on, we only skip the first div-rem-pairs opportunity, optimize one after it, ;; and then ignore all the others. There is 1 optimization opportunity in f1, 2 in f2, and another 1 in f3, diff --git a/llvm/test/Other/X86/debugcounter-partiallyinlinelibcalls.ll b/llvm/test/Other/X86/debugcounter-partiallyinlinelibcalls.ll index 5fdb3c2b80912..8024c05feea3b 100644 --- a/llvm/test/Other/X86/debugcounter-partiallyinlinelibcalls.ll +++ b/llvm/test/Other/X86/debugcounter-partiallyinlinelibcalls.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -S -debug-counter=partially-inline-libcalls-transform-skip=1,partially-inline-libcalls-transform-count=1 \ +; RUN: opt -S -debug-counter=partially-inline-libcalls-transform=1 \ ; RUN: -passes=partially-inline-libcalls -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s ;; Test that, with debug counters on, we will skip the first optimization opportunity, perform next 1, ;; and ignore all the others left. diff --git a/llvm/test/Other/debugcounter-dce.ll b/llvm/test/Other/debugcounter-dce.ll index 75302149e717a..54d929f219aef 100644 --- a/llvm/test/Other/debugcounter-dce.ll +++ b/llvm/test/Other/debugcounter-dce.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -passes=dce -S -debug-counter=dce-transform-skip=1,dce-transform-count=2 < %s | FileCheck %s +; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-2 < %s | FileCheck %s ;; Test that, with debug counters on, we will skip the first DCE opportunity, perform next 2, ;; and ignore all the others left. diff --git a/llvm/test/Other/debugcounter-earlycse.ll b/llvm/test/Other/debugcounter-earlycse.ll index b3e74e05f8db3..d3628c760ca33 100644 --- a/llvm/test/Other/debugcounter-earlycse.ll +++ b/llvm/test/Other/debugcounter-earlycse.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -S -debug-counter=early-cse-skip=1,early-cse-count=1 -passes=early-cse -earlycse-debug-hash < %s 2>&1 | FileCheck %s +; RUN: opt -S -debug-counter=early-cse=1 -passes=early-cse -earlycse-debug-hash < %s 2>&1 | FileCheck %s ;; Test that, with debug counters on, we only optimize the second CSE opportunity. define i32 @test(i32 %a, i32 %b) { ; CHECK-LABEL: @test( diff --git a/llvm/test/Other/debugcounter-newgvn.ll b/llvm/test/Other/debugcounter-newgvn.ll index 6ebca6016d3d8..fba21bd5de962 100644 --- a/llvm/test/Other/debugcounter-newgvn.ll +++ b/llvm/test/Other/debugcounter-newgvn.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; REQUIRES: asserts -; RUN: opt -S -debug-counter=newgvn-vn-skip=1,newgvn-vn-count=2 -passes=newgvn < %s 2>&1 | FileCheck %s +; RUN: opt -S -debug-counter=newgvn-vn=1-2 -passes=newgvn < %s 2>&1 | FileCheck %s ;; Test that, with debug counters on, we don't value number the first instruction, only the second and third, ;; which means we do not discover the return is constant. define i32 @vntest() { diff --git a/llvm/test/Other/debugcounter-predicateinfo.ll b/llvm/test/Other/debugcounter-predicateinfo.ll index 1f2b39dc62955..981bd1514f6e9 100644 --- a/llvm/test/Other/debugcounter-predicateinfo.ll +++ b/llvm/test/Other/debugcounter-predicateinfo.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; REQUIRES: asserts -; RUN: opt -debug-counter=predicateinfo-rename-skip=1,predicateinfo-rename-count=1 -passes=print-predicateinfo < %s 2>&1 | FileCheck %s +; RUN: opt -debug-counter=predicateinfo-rename=1 -passes=print-predicateinfo < %s 2>&1 | FileCheck %s ;; Test that, with debug counters on, we don't rename the first info, only the second define fastcc void @barney() { ; CHECK-LABEL: @barney( diff --git a/llvm/test/Other/print-debug-counter.ll b/llvm/test/Other/print-debug-counter.ll index 4b6e42570b315..0bba811f71c6d 100644 --- a/llvm/test/Other/print-debug-counter.ll +++ b/llvm/test/Other/print-debug-counter.ll @@ -1,16 +1,16 @@ ; REQUIRES: asserts -; RUN: opt -S -debug-counter=early-cse-skip=1,early-cse-count=1 -passes=early-cse,newgvn,instcombine -earlycse-debug-hash \ -; RUN: -debug-counter=newgvn-vn-skip=1,newgvn-vn-count=2 \ +; RUN: opt -S -debug-counter=early-cse=1 -passes=early-cse,newgvn,instcombine -earlycse-debug-hash \ +; RUN: -debug-counter=newgvn-vn=1-2 \ ; RUN: -print-debug-counter < %s 2>&1 | FileCheck %s ;; Test debug counter prints correct info in right order. ; CHECK-LABEL: Counters and values: ; CHECK: early-cse -; CHECK-SAME: {4,1,1} +; CHECK-SAME: {4,1} ; CHECK: instcombine-visit -; CHECK-SAME: {13,0,-1} +; CHECK-SAME: {13,empty} ; CHECK: newgvn-vn -; CHECK-SAME: {9,1,2} +; CHECK-SAME: {9,1-2} define i32 @f1(i32 %a, i32 %b) { bb: %add1 = add i32 %a, %b diff --git a/llvm/test/Transforms/DeadStoreElimination/debug-counter.ll b/llvm/test/Transforms/DeadStoreElimination/debug-counter.ll index 691b89d8d4ca9..ffa10e37c76f6 100644 --- a/llvm/test/Transforms/DeadStoreElimination/debug-counter.ll +++ b/llvm/test/Transforms/DeadStoreElimination/debug-counter.ll @@ -3,16 +3,16 @@ ; REQUIRES: asserts ; Eliminates store to %R in the entry block. -; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa-skip=0,dse-memoryssa-count=1 -S | FileCheck --check-prefix=SKIP0-COUNT1 %s +; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa=0 -S | FileCheck --check-prefix=SKIP0-COUNT1 %s ; Eliminates store to %P in the entry block. -; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa-skip=1,dse-memoryssa-count=1 -S | FileCheck --check-prefix=SKIP1-COUNT1 %s +; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa=1 -S | FileCheck --check-prefix=SKIP1-COUNT1 %s ; Eliminates both stores in the entry block. -; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa-skip=0,dse-memoryssa-count=2 -S | FileCheck --check-prefix=SKIP0-COUNT2 %s +; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa=0-1 -S | FileCheck --check-prefix=SKIP0-COUNT2 %s ; Eliminates no stores. -; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa-skip=2,dse-memoryssa-count=1 -S | FileCheck --check-prefix=SKIP2-COUNT1 %s +; RUN: opt < %s -passes=dse -debug-counter=dse-memoryssa=2 -S | FileCheck --check-prefix=SKIP2-COUNT1 %s target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" diff --git a/llvm/test/Transforms/Util/assume-builder-counter.ll b/llvm/test/Transforms/Util/assume-builder-counter.ll index 794a8d0b8443f..c11a69a2c3cd7 100644 --- a/llvm/test/Transforms/Util/assume-builder-counter.ll +++ b/llvm/test/Transforms/Util/assume-builder-counter.ll @@ -1,9 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; REQUIRES: asserts -; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=5,assume-builder-counter-count=1 -S %s | FileCheck %s --check-prefixes=COUNTER1 -; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=1,assume-builder-counter-count=3 -S %s | FileCheck %s --check-prefixes=COUNTER2 -; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=2,assume-builder-counter-count=200 -S %s | FileCheck %s --check-prefixes=COUNTER3 +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter=5 -S %s | FileCheck %s --check-prefixes=COUNTER1 +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter=1-3 -S %s | FileCheck %s --check-prefixes=COUNTER2 +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter=2-202 -S %s | FileCheck %s --check-prefixes=COUNTER3 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/tools/reduce-chunk-list/CMakeLists.txt b/llvm/tools/reduce-chunk-list/CMakeLists.txt new file mode 100644 index 0000000000000..5af76597bffac --- /dev/null +++ b/llvm/tools/reduce-chunk-list/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_tool(reduce-chunk-list + reduce-chunk-list.cpp + + DEPENDS + ) diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp new file mode 100644 index 0000000000000..a819af6e5f1a0 --- /dev/null +++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp @@ -0,0 +1,147 @@ +//===-- reduce-chunk-list.cpp - Reduce a chunks list to its minimal size --===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// See the llvm-project/llvm/docs/ProgrammersManual.rst to see how to use this +// tool +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/Program.h" + +using namespace llvm; + +cl::opt ReproductionCmd(cl::Positional, cl::Required); + +cl::opt StartChunks(cl::Positional, cl::Required); + +cl::opt Pessimist("pessimist", cl::init(false)); + +using Chunk = DebugCounter::Chunk; + +namespace { + +SmallVector simplifyChunksList(ArrayRef Chunks) { + SmallVector Res; + Res.push_back(Chunks.front()); + for (unsigned Idx = 1; Idx < Chunks.size(); Idx++) { + if (Chunks[Idx].Begin == Res.back().End + 1) + Res.back().End = Chunks[Idx].End; + else + Res.push_back(Chunks[Idx]); + } + return Res; +} + +bool isStillInteresting(ArrayRef Chunks) { + SmallVector SimpleChunks = simplifyChunksList(Chunks); + + std::string ChunkStr; + { + raw_string_ostream OS(ChunkStr); + DebugCounter::printChunks(OS, SimpleChunks); + } + + errs() << "Checking with: " << ChunkStr << "\n"; + + std::vector Argv; + Argv.push_back(ReproductionCmd); + Argv.push_back(ChunkStr); + + std::string ErrMsg; + bool ExecutionFailed; + int Result = sys::ExecuteAndWait(Argv[0], Argv, std::nullopt, {}, 0, 0, + &ErrMsg, &ExecutionFailed); + if (ExecutionFailed) { + errs() << "failed to execute : " << Argv[0] << " : " << ErrMsg << "\n"; + exit(1); + } + + bool Res = Result != 0; + if (Res) { + errs() << "SUCCESS : Still Interesting\n"; + } else { + errs() << "FAILURE : Not Interesting\n"; + } + return Res; +} + +bool increaseGranularity(SmallVector &Chunks) { + errs() << "Increasing granularity\n"; + SmallVector NewChunks; + bool SplitOne = false; + + for (auto &C : Chunks) { + if (C.Begin == C.End) { + NewChunks.push_back(C); + } else { + int Half = (C.Begin + C.End) / 2; + NewChunks.push_back({C.Begin, Half}); + NewChunks.push_back({Half + 1, C.End}); + SplitOne = true; + } + } + if (SplitOne) { + Chunks = std::move(NewChunks); + } + return SplitOne; +} + +} // namespace + +int main(int argc, char **argv) { + cl::ParseCommandLineOptions(argc, argv); + + SmallVector CurrChunks; + if (DebugCounter::parseChunks(StartChunks, CurrChunks)) { + return 1; + } + + auto Program = sys::findProgramByName(ReproductionCmd); + if (!Program) { + errs() << "failed to find command : " << ReproductionCmd << "\n"; + return 1; + } + ReproductionCmd.setValue(Program.get()); + + errs() << "Input Checking:\n"; + if (!isStillInteresting(CurrChunks)) { + errs() << "starting chunks are not interesting\n"; + return 1; + } + if (CurrChunks.size() == 1) + increaseGranularity(CurrChunks); + if (Pessimist) + while (increaseGranularity(CurrChunks)) + /* empty body */; + while (1) { + for (int Idx = (CurrChunks.size() - 1); Idx >= 0; Idx--) { + if (CurrChunks.size() == 1) + break; + + Chunk Testing = CurrChunks[Idx]; + errs() << "Trying to remove : "; + Testing.print(errs()); + errs() << "\n"; + + CurrChunks.erase(CurrChunks.begin() + Idx); + + if (!isStillInteresting(CurrChunks)) + CurrChunks.insert(CurrChunks.begin() + Idx, Testing); + } + bool HasSplit = increaseGranularity(CurrChunks); + if (!HasSplit) + break; + } + + errs() << "Minimal Chunks = "; + DebugCounter::printChunks(llvm::errs(), simplifyChunksList(CurrChunks)); + errs() << "\n"; +} diff --git a/llvm/unittests/Support/DebugCounterTest.cpp b/llvm/unittests/Support/DebugCounterTest.cpp index e7345b13cc172..820b1ce5ca0d3 100644 --- a/llvm/unittests/Support/DebugCounterTest.cpp +++ b/llvm/unittests/Support/DebugCounterTest.cpp @@ -13,28 +13,28 @@ using namespace llvm; #ifndef NDEBUG -TEST(DebugCounterTest, CounterCheck) { +TEST(DebugCounterTest, Basic) { DEBUG_COUNTER(TestCounter, "test-counter", "Counter used for unit test"); EXPECT_FALSE(DebugCounter::isCounterSet(TestCounter)); - auto DC = &DebugCounter::instance(); - DC->push_back("test-counter-skip=1"); - DC->push_back("test-counter-count=3"); + DC->push_back("test-counter=1:3-5:78:79:89:100-102:150"); EXPECT_TRUE(DebugCounter::isCounterSet(TestCounter)); - EXPECT_EQ(0, DebugCounter::getCounterValue(TestCounter)); - EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter)); - - EXPECT_EQ(1, DebugCounter::getCounterValue(TestCounter)); - EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter)); + SmallVector Res; + for (unsigned Idx = 0; Idx < 200; Idx++) { + if (DebugCounter::shouldExecute(TestCounter)) + Res.push_back(Idx); + } - DebugCounter::setCounterValue(TestCounter, 3); - EXPECT_TRUE(DebugCounter::shouldExecute(TestCounter)); - EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter)); + SmallVector Expected = {1, 3, 4, 5, 78, 79, 89, 100, 101, 102, 150}; + EXPECT_EQ(Expected, Res); - DebugCounter::setCounterValue(TestCounter, 100); - EXPECT_FALSE(DebugCounter::shouldExecute(TestCounter)); + std::string Str; + llvm::raw_string_ostream OS(Str); + DC->print(OS); + EXPECT_TRUE(StringRef(Str).contains("{200,1:3-5:78:79:89:100-102:150}")); } + #endif