Skip to content

Commit 42755f2

Browse files
Add filtering capabilities to HL_DEBUG_CODEGEN (#8627)
This PR extends the semantics of HL_DEBUG_CODEGEN with features for filtering messages only from certain files, functions, and lines. The new syntax for HL_DEBUG_CODEGEN is a ;-delimited list of rules of the form: verbosity[,file_suffix[:line_low[-line_high]]][@function_suffix] * verbosity is a positive integer indicating the maximum (inclusive) verbosity level to emit. This is equivalent to the old semantics. * file_suffix is a maximal string of non-:@ characters. If it is specified, the rule applies only to files ending in this string. Typically, this will be a path fragment like src/Func.cpp. * The values line_low and line_high specify a range of line numbers, inclusive on both sides. If neither is specified, they are 0 and INT_MAX. If only line_low is specified, line_high is set equal to line_low. * function_suffix is a string. If it is specified, the rule applies only to functions (as determined by __FUNCTION__) whose names end in this string. Typically, this will be the whole name of a function (e.g. rfactor). The motivation is to enable higher-signal print debugging. For example, a useful setting while debugging rfactor might be: 1;5,Func.cpp@rfactor;5,Associativity.cpp This commit also replaces Halide::Internal::debug with a macro that is no longer exported by default. To regain access to the internal Halide debugging and error handling macros, define HALIDE_KEEP_MACROS before including Halide.h Thanks to Martijn Courteaux for suggesting the syntax for the rules. --------- Co-authored-by: Martijn Courteaux <[email protected]>
1 parent b7991c1 commit 42755f2

30 files changed

+291
-186
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ TEST_CXX_FLAGS += -DLLVM_VERSION=$(LLVM_VERSION_TIMES_10)
271271
# In the tests, default to exporting no symbols that aren't explicitly exported
272272
TEST_CXX_FLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
273273

274+
# In the tests, enable the debug() and internal_assert() macros
275+
TEST_CXX_FLAGS += -DHALIDE_KEEP_MACROS
276+
274277
# gcc 4.8 fires a bogus warning on old versions of png.h
275278
ifneq (,$(findstring g++,$(CXX_VERSION)))
276279
ifneq (,$(findstring 4.8,$(CXX_VERSION)))

cmake/HalideTestHelpers.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ if (NOT TARGET Halide::Test)
1818
# Obviously link to libHalide, but also grant all tests access to the threads library.
1919
target_link_libraries(Halide_test INTERFACE Halide::Halide Threads::Threads)
2020

21+
# Make internal_assert, debug, etc. available to tests
22+
target_compile_definitions(Halide_test INTERFACE HALIDE_KEEP_MACROS)
23+
2124
# Everyone gets to see the common headers
2225
target_include_directories(Halide_test
2326
INTERFACE

src/AddImageChecks.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,17 @@ Stmt add_image_checks_inner(Stmt s,
359359
}
360360

361361
// Check that the region passed in (after applying constraints) is within the region used
362-
if (debug::debug_level() >= 3) {
363-
debug(3) << "In image " << name << " region touched is:\n";
362+
debug(3) << [&] {
363+
std::stringstream ss;
364+
ss << "In image " << name << " region touched is:\n";
364365
for (int j = 0; j < dimensions; j++) {
365-
debug(3) << " " << j << ": " << (touched.empty() ? Expr() : touched[j].min)
366-
<< " .. "
367-
<< (touched.empty() ? Expr() : touched[j].max)
368-
<< "\n";
366+
ss << " " << j << ": " << (touched.empty() ? Expr() : touched[j].min)
367+
<< " .. "
368+
<< (touched.empty() ? Expr() : touched[j].max)
369+
<< "\n";
369370
}
370-
}
371+
return ss.str();
372+
}();
371373

372374
for (int j = 0; j < dimensions; j++) {
373375
string dim = std::to_string(j);

src/CodeGen_LLVM.cpp

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,9 +1094,10 @@ void CodeGen_LLVM::optimize_module() {
10941094

10951095
auto time_start = std::chrono::high_resolution_clock::now();
10961096

1097-
if (debug::debug_level() >= 3) {
1097+
debug(3) << [&] {
10981098
module->print(dbgs(), nullptr, false, true);
1099-
}
1099+
return "";
1100+
}();
11001101

11011102
std::unique_ptr<TargetMachine> tm = make_target_machine(*module);
11021103

@@ -1239,9 +1240,10 @@ void CodeGen_LLVM::optimize_module() {
12391240
}
12401241

12411242
debug(3) << "After LLVM optimizations:\n";
1242-
if (debug::debug_level() >= 2) {
1243+
debug(2) << [&] {
12431244
module->print(dbgs(), nullptr, false, true);
1244-
}
1245+
return "";
1246+
}();
12451247

12461248
auto *logger = get_compiler_logger();
12471249
if (logger) {
@@ -1264,23 +1266,15 @@ void CodeGen_LLVM::sym_pop(const string &name) {
12641266

12651267
llvm::Value *CodeGen_LLVM::sym_get(const string &name, bool must_succeed) const {
12661268
// look in the symbol table
1267-
llvm::Value *const *v = symbol_table.find(name);
1268-
if (!v) {
1269-
if (must_succeed) {
1270-
std::ostringstream err;
1271-
err << "Symbol not found: " << name << "\n";
1272-
1273-
if (debug::debug_level() > 0) {
1274-
err << "The following names are in scope:\n"
1275-
<< symbol_table << "\n";
1276-
}
1277-
1278-
internal_error << err.str();
1279-
} else {
1280-
return nullptr;
1281-
}
1269+
if (const auto *v = symbol_table.find(name)) {
1270+
return *v;
12821271
}
1283-
return *v;
1272+
if (must_succeed) {
1273+
debug(1) << "The following names are in scope:\n"
1274+
<< symbol_table;
1275+
internal_error << "Symbol not found: " << name;
1276+
}
1277+
return nullptr;
12841278
}
12851279

12861280
bool CodeGen_LLVM::sym_exists(const string &name) const {
@@ -1318,12 +1312,14 @@ Value *CodeGen_LLVM::codegen(const Expr &e) {
13181312
e.type().is_handle() ||
13191313
value->getType()->isVoidTy() ||
13201314
value->getType() == llvm_type_of(e.type());
1321-
if (!types_match && debug::debug_level() > 0) {
1322-
debug(1) << "Unexpected LLVM type for generated expression. Expected (llvm_type_of(e.type())): ";
1323-
llvm_type_of(e.type())->print(dbgs(), true);
1324-
debug(1) << " got (value->getType()): ";
1325-
value->print(dbgs(), true);
1326-
debug(1) << "\n";
1315+
if (!types_match) {
1316+
debug(1) << [&] {
1317+
std::cerr << "Unexpected LLVM type for generated expression. Expected (llvm_type_of(e.type())): ";
1318+
llvm_type_of(e.type())->print(dbgs(), true);
1319+
std::cerr << " got (value->getType()): ";
1320+
value->print(dbgs(), true);
1321+
return "\n";
1322+
}();
13271323
}
13281324
internal_assert(types_match)
13291325
<< "Codegen of Expr " << e

src/CodeGen_PTX_Dev.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -730,20 +730,19 @@ vector<char> CodeGen_PTX_Dev::compile_to_src() {
730730
module_pass_manager.run(*module);
731731

732732
// Codegen pipeline completed.
733-
if (debug::debug_level() >= 2) {
733+
debug(2) << [&] {
734734
dump();
735-
}
736-
debug(2) << "Done with CodeGen_PTX_Dev::compile_to_src";
735+
return "Done with CodeGen_PTX_Dev::compile_to_src";
736+
}();
737737

738738
debug(1) << "PTX kernel:\n"
739739
<< outstr.c_str() << "\n";
740740

741741
vector<char> buffer(outstr.begin(), outstr.end());
742742

743743
// Dump the SASS too if the cuda SDK is in the path
744-
if (debug::debug_level() >= 2) {
745-
debug(2) << "Compiling PTX to SASS. Will fail if CUDA SDK is not installed (and in the path).\n";
746-
744+
debug(2) << "Compiling PTX to SASS. Will fail if CUDA SDK is not installed (and in the path).\n";
745+
debug(2) << [&] {
747746
TemporaryFile ptx(get_current_kernel_name(), ".ptx");
748747
TemporaryFile sass(get_current_kernel_name(), ".sass");
749748

@@ -772,7 +771,8 @@ vector<char> CodeGen_PTX_Dev::compile_to_src() {
772771
f.read(buffer.data(), sz);
773772
}
774773
*/
775-
}
774+
return "";
775+
}();
776776

777777
// Null-terminate the ptx source
778778
buffer.push_back(0);

src/CodeGen_Vulkan_Dev.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,9 +2866,10 @@ void CodeGen_Vulkan_Dev::add_kernel(Stmt stmt,
28662866
emitter.encode_spirv_module(spirv_module);
28672867

28682868
// Dump the SPIR-V if debug is enabled
2869-
if (debug::debug_level() >= 2) {
2869+
debug(2) << [&] {
28702870
emitter.dump_spirv_module();
2871-
}
2871+
return "";
2872+
}();
28722873

28732874
// Copy the SPIR-V module into the Kernel Module table
28742875
KernelModule kernel_module;
@@ -3059,4 +3060,4 @@ std::unique_ptr<CodeGen_GPU_Dev> new_CodeGen_Vulkan_Dev(const Target &target) {
30593060
} // namespace Internal
30603061
} // namespace Halide
30613062

3062-
#endif // WITH_SPIRV
3063+
#endif // WITH_SPIRV

src/Debug.cpp

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,117 @@
11
#include "Debug.h"
2+
#include "Error.h"
23
#include "Util.h"
34

4-
namespace Halide {
5-
namespace Internal {
5+
#include <algorithm>
6+
#include <climits>
7+
#include <optional>
68

7-
int debug::debug_level() {
8-
static int cached_debug_level = ([]() -> int {
9-
std::string lvl = get_env_variable("HL_DEBUG_CODEGEN");
10-
return !lvl.empty() ? atoi(lvl.c_str()) : 0;
11-
})();
12-
return cached_debug_level;
9+
namespace Halide::Internal {
10+
11+
namespace {
12+
13+
std::string read_until(const char *&str, const char *delims) {
14+
const char *start = str;
15+
for (; *str; ++str) {
16+
for (const char *ch = delims; *ch; ++ch) {
17+
if (*str == *ch) {
18+
return {start, str};
19+
}
20+
}
21+
}
22+
return {start, str};
23+
}
24+
25+
bool parse_int(const std::string &number, int &value) {
26+
const char *start = number.c_str();
27+
char *end;
28+
value = static_cast<int>(strtol(start, &end, 10));
29+
return start < end && *end == '\0';
30+
}
31+
32+
class DebugRule {
33+
int verbosity = 0;
34+
std::string file_suffix = "";
35+
int line_low = -1;
36+
int line_high = INT_MAX;
37+
std::string function_suffix = "";
38+
enum Complexity { VerbosityOnly,
39+
NeedsMatching } complexity = VerbosityOnly;
40+
41+
public:
42+
static std::optional<DebugRule> parse(const std::string &spec) {
43+
DebugRule rule;
44+
const char *ptr = spec.c_str();
45+
46+
if (!parse_int(read_until(ptr, ",@"), rule.verbosity)) {
47+
return std::nullopt;
48+
}
49+
50+
if (*ptr == '\0') {
51+
return rule;
52+
}
53+
54+
if (*ptr == ',') {
55+
rule.file_suffix = read_until(++ptr, ":@");
56+
if (*ptr == ':') {
57+
if (!parse_int(read_until(++ptr, "-@"), rule.line_low)) {
58+
return std::nullopt;
59+
}
60+
rule.line_high = rule.line_low;
61+
if (*ptr == '-') {
62+
if (!parse_int(read_until(++ptr, "@"), rule.line_high)) {
63+
return std::nullopt;
64+
}
65+
}
66+
}
67+
}
68+
69+
if (*ptr == '@') {
70+
rule.function_suffix = std::string{ptr + 1};
71+
}
72+
73+
rule.complexity = NeedsMatching;
74+
return rule;
75+
}
76+
77+
bool accepts(const int verbosity, const char *file, const char *function,
78+
const int line) const {
79+
switch (complexity) {
80+
case VerbosityOnly:
81+
return verbosity <= this->verbosity;
82+
case NeedsMatching:
83+
return verbosity <= this->verbosity &&
84+
ends_with(file, file_suffix) &&
85+
ends_with(function, function_suffix) &&
86+
line_low <= line && line <= line_high;
87+
}
88+
return false;
89+
}
90+
};
91+
92+
std::vector<DebugRule> parse_rules(const std::string &env) {
93+
std::vector<DebugRule> rules;
94+
for (const std::string &spec : split_string(env, ";")) {
95+
if (auto rule = DebugRule::parse(spec)) {
96+
rules.push_back(*rule);
97+
} else if (!spec.empty()) {
98+
user_warning
99+
<< "Ignoring malformed HL_DEBUG_CODEGEN entry: [" << spec << "]\n"
100+
<< "The expected format is:\n "
101+
<< "verbosity[,filename[:line_low[-line_high]]][@func]";
102+
}
103+
}
104+
return rules;
105+
}
106+
107+
} // namespace
108+
109+
bool debug_is_active_impl(const int verbosity, const char *file, const char *function,
110+
const int line) {
111+
static const std::vector<DebugRule> rules = parse_rules(get_env_variable("HL_DEBUG_CODEGEN"));
112+
return std::any_of(rules.begin(), rules.end(), [&](const auto &rule) {
113+
return rule.accepts(verbosity, file, function, line);
114+
});
13115
}
14116

15-
} // namespace Internal
16-
} // namespace Halide
117+
} // namespace Halide::Internal

src/Debug.h

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,38 +32,25 @@ std::ostream &operator<<(std::ostream &stream, const Stmt &);
3232
struct LoweredFunc;
3333
std::ostream &operator<<(std::ostream &, const LoweredFunc &);
3434

35-
/** For optional debugging during codegen, use the debug class as
35+
bool debug_is_active_impl(int verbosity, const char *file, const char *function, int line);
36+
#define debug_is_active(n) (::Halide::Internal::debug_is_active_impl((n), __FILE__, __FUNCTION__, __LINE__))
37+
38+
/** For optional debugging during codegen, use the debug macro as
3639
* follows:
3740
*
38-
\code
39-
debug(verbosity) << "The expression is " << expr << "\n";
40-
\endcode
41+
* \code
42+
* debug(verbosity) << "The expression is " << expr << "\n";
43+
* \endcode
4144
*
4245
* verbosity of 0 always prints, 1 should print after every major
4346
* stage, 2 should be used for more detail, and 3 should be used for
4447
* tracing everything that occurs. The verbosity with which to print
4548
* is determined by the value of the environment variable
4649
* HL_DEBUG_CODEGEN
4750
*/
48-
49-
class debug {
50-
const bool logging;
51-
52-
public:
53-
debug(int verbosity)
54-
: logging(verbosity <= debug_level()) {
55-
}
56-
57-
template<typename T>
58-
debug &operator<<(T &&x) {
59-
if (logging) {
60-
std::cerr << std::forward<T>(x);
61-
}
62-
return *this;
63-
}
64-
65-
static int debug_level();
66-
};
51+
#define debug(n) \
52+
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
53+
(!debug_is_active((n))) ? (void)0 : ::Halide::Internal::Voidifier() & std::cerr
6754

6855
/** Allow easily printing the contents of containers, or std::vector-like containers,
6956
* in debug output. Used like so:

0 commit comments

Comments
 (0)