-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
gh-130415: Eliminate guards for constant CALL_BUILTIN_O/FAST #132708
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
base: main
Are you sure you want to change the base?
Conversation
Fidget-Spinner
commented
Apr 18, 2025
•
edited by bedevere-app
bot
Loading
edited by bedevere-app
bot
- Issue: Better constant narrowing in the JIT optimizer #130415
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.
Some suggestions, but looks good!
int total_args = oparg; | ||
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { | ||
total_args += sym_is_not_null(self_or_null); | ||
// Constant propagate | ||
if (total_args == 1 && sym_is_const(ctx, callable)) { | ||
PyObject *callable_o = sym_get_const(ctx, callable); | ||
if (PyCFunction_CheckExact(callable_o) && | ||
PyCFunction_GET_FLAGS(callable_o) == METH_O) { | ||
REPLACE_OP(this_instr, _NOP, 0 ,0); | ||
} | ||
} | ||
} |
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.
Not sure how much this helps in practice, but in theory we could infer a NULL
/non-NULL
here based on the oparg, and narrow the type of the callable:
int total_args = oparg; | |
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { | |
total_args += sym_is_not_null(self_or_null); | |
// Constant propagate | |
if (total_args == 1 && sym_is_const(ctx, callable)) { | |
PyObject *callable_o = sym_get_const(ctx, callable); | |
if (PyCFunction_CheckExact(callable_o) && | |
PyCFunction_GET_FLAGS(callable_o) == METH_O) { | |
REPLACE_OP(this_instr, _NOP, 0 ,0); | |
} | |
} | |
} | |
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { | |
int total_args = oparg + sym_is_not_null(self_or_null); | |
// Constant propagate | |
if (total_args == 1 && sym_is_const(ctx, callable)) { | |
PyObject *callable_o = sym_get_const(ctx, callable); | |
if (PyCFunction_CheckExact(callable_o) && | |
PyCFunction_GET_FLAGS(callable_o) == METH_O) { | |
REPLACE_OP(this_instr, _NOP, 0 ,0); | |
} | |
} | |
} | |
else if (oparg == 0) { | |
sym_set_non_null(ctx, self_or_null); | |
} | |
else { | |
assert(oparg == 1); | |
sym_set_null(ctx, self_or_null); | |
} | |
sym_set_type(callable, &PyCFunction_Type); |
op(_GUARD_CALL_BUILTIN_O, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { | ||
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); | ||
|
||
int total_args = oparg; | ||
if (!PyStackRef_IsNull(self_or_null)) { | ||
args--; | ||
total_args++; | ||
} | ||
int total_args = oparg + (!PyStackRef_IsNull(self_or_null)); | ||
EXIT_IF(total_args != 1); | ||
EXIT_IF(!PyCFunction_CheckExact(callable_o)); | ||
EXIT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_O); | ||
} |
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.
You might consider breaking this in two: one guard for the number of args, and one guard for callable type/flags. Could be there are situations where you could remove one but not the other.
Speaking of which, is it equally common to have NULL
or self
for these two instructions? Or is it more likely that it's always one or the other?
It might be worth tightening up the specialization for only one of the cases, since that makes the guards much simpler (and easier to remove). Maybe it's worth running stats.