Skip to content

Quick test to see if removing the tier2 interpreter speeds up tier1 #117908

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
7 changes: 5 additions & 2 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -33,6 +33,7 @@ def clear_executors(func):
func.__code__ = func.__code__.replace()


@requires_tier2
@requires_specialization
class TestOptimizerAPI(unittest.TestCase):

Expand Down Expand Up @@ -136,6 +137,7 @@ def get_opnames(ex):


@requires_specialization
@requires_tier2
class TestExecutorInvalidation(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -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):

Expand Down Expand Up @@ -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):

Expand Down
2 changes: 1 addition & 1 deletion Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions Modules/_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
18 changes: 18 additions & 0 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -2364,6 +2365,7 @@ dummy_func(
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
}
#endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_NOTIER2 */
}

pseudo(JUMP) = {
Expand All @@ -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];
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down
20 changes: 20 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -958,6 +962,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
goto error;


#ifndef _Py_NOTIER2

// Tier 2 is also here!
enter_tier_two:
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading