-
Notifications
You must be signed in to change notification settings - Fork 15
Initial Numba support #626
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
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
3c54f1f
add FFI as a requirement for dynamic calls
wlav 0a1ae32
add basic JIT (Numba) test
wlav 38a02c2
initial Numba/JIT support
wlav 8fb7494
clang-format fixes
wlav fade533
simplify observer insertion code
wlav fa90040
proper dyncall return type
wlav 879bd53
basic jit test
wlav 8c90768
query -> selector, following the API change
wlav f5a4625
apparently, numba has Float and float32 and they aren't the same thing
wlav 12cbdca
test all supported JIT types
wlav 9fa2ba1
changes to make clang-tidy happy
wlav 622e7d2
rejig dyncall to use std::variant instead of a union, per clang-tidy
wlav 35d4fed
clang-format fixes
wlav e4e696f
clang-tidy workarounds
wlav 2f1d501
try again to make clang-tidy shut up
wlav ea7dff7
Apply header-guards fixes
github-actions[bot] c6d6874
Merge branch 'numba-support' of github.com:wlav/phlex into numba-support
wlav 913391c
ruff fix
wlav 9e2d06c
Merge branch 'main' into numba-support
wlav 81d44e7
Merge branch 'main' into numba-support
wlav 74dc348
Revert "Introduce layer_path class (#640)"
wlav f3a101c
Revert "Revert "Introduce layer_path class (#640)""
wlav 4c4741b
move adding of properties to the end to ensure test is defined first
wlav 90cc0ed
add support for Numba-jited observers
wlav 7b919b9
properly NULL pointers in callback move operators
wlav 6436017
Merge branch 'main' into numba-support
wlav 9bf1f5b
address formatting issues
wlav 29ce5f3
gersemi format fix
wlav af07260
from coderabbit: spelling errors in comments and missing decrefs
wlav 6829604
from coderabbit: make get() const
wlav 025390c
make clang-tidy happy by adding some dead writes
wlav 36288a6
Merge branch 'main' into numba-support
wlav 51de0fe
use cfunc from the top-level module instead of the decorators one
wlav de38075
Revert "move adding of properties to the end to ensure test is define…
wlav c6c0121
move setting of py:phlex properties until after it has been created
wlav 701fb79
check for missing output suffixes on transform and report a proper er…
wlav 89b1951
pass transform concurrency on to the converter nodes
wlav cafb828
verify conversion result in python providers
wlav fe5d983
delete py_callback_base move constructor/assignment
wlav 224eefd
coding conventions
wlav 73f52ec
fix shadowing of ffi_type
wlav 1e008e3
coding convention
wlav 5429b2d
add missing decref
wlav d61f52d
improve reporting of conversion errors
wlav 356718d
temporary "fix" to make the AI happy; to be removed after release
wlav a0fb468
guarantee that a path that is supposed to fail actually does
wlav File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| // Dynamic dispatcher from generically packaged args to any C or Python function. | ||
| // | ||
| // Note: this particular implementation is based on libffi, presumed to be for | ||
| // now the minimal dependency, but an alternative could be based on JITing | ||
| // using Cling or even Numba's llvmlite. | ||
|
|
||
| #include "dyncall.hpp" | ||
| #include <stdexcept> | ||
|
|
||
| #include <ffi.h> | ||
|
|
||
| using namespace phlex::experimental; | ||
|
|
||
| phlex::experimental::dcarg phlex::experimental::dcarg::from_str(std::string const& stype) | ||
| { | ||
| // only types currently used in modulewrap are added, not all ffi types | ||
| if (stype == "bool") | ||
| return dcarg(false); | ||
| else if (stype == "int32_t") | ||
| return dcarg(static_cast<std::int32_t>(0)); | ||
| else if (stype == "uint32_t") | ||
| return dcarg(static_cast<std::uint32_t>(0)); | ||
| else if (stype == "int64_t") | ||
| return dcarg(static_cast<ph_long_t>(0)); | ||
| else if (stype == "uint64_t") | ||
| return dcarg(static_cast<ph_ulong_t>(0)); | ||
| else if (stype == "float") | ||
| return dcarg(0.0f); | ||
| else if (stype == "double") | ||
| return dcarg(0.0); | ||
| else if (stype == "void") | ||
| return dcarg{}; | ||
|
|
||
| throw std::invalid_argument("unknown type string: " + stype); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| void* phlex::experimental::dcarg::value_ptr() | ||
| { | ||
| return std::visit( | ||
| [](auto& val) -> void* { | ||
| using T = std::decay_t<decltype(val)>; | ||
| if constexpr (std::is_same_v<T, std::monostate>) { | ||
| return nullptr; | ||
| } else { | ||
| return static_cast<void*>(&val); | ||
| } | ||
| }, | ||
| m_value); | ||
| } | ||
|
|
||
| namespace { | ||
| static ffi_type* get_ffi_type(dcarg const& d) | ||
| { | ||
| return std::visit( | ||
| [](auto&& val) -> ffi_type* { | ||
| using T = std::decay_t<decltype(val)>; | ||
|
|
||
| // there are duplicate bodies here b/c bool is represented by uint8, | ||
| // just as uint8 is, there being no bool in C; the code is cleaner | ||
| // with each type on its own line, however, rather than combining the | ||
| // two in a single predicate as a special case | ||
| // NOLINTBEGIN(bugprone-branch-clone) | ||
| if constexpr (std::is_same_v<T, std::monostate>) | ||
| return &ffi_type_void; | ||
| else if constexpr (std::is_same_v<T, void*>) | ||
| return &ffi_type_pointer; | ||
| else if constexpr (std::is_same_v<T, bool>) | ||
| return &ffi_type_uint8; | ||
| else if constexpr (std::is_same_v<T, std::int8_t>) | ||
| return &ffi_type_sint8; | ||
| else if constexpr (std::is_same_v<T, std::uint8_t>) | ||
| return &ffi_type_uint8; | ||
| else if constexpr (std::is_same_v<T, std::int16_t>) | ||
| return &ffi_type_sint16; | ||
| else if constexpr (std::is_same_v<T, std::uint16_t>) | ||
| return &ffi_type_uint16; | ||
| else if constexpr (std::is_same_v<T, std::int32_t>) | ||
| return &ffi_type_sint32; | ||
| else if constexpr (std::is_same_v<T, std::uint32_t>) | ||
| return &ffi_type_uint32; | ||
| else if constexpr (std::is_same_v<T, ph_long_t>) | ||
| return &ffi_type_sint64; | ||
| else if constexpr (std::is_same_v<T, ph_ulong_t>) | ||
| return &ffi_type_uint64; | ||
| else if constexpr (std::is_same_v<T, float>) | ||
| return &ffi_type_float; | ||
| else if constexpr (std::is_same_v<T, double>) | ||
| return &ffi_type_double; | ||
| // NOLINTEND(bugprone-branch-clone) | ||
| }, | ||
| d.m_value); | ||
| } | ||
| } | ||
|
|
||
| void phlex::experimental::dyncall(void* fn, dcarg& result, dcargs_t& args, int var_offset) | ||
| { | ||
| // Perform a dynamic call of function fn, taking arguments `args` and returning | ||
| // `result`. Set `var_offset` to the appropriate number of positional arguments | ||
| // if the other arguments are variational. | ||
|
|
||
| // Except for the memory management unique_ptrs, this code is essentially C, | ||
| // because libffi is, and that yields a plethora of warnings from clang-tidy, | ||
| // none of which warrant actual changes. | ||
| // NOLINTBEGIN | ||
| std::size_t nargs = (std::size_t)args.size(); | ||
|
|
||
| auto t = std::make_unique<ffi_type*[]>(nargs); | ||
| auto p = std::make_unique<void*[]>(nargs); | ||
|
|
||
| for (dcargs_t::size_type i = 0; i < nargs; ++i) { | ||
| auto& a = args[i]; | ||
| t[i] = get_ffi_type(a); | ||
| p[i] = a.value_ptr(); | ||
| } | ||
|
|
||
| ffi_cif cif; | ||
| ffi_status status; | ||
| if (0 < var_offset) | ||
| status = | ||
| ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, var_offset, nargs, get_ffi_type(result), t.get()); | ||
| else | ||
| status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs, get_ffi_type(result), t.get()); | ||
|
|
||
| if (status) | ||
| throw std::runtime_error("ffi prep failed"); | ||
|
|
||
| ffi_call(&cif, (void (*)())fn, result.value_ptr(), p.get()); | ||
| // NOLINTEND | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| #ifndef PLUGINS_PYTHON_SRC_DYNCALL_HPP | ||
| #define PLUGINS_PYTHON_SRC_DYNCALL_HPP | ||
|
|
||
| // ======================================================================================= | ||
| // | ||
| // Dynamic dispatcher from generically packaged args to any C or Python function. | ||
| // | ||
| // Design rationale | ||
| // ================ | ||
| // | ||
| // Python code is inserted in the Phlex execution graph using generic types to avoid a | ||
| // combinatorial explosion of types. This way, all template instantiations can be done at | ||
| // compile time. Callback wrappers are then needed to either pack from generic to Python | ||
| // or to unpack from generic to C/C++ and perform the call. This dynamic dispatcher | ||
| // provides that functionality. | ||
| // | ||
| // ======================================================================================= | ||
|
|
||
| #include "Python.h" // for PyObject* get<> specialization only | ||
|
|
||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <string> | ||
| #include <variant> | ||
| #include <vector> | ||
|
wlav marked this conversation as resolved.
|
||
|
|
||
| #if defined(__APPLE__) && defined(__MACH__) | ||
| // This is a temporary workaround until we have a solution for handling translation of types | ||
| // between C++ and Python. | ||
| typedef long ph_long_t; | ||
| typedef unsigned long ph_ulong_t; | ||
| #else | ||
| typedef std::int64_t ph_long_t; | ||
| typedef std::uint64_t ph_ulong_t; | ||
| #endif | ||
|
wlav marked this conversation as resolved.
|
||
|
|
||
| namespace phlex::experimental { | ||
|
|
||
| struct dcarg { | ||
| using ffi_variant_type = std::variant<std::monostate, // void (default) | ||
| void*, | ||
| bool, | ||
| std::int8_t, | ||
| std::uint8_t, | ||
| std::int16_t, | ||
| std::uint16_t, | ||
| std::int32_t, | ||
| std::uint32_t, | ||
| ph_long_t, | ||
| ph_ulong_t, | ||
| float, | ||
| double>; | ||
|
|
||
| ffi_variant_type m_value; | ||
|
|
||
| // convenience mapper of human-readable string to dcarg | ||
| static dcarg from_str(std::string const& stype); | ||
|
|
||
| // factory-style constructors to guarantee value/type match | ||
| dcarg() : m_value(std::monostate{}) {} | ||
| explicit dcarg(void* v) : m_value(v) {} | ||
| explicit dcarg(bool v) : m_value(v) {} | ||
| explicit dcarg(std::int8_t v) : m_value(v) {} | ||
| explicit dcarg(std::uint8_t v) : m_value(v) {} | ||
| explicit dcarg(std::int16_t v) : m_value(v) {} | ||
| explicit dcarg(std::uint16_t v) : m_value(v) {} | ||
| explicit dcarg(std::int32_t v) : m_value(v) {} | ||
| explicit dcarg(std::uint32_t v) : m_value(v) {} | ||
| explicit dcarg(ph_long_t v) : m_value(v) {} | ||
| explicit dcarg(ph_ulong_t v) : m_value(v) {} | ||
| explicit dcarg(float v) : m_value(v) {} | ||
| explicit dcarg(double v) : m_value(v) {} | ||
|
|
||
| // pointer access to payload | ||
| void* value_ptr(); | ||
|
|
||
| // value access to payload | ||
| template <typename T> | ||
| T get() const | ||
| { | ||
| return std::get<T>(m_value); | ||
| } | ||
| }; | ||
|
wlav marked this conversation as resolved.
|
||
|
|
||
| // specialization to simplify a very common case | ||
| template <> | ||
| inline PyObject* dcarg::get<PyObject*>() const | ||
| { | ||
| return reinterpret_cast<PyObject*>(std::get<void*>(m_value)); | ||
| } | ||
|
|
||
| typedef std::vector<dcarg> dcargs_t; | ||
|
|
||
| void dyncall(void* fn, dcarg& result, dcargs_t& args, int var_offset = -1); | ||
|
|
||
| } // phlex::experimental | ||
|
|
||
| #endif // PLUGINS_PYTHON_SRC_DYNCALL_HPP | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.