Add runtime namespace renaming for import/export/internal scopes#9240
Add runtime namespace renaming for import/export/internal scopes#9240derek-gerstmann wants to merge 10 commits into
Conversation
…l scopes
Rename halide_-prefixed runtime symbols in generated modules according to
user-supplied prefixes, so runtimes and kernels can be built with a custom
ABI prefix that doesn't collide with other Halide runtimes.
- Add apply_runtime_namespace_prefixes in CodeGen_LLVM: a single pass over the
final llvm::Module (call sites auto-follow renamed functions via use-lists).
Classifies each halide_ symbol by role:
* definition -> export prefix
* declaration called by the kernel -> import prefix
* declaration called only by other runtime methods -> internal prefix
Only halide_-prefixed symbols are touched; pipeline entry points and libc
symbols are left alone.
- Plumb RuntimeNamespaceParams from Pipeline/Generator onto the Module:
Module::set_runtime_namespace_map, Pipeline::apply_runtime_namespace stores
the params, compile_to_module attaches the map, and AbstractGenerator wires
the GeneratorContext prefixes through.
- Reject runtime-namespace prefixes on JIT targets: the JIT resolves runtime
calls against a process-global, non-namespaced shared runtime.
- Fix GeneratorParam_RuntimeNamespaceParams::try_set, whose
(key == n) && starts_with(key, n + ".") guard could never match, so
runtime_namespace.import/export/internal were never parsed.
- Add test/correctness/runtime_namespace.cpp covering baseline, export, import,
the import/internal discriminator, all optional-prefix combinations for AOT
and NoRuntime, and the JIT error case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
There are also internal globals not prefixed with halide_ in the runtime - i.e. stuff in Halide::Runtime::Internal. I suggest an aot generator test (or demo app, if that's easier) that links two differently-prefixed runtimes in the same process, and has a pipeline that uses each. It could check that they don't interfere with each other by tweaking internal state of each (e.g. number of threads in the thread pool, the existence of a custom malloc override, etc). |
|
Ah, yes ... good idea. I'll add an AOT generator and binary that links in multiple namespaced runtimes to validate we have no collisions. |
…l scopes
Rename halide runtime symbols in generated modules according to user-supplied
prefixes, so runtimes and kernels can be built with a custom ABI namespace that
does not collide with other Halide runtimes -- including keeping each runtime's
mutable state independent when several differently-namespaced runtimes are
linked into the same process.
LLVM backend (apply_runtime_namespace_prefixes in CodeGen_LLVM, a single pass
over the final llvm::Module; call sites and initializers auto-follow via
use-lists):
- The halide_-prefixed extern "C" ABI, replacing the leading "halide_":
* definition -> export prefix
* declaration called by the kernel -> import prefix
* declaration called only by other runtime methods -> internal prefix
Import vs. internal is decided by the caller (kernel entry points vs. other
runtime methods); kernel-import wins for a declaration with mixed callers.
- The runtime's internal C++ symbols in the Halide::Runtime::Internal namespace
(functions and, crucially, the linkonce state globals such as custom_malloc,
the thread-pool work queue, the memoization cache, ...). These carry no
"halide_" to replace, so the internal prefix is prepended. Without this, two
namespaced runtimes' linkonce state globals would be merged into one shared
copy, defeating isolation of the renamed halide_set_*/halide_get_* methods.
C backend (CodeGen_C): a kernel that calls an external runtime, so only the
import prefix applies. It emits a block of `#define halide_x <prefix>x` for the
runtime C ABI functions (discovered by scanning the embedded runtime header) at
the top of the generated *source* (never the header, so namespaced headers can
be co-included). The preprocessor rewrites the runtime declarations and all call
sites consistently while leaving types, typedefs, and enum values untouched.
Pipeline entry points and libc symbols are left untouched. JIT is rejected: it
resolves runtime calls against a process-global, non-namespaced shared runtime.
Plumbing:
- Module::set_runtime_namespace_map; Pipeline::apply_runtime_namespace stores
the params (and rejects JIT); compile_to_module attaches the map to the Module
and guards JIT; AbstractGenerator wires the GeneratorContext prefixes through;
Module.cpp forwards the import prefix to CodeGen_C for c_source output.
- The generator command-line -r (standalone runtime) path forwards
runtime_namespace.{import,export,internal} params to compile_standalone_runtime.
- add_halide_runtime() (CMake) gains a PARAMS argument, forwarded to GenRT, so
namespaced runtimes can be produced from the build system.
- Fixes GeneratorParam_RuntimeNamespaceParams::try_set, whose
(key == n) && starts_with(key, n + ".") guard could never match.
Docs: doc/CustomRuntimes.md describes the use case, scopes, backends, and usage
from C++, the GenGen command line, and CMake.
Tests:
- test/correctness/runtime_namespace.cpp: baseline, export, import, internal
(namespace-state renaming + the import/internal discriminator), all
optional-prefix combinations for AOT and NoRuntime, and the JIT error case.
- test/generator/runtime_namespace_iso: builds three variants of one pipeline
with both the LLVM and the C backend, each linked against a runtime with a
distinct namespace (none/a/b), links them all into one program, and checks
that per-runtime state stays independent across the custom allocator,
halide_set/get_num_threads, and the error handler, while each pipeline still
computes correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9240 +/- ##
==========================================
- Coverage 70.17% 70.05% -0.12%
==========================================
Files 255 255
Lines 78904 79016 +112
Branches 18866 18900 +34
==========================================
- Hits 55367 55353 -14
- Misses 17883 17967 +84
- Partials 5654 5696 +42 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…e implementation Resolve conflicts in src/CodeGen_LLVM.cpp and test/correctness/runtime_namespace.cpp in favor of the local branch, which supersedes the remote's earlier snapshot (adds the Halide::Runtime::Internal state-global renaming, the C backend, and the my_ns_ example prefixes).
The prior binding of compile_standalone_runtime() took a std::map<RuntimeVisibility, std::string> but RuntimeVisibility was never bound, so the map could not be constructed from Python, and the map argument had no default (a regression for existing callers). - Bind the RuntimeVisibility enum (Import/Export/Internal). - Give compile_standalone_runtime()'s namespace_map argument an empty default. - Bind Pipeline.apply_runtime_namespace(target, namespace_map), accepting a dict keyed by RuntimeVisibility (rejects JIT targets, matching the C++ API). - Add python_bindings/test/correctness/runtime_namespace.py covering the enum, a namespaced standalone runtime, the optional/default map, a namespaced AOT pipeline, and the JIT error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correctness tests (runtime_namespace.cpp) and the Python test are picked up automatically (the former via the correctness glob; the Makefile does not build the Python bindings tests). The generator isolation test needs custom rules because it links three separately namespaced standalone runtimes plus both LLVM- and C-backend kernels for each into a single binary, which the generic single-runtime generator rules cannot express. - Add rules for the three namespaced runtimes (stock, runtime_a_, runtime_b_) and the six kernels (LLVM + C backend, distinct function names, matching import/internal prefixes), forwarding runtime_namespace.* to the generator. - Add a custom generator_aot_runtime_namespace_iso link rule that combines them. - Exclude generator_aotcpp_runtime_namespace_iso: the single test already links both backends, so there is no separate C++-backend-only variant. Note: the Makefile build could not be exercised in this environment (missing libpng/pkg-config); the rules follow the existing alias/cxx_mangling/pyramid precedents and the -r -e static_library + runtime_namespace.* flags were verified against a built generator binary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
@alexreinking Have a look at the added PARAMS in the CMake helper. Let me know if you feel this is an appropriate way of handling additional |
Implement runtime namespace symbol renaming for import/export/internal scopes.
Use Case:
Enable custom runtimes to be built that partially override existing behavior,
by encapsulating the Halide runtime methods into namespaced functions
with user given prefixes for three scopes:
Implementation:
Rename halide runtime symbols in generated modules according to user-supplied
prefixes, so runtimes and kernels can be built with a custom ABI namespace that
does not collide with other Halide runtimes -- including keeping each runtime's
mutable state independent when several differently-namespaced runtimes are
linked into the same process.
LLVM backend (apply_runtime_namespace_prefixes in CodeGen_LLVM, a single pass
over the final llvm::Module; call sites and initializers auto-follow via
use-lists):
The halide_-prefixed extern "C" ABI, replacing the leading "halide_":
Import vs. internal is decided by the caller (kernel entry points vs. other
runtime methods); kernel-import wins for a declaration with mixed callers.
The runtime's internal C++ symbols in the Halide::Runtime::Internal namespace
(functions and, crucially, the linkonce state globals such as custom_malloc,
the thread-pool work queue, the memoization cache, ...). These carry no
"halide_" to replace, so the internal prefix is prepended. Without this, two
namespaced runtimes' linkonce state globals would be merged into one shared
copy, defeating isolation of the renamed halide_set_/halide_get_ methods.
C backend (CodeGen_C): a kernel that calls an external runtime, so only the
import prefix applies. It emits a block of
#define halide_x <prefix>xfor theruntime C ABI functions (discovered by scanning the embedded runtime header) at
the top of the generated source (never the header, so namespaced headers can
be co-included). The preprocessor rewrites the runtime declarations and all call
sites consistently while leaving types, typedefs, and enum values untouched.
Pipeline entry points and libc symbols are left untouched. JIT is rejected: it
resolves runtime calls against a process-global, non-namespaced shared runtime.
Details:
the params (and rejects JIT); compile_to_module attaches the map to the Module
and guards JIT; AbstractGenerator wires the GeneratorContext prefixes through;
Module.cpp forwards the import prefix to CodeGen_C for c_source output.
runtime_namespace.{import,export,internal} params to compile_standalone_runtime.
namespaced runtimes can be produced from the build system.
(key == n) && starts_with(key, n + ".") guard could never match.
Docs: doc/CustomRuntimes.md describes the use case, scopes, backends, and usage
from C++, the GenGen command line, and CMake.
Tests:
(namespace-state renaming + the import/internal discriminator), all
optional-prefix combinations for AOT and NoRuntime, and the JIT error case.
with both the LLVM and the C backend, each linked against a runtime with a
distinct namespace (none/a/b), links them all into one program, and checks
that per-runtime state stays independent across the custom allocator,
halide_set/get_num_threads, and the error handler, while each pipeline still
computes correctly.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Issues:
This only covers AOT. For JIT, I'm not sure how to handle this reliably, so we currently
assert if a namespace prefix is specified.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Checklist