Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-131798: JIT: Narrow the return type ofisinstance for some known arguments#133172

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

Merged
brandtbucher merged 17 commits intopython:mainfromtomasr8:jit-isinstance
May 19, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
59b37f7
Optimize CALL_ISINSTANCE
tomasr8Apr 26, 2025
a81c02e
Add news entry
tomasr8Apr 26, 2025
b1eb6a0
Optimize for subclasses as well
tomasr8Apr 29, 2025
c42172d
Merge branch 'main' into jit-isinstance
tomasr8May 8, 2025
770f7ed
Regen cases
tomasr8May 8, 2025
3e86810
Add a comment
tomasr8May 8, 2025
0820a3c
Merge remote-tracking branch 'upstream/main' into jit-isinstance
tomasr8May 8, 2025
60d21fa
Simplify
tomasr8May 8, 2025
38370a3
Add a metaclass test
tomasr8May 8, 2025
bb228eb
Improve test
tomasr8May 8, 2025
0ede9f3
Update comment
tomasr8May 9, 2025
3d0df4c
Mark some stackrefs as unused
tomasr8May 9, 2025
ce0cd38
Simplify even more
tomasr8May 9, 2025
20fd185
Simplify code
tomasr8May 19, 2025
62854fe
Merge branch 'main' into jit-isinstance
brandtbucherMay 19, 2025
f985963
make regen-cases
tomasr8May 19, 2025
27b1b43
Merge branch 'main' into jit-isinstance
tomasr8May 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletionsLib/test/test_capi/test_opt.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1959,6 +1959,121 @@ def testfunc(n):
self.assertNotIn("_GUARD_THIRD_NULL", uops)
self.assertNotIn("_GUARD_CALLABLE_ISINSTANCE", uops)

def test_call_isinstance_is_true(self):
def testfunc(n):
x = 0
for _ in range(n):
y = isinstance(42, int)
if y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)

def test_call_isinstance_is_false(self):
def testfunc(n):
x = 0
for _ in range(n):
y = isinstance(42, str)
if not y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertNotIn("_GUARD_IS_FALSE_POP", uops)

def test_call_isinstance_subclass(self):
def testfunc(n):
x = 0
for _ in range(n):
y = isinstance(True, int)
if y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)

def test_call_isinstance_unknown_object(self):
def testfunc(n):
x = 0
for _ in range(n):
# The optimizer doesn't know the return type here:
bar = eval("42")
# This will only narrow to bool:
y = isinstance(bar, int)
if y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertIn("_GUARD_IS_TRUE_POP", uops)

def test_call_isinstance_tuple_of_classes(self):
def testfunc(n):
x = 0
for _ in range(n):
# A tuple of classes is currently not optimized,
# so this is only narrowed to bool:
y = isinstance(42, (int, str))
if y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertIn("_GUARD_IS_TRUE_POP", uops)

def test_call_isinstance_metaclass(self):
class EvenNumberMeta(type):
def __instancecheck__(self, number):
return number % 2 == 0

class EvenNumber(metaclass=EvenNumberMeta):
pass

def testfunc(n):
x = 0
for _ in range(n):
# Only narrowed to bool
y = isinstance(42, EvenNumber)
if y:
x += 1
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertIn("_GUARD_IS_TRUE_POP", uops)


def global_identity(x):
return x
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
Narrow the return type and constant-evaluate ``CALL_ISINSTANCE`` for a
subset of known values in the JIT. Patch by Tomas Roun
20 changes: 20 additions & 0 deletionsPython/optimizer_bytecodes.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -890,6 +890,26 @@ dummy_func(void) {
}
}

op(_CALL_ISINSTANCE, (unused, unused, instance, cls -- res)) {
// the result is always a bool, but sometimes we can
// narrow it down to True or False
res = sym_new_type(ctx, &PyBool_Type);
PyTypeObject *inst_type = sym_get_type(instance);
PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, cls);
if (inst_type && cls_o && sym_matches_type(cls, &PyType_Type)) {
// isinstance(inst, cls) where both inst and cls have
// known types, meaning we can deduce either True or False

// The below check is equivalent to PyObject_TypeCheck(inst, cls)
if (inst_type == cls_o || PyType_IsSubtype(inst_type, cls_o)) {
sym_set_const(res, Py_True);
}
else {
sym_set_const(res, Py_False);
}
}
}

op(_GUARD_IS_TRUE_POP, (flag -- )) {
if (sym_is_const(ctx, flag)) {
PyObject *value = sym_get_const(ctx, flag);
Expand Down
16 changes: 15 additions & 1 deletionPython/optimizer_cases.c.h
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

Loading

[8]ページ先頭

©2009-2025 Movatter.jp