diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 688051bbff7aac..786b5483b38ec6 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -300,7 +300,7 @@ extern int _PyStaticCode_Init(PyCodeObject *co); #define STAT_INC(opname, name) do { if (_Py_stats) _Py_stats->opcode_stats[opname].specialization.name++; } while (0) #define STAT_DEC(opname, name) do { if (_Py_stats) _Py_stats->opcode_stats[opname].specialization.name--; } while (0) #define OPCODE_EXE_INC(opname) do { if (_Py_stats) _Py_stats->opcode_stats[opname].execution_count++; } while (0) -#define CALL_STAT_INC(name) do { if (_Py_stats) _Py_stats->call_stats.name++; } while (0) +#define REAL_CALL_STAT_INC(name) do { if (_Py_stats) _Py_stats->call_stats.name++; } while (0) #define OBJECT_STAT_INC(name) do { if (_Py_stats) _Py_stats->object_stats.name++; } while (0) #define OBJECT_STAT_INC_COND(name, cond) \ do { if (_Py_stats && cond) _Py_stats->object_stats.name++; } while (0) @@ -329,7 +329,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); #define STAT_INC(opname, name) ((void)0) #define STAT_DEC(opname, name) ((void)0) #define OPCODE_EXE_INC(opname) ((void)0) -#define CALL_STAT_INC(name) ((void)0) +#define REAL_CALL_STAT_INC(name) ((void)0) #define OBJECT_STAT_INC(name) ((void)0) #define OBJECT_STAT_INC_COND(name, cond) ((void)0) #define EVAL_CALL_STAT_INC(name) ((void)0) @@ -343,6 +343,9 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); #define RARE_EVENT_STAT_INC(name) ((void)0) #endif // !Py_STATS +// We do a little dance here so we can redefine and restore CALL_STAT_INC +#define CALL_STAT_INC(name) REAL_CALL_STAT_INC(name) + // Utility functions for reading/writing 32/64-bit values in the inline caches. // Great care should be taken to ensure that these functions remain correct and // performant! They should compile to just "move" instructions on all supported diff --git a/Include/internal/pycore_jit.h b/Include/internal/pycore_jit.h index 17bd23f0752be2..c7559602a350ba 100644 --- a/Include/internal/pycore_jit.h +++ b/Include/internal/pycore_jit.h @@ -11,6 +11,10 @@ extern "C" { #ifdef _Py_JIT +#ifdef _Py_NOTIER2 +# error "can't define _Py_JIT and _Py_NOTIER2 at the same time" +#endif + typedef _Py_CODEUNIT *(*jit_func)(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState *tstate); int _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size_t length); diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index be3f93ab2e5fd1..7eac1ccdbb0668 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1188,6 +1188,10 @@ def requires_specialization(test): _opcode.ENABLE_SPECIALIZATION, "requires specialization")(test) +def requires_tier2(test): + return unittest.skipIf(_opcode.NOTIER2, "requires tier2")(test) + + #======================================================================= # Check for the presence of docstrings. diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 28d18739b6d4a5..58192422bb359f 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -7,7 +7,7 @@ import _opcode -from test.support import script_helper, requires_specialization, import_helper +from test.support import script_helper, requires_specialization, requires_tier2, import_helper _testinternalcapi = import_helper.import_module("_testinternalcapi") @@ -33,6 +33,7 @@ def clear_executors(func): func.__code__ = func.__code__.replace() +@requires_tier2 @requires_specialization class TestOptimizerAPI(unittest.TestCase): @@ -136,6 +137,7 @@ def get_opnames(ex): @requires_specialization +@requires_tier2 class TestExecutorInvalidation(unittest.TestCase): def setUp(self): @@ -215,6 +217,7 @@ def f(): @requires_specialization +@requires_tier2 @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUops(unittest.TestCase): @@ -579,6 +582,7 @@ def testfunc(n): @requires_specialization +@requires_tier2 @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUopsOptimization(unittest.TestCase): diff --git a/Makefile.pre.in b/Makefile.pre.in index fd8678cdaf8207..228440fc07d58a 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -100,7 +100,7 @@ CONFIGURE_LDFLAGS= @LDFLAGS@ # command line to append to these values without stomping the pre-set # values. PY_CFLAGS= $(BASECFLAGS) $(OPT) $(CONFIGURE_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) -PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST) -I$(srcdir)/Include/internal -I$(srcdir)/Include/internal/mimalloc +PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST) -I$(srcdir)/Include/internal -I$(srcdir)/Include/internal/mimalloc -D_Py_NOTIER2 # Both CPPFLAGS and LDFLAGS need to contain the shell's value for setup.py to # be able to build extension modules using the directories specified in the # environment variables diff --git a/Modules/_opcode.c b/Modules/_opcode.c index 5350adb456b859..02dc9cf0f1c2ec 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -394,6 +394,14 @@ _opcode_exec(PyObject *m) { if (PyModule_AddIntMacro(m, ENABLE_SPECIALIZATION) < 0) { return -1; } +#ifdef _Py_NOTIER2 + int notier2 = 1; +#else + int notier2 = 0; +#endif + if (PyModule_AddIntConstant(m, "NOTIER2", notier2) < 0) { + return -1; + } return 0; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d6fb66a7be34ac..04978ce4d3de2d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2339,6 +2339,7 @@ dummy_func( CHECK_EVAL_BREAKER(); assert(oparg <= INSTR_OFFSET()); JUMPBY(-oparg); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION _Py_BackoffCounter counter = this_instr[1].counter; if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { @@ -2364,6 +2365,7 @@ dummy_func( ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); } #endif /* ENABLE_SPECIALIZATION */ + #endif /* _Py_NOTIER2 */ } pseudo(JUMP) = { @@ -2377,6 +2379,9 @@ dummy_func( }; tier1 inst(ENTER_EXECUTOR, (--)) { + #ifdef _Py_NOTIER2 + assert(0); + #else CHECK_EVAL_BREAKER(); PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; @@ -2387,23 +2392,28 @@ dummy_func( tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); + #endif // _Py_NOTIER2 } replaced op(_POP_JUMP_IF_FALSE, (cond -- )) { assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); } replaced op(_POP_JUMP_IF_TRUE, (cond -- )) { assert(PyBool_Check(cond)); int flag = Py_IsTrue(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); } @@ -3973,9 +3983,11 @@ dummy_func( assert(PyBool_Check(cond)); int flag = Py_IsTrue(cond); int offset = flag * oparg; + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -3984,9 +3996,11 @@ dummy_func( assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); int offset = flag * oparg; + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -4001,9 +4015,11 @@ dummy_func( Py_DECREF(value); offset = 0; } + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -4018,9 +4034,11 @@ dummy_func( Py_DECREF(value); offset = oparg; } + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } diff --git a/Python/ceval.c b/Python/ceval.c index f718a77fb029cb..eecab34c82cf57 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -678,6 +678,7 @@ extern void _PyUOpPrint(const _PyUOpInstruction *uop); * so consume 3 units of C stack */ #define PY_EVAL_C_STACK_UNITS 2 +#ifndef _Py_NOTIER2 #if defined(_MSC_VER) && defined(_Py_USING_PGO) /* gh-111786: _PyEval_EvalFrameDefault is too large to optimize for speed with PGO on MSVC. Disable that optimization temporarily. If this is fixed @@ -686,6 +687,7 @@ extern void _PyUOpPrint(const _PyUOpInstruction *uop); # pragma optimize("t", off) /* This setting is reversed below following _PyEval_EvalFrameDefault */ #endif +#endif PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) @@ -754,11 +756,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _Py_CODEUNIT *next_instr; PyObject **stack_pointer; +#ifndef _Py_NOTIER2 #ifndef _Py_JIT /* Tier 2 interpreter state */ _PyExecutorObject *current_executor = NULL; const _PyUOpInstruction *next_uop = NULL; #endif +#endif start_frame: if (_Py_EnterRecursivePy(tstate)) { @@ -958,6 +962,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int goto error; +#ifndef _Py_NOTIER2 // Tier 2 is also here! enter_tier_two: @@ -1108,14 +1113,29 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif // _Py_JIT +#endif // _Py_NOTIER2 + +// Undefine the macros we redefined, to avoid using the wrong ones below +#undef LOAD_IP +#undef GOTO_ERROR +#undef ENABLE_SPECIALIZATION +#undef STAT_INC +#undef STAT_DEC +#undef CALL_STAT_INC + +// Restore this one +#define CALL_STAT_INC(name) REAL_CALL_STAT_INC(name) + } #if defined(__GNUC__) # pragma GCC diagnostic pop #elif defined(_MSC_VER) /* MS_WINDOWS */ # pragma warning(pop) +#ifdef _Py_NOTIER2 # pragma optimize("", on) #endif +#endif static void format_missing(PyThreadState *tstate, const char *kind, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a7764b0ec12e10..037d1133b6872d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2501,6 +2501,9 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); + #ifdef _Py_NOTIER2 + assert(0); + #else CHECK_EVAL_BREAKER(); PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; @@ -2511,6 +2514,7 @@ tstate->previous_executor = Py_None; Py_INCREF(executor); GOTO_TIER_TWO(executor); + #endif // _Py_NOTIER2 DISPATCH(); } @@ -3191,9 +3195,11 @@ assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); int offset = flag * oparg; + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -3214,9 +3220,11 @@ Py_DECREF(value); offset = 0; } + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -3237,9 +3245,11 @@ Py_DECREF(value); offset = oparg; } + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -3254,9 +3264,11 @@ assert(PyBool_Check(cond)); int flag = Py_IsTrue(cond); int offset = flag * oparg; + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -3414,6 +3426,7 @@ CHECK_EVAL_BREAKER(); assert(oparg <= INSTR_OFFSET()); JUMPBY(-oparg); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION _Py_BackoffCounter counter = this_instr[1].counter; if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { @@ -3439,6 +3452,7 @@ ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); } #endif /* ENABLE_SPECIALIZATION */ + #endif /* _Py_NOTIER2 */ DISPATCH(); } @@ -4710,9 +4724,11 @@ cond = stack_pointer[-1]; assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); stack_pointer += -1; DISPATCH(); @@ -4743,9 +4759,11 @@ { assert(PyBool_Check(cond)); int flag = Py_IsTrue(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); } stack_pointer += -1; @@ -4777,9 +4795,11 @@ { assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); } stack_pointer += -1; @@ -4796,9 +4816,11 @@ cond = stack_pointer[-1]; assert(PyBool_Check(cond)); int flag = Py_IsTrue(cond); + #ifndef _Py_NOTIER2 #if ENABLE_SPECIALIZATION this_instr[1].cache = (this_instr[1].cache << 1) | flag; #endif + #endif JUMPBY(oparg * flag); stack_pointer += -1; DISPATCH(); diff --git a/configure b/configure index 80403255a814af..06f31b60a2483a 100755 --- a/configure +++ b/configure @@ -1087,6 +1087,7 @@ with_pydebug with_trace_refs enable_pystats with_assertions +enable_tier2 enable_experimental_jit enable_optimizations with_lto @@ -1815,6 +1816,7 @@ Optional Features: --disable-gil enable experimental support for running without the GIL (default is no) --enable-pystats enable internal statistics gathering (default is no) + --disable-tier2 disable tier 2 interpreter support (default is no) --enable-experimental-jit build the experimental just-in-time compiler (default is no) @@ -8208,6 +8210,34 @@ else printf "%s\n" "no" >&6; } fi +# Check for --disable-tier2 +# --disable-tier2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --disable-tier2" >&5 +printf %s "checking for --disable-tier2... " >&6; } +# Check whether --enable-tier2 was given. +if test ${enable_tier2+y} +then : + enableval=$enable_tier2; if test "x$enable_tier2" = xyes +then : + disable_tier2=no +else $as_nop + disable_tier2=yes +fi +else $as_nop + disable_tier2=no + +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $disable_tier2" >&5 +printf "%s\n" "$disable_tier2" >&6; } + +if test "$disable_tier2" = "yes" +then + +printf "%s\n" "#define _Py_NOTIER2 1" >>confdefs.h + +fi + # Check for --enable-experimental-jit: { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-experimental-jit" >&5 printf %s "checking for --enable-experimental-jit... " >&6; } diff --git a/configure.ac b/configure.ac index ec925d4d4a0a5a..1afb75e6404136 100644 --- a/configure.ac +++ b/configure.ac @@ -1762,6 +1762,21 @@ else AC_MSG_RESULT([no]) fi +# Check for --disable-tier2 +# --disable-tier2 +AC_MSG_CHECKING([for --disable-tier2]) +AC_ARG_ENABLE([tier2], + [AS_HELP_STRING([--disable-tier2], [disable tier 2 interpreter support (default is no)])], + [AS_VAR_IF([enable_tier2], [yes], [disable_tier2=no], [disable_tier2=yes])], [disable_tier2=no] +) +AC_MSG_RESULT([$disable_tier2]) + +if test "$disable_tier2" = "yes" +then + AC_DEFINE([_Py_NOTIER2], [1], + [Define if you want to disable the tier 2 interpreter entirely]) +fi + # Check for --enable-experimental-jit: AC_MSG_CHECKING([for --enable-experimental-jit]) AC_ARG_ENABLE([experimental-jit], diff --git a/pyconfig.h.in b/pyconfig.h.in index e28baef51d5737..58374f94535476 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1923,6 +1923,9 @@ /* framework name */ #undef _PYTHONFRAMEWORK +/* Define if you want to disable the tier 2 interpreter entirely */ +#undef _Py_NOTIER2 + /* Define to force use of thread-safe errno, h_errno, and other functions */ #undef _REENTRANT