Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.3k
gh-131798: JIT: Further optimize_CALL_ISINSTANCE
for class tuples#134543
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 ourterms of service andprivacy 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?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
_CALL_ISINSTANCE
for class tuples_CALL_ISINSTANCE
for class tuplesself.assertIn("_BUILD_TUPLE", uops) | ||
self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) |
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.
_BUILD_TUPLE
is preventing us from optimizing out_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW
.
The bytecode is basically:
LOAD_CONSTLOAD_CONST_BUILD_TUPLE_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW
To optimize this, we'd need some special handling for_BUILD_TUPLE
inremove_unneeded_uops
.
Python/optimizer_bytecodes.c Outdated
@@ -938,6 +938,9 @@ dummy_func(void) { | |||
} | |||
op(_CALL_ISINSTANCE, (unused, unused, instance, cls -- res)) { | |||
// The below define is equivalent to PyObject_TypeCheck(inst, cls) | |||
#define sym_IS_SUBTYPE(inst, cls) ((inst) == (cls) || PyType_IsSubtype(inst, cls)) |
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 not sure about this define, maybe it's fine to duplicate this logic?
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'll let you choose. We should probably either duplicate it, or just make it a proper function inoptimizer_symbols.c
.
Python/optimizer_bytecodes.c Outdated
if (!sym_has_type(item)) { | ||
// There is an unknown item in the tuple, | ||
// we can no longer deduce False. | ||
all_items_known = false; | ||
continue; | ||
} |
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.
We can't inferanything in this case, since the class could doanything in its__instancecheck__
or whatever, so it's no longer side-effect free. Need to bail on the whole optimization at this point.
So I don't think we needall_items_known
, either. We can either:
- Break early on our first
True
, like you do below, and inferTrue
. - Loop overeverything and infer
False
. - Bail on an unknown thing and infer
bool
.
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 guess we could still do something like this if we knowsym_get_type(item) == &PyType_Type
, so it's guaranteed side-effect-free, we just don't know the result of the test. But that seems like a rare case (knowing something is atype
, but not which type it actually is).
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.
We can't infer anything in this case, since the class could do anything in itsinstancecheck or whatever, so it's no longer side-effect free. Need to bail on the whole optimization at this point.
Just to check if I understood this point: even if we have something likeisinstance(42, (unknown, int))
we can't inferTrue
because ifunknown
defines its own__instancecheck__
, it would change the semantics of the program if we inferTrue
. This is becauseunknown.__instancecheck__
would no longer be called, right?
So basically what you said, once we see an item with an unknown type, we must stop.
brandtbucherJul 3, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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.
Basically, yeah. It's a bit more subtle, though: wecan infer the result to beTrue
, but wecan't remove theisinstance
call itself.
So we can set areplace_op = false
flag or something to keep the inferred value but avoid theREPLACE_OP
call. That would at least allow us to remove the following branch inif isinstance(42, (has_metaclass, int)): ...
, even if theisinstance
call itself remains.
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.
Basically, yeah. It's a bit more subtle, though: we can infer the result to be True, but we can't remove the isinstance call itself.
Awesome, that was pretty much my understanding as well :)
I'll fix the PR hopefully this weekend.
Python/optimizer_bytecodes.c Outdated
@@ -938,6 +938,9 @@ dummy_func(void) { | |||
} | |||
op(_CALL_ISINSTANCE, (unused, unused, instance, cls -- res)) { | |||
// The below define is equivalent to PyObject_TypeCheck(inst, cls) | |||
#define sym_IS_SUBTYPE(inst, cls) ((inst) == (cls) || PyType_IsSubtype(inst, cls)) |
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'll let you choose. We should probably either duplicate it, or just make it a proper function inoptimizer_symbols.c
.
When you're done making the requested changes, leave the comment: |
Uh oh!
There was an error while loading.Please reload this page.
We can already const eval
isinstance(inst, cls)
calls when both arguments are known, but only ifcls
is a single class (e.g.int
).This PR adds support for
isinstance(inst, (cls1, cls2, ..., clsN))
. This allows us to optimize for example:isinstance(42, (int, str))
(toTrue
)isinstance(42, (bool, str))
(toFalse
)We can narrow to
True
even when only some of the classes are known, as long asinst
is an instance of one of the known classes.