-
-
Notifications
You must be signed in to change notification settings - Fork 29.2k
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
gh-117958: Expose JIT code via access method in experimental UOpExecutor #117959
Conversation
@brandtbucher copy of the original PR to your JIT branch. |
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Python/optimizer.c
Outdated
} | ||
_PyExecutorObject *executor = (_PyExecutorObject *)self; | ||
if (executor->jit_code == NULL || executor->jit_size == 0) { | ||
PyErr_SetString(PyExc_ValueError, "No JIT code available."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could return an empty string instead of a error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the exception makes more sense -- though maybe you should only check for jit_code == NULL
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM. I have a nit for the news item and a suggestion for the actual code.
Curious what you're planning to do with this?
We might also worry about security implications -- while this doesn't allow writing the JIT code, it might give an attacker an easier way to analyze the JIT code and look for vulnerabilities. Though they can access this using ctypes
as well, of course.
Python/optimizer.c
Outdated
} | ||
_PyExecutorObject *executor = (_PyExecutorObject *)self; | ||
if (executor->jit_code == NULL || executor->jit_size == 0) { | ||
PyErr_SetString(PyExc_ValueError, "No JIT code available."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the exception makes more sense -- though maybe you should only check for jit_code == NULL
.
Misc/NEWS.d/next/Core and Builtins/2024-04-18-03-49-41.gh-issue-117958.-EsfUs.rst
Outdated
Show resolved
Hide resolved
…e-117958.-EsfUs.rst Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly leaning towards ditching the exceptions and returning None
in the case where either _Py_JIT
is not defined or jit_code == NULL
, and an empty string if jit_size == 0
.
I'm thinking about things like regression tests or runtime introspection (where it's more ergonomic to check for None
instead of catching an exception in cases where the JIT was built but is disabled, etc.). Not a huge deal, but I think I'd personally rather see an empty string if the JIT code is empty or a None
if no JIT code exists when debugging a buggy JIT. :)
Otherwise, this looks good. What do you think?
Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
…to jit_access_method
@brandtbucher amended with your feedback.
|
Hello, thanks for the PR! It certainly does the job of capturing the machine code generated by the JIT but I was hoping to have a map between the uop byte code and the related machine code similarly to what I was envisaging here |
|
||
static PyMethodDef uop_executor_methods[] = { | ||
{ "is_valid", is_valid, METH_NOARGS, NULL }, | ||
{ "get_jit_code", get_jit_code, METH_NOARGS, NULL}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to merge at this point, but did you consider putting this line inside #ifdef _Py_JIT
instead? (And then the entire function definition as well.) That would make it possible to test whether this functionality exists without calling it, which is generally Pythonic API design.
So, I've thought about this, and it should be possible with a couple of tweaks. Basically, this current PR returns a byte string, which consists of the code for each instruction in sequence, followed by the auxiliary data for each instruction in sequence. Meaning, for a trace of:
It returns:
However, the executor knows the uops that make up its trace. If we Maybe @tonybaloney and @diegorusso can confirm, but it seems like the most useful info to return would be a 3-tuple of base address, a list of code byte strings (corresponding to uops) and a list of data byte strings (again, corresponding to uops). So, for the above example, the return value would be: (
<base address>,
[<A code>, <B code>, <C code>, <D code>],
[<A data>, <B data>, <C data>, <D data>],
) (I think base address is needed for some absolute addressing that we use in places.) So each of the code or data lists can be Would this meet everyone's needs, or am I overthinking it? Even though it's internal, I don't want to tweak this too much after the beta freeze on Monday, so I'm leaning towards providing more information rather than less. |
I'd be inclined to keep it simple (just returning a bytes object) for 3.13 as feature freeze is imminent. Unless, someone really needs the fancier API and is able and willing to implement it in the next two or three days. |
Hello, thanks for the follow up. I was going through a different route by adding a couple of fields in the executor struct
and then work out where every instruction starts. Anyway, because the feature freeze is imminent, I would vote for accepting this PR as it is, improve it in the next cycle and dedicate more thinking to the API. Better something good enough than nothing perfect :) Also it helps the fact that I'm off for a few days and I would miss anyway the feature freeze deadline. |
Okay, then I'll merge it as is. |
Adds the
get_jit_code()
access method to the UOp Executor along with the existing access methods.This is only accessible via internal C APIs but would be helpful for testing and debugging.