-
-
Notifications
You must be signed in to change notification settings - Fork 32k
GH-105848: Simplify the arrangement of CALL
's stack
#107788
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
Conversation
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 this can go in just like this.
if (oparg & 1) { stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = null; } | ||
stack_pointer[-1] = v; | ||
stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; | ||
if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = 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.
Theoretically this could be slightly less performant than the original (not that it matters, and the C compiler might see right through this).
Python/bytecodes.c
Outdated
@@ -1616,7 +1616,7 @@ dummy_func( | |||
PyObject *dict = PEEK(oparg + 1); // update is still on the stack |
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.
Maybe we could make the DSL less of a lie, e.g. using
inst(DICT_MERGE, (dict, unused[oparg-1], update -- dict, unused[oparg-1])) {
?
(I guess the peeks in the error handling code suggest it's even more complicated.)
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 figured the presence of the PEEK
was an indicator that someone had tried that already. Maybe not, though... let me see!
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.
Maybe it was an oversight. Maybe the code generator didn't use to handle this case.
I tried what I suggested and it seems to work. I didn't try to figure out what to do for the error handling (probably some more dummies below dict
).
Python/bytecodes.c
Outdated
@@ -1644,7 +1644,7 @@ dummy_func( | |||
LOAD_SUPER_ATTR_METHOD, | |||
}; | |||
|
|||
inst(LOAD_SUPER_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) { | |||
inst(LOAD_SUPER_ATTR, (unused/1, global_super, class, self -- attr, self_or_null if (oparg & 1))) { |
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.
self_or_null
is unused, so replace with unused
?
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.
Putting unused
here breaks things. Since this is named it's set to NULL
in the generated code... if it's called unused
, it just ignores it and now there's a garbage value there.
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.
Oh sneaky. I think you found a hole in the code generator. If you want it to (conditionally) push NULL you should initialize it, not depend on the default initialization. IIRC the default initialization is there only to avoid C compiler warnings.
Python/bytecodes.c
Outdated
@@ -1705,39 +1705,38 @@ dummy_func( | |||
LOAD_SUPER_ATTR, | |||
}; | |||
|
|||
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) { | |||
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- attr, self_or_null if (oparg & 1))) { |
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.
Ditto.
@@ -2795,9 +2792,9 @@ dummy_func( | |||
} | |||
|
|||
inst(INSTRUMENTED_CALL, ( -- )) { | |||
int is_meth = PEEK(oparg+2) != NULL; | |||
int is_meth = PEEK(oparg + 1) != 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.
Yes!
How about some buildbot runs? |
🤖 New build scheduled with the buildbot fleet by @brandtbucher for commit fd6607f 🤖 If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again. |
🤖 New build scheduled with the buildbot fleet by @brandtbucher for commit fd6607f 🤖 If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again. |
Perf is neutral. |
🤖 New build scheduled with the buildbot fleet by @brandtbucher for commit a5a2d8f 🤖 If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again. |
🤖 New build scheduled with the buildbot fleet by @brandtbucher for commit a5a2d8f 🤖 If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again. |
No new buildbot failures. |
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.
A bunch of minor style issues, but nothing blocking.
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
callable = method; | ||
if (self_or_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 is perfectly correct, but could you make this consistent with the other NULL checks and use self_or_null != NULL
to make it clear to the reader that this is a NULL
check not a test of a boolean/int flag.
Python/bytecodes.c
Outdated
int argcount = oparg; | ||
if (is_meth) { | ||
callable = method; | ||
if (self_or_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.
ditto
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
callable = method; | ||
if (self_or_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.
ditto
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
callable = method; | ||
if (self_or_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.
likewise
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
callable = method; | ||
if (self_or_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.
and here
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
if (self_or_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.
another
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
if (self_or_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.
almost the last one
Python/bytecodes.c
Outdated
int total_args = oparg; | ||
if (is_meth) { | ||
if (self_or_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.
the last one
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.
Actually, you missed one. But don't worry, I got it. ;)
Python/bytecodes.c
Outdated
@@ -2712,7 +2704,7 @@ dummy_func( | |||
exc_info->exc_value = Py_NewRef(new_exc); | |||
} | |||
|
|||
inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, self -- res2 if (1), res)) { | |||
inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, self -- attr, self_or_null if (1))) { |
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 isn't self_or_null
, it's just self
.
Python/bytecodes.c
Outdated
@@ -1705,39 +1702,38 @@ dummy_func( | |||
LOAD_SUPER_ATTR, | |||
}; | |||
|
|||
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) { | |||
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- attr, unused if (oparg & 1))) { |
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.
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- attr, unused if (oparg & 1))) { | |
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- attr, unused if (0))) { |
Python/bytecodes.c
Outdated
DECREF_INPUTS(); | ||
} | ||
|
||
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused if (oparg & 1), unused)) { | ||
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (oparg & 1))) { |
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.
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (oparg & 1))) { | |
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (0))) { |
Instead of supporting both
[callable, self, args...]
and[NULL, callable, args...]
, changeCALL
's stack to be[callable, self_or_null, args...]
. This always puts the desired callable at the same location on the stack, simplifying (or removing) the resulting shuffles and adjustments necessary for the shared code paths that follow.This also changes
CALL_FUNCTION_EX
,DICT_MERGE
,LOAD_ATTR
,LOAD_GLOBAL
,LOAD_SUPER_ATTR
to support the new stack layout. In some cases, I've also changed the names of the stack items for consistency within a family.As a side-effect of the new layout,
LOAD_ATTR_PROPERTY
andLOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
(which are both implemented as inlined calls) don't support pushing an additional item to the stack for a followingCALL
(oparg & 1
) anymore. I think this is fine, since it seems rare to call a "method" that's looked up in this manner.CALL
sequence #105848