GH-126599: Remove the PyOptimizer API (GH-129194)

This commit is contained in:
Brandt Bucher 2025-01-28 16:10:51 -08:00 committed by GitHub
parent 5c930a26fb
commit 828b27680f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 345 additions and 435 deletions

View file

@ -31,7 +31,7 @@ extern "C" {
#include "pycore_list.h" // struct _Py_list_state
#include "pycore_mimalloc.h" // struct _mimalloc_interp_state
#include "pycore_object_state.h" // struct _py_object_state
#include "pycore_optimizer.h" // _PyOptimizerObject
#include "pycore_optimizer.h" // _PyExecutorObject
#include "pycore_obmalloc.h" // struct _obmalloc_state
#include "pycore_qsbr.h" // struct _qsbr_state
#include "pycore_stackref.h" // Py_STACKREF_DEBUG
@ -262,7 +262,7 @@ struct _is {
struct ast_state ast;
struct types_state types;
struct callable_cache callable_cache;
_PyOptimizerObject *optimizer;
bool jit;
_PyExecutorObject *executor_list_head;
size_t trace_run_counter;
_rare_events rare_events;

View file

@ -271,8 +271,12 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
return 0;
case JUMP_BACKWARD:
return 0;
case JUMP_BACKWARD_JIT:
return 0;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case JUMP_BACKWARD_NO_JIT:
return 0;
case JUMP_FORWARD:
return 0;
case JUMP_IF_FALSE:
@ -742,8 +746,12 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
return 0;
case JUMP_BACKWARD:
return 0;
case JUMP_BACKWARD_JIT:
return 0;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case JUMP_BACKWARD_NO_JIT:
return 0;
case JUMP_FORWARD:
return 0;
case JUMP_IF_FALSE:
@ -1467,10 +1475,18 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) {
*effect = 0;
return 0;
}
case JUMP_BACKWARD_JIT: {
*effect = 0;
return 0;
}
case JUMP_BACKWARD_NO_INTERRUPT: {
*effect = 0;
return 0;
}
case JUMP_BACKWARD_NO_JIT: {
*effect = 0;
return 0;
}
case JUMP_FORWARD: {
*effect = 0;
return 0;
@ -2110,7 +2126,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
[INTERPRETER_EXIT] = { true, INSTR_FMT_IX, 0 },
[IS_OP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
[JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[JUMP_BACKWARD_JIT] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[JUMP_BACKWARD_NO_INTERRUPT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG },
[JUMP_BACKWARD_NO_JIT] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG },
[LIST_APPEND] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
[LIST_EXTEND] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
@ -2539,7 +2557,9 @@ const char *_PyOpcode_OpName[266] = {
[IS_OP] = "IS_OP",
[JUMP] = "JUMP",
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[JUMP_BACKWARD_JIT] = "JUMP_BACKWARD_JIT",
[JUMP_BACKWARD_NO_INTERRUPT] = "JUMP_BACKWARD_NO_INTERRUPT",
[JUMP_BACKWARD_NO_JIT] = "JUMP_BACKWARD_NO_JIT",
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE] = "JUMP_IF_FALSE",
[JUMP_IF_TRUE] = "JUMP_IF_TRUE",
@ -2800,7 +2820,9 @@ const uint8_t _PyOpcode_Deopt[256] = {
[INTERPRETER_EXIT] = INTERPRETER_EXIT,
[IS_OP] = IS_OP,
[JUMP_BACKWARD] = JUMP_BACKWARD,
[JUMP_BACKWARD_JIT] = JUMP_BACKWARD,
[JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
[JUMP_BACKWARD_NO_JIT] = JUMP_BACKWARD,
[JUMP_FORWARD] = JUMP_FORWARD,
[LIST_APPEND] = LIST_APPEND,
[LIST_EXTEND] = LIST_EXTEND,
@ -2939,8 +2961,6 @@ const uint8_t _PyOpcode_Deopt[256] = {
case 146: \
case 147: \
case 148: \
case 230: \
case 231: \
case 232: \
case 233: \
case 234: \

View file

@ -83,23 +83,6 @@ typedef struct _PyExecutorObject {
_PyExitData exits[1];
} _PyExecutorObject;
typedef struct _PyOptimizerObject _PyOptimizerObject;
/* Should return > 0 if a new executor is created. O if no executor is produced and < 0 if an error occurred. */
typedef int (*_Py_optimize_func)(
_PyOptimizerObject* self, struct _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr, _PyExecutorObject **exec_ptr,
int curr_stackentries, bool progress_needed);
struct _PyOptimizerObject {
PyObject_HEAD
_Py_optimize_func optimize;
/* Data needed by the optimizer goes here, but is opaque to the VM */
};
/** Test support **/
_PyOptimizerObject *_Py_SetOptimizer(PyInterpreterState *interp, _PyOptimizerObject* optimizer);
// Export for '_opcode' shared extension (JIT compiler).
PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset);
@ -110,12 +93,6 @@ void _Py_BloomFilter_Init(_PyBloomFilter *);
void _Py_BloomFilter_Add(_PyBloomFilter *bloom, void *obj);
PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj);
// For testing
// Export for '_testinternalcapi' shared extension.
PyAPI_FUNC(_PyOptimizerObject *) _Py_GetOptimizer(void);
PyAPI_FUNC(int) _Py_SetTier2Optimizer(_PyOptimizerObject* optimizer);
PyAPI_FUNC(PyObject *) _PyOptimizer_NewUOpOptimizer(void);
#define _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS 3
#define _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS 6
@ -144,9 +121,7 @@ int _Py_uop_analyze_and_optimize(struct _PyInterpreterFrame *frame,
_PyUOpInstruction *trace, int trace_len, int curr_stackentries,
_PyBloomFilter *dependencies);
extern PyTypeObject _PyDefaultOptimizer_Type;
extern PyTypeObject _PyUOpExecutor_Type;
extern PyTypeObject _PyUOpOptimizer_Type;
#define UOP_FORMAT_TARGET 0

72
Include/opcode_ids.h generated
View file

@ -174,41 +174,43 @@ extern "C" {
#define FOR_ITER_LIST 192
#define FOR_ITER_RANGE 193
#define FOR_ITER_TUPLE 194
#define LOAD_ATTR_CLASS 195
#define LOAD_ATTR_CLASS_WITH_METACLASS_CHECK 196
#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 197
#define LOAD_ATTR_INSTANCE_VALUE 198
#define LOAD_ATTR_METHOD_LAZY_DICT 199
#define LOAD_ATTR_METHOD_NO_DICT 200
#define LOAD_ATTR_METHOD_WITH_VALUES 201
#define LOAD_ATTR_MODULE 202
#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 203
#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 204
#define LOAD_ATTR_PROPERTY 205
#define LOAD_ATTR_SLOT 206
#define LOAD_ATTR_WITH_HINT 207
#define LOAD_CONST_IMMORTAL 208
#define LOAD_CONST_MORTAL 209
#define LOAD_GLOBAL_BUILTIN 210
#define LOAD_GLOBAL_MODULE 211
#define LOAD_SUPER_ATTR_ATTR 212
#define LOAD_SUPER_ATTR_METHOD 213
#define RESUME_CHECK 214
#define SEND_GEN 215
#define STORE_ATTR_INSTANCE_VALUE 216
#define STORE_ATTR_SLOT 217
#define STORE_ATTR_WITH_HINT 218
#define STORE_SUBSCR_DICT 219
#define STORE_SUBSCR_LIST_INT 220
#define TO_BOOL_ALWAYS_TRUE 221
#define TO_BOOL_BOOL 222
#define TO_BOOL_INT 223
#define TO_BOOL_LIST 224
#define TO_BOOL_NONE 225
#define TO_BOOL_STR 226
#define UNPACK_SEQUENCE_LIST 227
#define UNPACK_SEQUENCE_TUPLE 228
#define UNPACK_SEQUENCE_TWO_TUPLE 229
#define JUMP_BACKWARD_JIT 195
#define JUMP_BACKWARD_NO_JIT 196
#define LOAD_ATTR_CLASS 197
#define LOAD_ATTR_CLASS_WITH_METACLASS_CHECK 198
#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 199
#define LOAD_ATTR_INSTANCE_VALUE 200
#define LOAD_ATTR_METHOD_LAZY_DICT 201
#define LOAD_ATTR_METHOD_NO_DICT 202
#define LOAD_ATTR_METHOD_WITH_VALUES 203
#define LOAD_ATTR_MODULE 204
#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 205
#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 206
#define LOAD_ATTR_PROPERTY 207
#define LOAD_ATTR_SLOT 208
#define LOAD_ATTR_WITH_HINT 209
#define LOAD_CONST_IMMORTAL 210
#define LOAD_CONST_MORTAL 211
#define LOAD_GLOBAL_BUILTIN 212
#define LOAD_GLOBAL_MODULE 213
#define LOAD_SUPER_ATTR_ATTR 214
#define LOAD_SUPER_ATTR_METHOD 215
#define RESUME_CHECK 216
#define SEND_GEN 217
#define STORE_ATTR_INSTANCE_VALUE 218
#define STORE_ATTR_SLOT 219
#define STORE_ATTR_WITH_HINT 220
#define STORE_SUBSCR_DICT 221
#define STORE_SUBSCR_LIST_INT 222
#define TO_BOOL_ALWAYS_TRUE 223
#define TO_BOOL_BOOL 224
#define TO_BOOL_INT 225
#define TO_BOOL_LIST 226
#define TO_BOOL_NONE 227
#define TO_BOOL_STR 228
#define UNPACK_SEQUENCE_LIST 229
#define UNPACK_SEQUENCE_TUPLE 230
#define UNPACK_SEQUENCE_TWO_TUPLE 231
#define INSTRUMENTED_END_FOR 235
#define INSTRUMENTED_POP_ITER 236
#define INSTRUMENTED_END_SEND 237

View file

@ -38,12 +38,8 @@ executor in `co_executors`.
## The micro-op optimizer
The optimizer that `_PyOptimizer_Optimize()` runs is configurable via the
`_Py_SetTier2Optimizer()` function (this is used in test via
`_testinternalcapi.set_optimizer()`.)
The micro-op (abbreviated `uop` to approximate `μop`) optimizer is defined in
[`Python/optimizer.c`](../Python/optimizer.c) as the type `_PyUOpOptimizer_Type`.
[`Python/optimizer.c`](../Python/optimizer.c) as `_PyOptimizer_Optimize`.
It translates an instruction trace into a sequence of micro-ops by replacing
each bytecode by an equivalent sequence of micro-ops (see
`_PyOpcode_macro_expansion` in

View file

@ -85,6 +85,10 @@ _specializations = {
"CONTAINS_OP_SET",
"CONTAINS_OP_DICT",
],
"JUMP_BACKWARD": [
"JUMP_BACKWARD_NO_JIT",
"JUMP_BACKWARD_JIT",
],
"FOR_ITER": [
"FOR_ITER_LIST",
"FOR_ITER_TUPLE",
@ -167,41 +171,43 @@ _specialized_opmap = {
'FOR_ITER_LIST': 192,
'FOR_ITER_RANGE': 193,
'FOR_ITER_TUPLE': 194,
'LOAD_ATTR_CLASS': 195,
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 196,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 197,
'LOAD_ATTR_INSTANCE_VALUE': 198,
'LOAD_ATTR_METHOD_LAZY_DICT': 199,
'LOAD_ATTR_METHOD_NO_DICT': 200,
'LOAD_ATTR_METHOD_WITH_VALUES': 201,
'LOAD_ATTR_MODULE': 202,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 203,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 204,
'LOAD_ATTR_PROPERTY': 205,
'LOAD_ATTR_SLOT': 206,
'LOAD_ATTR_WITH_HINT': 207,
'LOAD_CONST_IMMORTAL': 208,
'LOAD_CONST_MORTAL': 209,
'LOAD_GLOBAL_BUILTIN': 210,
'LOAD_GLOBAL_MODULE': 211,
'LOAD_SUPER_ATTR_ATTR': 212,
'LOAD_SUPER_ATTR_METHOD': 213,
'RESUME_CHECK': 214,
'SEND_GEN': 215,
'STORE_ATTR_INSTANCE_VALUE': 216,
'STORE_ATTR_SLOT': 217,
'STORE_ATTR_WITH_HINT': 218,
'STORE_SUBSCR_DICT': 219,
'STORE_SUBSCR_LIST_INT': 220,
'TO_BOOL_ALWAYS_TRUE': 221,
'TO_BOOL_BOOL': 222,
'TO_BOOL_INT': 223,
'TO_BOOL_LIST': 224,
'TO_BOOL_NONE': 225,
'TO_BOOL_STR': 226,
'UNPACK_SEQUENCE_LIST': 227,
'UNPACK_SEQUENCE_TUPLE': 228,
'UNPACK_SEQUENCE_TWO_TUPLE': 229,
'JUMP_BACKWARD_JIT': 195,
'JUMP_BACKWARD_NO_JIT': 196,
'LOAD_ATTR_CLASS': 197,
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 198,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 199,
'LOAD_ATTR_INSTANCE_VALUE': 200,
'LOAD_ATTR_METHOD_LAZY_DICT': 201,
'LOAD_ATTR_METHOD_NO_DICT': 202,
'LOAD_ATTR_METHOD_WITH_VALUES': 203,
'LOAD_ATTR_MODULE': 204,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 205,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 206,
'LOAD_ATTR_PROPERTY': 207,
'LOAD_ATTR_SLOT': 208,
'LOAD_ATTR_WITH_HINT': 209,
'LOAD_CONST_IMMORTAL': 210,
'LOAD_CONST_MORTAL': 211,
'LOAD_GLOBAL_BUILTIN': 212,
'LOAD_GLOBAL_MODULE': 213,
'LOAD_SUPER_ATTR_ATTR': 214,
'LOAD_SUPER_ATTR_METHOD': 215,
'RESUME_CHECK': 216,
'SEND_GEN': 217,
'STORE_ATTR_INSTANCE_VALUE': 218,
'STORE_ATTR_SLOT': 219,
'STORE_ATTR_WITH_HINT': 220,
'STORE_SUBSCR_DICT': 221,
'STORE_SUBSCR_LIST_INT': 222,
'TO_BOOL_ALWAYS_TRUE': 223,
'TO_BOOL_BOOL': 224,
'TO_BOOL_INT': 225,
'TO_BOOL_LIST': 226,
'TO_BOOL_NONE': 227,
'TO_BOOL_STR': 228,
'UNPACK_SEQUENCE_LIST': 229,
'UNPACK_SEQUENCE_TUPLE': 230,
'UNPACK_SEQUENCE_TWO_TUPLE': 231,
}
opmap = {

View file

@ -58,7 +58,8 @@ __all__ = [
"LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT",
"Py_DEBUG", "exceeds_recursion_limit", "get_c_recursion_limit",
"skip_on_s390x",
"without_optimizer",
"requires_jit_enabled",
"requires_jit_disabled",
"force_not_colorized",
"force_not_colorized_test_class",
"make_clean_env",
@ -2620,21 +2621,13 @@ skip_on_s390x = unittest.skipIf(is_s390x, 'skipped on s390x')
Py_TRACE_REFS = hasattr(sys, 'getobjects')
# Decorator to disable optimizer while a function run
def without_optimizer(func):
try:
from _testinternalcapi import get_optimizer, set_optimizer
except ImportError:
return func
@functools.wraps(func)
def wrapper(*args, **kwargs):
save_opt = get_optimizer()
try:
set_optimizer(None)
return func(*args, **kwargs)
finally:
set_optimizer(save_opt)
return wrapper
try:
from _testinternalcapi import jit_enabled
except ImportError:
requires_jit_enabled = requires_jit_disabled = unittest.skip("requires _testinternalcapi")
else:
requires_jit_enabled = unittest.skipUnless(jit_enabled(), "requires JIT enabled")
requires_jit_disabled = unittest.skipIf(jit_enabled(), "requires JIT disabled")
_BASE_COPY_SRC_DIR_IGNORED_NAMES = frozenset({

View file

@ -306,7 +306,7 @@ class CAPITest(unittest.TestCase):
CURRENT_THREAD_REGEX +
r' File .*, line 6 in <module>\n'
r'\n'
r'Extension modules: _testcapi \(total: 1\)\n')
r'Extension modules: _testcapi, _testinternalcapi \(total: 2\)\n')
else:
# Python built with NDEBUG macro defined:
# test _Py_CheckFunctionResult() instead.

View file

@ -9,21 +9,12 @@ import os
import _opcode
from test.support import (script_helper, requires_specialization,
import_helper, Py_GIL_DISABLED)
import_helper, Py_GIL_DISABLED, requires_jit_enabled)
_testinternalcapi = import_helper.import_module("_testinternalcapi")
from _testinternalcapi import TIER2_THRESHOLD
@contextlib.contextmanager
def temporary_optimizer(opt):
old_opt = _testinternalcapi.get_optimizer()
_testinternalcapi.set_optimizer(opt)
try:
yield
finally:
_testinternalcapi.set_optimizer(old_opt)
@contextlib.contextmanager
def clear_executors(func):
@ -57,8 +48,7 @@ def get_opnames(ex):
@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
@requires_jit_enabled
class TestExecutorInvalidation(unittest.TestCase):
def test_invalidate_object(self):
@ -75,10 +65,8 @@ class TestExecutorInvalidation(unittest.TestCase):
funcs = [ ns[f'f{n}'] for n in range(5)]
objects = [object() for _ in range(5)]
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
for f in funcs:
f()
for f in funcs:
f()
executors = [get_first_executor(f) for f in funcs]
# Set things up so each executor depends on the objects
# with an equal or lower index.
@ -106,9 +94,7 @@ class TestExecutorInvalidation(unittest.TestCase):
pass
"""), ns, ns)
f = ns['f']
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
f()
f()
exe = get_first_executor(f)
self.assertIsNotNone(exe)
self.assertTrue(exe.is_valid())
@ -119,9 +105,7 @@ class TestExecutorInvalidation(unittest.TestCase):
def f():
for _ in range(TIER2_THRESHOLD):
pass
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
f()
f()
exe = get_first_executor(f)
self.assertIsNotNone(exe)
self.assertTrue(exe.is_valid())
@ -133,8 +117,7 @@ class TestExecutorInvalidation(unittest.TestCase):
@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
@requires_jit_enabled
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
class TestUops(unittest.TestCase):
@ -144,9 +127,7 @@ class TestUops(unittest.TestCase):
while i < x:
i += 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -192,11 +173,9 @@ class TestUops(unittest.TestCase):
"""), ns, ns)
many_vars = ns["many_vars"]
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
ex = get_first_executor(many_vars)
self.assertIsNone(ex)
many_vars()
ex = get_first_executor(many_vars)
self.assertIsNone(ex)
many_vars()
ex = get_first_executor(many_vars)
self.assertIsNotNone(ex)
@ -215,10 +194,7 @@ class TestUops(unittest.TestCase):
while i < x:
i += 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -231,9 +207,7 @@ class TestUops(unittest.TestCase):
while i < n:
i += 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -246,9 +220,7 @@ class TestUops(unittest.TestCase):
if x is None:
x = 0
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(range(TIER2_THRESHOLD))
testfunc(range(TIER2_THRESHOLD))
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -263,9 +235,7 @@ class TestUops(unittest.TestCase):
if x is not None:
x = 0
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(range(TIER2_THRESHOLD))
testfunc(range(TIER2_THRESHOLD))
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -279,9 +249,7 @@ class TestUops(unittest.TestCase):
while not i >= n:
i += 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -294,9 +262,7 @@ class TestUops(unittest.TestCase):
while i < n:
i += 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -314,9 +280,7 @@ class TestUops(unittest.TestCase):
a += 1
return a
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -332,10 +296,8 @@ class TestUops(unittest.TestCase):
total += i
return total
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
total = testfunc(TIER2_THRESHOLD)
self.assertEqual(total, sum(range(TIER2_THRESHOLD)))
total = testfunc(TIER2_THRESHOLD)
self.assertEqual(total, sum(range(TIER2_THRESHOLD)))
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -353,11 +315,9 @@ class TestUops(unittest.TestCase):
total += i
return total
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
a = list(range(TIER2_THRESHOLD))
total = testfunc(a)
self.assertEqual(total, sum(a))
a = list(range(TIER2_THRESHOLD))
total = testfunc(a)
self.assertEqual(total, sum(a))
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -375,11 +335,9 @@ class TestUops(unittest.TestCase):
total += i
return total
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
a = tuple(range(TIER2_THRESHOLD))
total = testfunc(a)
self.assertEqual(total, sum(a))
a = tuple(range(TIER2_THRESHOLD))
total = testfunc(a)
self.assertEqual(total, sum(a))
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -395,14 +353,12 @@ class TestUops(unittest.TestCase):
for x in it:
pass
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
a = [1, 2, 3]
it = iter(a)
testfunc(it)
a.append(4)
with self.assertRaises(StopIteration):
next(it)
a = [1, 2, 3]
it = iter(a)
testfunc(it)
a.append(4)
with self.assertRaises(StopIteration):
next(it)
def test_call_py_exact_args(self):
def testfunc(n):
@ -411,9 +367,7 @@ class TestUops(unittest.TestCase):
for i in range(n):
dummy(i)
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -429,9 +383,7 @@ class TestUops(unittest.TestCase):
else:
i = 1
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -457,9 +409,7 @@ class TestUops(unittest.TestCase):
x += 1000*i + j
return x
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
x = testfunc(TIER2_THRESHOLD, TIER2_THRESHOLD)
x = testfunc(TIER2_THRESHOLD, TIER2_THRESHOLD)
self.assertEqual(x, sum(range(TIER2_THRESHOLD)) * TIER2_THRESHOLD * 1001)
@ -484,9 +434,7 @@ class TestUops(unittest.TestCase):
bits += 1
return bits
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
x = testfunc(TIER2_THRESHOLD * 2)
x = testfunc(TIER2_THRESHOLD * 2)
self.assertEqual(x, TIER2_THRESHOLD * 5)
ex = get_first_executor(testfunc)
@ -499,16 +447,12 @@ class TestUops(unittest.TestCase):
@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
@requires_jit_enabled
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
class TestUopsOptimization(unittest.TestCase):
def _run_with_optimizer(self, testfunc, arg):
res = None
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
res = testfunc(arg)
res = testfunc(arg)
ex = get_first_executor(testfunc)
return res, ex
@ -542,10 +486,7 @@ class TestUopsOptimization(unittest.TestCase):
num += 1
return a
opt = _testinternalcapi.new_uop_optimizer()
res = None
with temporary_optimizer(opt):
res = testfunc(TIER2_THRESHOLD)
res = testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -566,10 +507,7 @@ class TestUopsOptimization(unittest.TestCase):
num += 1
return x
opt = _testinternalcapi.new_uop_optimizer()
res = None
with temporary_optimizer(opt):
res = testfunc(TIER2_THRESHOLD)
res = testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -661,16 +599,14 @@ class TestUopsOptimization(unittest.TestCase):
for i in range(n):
dummy(i)
opt = _testinternalcapi.new_uop_optimizer()
# Trigger specialization
testfunc(8)
with temporary_optimizer(opt):
del dummy
gc.collect()
del dummy
gc.collect()
def dummy(x):
return x + 2
testfunc(32)
def dummy(x):
return x + 2
testfunc(32)
ex = get_first_executor(testfunc)
# Honestly as long as it doesn't crash it's fine.
@ -703,8 +639,6 @@ class TestUopsOptimization(unittest.TestCase):
x = range(i)
return x
opt = _testinternalcapi.new_uop_optimizer()
_testinternalcapi.set_optimizer(opt)
testfunc(_testinternalcapi.TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
@ -712,7 +646,7 @@ class TestUopsOptimization(unittest.TestCase):
uops = get_opnames(ex)
assert "_LOAD_GLOBAL_BUILTINS" not in uops
assert "_LOAD_CONST_INLINE_BORROW" in uops
"""))
"""), PYTHON_JIT="1")
self.assertEqual(result[0].rc, 0, result)
def test_float_add_constant_propagation(self):
@ -1399,9 +1333,7 @@ class TestUopsOptimization(unittest.TestCase):
# Only works on functions promoted to constants
global_identity(i)
opt = _testinternalcapi.new_uop_optimizer()
with temporary_optimizer(opt):
testfunc(TIER2_THRESHOLD)
testfunc(TIER2_THRESHOLD)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
@ -1488,12 +1420,12 @@ class TestUopsOptimization(unittest.TestCase):
class Convert9999ToNone:
def __del__(self):
ns = sys._getframe(1).f_locals
if ns["i"] == 9999:
if ns["i"] == _testinternalcapi.TIER2_THRESHOLD:
ns["i"] = None
def crash_addition():
try:
for i in range(10000):
for i in range(_testinternalcapi.TIER2_THRESHOLD + 1):
n = Convert9999ToNone()
i + i # Remove guards for i.
n = None # Change i.

View file

@ -15,7 +15,7 @@ import types
import unittest
from test.support import (captured_stdout, requires_debug_ranges,
requires_specialization, cpython_only,
os_helper)
os_helper, import_helper)
from test.support.bytecode_helper import BytecodeTestCase
@ -904,7 +904,7 @@ dis_loop_test_quickened_code = """\
LOAD_FAST 0 (i)
CALL_PY_GENERAL 1
POP_TOP
JUMP_BACKWARD 16 (to L1)
JUMP_BACKWARD_{: <6} 16 (to L1)
%3d L2: END_FOR
POP_ITER
@ -1308,7 +1308,8 @@ class DisTests(DisTestBase):
# Loop can trigger a quicken where the loop is located
self.code_quicken(loop_test, 4)
got = self.get_disassembly(loop_test, adaptive=True)
expected = dis_loop_test_quickened_code
jit = import_helper.import_module("_testinternalcapi").jit_enabled()
expected = dis_loop_test_quickened_code.format("JIT" if jit else "NO_JIT")
self.do_disassembly_compare(got, expected)
@cpython_only

View file

@ -6,7 +6,7 @@ import types
import unittest
from test.support import (threading_helper, check_impl_detail,
requires_specialization, requires_specialization_ft,
cpython_only)
cpython_only, requires_jit_disabled)
from test.support.import_helper import import_module
# Skip this module on other interpreters, it is cpython specific:
@ -16,20 +16,6 @@ if check_impl_detail(cpython=False):
_testinternalcapi = import_module("_testinternalcapi")
def disabling_optimizer(func):
def wrapper(*args, **kwargs):
if not hasattr(_testinternalcapi, "get_optimizer"):
return func(*args, **kwargs)
old_opt = _testinternalcapi.get_optimizer()
_testinternalcapi.set_optimizer(None)
try:
return func(*args, **kwargs)
finally:
_testinternalcapi.set_optimizer(old_opt)
return wrapper
class TestBase(unittest.TestCase):
def assert_specialized(self, f, opname):
instructions = dis.get_instructions(f, adaptive=True)
@ -526,7 +512,7 @@ class TestCallCache(TestBase):
f(None)
f()
@disabling_optimizer
@requires_jit_disabled
@requires_specialization_ft
def test_assign_init_code(self):
class MyClass:
@ -549,7 +535,7 @@ class TestCallCache(TestBase):
MyClass.__init__.__code__ = count_args.__code__
instantiate()
@disabling_optimizer
@requires_jit_disabled
@requires_specialization_ft
def test_push_init_frame_fails(self):
def instantiate():
@ -583,7 +569,7 @@ class TestRacesDoNotCrash(TestBase):
WARMUPS = 2
WRITERS = 2
@disabling_optimizer
@requires_jit_disabled
def assert_races_do_not_crash(
self, opname, get_items, read, write, *, check_items=False
):

View file

@ -1185,7 +1185,7 @@ class ArgsTestCase(BaseTestCase):
stats=TestStats(4, 1),
forever=True)
@support.without_optimizer
@support.requires_jit_disabled
def check_leak(self, code, what, *, run_workers=False):
test = self.create_test('huntrleaks', code=code)

View file

@ -950,39 +950,14 @@ get_co_framesize(PyObject *self, PyObject *arg)
return PyLong_FromLong(code->co_framesize);
}
static PyObject *
jit_enabled(PyObject *self, PyObject *arg)
{
return PyBool_FromLong(_PyInterpreterState_GET()->jit);
}
#ifdef _Py_TIER2
static PyObject *
new_uop_optimizer(PyObject *self, PyObject *arg)
{
return _PyOptimizer_NewUOpOptimizer();
}
static PyObject *
set_optimizer(PyObject *self, PyObject *opt)
{
if (opt == Py_None) {
opt = NULL;
}
if (_Py_SetTier2Optimizer((_PyOptimizerObject*)opt) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *opt = NULL;
#ifdef _Py_TIER2
opt = (PyObject *)_Py_GetOptimizer();
#endif
if (opt == NULL) {
Py_RETURN_NONE;
}
return opt;
}
static PyObject *
add_executor_dependency(PyObject *self, PyObject *args)
{
@ -2047,10 +2022,8 @@ static PyMethodDef module_functions[] = {
{"iframe_getline", iframe_getline, METH_O, NULL},
{"iframe_getlasti", iframe_getlasti, METH_O, NULL},
{"get_co_framesize", get_co_framesize, METH_O, NULL},
{"jit_enabled", jit_enabled, METH_NOARGS, NULL},
#ifdef _Py_TIER2
{"get_optimizer", get_optimizer, METH_NOARGS, NULL},
{"set_optimizer", set_optimizer, METH_O, NULL},
{"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL},
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
{"invalidate_executors", invalidate_executors, METH_O, NULL},
#endif

View file

@ -19,7 +19,7 @@
#include "pycore_object.h" // PyAPI_DATA() _Py_SwappedOp definition
#include "pycore_object_state.h" // struct _reftracer_runtime_state
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_optimizer.h" // _PyUOpExecutor_Type, _PyUOpOptimizer_Type, ...
#include "pycore_optimizer.h" // _PyUOpExecutor_Type, ...
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyThreadState_GET()
@ -2379,9 +2379,6 @@ static PyTypeObject* static_types[] = {
&_PyBufferWrapper_Type,
&_PyContextTokenMissing_Type,
&_PyCoroWrapper_Type,
#ifdef _Py_TIER2
&_PyDefaultOptimizer_Type,
#endif
&_Py_GenericAliasIterType,
&_PyHamtItems_Type,
&_PyHamtKeys_Type,
@ -2404,7 +2401,6 @@ static PyTypeObject* static_types[] = {
&_PyUnion_Type,
#ifdef _Py_TIER2
&_PyUOpExecutor_Type,
&_PyUOpOptimizer_Type,
#endif
&_PyWeakref_CallableProxyType,
&_PyWeakref_ProxyType,

View file

@ -2782,13 +2782,26 @@ dummy_func(
JUMPBY(oparg);
}
tier1 op(_JUMP_BACKWARD, (the_counter/1 --)) {
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
#ifdef _Py_TIER2
#if ENABLE_SPECIALIZATION
family(JUMP_BACKWARD, 1) = {
JUMP_BACKWARD_NO_JIT,
JUMP_BACKWARD_JIT,
};
tier1 op(_SPECIALIZE_JUMP_BACKWARD, (--)) {
#if ENABLE_SPECIALIZATION
if (this_instr->op.code == JUMP_BACKWARD) {
this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT;
// Need to re-dispatch so the warmup counter isn't off by one:
next_instr = this_instr;
DISPATCH_SAME_OPARG();
}
#endif
}
tier1 op(_JIT, (--)) {
#ifdef _Py_TIER2
_Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) {
_Py_CODEUNIT *start = this_instr;
/* Back up over EXTENDED_ARGs so optimizer sees the whole instruction */
while (oparg > 255) {
@ -2811,13 +2824,25 @@ dummy_func(
else {
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
}
#endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_TIER2 */
#endif
}
macro(JUMP_BACKWARD) =
unused/1 +
_SPECIALIZE_JUMP_BACKWARD +
_CHECK_PERIODIC +
_JUMP_BACKWARD;
JUMP_BACKWARD_NO_INTERRUPT;
macro(JUMP_BACKWARD_NO_JIT) =
unused/1 +
_CHECK_PERIODIC +
JUMP_BACKWARD_NO_INTERRUPT;
macro(JUMP_BACKWARD_JIT) =
unused/1 +
_CHECK_PERIODIC +
JUMP_BACKWARD_NO_INTERRUPT +
_JIT;
pseudo(JUMP, (--)) = {
JUMP_FORWARD,
@ -2906,6 +2931,7 @@ dummy_func(
* generator or coroutine, so we deliberately do not check it here.
* (see bpo-30039).
*/
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}

View file

@ -180,6 +180,7 @@
TARGET(BINARY_OP_EXTEND) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 6;
INSTRUCTION_STATS(BINARY_OP_EXTEND);
static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size");
@ -1087,6 +1088,7 @@
TARGET(CALL_ALLOC_AND_ENTER_INIT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
@ -1185,6 +1187,7 @@
TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
@ -1288,6 +1291,7 @@
TARGET(CALL_BOUND_METHOD_GENERAL) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_BOUND_METHOD_GENERAL);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
@ -2113,6 +2117,7 @@
TARGET(CALL_KW_BOUND_METHOD) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_KW_BOUND_METHOD);
static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size");
@ -2313,6 +2318,7 @@
TARGET(CALL_KW_PY) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_KW_PY);
static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size");
@ -2890,6 +2896,7 @@
TARGET(CALL_PY_EXACT_ARGS) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_PY_EXACT_ARGS);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
@ -2971,6 +2978,7 @@
TARGET(CALL_PY_GENERAL) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(CALL_PY_GENERAL);
static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size");
@ -5160,10 +5168,24 @@
}
TARGET(JUMP_BACKWARD) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(JUMP_BACKWARD);
PREDICTED_JUMP_BACKWARD:;
_Py_CODEUNIT* const this_instr = next_instr - 2;
(void)this_instr;
/* Skip 1 cache entry */
// _SPECIALIZE_JUMP_BACKWARD
{
#if ENABLE_SPECIALIZATION
if (this_instr->op.code == JUMP_BACKWARD) {
this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT;
// Need to re-dispatch so the warmup counter isn't off by one:
next_instr = this_instr;
DISPATCH_SAME_OPARG();
}
#endif
}
// _CHECK_PERIODIC
{
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
@ -5175,16 +5197,52 @@
if (err != 0) goto error;
}
}
// _JUMP_BACKWARD
// _JUMP_BACKWARD_NO_INTERRUPT
{
uint16_t the_counter = read_u16(&this_instr[1].cache);
(void)the_counter;
/* This bytecode is used in the `yield from` or `await` loop.
* If there is an interrupt, we want it handled in the innermost
* generator or coroutine, so we deliberately do not check it here.
* (see bpo-30039).
*/
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
DISPATCH();
}
TARGET(JUMP_BACKWARD_JIT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 2;
INSTRUCTION_STATS(JUMP_BACKWARD_JIT);
static_assert(1 == 1, "incorrect cache size");
/* Skip 1 cache entry */
// _CHECK_PERIODIC
{
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_HandlePending(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err != 0) goto error;
}
}
// _JUMP_BACKWARD_NO_INTERRUPT
{
/* This bytecode is used in the `yield from` or `await` loop.
* If there is an interrupt, we want it handled in the innermost
* generator or coroutine, so we deliberately do not check it here.
* (see bpo-30039).
*/
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
// _JIT
{
#ifdef _Py_TIER2
#if ENABLE_SPECIALIZATION
_Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) {
_Py_CODEUNIT *start = this_instr;
/* Back up over EXTENDED_ARGs so optimizer sees the whole instruction */
while (oparg > 255) {
@ -5211,8 +5269,7 @@
else {
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
}
#endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_TIER2 */
#endif
}
DISPATCH();
}
@ -5226,10 +5283,41 @@
* generator or coroutine, so we deliberately do not check it here.
* (see bpo-30039).
*/
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
DISPATCH();
}
TARGET(JUMP_BACKWARD_NO_JIT) {
frame->instr_ptr = next_instr;
next_instr += 2;
INSTRUCTION_STATS(JUMP_BACKWARD_NO_JIT);
static_assert(1 == 1, "incorrect cache size");
/* Skip 1 cache entry */
// _CHECK_PERIODIC
{
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = _Py_HandlePending(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err != 0) goto error;
}
}
// _JUMP_BACKWARD_NO_INTERRUPT
{
/* This bytecode is used in the `yield from` or `await` loop.
* If there is an interrupt, we want it handled in the innermost
* generator or coroutine, so we deliberately do not check it here.
* (see bpo-30039).
*/
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
}
DISPATCH();
}
TARGET(JUMP_FORWARD) {
frame->instr_ptr = next_instr;
next_instr += 1;
@ -5369,6 +5457,7 @@
TARGET(LOAD_ATTR_CLASS) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_CLASS);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5407,6 +5496,7 @@
TARGET(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5451,6 +5541,7 @@
TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5487,6 +5578,7 @@
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5540,6 +5632,7 @@
TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5583,6 +5676,7 @@
TARGET(LOAD_ATTR_METHOD_NO_DICT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5619,6 +5713,7 @@
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5669,6 +5764,7 @@
TARGET(LOAD_ATTR_MODULE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_MODULE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5724,6 +5820,7 @@
TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5755,6 +5852,7 @@
TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5799,6 +5897,7 @@
TARGET(LOAD_ATTR_PROPERTY) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_PROPERTY);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5864,6 +5963,7 @@
TARGET(LOAD_ATTR_SLOT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_SLOT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -5909,6 +6009,7 @@
TARGET(LOAD_ATTR_WITH_HINT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 10;
INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
@ -6326,6 +6427,7 @@
TARGET(LOAD_GLOBAL_BUILTIN) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 5;
INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN);
static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size");
@ -6380,6 +6482,7 @@
TARGET(LOAD_GLOBAL_MODULE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 5;
INSTRUCTION_STATS(LOAD_GLOBAL_MODULE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size");
@ -7557,6 +7660,7 @@
TARGET(STORE_ATTR_INSTANCE_VALUE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 5;
INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE);
static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size");
@ -7615,6 +7719,7 @@
TARGET(STORE_ATTR_SLOT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 5;
INSTRUCTION_STATS(STORE_ATTR_SLOT);
static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size");
@ -7652,6 +7757,7 @@
TARGET(STORE_ATTR_WITH_HINT) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 5;
INSTRUCTION_STATS(STORE_ATTR_WITH_HINT);
static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size");
@ -8046,6 +8152,7 @@
TARGET(TO_BOOL_ALWAYS_TRUE) {
_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;
(void)this_instr;
next_instr += 4;
INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE);
static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size");

View file

@ -194,6 +194,8 @@ static void *opcode_targets[256] = {
&&TARGET_FOR_ITER_LIST,
&&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_TUPLE,
&&TARGET_JUMP_BACKWARD_JIT,
&&TARGET_JUMP_BACKWARD_NO_JIT,
&&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
@ -232,8 +234,6 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_INSTRUMENTED_END_FOR,
&&TARGET_INSTRUMENTED_POP_ITER,
&&TARGET_INSTRUMENTED_END_SEND,

View file

@ -91,70 +91,13 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
instr->op.arg = index;
}
static int
never_optimize(
_PyOptimizerObject* self,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr,
_PyExecutorObject **exec,
int Py_UNUSED(stack_entries),
bool Py_UNUSED(progress_needed))
{
// This may be called if the optimizer is reset
return 0;
}
PyTypeObject _PyDefaultOptimizer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "noop_optimizer",
.tp_basicsize = sizeof(_PyOptimizerObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
};
static _PyOptimizerObject _PyOptimizer_Default = {
PyObject_HEAD_INIT(&_PyDefaultOptimizer_Type)
.optimize = never_optimize,
};
_PyOptimizerObject *
_Py_GetOptimizer(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->optimizer == &_PyOptimizer_Default) {
return NULL;
}
Py_INCREF(interp->optimizer);
return interp->optimizer;
}
static _PyExecutorObject *
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies);
_PyOptimizerObject *
_Py_SetOptimizer(PyInterpreterState *interp, _PyOptimizerObject *optimizer)
{
if (optimizer == NULL) {
optimizer = &_PyOptimizer_Default;
}
_PyOptimizerObject *old = interp->optimizer;
if (old == NULL) {
old = &_PyOptimizer_Default;
}
Py_INCREF(optimizer);
interp->optimizer = optimizer;
return old;
}
int
_Py_SetTier2Optimizer(_PyOptimizerObject *optimizer)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
_PyOptimizerObject *old = _Py_SetOptimizer(interp, optimizer);
Py_XDECREF(old);
return old == NULL ? -1 : 0;
}
static int
uop_optimize(_PyInterpreterFrame *frame, _Py_CODEUNIT *instr,
_PyExecutorObject **exec_ptr, int curr_stackentries,
bool progress_needed);
/* Returns 1 if optimized, 0 if not optimized, and -1 for an error.
* If optimized, *executor_ptr contains a new reference to the executor
@ -164,6 +107,7 @@ _PyOptimizer_Optimize(
_PyInterpreterFrame *frame, _Py_CODEUNIT *start,
_PyStackRef *stack_pointer, _PyExecutorObject **executor_ptr, int chain_depth)
{
assert(_PyInterpreterState_GET()->jit);
// The first executor in a chain and the MAX_CHAIN_DEPTH'th executor *must*
// make progress in order to avoid infinite loops or excessively-long
// side-exit chains. We can only insert the executor into the bytecode if
@ -172,12 +116,10 @@ _PyOptimizer_Optimize(
bool progress_needed = chain_depth == 0;
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(PyCode_Check(code));
PyInterpreterState *interp = _PyInterpreterState_GET();
if (progress_needed && !has_space_for_executor(code, start)) {
return 0;
}
_PyOptimizerObject *opt = interp->optimizer;
int err = opt->optimize(opt, frame, start, executor_ptr, (int)(stack_pointer - _PyFrame_Stackbase(frame)), progress_needed);
int err = uop_optimize(frame, start, executor_ptr, (int)(stack_pointer - _PyFrame_Stackbase(frame)), progress_needed);
if (err <= 0) {
return err;
}
@ -684,6 +626,7 @@ translate_bytecode_to_trace(
}
case JUMP_BACKWARD:
case JUMP_BACKWARD_JIT:
ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target);
_Py_FALLTHROUGH;
case JUMP_BACKWARD_NO_INTERRUPT:
@ -1241,7 +1184,6 @@ int effective_trace_length(_PyUOpInstruction *buffer, int length)
static int
uop_optimize(
_PyOptimizerObject *self,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr,
_PyExecutorObject **exec_ptr,
@ -1299,31 +1241,6 @@ uop_optimize(
return 1;
}
static void
uop_opt_dealloc(PyObject *self) {
PyObject_Free(self);
}
PyTypeObject _PyUOpOptimizer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "uop_optimizer",
.tp_basicsize = sizeof(_PyOptimizerObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_dealloc = uop_opt_dealloc,
};
PyObject *
_PyOptimizer_NewUOpOptimizer(void)
{
_PyOptimizerObject *opt = PyObject_New(_PyOptimizerObject, &_PyUOpOptimizer_Type);
if (opt == NULL) {
return NULL;
}
opt->optimize = uop_optimize;
return (PyObject *)opt;
}
/*****************************************
* Executor management

View file

@ -1306,14 +1306,7 @@ init_interp_main(PyThreadState *tstate)
} else
#endif
{
PyObject *opt = _PyOptimizer_NewUOpOptimizer();
if (opt == NULL) {
return _PyStatus_ERR("can't initialize optimizer");
}
if (_Py_SetTier2Optimizer((_PyOptimizerObject *)opt)) {
return _PyStatus_ERR("can't install optimizer");
}
Py_DECREF(opt);
interp->jit = true;
}
}
}
@ -1665,11 +1658,10 @@ finalize_modules(PyThreadState *tstate)
{
PyInterpreterState *interp = tstate->interp;
// Invalidate all executors and turn off JIT:
interp->jit = false;
#ifdef _Py_TIER2
// Invalidate all executors and turn off tier 2 optimizer
_Py_Executors_InvalidateAll(interp, 0);
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
Py_XDECREF(old);
#endif
// Stop watching __builtin__ modifications

View file

@ -655,11 +655,9 @@ init_interpreter(PyInterpreterState *interp,
}
interp->sys_profile_initialized = false;
interp->sys_trace_initialized = false;
#ifdef _Py_TIER2
(void)_Py_SetOptimizer(interp, NULL);
interp->jit = false;
interp->executor_list_head = NULL;
interp->trace_run_counter = JIT_CLEANUP_THRESHOLD;
#endif
if (interp != &runtime->_main_interpreter) {
/* Fix the self-referential, statically initialized fields. */
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
@ -829,12 +827,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
tstate->_status.cleared = 0;
}
#ifdef _Py_TIER2
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
assert(old != NULL);
Py_DECREF(old);
#endif
/* It is possible that any of the objects below have a finalizer
that runs Python code or otherwise relies on a thread state
or even the interpreter state. For now we trust that isn't

View file

@ -2265,9 +2265,7 @@ sys_activate_stack_trampoline_impl(PyObject *module, const char *backend)
{
#ifdef PY_HAVE_PERF_TRAMPOLINE
#ifdef _Py_JIT
_PyOptimizerObject* optimizer = _Py_GetOptimizer();
if (optimizer != NULL) {
Py_DECREF(optimizer);
if (_PyInterpreterState_GET()->jit) {
PyErr_SetString(PyExc_ValueError, "Cannot activate the perf trampoline if the JIT is active");
return NULL;
}

View file

@ -388,9 +388,7 @@ Python/sysmodule.c - perf_map_state -
Python/sysmodule.c - _PySys_ImplCacheTag -
Python/sysmodule.c - _PySys_ImplName -
Python/sysmodule.c - whatstrings -
Python/optimizer.c - _PyDefaultOptimizer_Type -
Python/optimizer.c - _PyUOpExecutor_Type -
Python/optimizer.c - _PyUOpOptimizer_Type -
Python/optimizer.c - _PyOptimizer_Default -
Python/optimizer.c - _ColdExit_Type -
Python/optimizer.c - Py_FatalErrorExecutor -

Can't render this file because it has a wrong number of fields in line 4.

View file

@ -202,7 +202,7 @@ def generate_tier1_cases(
needs_this = uses_this(inst)
out.emit("\n")
out.emit(f"TARGET({name}) {{\n")
unused_guard = "(void)this_instr;\n" if inst.family is None else ""
unused_guard = "(void)this_instr;\n"
if inst.properties.needs_prev:
out.emit(f"_Py_CODEUNIT* const prev_instr = frame->instr_ptr;\n")
if needs_this and not inst.is_target: