Index: include/llvm/Support/CommandLine.h =================================================================== --- include/llvm/Support/CommandLine.h +++ include/llvm/Support/CommandLine.h @@ -1441,6 +1441,7 @@ template > class list : public Option, public list_storage { +protected: std::vector Positions; ParserClass Parser; Index: include/llvm/Support/DebugCounter.h =================================================================== --- /dev/null +++ include/llvm/Support/DebugCounter.h @@ -0,0 +1,128 @@ +//===- llvm/Support/DebugCounter.h - Debug counter support ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// \file This file provides an implementation of debug counters. Debug counters +// are a tool that let you narrow down a miscompilation to a specific thing +// happening. To give a use case: Imagine you have a file, very large, and you +// are trying to understand the minimal transformation that breaks it. Bugpoint +// and bisection is often helpful here in narrowing it down to a specific pass, +// but it's still a very large file, and a very complicated pass to try to +// 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. + +// 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 +// counter, like so: +// +// if (!DebugCounter::shouldExecute(RenameCounter) +// +// +// Now I can, from the command line, make it rename or not rename certain uses +// by setting the skip and count. +// So for example +// bin/opt -debug-counter=predicateinfo-skip=47,predicateinfo-count=1 +// 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/DenseMap.h" +#include "llvm/ADT/UniqueVector.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { + +class DebugCounter { +public: + /// \brief Returns a reference to the singleton instance. + static DebugCounter &instance(); + + // Used by the command line option parser to push a new value it parsed. + void push_back(const std::string &); + + // Register a counter with the specified name. + // + // FIXME: Currently, counter registration is required to happen before command + // line option parsing. The main reason to register counters is to produce a + // nice list of them on the command line, but i'm not sure this is worth it. + static unsigned registerCounter(StringRef Name, StringRef Desc) { + return instance().addCounter(Name, Desc); + } + inline static bool shouldExecute(unsigned CounterName) { +// Compile to nothing when debugging is off +#ifdef NDEBUG + return true; +#else + auto &Us = instance(); + auto Result = Us.Counters.find(CounterName); + if (Result != Us.Counters.end()) { + auto &CounterPair = Result->second; + // We only execute while the skip (first) is zero and the count (second) + // is non-zero + if (CounterPair.first < 0) + return true; + if (CounterPair.first != 0) { + --CounterPair.first; + return false; + } + if (CounterPair.second < 0) + return true; + if (CounterPair.second != 0) { + --CounterPair.second; + return true; + } + return false; + } + // Didn't find the counter, should we warn? + return true; +#endif // NDEBUG + } + + // Dump or print the current counter set. + LLVM_DUMP_METHOD void dump() { print(dbgs()); } + + void print(raw_ostream &OS); + // Get the counter ID for a given named counter, or return 0 if none is found. + unsigned getCounterId(const std::string &Name) const { + return RegisteredCounters.idFor(Name); + } + unsigned int getNumCounters() const { return Counters.size(); } + + std::pair getCounterInfo(unsigned ID) { + return std::make_pair(RegisteredCounters[ID], CounterDesc[ID]); + } + +private: + unsigned addCounter(const std::string &Name, const std::string &Desc) { + unsigned Result = RegisteredCounters.insert(Name); + CounterDesc[Result] = Desc; + return Result; + } + DenseMap> Counters; + DenseMap CounterDesc; + UniqueVector RegisteredCounters; +}; + +#define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \ + static const unsigned VARNAME = \ + DebugCounter::registerCounter(COUNTERNAME, DESC); + +} // namespace llvm +#endif Index: lib/Support/CMakeLists.txt =================================================================== --- lib/Support/CMakeLists.txt +++ lib/Support/CMakeLists.txt @@ -47,6 +47,7 @@ CrashRecoveryContext.cpp DataExtractor.cpp Debug.cpp + DebugCounter.cpp DeltaAlgorithm.cpp DAGDeltaAlgorithm.cpp Dwarf.cpp Index: lib/Support/DebugCounter.cpp =================================================================== --- /dev/null +++ lib/Support/DebugCounter.cpp @@ -0,0 +1,82 @@ +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Options.h" + +using namespace llvm; + +// This class overrides the default list implementation of printing so we +// can pretty print the list of debug counter options. This type of +// dynamic option is pretty rare (basically this and pass lists). + +class DebugCounterList : public cl::list { +private: + using Base = cl::list; + +public: + template + explicit DebugCounterList(const Mods &... Ms) : Base(Ms...) {} + +private: + void printOptionInfo(size_t GlobalWidth) const override { + auto CounterInstance = DebugCounter::instance(); + for (unsigned i = 0; i < CounterInstance.getNumCounters(); ++i) { + const auto Info = CounterInstance.getCounterInfo(i); + } + // FIXME: Print options + this->Base::printOptionInfo(GlobalWidth); + } +}; + +// Create our command line option +static DebugCounterList DebugCounterOption( + "debug-counter", + cl::desc("Comma separated list of debug counter skip and count"), + cl::CommaSeparated, cl::ZeroOrMore, cl::location(DebugCounter::instance())); + +static ManagedStatic DC; + +DebugCounter &DebugCounter::instance() { return *DC; } + +void DebugCounter::push_back(const std::string &Val) { + if (Val.empty()) + return; + // The strings should come in as counter=value + auto CounterPair = StringRef(Val).split('='); + if (CounterPair.second.empty()) + // TODO: Figure out proper way to warn user on command line + return; + // Now we have counter=value. + // First, process value. + long CounterVal; + if (CounterPair.second.getAsInteger(0, CounterVal)) + // Ditto above + 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.endswith("-skip")) { + auto CounterName = CounterPair.first.drop_back(5); + unsigned CounterID = RegisteredCounters.idFor(CounterName); + if (!CounterID) + return; + auto Res = Counters.insert({CounterID, {-1, -1}}); + Res.first->second.first = CounterVal; + } else if (CounterPair.first.endswith("-count")) { + auto CounterName = CounterPair.first.drop_back(6); + unsigned CounterID = RegisteredCounters.idFor(CounterName); + if (!CounterID) + return; + auto Res = Counters.insert({CounterID, {-1, -1}}); + Res.first->second.second = CounterVal; + } else { + // FIXME: Issue warning? + } +} + +void DebugCounter::print(raw_ostream &OS) { + OS << "Counters and values:\n"; + for (const auto &KV : Counters) + OS << left_justify(RegisteredCounters[KV.first], 32) << ": {" + << KV.second.first << "," << KV.second.second << "}\n"; +} Index: lib/Transforms/Utils/PredicateInfo.cpp =================================================================== --- lib/Transforms/Utils/PredicateInfo.cpp +++ lib/Transforms/Utils/PredicateInfo.cpp @@ -31,6 +31,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/DebugCounter.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Transforms/Scalar.h" #include @@ -48,6 +49,9 @@ static cl::opt VerifyPredicateInfo( "verify-predicateinfo", cl::init(false), cl::Hidden, cl::desc("Verify PredicateInfo in legacy printer pass.")); +DEBUG_COUNTER(RenameCounter, "predicateinfo-rename", + "Controls which variables are renamed with predicateinfo"); + namespace llvm { namespace PredicateInfoClasses { enum LocalNum { @@ -552,6 +556,10 @@ // Skip values, only want to rename the uses if (VD.Def || PossibleCopy) continue; + if (!DebugCounter::shouldExecute(RenameCounter)) { + DEBUG(dbgs() << "Skipping execution due to debug counter\n"); + continue; + } ValueDFS &Result = RenameStack.back(); // If the possible copy dominates something, materialize our stack up to Index: utils/bisect-skip-count =================================================================== --- /dev/null +++ utils/bisect-skip-count @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import os +import sys +import argparse +import subprocess32 as subprocess + +parser = argparse.ArgumentParser() + +parser.add_argument('--skipstart', type=int, default=0) +parser.add_argument('--skipend', type=int, default=(1 << 32)) +parser.add_argument('--countstart', type=int, default=0) +parser.add_argument('--countend', type=int, default=(1 << 32)) +parser.add_argument('--timeout', type=int, default=None) +parser.add_argument('command', nargs='+') + +args = parser.parse_args() + +start = args.skipstart +end = args.skipend + +print("Bisect Starting!") +print("Start: %d" % start) +print("End: %d" % end) + +last = None +while start != end and start != end-1: + count = start + (end - start)/2 + print("Visiting Skip: %d with (Start, End) = (%d,%d)" % (count, start, end)) + cmd = [x % {'skip':count, 'count':-1} for x in args.command] + print cmd + try: + result = subprocess.call(cmd, shell=True, timeout=args.timeout) + if result == 0: + print(" PASSES! Setting start to count") + end = count + else: + print(" FAILS! Setting end to count") + start = count + except: + print(" TIMEOUT, setting end to count") + end = count +firstcount = start +print("Last good count: %d" % start) +start = args.countstart +end = args.countend +while start != end and start != end-1: + count = start + (end - start)/2 + print("Visiting Count: %d with (Start, End) = (%d,%d)" % (count, start, end)) + cmd = [x % {'count':count, 'skip':firstcount } for x in args.command] + print cmd + try: + result = subprocess.call(cmd, shell=True, timeout=20) + if result == 0: + print(" PASSES! Setting start to count") + start = count + else: + print(" FAILS! Setting end to count") + end = count + except: + print(" TIMEOUT, setting end to count") + start = count + +print("Last good count: %d" % start)