gh-104584: Clean up and fix uops tests and fix crash (#106492)

The uops test wasn't testing anything by default,
and was failing when run with -Xuops.

Made the two executor-related context managers global,
so TestUops can use them (notably `with temporary_optimizer(opt)`).

Made clear_executor() a little more thorough.

Fixed a crash upon finalizing a uop optimizer,
by adding a `tp_dealloc` handler.
This commit is contained in:
Guido van Rossum 2023-07-06 15:45:56 -07:00 committed by GitHub
parent 67a798888d
commit 76fac7bce5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 27 deletions

View file

@ -2343,24 +2343,27 @@ class Test_Pep523API(unittest.TestCase):
names = ["func", "outer", "outer", "inner", "inner", "outer", "inner"] names = ["func", "outer", "outer", "inner", "inner", "outer", "inner"]
self.do_test(func, names) self.do_test(func, names)
class TestOptimizerAPI(unittest.TestCase):
@contextlib.contextmanager @contextlib.contextmanager
def temporary_optimizer(self, opt): def temporary_optimizer(opt):
_testinternalcapi.set_optimizer(opt) _testinternalcapi.set_optimizer(opt)
try: try:
yield yield
finally: finally:
_testinternalcapi.set_optimizer(None) _testinternalcapi.set_optimizer(None)
@contextlib.contextmanager @contextlib.contextmanager
def clear_executors(self, func): def clear_executors(func):
# Clear executors in func before and after running a block
func.__code__ = func.__code__.replace()
try: try:
yield yield
finally: finally:
#Clear executors
func.__code__ = func.__code__.replace() func.__code__ = func.__code__.replace()
class TestOptimizerAPI(unittest.TestCase):
def test_get_set_optimizer(self): def test_get_set_optimizer(self):
self.assertEqual(_testinternalcapi.get_optimizer(), None) self.assertEqual(_testinternalcapi.get_optimizer(), None)
opt = _testinternalcapi.get_counter_optimizer() opt = _testinternalcapi.get_counter_optimizer()
@ -2381,9 +2384,9 @@ class TestOptimizerAPI(unittest.TestCase):
for repeat in range(5): for repeat in range(5):
opt = _testinternalcapi.get_counter_optimizer() opt = _testinternalcapi.get_counter_optimizer()
with self.temporary_optimizer(opt): with temporary_optimizer(opt):
self.assertEqual(opt.get_count(), 0) self.assertEqual(opt.get_count(), 0)
with self.clear_executors(loop): with clear_executors(loop):
loop() loop()
self.assertEqual(opt.get_count(), 1000) self.assertEqual(opt.get_count(), 1000)
@ -2409,7 +2412,7 @@ class TestOptimizerAPI(unittest.TestCase):
long_loop = ns['long_loop'] long_loop = ns['long_loop']
opt = _testinternalcapi.get_counter_optimizer() opt = _testinternalcapi.get_counter_optimizer()
with self.temporary_optimizer(opt): with temporary_optimizer(opt):
self.assertEqual(opt.get_count(), 0) self.assertEqual(opt.get_count(), 0)
long_loop() long_loop()
self.assertEqual(opt.get_count(), 10) self.assertEqual(opt.get_count(), 10)
@ -2418,24 +2421,27 @@ class TestOptimizerAPI(unittest.TestCase):
class TestUops(unittest.TestCase): class TestUops(unittest.TestCase):
def test_basic_loop(self): def test_basic_loop(self):
def testfunc(x): def testfunc(x):
i = 0 i = 0
while i < x: while i < x:
i += 1 i += 1
opt = _testinternalcapi.get_uop_optimizer()
with temporary_optimizer(opt):
testfunc(1000) testfunc(1000)
ex = None ex = None
for offset in range(0, 100, 2): for offset in range(0, len(testfunc.__code__.co_code), 2):
try: try:
ex = _testinternalcapi.get_executor(testfunc.__code__, offset) ex = _testinternalcapi.get_executor(testfunc.__code__, offset)
break break
except ValueError: except ValueError:
pass pass
if ex is None: self.assertIsNotNone(ex)
return uops = {opname for opname, _ in ex}
self.assertIn("SAVE_IP", str(ex)) self.assertIn("SAVE_IP", uops)
self.assertIn("LOAD_FAST", uops)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -532,7 +532,7 @@ uop_optimize(
return trace_length; return trace_length;
} }
OBJECT_STAT_INC(optimization_traces_created); OBJECT_STAT_INC(optimization_traces_created);
_PyUOpExecutorObject *executor = (_PyUOpExecutorObject *)_PyObject_New(&UOpExecutor_Type); _PyUOpExecutorObject *executor = PyObject_New(_PyUOpExecutorObject, &UOpExecutor_Type);
if (executor == NULL) { if (executor == NULL) {
return -1; return -1;
} }
@ -542,18 +542,24 @@ uop_optimize(
return 1; return 1;
} }
static void
uop_opt_dealloc(PyObject *self) {
PyObject_Free(self);
}
static PyTypeObject UOpOptimizer_Type = { static PyTypeObject UOpOptimizer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "uop_optimizer", .tp_name = "uop_optimizer",
.tp_basicsize = sizeof(_PyOptimizerObject), .tp_basicsize = sizeof(_PyOptimizerObject),
.tp_itemsize = 0, .tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_dealloc = uop_opt_dealloc,
}; };
PyObject * PyObject *
PyUnstable_Optimizer_NewUOpOptimizer(void) PyUnstable_Optimizer_NewUOpOptimizer(void)
{ {
_PyOptimizerObject *opt = (_PyOptimizerObject *)_PyObject_New(&UOpOptimizer_Type); _PyOptimizerObject *opt = PyObject_New(_PyOptimizerObject, &UOpOptimizer_Type);
if (opt == NULL) { if (opt == NULL) {
return NULL; return NULL;
} }