Halide 14.0.0
Halide compiler and libraries
Introspection.h
Go to the documentation of this file.
1#ifndef HALIDE_INTROSPECTION_H
2#define HALIDE_INTROSPECTION_H
3
4#include <cstdint>
5#include <iostream>
6#include <string>
7
8/** \file
9 *
10 * Defines methods for introspecting in C++. Relies on DWARF debugging
11 * metadata, so the compilation unit that uses this must be compiled
12 * with -g.
13 */
14
15namespace Halide {
16namespace Internal {
17
18namespace Introspection {
19/** Get the name of a stack variable from its address. The stack
20 * variable must be in a compilation unit compiled with -g to
21 * work. The expected type helps distinguish between variables at the
22 * same address, e.g a class instance vs its first member. */
23std::string get_variable_name(const void *, const std::string &expected_type);
24
25/** Register an untyped heap object. Derive type information from an
26 * introspectable pointer to a pointer to a global object of the same
27 * type. Not thread-safe. */
28void register_heap_object(const void *obj, size_t size, const void *helper);
29
30/** Deregister a heap object. Not thread-safe. */
31void deregister_heap_object(const void *obj, size_t size);
32
33/** Dump the contents of the stack frame of the calling function. Used
34 * for debugging stack frame sizes inside the compiler. Returns
35 * whether or not it was able to find the relevant debug
36 * information. */
38
39#define HALIDE_DUMP_STACK_FRAME \
40 { \
41 static bool check = Halide::Internal::Introspection::dump_stack_frame(); \
42 (void)check; \
43 }
44
45/** Return the address of a global with type T *. Call this to
46 * generate something to pass as the last argument to
47 * register_heap_object.
48 */
49template<typename T>
51 static T *introspection_helper = nullptr;
52 return &introspection_helper;
53}
54
55/** Get the source location in the call stack, skipping over calls in
56 * the Halide namespace. */
57std::string get_source_location();
58
59// This gets called automatically by anyone who includes Halide.h by
60// the code below. It tests if this functionality works for the given
61// compilation unit, and disables it if not.
62void test_compilation_unit(bool (*test)(bool (*)(const void *, const std::string &)),
63 bool (*test_a)(const void *, const std::string &),
64 void (*calib)());
65} // namespace Introspection
66
67} // namespace Internal
68} // namespace Halide
69
70// This code verifies that introspection is working before relying on
71// it. The definitions must appear in Halide.h, but they should not
72// appear in libHalide itself. They're defined as static so that clients
73// can include Halide.h multiple times without link errors.
74#ifndef COMPILING_HALIDE
75
76namespace Halide {
77namespace Internal {
78static bool check_introspection(const void *var, const std::string &type,
79 const std::string &correct_name,
80 const std::string &correct_file, int line) {
81 std::string correct_loc = correct_file + ":" + std::to_string(line);
82 std::string loc = Introspection::get_source_location();
83 std::string name = Introspection::get_variable_name(var, type);
84 return name == correct_name && loc == correct_loc;
85}
86} // namespace Internal
87} // namespace Halide
88
90
91// A function that acts as a signpost. By taking it's address and
92// comparing it to the program counter listed in the debugging info,
93// we can calibrate for any offset between the debugging info and the
94// actual memory layout where the code was loaded.
95static void offset_marker() {
96 std::cerr << "You should not have called this function\n";
97}
98
99struct A {
101
102 class B {
103 int private_member = 17;
104
105 public:
106 float a_float;
108 B() {
109 a_float = private_member * 2.0f;
110 }
111 };
112
114
115 A() {
116 a_b.parent = this;
117 }
118
119 bool test(const std::string &my_name);
120};
121
122static bool test_a(const void *a_ptr, const std::string &my_name) {
123 const A *a = (const A *)a_ptr;
124 bool success = true;
125 success &= Halide::Internal::check_introspection(&a->an_int, "int", my_name + ".an_int", __FILE__, __LINE__);
126 success &= Halide::Internal::check_introspection(&a->a_b, "HalideIntrospectionCanary::A::B", my_name + ".a_b", __FILE__, __LINE__);
127 success &= Halide::Internal::check_introspection(&a->a_b.parent, "HalideIntrospectionCanary::A \\*", my_name + ".a_b.parent", __FILE__, __LINE__);
128 success &= Halide::Internal::check_introspection(&a->a_b.a_float, "float", my_name + ".a_b.a_float", __FILE__, __LINE__);
129 success &= Halide::Internal::check_introspection(a->a_b.parent, "HalideIntrospectionCanary::A", my_name, __FILE__, __LINE__);
130 return success;
131}
132
133static bool test(bool (*f)(const void *, const std::string &)) {
134 A a1, a2;
135
136 // Call via pointer to prevent inlining.
137 return f(&a1, "a1") && f(&a2, "a2");
138}
139
140// Run the tests, and calibrate for the PC offset at static initialization time.
141namespace {
142struct TestCompilationUnit {
143 TestCompilationUnit() {
144 Halide::Internal::Introspection::test_compilation_unit(&test, &test_a, &offset_marker);
145 }
146};
147} // namespace
148
149static TestCompilationUnit test_object;
150
151} // namespace HalideIntrospectionCanary
152
153#endif
154
155#endif
void test_compilation_unit(bool(*test)(bool(*)(const void *, const std::string &)), bool(*test_a)(const void *, const std::string &), void(*calib)())
const void * get_introspection_helper()
Return the address of a global with type T *.
Definition: Introspection.h:50
void register_heap_object(const void *obj, size_t size, const void *helper)
Register an untyped heap object.
void deregister_heap_object(const void *obj, size_t size)
Deregister a heap object.
std::string get_variable_name(const void *, const std::string &expected_type)
Get the name of a stack variable from its address.
std::string get_source_location()
Get the source location in the call stack, skipping over calls in the Halide namespace.
bool dump_stack_frame()
Dump the contents of the stack frame of the calling function.
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
@ Internal
Not visible externally, similar to 'static' linkage in C.
bool test(const std::string &my_name)