Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate JIT runtime override methods that take void * #6344

Merged
merged 2 commits into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/Func.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3258,29 +3258,38 @@ void Func::compile_to_assembly(const string &filename, const vector<Argument> &a

// JIT-related code

namespace {
template<typename A, typename B>
void set_handler(A &a, B b) {
a = (A)b;
}
} // namespace

// Deprecated setters for JIT handlers
void Func::set_error_handler(void (*handler)(void *, const char *)) {
pipeline().set_error_handler(handler);
set_handler(jit_handlers().custom_error, handler);
}

void Func::set_custom_allocator(void *(*cust_malloc)(void *, size_t),
void (*cust_free)(void *, void *)) {
pipeline().set_custom_allocator(cust_malloc, cust_free);
set_handler(jit_handlers().custom_malloc, cust_malloc);
set_handler(jit_handlers().custom_free, cust_free);
}

void Func::set_custom_do_par_for(int (*cust_do_par_for)(void *, int (*)(void *, int, uint8_t *), int, int, uint8_t *)) {
pipeline().set_custom_do_par_for(cust_do_par_for);
set_handler(jit_handlers().custom_do_par_for, cust_do_par_for);
}

void Func::set_custom_do_task(int (*cust_do_task)(void *, int (*)(void *, int, uint8_t *), int, uint8_t *)) {
pipeline().set_custom_do_task(cust_do_task);
set_handler(jit_handlers().custom_do_task, cust_do_task);
}

void Func::set_custom_trace(int (*trace_fn)(void *, const halide_trace_event_t *)) {
pipeline().set_custom_trace(trace_fn);
set_handler(jit_handlers().custom_trace, trace_fn);
}

void Func::set_custom_print(void (*cust_print)(void *, const char *)) {
pipeline().set_custom_print(cust_print);
set_handler(jit_handlers().custom_print, cust_print);
}

void Func::add_custom_lowering_pass(IRMutator *pass, std::function<void()> deleter) {
Expand Down
93 changes: 10 additions & 83 deletions src/Func.h
Original file line number Diff line number Diff line change
Expand Up @@ -1048,101 +1048,28 @@ class Func {
*/
void compile_jit(const Target &target = get_jit_target_from_environment());

/** Set the error handler function that be called in the case of
* runtime errors during halide pipelines. If you are compiling
* statically, you can also just define your own function with
* signature
\code
extern "C" void halide_error(void *user_context, const char *);
\endcode
* This will clobber Halide's version.
*/
/** Deprecated variants of the above that use a void pointer
* instead of a JITUserContext pointer. */
// @{
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_error_handler(void (*handler)(void *, const char *));

/** Set a custom malloc and free for halide to use. Malloc should
* return 32-byte aligned chunks of memory, and it should be safe
* for Halide to read slightly out of bounds (up to 8 bytes before
* the start or beyond the end). If compiling statically, routines
* with appropriate signatures can be provided directly
\code
extern "C" void *halide_malloc(void *, size_t)
extern "C" void halide_free(void *, void *)
\endcode
* These will clobber Halide's versions. See HalideRuntime.h
* for declarations.
*/
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_custom_allocator(void *(*malloc)(void *, size_t),
void (*free)(void *, void *));

/** Set a custom task handler to be called by the parallel for
* loop. It is useful to set this if you want to do some
* additional bookkeeping at the granularity of parallel
* tasks. The default implementation does this:
\code
extern "C" int halide_do_task(void *user_context,
int (*f)(void *, int, uint8_t *),
int idx, uint8_t *state) {
return f(user_context, idx, state);
}
\endcode
* If you are statically compiling, you can also just define your
* own version of the above function, and it will clobber Halide's
* version.
*
* If you're trying to use a custom parallel runtime, you probably
* don't want to call this. See instead \ref Func::set_custom_do_par_for .
*/
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_custom_do_task(
int (*custom_do_task)(void *, int (*)(void *, int, uint8_t *),
int, uint8_t *));

/** Set a custom parallel for loop launcher. Useful if your app
* already manages a thread pool. The default implementation is
* equivalent to this:
\code
extern "C" int halide_do_par_for(void *user_context,
int (*f)(void *, int, uint8_t *),
int min, int extent, uint8_t *state) {
int exit_status = 0;
parallel for (int idx = min; idx < min+extent; idx++) {
int job_status = halide_do_task(user_context, f, idx, state);
if (job_status) exit_status = job_status;
}
return exit_status;
}
\endcode
*
* However, notwithstanding the above example code, if one task
* fails, we may skip over other tasks, and if two tasks return
* different error codes, we may select one arbitrarily to return.
*
* If you are statically compiling, you can also just define your
* own version of the above function, and it will clobber Halide's
* version.
*/
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_custom_do_par_for(
int (*custom_do_par_for)(void *, int (*)(void *, int, uint8_t *), int,
int, uint8_t *));

/** Set custom routines to call when tracing is enabled. Call this
* on the output Func of your pipeline. This then sets custom
* routines for the entire pipeline, not just calls to this
* Func.
*
* If you are statically compiling, you can also just define your
* own versions of the tracing functions (see HalideRuntime.h),
* and they will clobber Halide's versions. */
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_custom_trace(int (*trace_fn)(void *, const halide_trace_event_t *));

/** Set the function called to print messages from the runtime.
* If you are compiling statically, you can also just define your
* own function with signature
\code
extern "C" void halide_print(void *user_context, const char *);
\endcode
* This will clobber Halide's version.
*/
HALIDE_ATTRIBUTE_DEPRECATED("Custom handlers should by set by modifying the struct returned by jit_handlers()")
void set_custom_print(void (*handler)(void *, const char *));
// @}

/** Get a struct containing the currently set custom functions
* used by JIT. This can be mutated. Changes will take effect the
Expand Down
4 changes: 2 additions & 2 deletions src/JITModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ void free_handler(JITUserContext *context, void *ptr) {
}
}

int do_task_handler(JITUserContext *context, halide_task_t f, int idx,
int do_task_handler(JITUserContext *context, int (*f)(JITUserContext *, int, uint8_t *), int idx,
uint8_t *closure) {
if (context) {
return (*context->handlers.custom_do_task)(context, f, idx, closure);
Expand All @@ -541,7 +541,7 @@ int do_task_handler(JITUserContext *context, halide_task_t f, int idx,
}
}

int do_par_for_handler(JITUserContext *context, halide_task_t f,
int do_par_for_handler(JITUserContext *context, int (*f)(JITUserContext *, int, uint8_t *),
int min, int size, uint8_t *closure) {
if (context) {
return (*context->handlers.custom_do_par_for)(context, f, min, size, closure);
Expand Down
102 changes: 100 additions & 2 deletions src/JITModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,113 @@ struct JITUserContext;

/** A set of custom overrides of runtime functions */
struct JITHandlers {
/** Set the function called to print messages from the runtime.
* If you are compiling statically, you can also just define your
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments about "if you are compiling statically" are arguably confusing and/or misplaced here. I'd suggest replacing everything after the first sentence with something like "(Note that this applies only when jitting code; if you are doing ahead-of-time compilation, see [README_foo.md or wherever we document this, rather than trying to replicate that documentation here])".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* own function with signature
\code
extern "C" void halide_print(void *user_context, const char *);
\endcode
* This will clobber Halide's version.
*/
void (*custom_print)(JITUserContext *, const char *){nullptr};

/** A custom malloc and free for halide to use. Malloc should
* return 32-byte aligned chunks of memory, and it should be safe
* for Halide to read slightly out of bounds (up to 8 bytes before
* the start or beyond the end). If compiling statically, routines
* with appropriate signatures can be provided directly
\code
extern "C" void *halide_malloc(void *, size_t)
extern "C" void halide_free(void *, void *)
\endcode
* These will clobber Halide's versions. See HalideRuntime.h
* for declarations.
*/
// @{
void *(*custom_malloc)(JITUserContext *, size_t){nullptr};
void (*custom_free)(JITUserContext *, void *){nullptr};
int (*custom_do_task)(JITUserContext *, halide_task_t, int, uint8_t *){nullptr};
int (*custom_do_par_for)(JITUserContext *, halide_task_t, int, int, uint8_t *){nullptr};
// @}

/** A custom task handler to be called by the parallel for
* loop. It is useful to set this if you want to do some
* additional bookkeeping at the granularity of parallel
* tasks. The default implementation does this:
\code
extern "C" int halide_do_task(JITUserContext *user_context,
int (*f)(void *, int, uint8_t *),
int idx, uint8_t *state) {
return f(user_context, idx, state);
}
\endcode
* If you are statically compiling, you can also just define your
* own version of the above function that takes a void * instead
* of a JITUserContext *, and it will clobber Halide's version.
*
* If you're trying to use a custom parallel runtime, you probably
* don't want to call this. See instead \ref Func::set_custom_do_par_for .
*/
int (*custom_do_task)(JITUserContext *, int (*)(JITUserContext *, int, uint8_t *), int, uint8_t *){nullptr};

/** A custom parallel for loop launcher. Useful if your app
* already manages a thread pool. The default implementation is
* equivalent to this:
\code
extern "C" int halide_do_par_for(JITUserContext *user_context,
int (*f)(void *, int, uint8_t *),
int min, int extent, uint8_t *state) {
int exit_status = 0;
parallel for (int idx = min; idx < min+extent; idx++) {
int job_status = halide_do_task(user_context, f, idx, state);
if (job_status) exit_status = job_status;
}
return exit_status;
}
\endcode
*
* However, notwithstanding the above example code, if one task
* fails, we may skip over other tasks, and if two tasks return
* different error codes, we may select one arbitrarily to return.
*
* If you are statically compiling, you can also just define your
* own version of the above function that takes a void * instead
* of a JITUserContext *, and it will clobber Halide's version.
*/
int (*custom_do_par_for)(JITUserContext *, int (*)(JITUserContext *, int, uint8_t *), int, int, uint8_t *){nullptr};

/** The error handler function that be called in the case of
* runtime errors during halide pipelines. If you are compiling
* statically, you can also just define your own function with
* signature
\code
extern "C" void halide_error(void *user_context, const char *);
\endcode
* This will clobber Halide's version.
*/
void (*custom_error)(JITUserContext *, const char *){nullptr};

/** A custom routine to call when tracing is enabled. Call this
* on the output Func of your pipeline. This then sets custom
* routines for the entire pipeline, not just calls to this
* Func.
*
* If you are statically compiling, you can also just define your
* own versions of the tracing functions (see HalideRuntime.h),
* and they will clobber Halide's versions. */
int32_t (*custom_trace)(JITUserContext *, const halide_trace_event_t *){nullptr};

/** A method to use for Halide to resolve symbol names dynamically
* in the calling process or library from within the Halide
* runtime. Equivalent to dlsym with a null first argument. */
void *(*custom_get_symbol)(const char *name){nullptr};

/** A method to use for Halide to dynamically load libraries from
* within the runtime. Equivalent to dlopen. Returns a handle to
* the opened library. */
void *(*custom_load_library)(const char *name){nullptr};

/** A method to use for Halide to dynamically find a symbol within
* an opened library. Equivalent to dlsym. Takes a handle
* returned by custom_load_library as the first argument. */
void *(*custom_get_library_symbol)(void *lib, const char *name){nullptr};
};

Expand Down
Loading